为什么包含带有外部variables和funcs的h文件会导致未定义的引用

如果我想要在运行时用dlopen解决这些外部事件呢?

我试图理解为什么包括一个h文件,共享库外部variables和funcs,到C可执行程序导致未定义/未解决。 (链接时)

为什么我必须添加“-lsomelib”标志到gcc链接,如果我只希望这些符号在运行时被parsing。

链接时间链接器需要什么这些定义解决scheme。 为什么不能在运行时使用dlopen等待分辨率。

任何人都可以帮我理解这个吗?

这里有一些可能有助于理解的东西:有三种类型的链接:

  • 静态链接(.a):编译器在链接时将库的内容包含到您的代码中,以便您可以将代码移动到具有相同体系结构的其他计算机并运行它。
  • 动态链接(.so):编译器在链接时解析符号(编译期间); 但是不包含可执行文件中的库的代码。 当程序启动时,库被加载。 如果图书馆没有找到程序停止。 您需要运行该程序的计算机上的库
  • 动态加载:您负责在运行时加载库函数,使用dlopen等。专门用于插件

另请参阅: http : //www.ibm.com/developerworks/library/l-dynamic-libraries/和共享对象(.so),静态库(.a)和DLL(.so)之间的区别?

头文件(例如某个#include指令引用的*.h文件)与C或C ++ 编译器相关 。 链接器不知道源文件 (输入到编译器),而只知道汇编器生成的目标文件 ( 可执行文件和可链接的格式 ,即ELF)

库文件(由-lfoo )仅在链接时相关。 编译器不了解库。

动态链接器需要知道哪些库应该链接。 在运行时,它执行符号解析(针对固定和已知的一组共享库)。 动态链接器不会尝试链接系统中所有可能的共享库(因为它有太多的共享对象,或者因为它可能有一个给定库的几个冲突版本),它只会链接一组固定的库在可执行文件里面 使用objdump(1) & readelf(1) & nm(1)探索ELF对象文件和可执行文件,使用ldd(1)来了解共享库依赖关系。

请注意, g++程序既用于编译,也用于链接。 (实际上它是一个驱动程序:它启动了一些cc1plus C ++编译器 – 将C ++代码编译成一个汇编文件,一些汇编程序 – 将一个汇编文件汇编成一个目标文件,以及一些链接器- 链接目标文件和库 )。

运行g++作为g++ -v来理解它在做什么,即运行什么程序。

如果不链接所需的库,则在链接时,某些引用仍然未解析(因为某些对象文件包含外部引用和重定位 )。

(事情稍微复杂些,我们可以忽略链接时间优化

另请参阅程序库HowTo , Levine的书籍链接器和装载器以及Drepper的论文: 如何编写共享库

如果您在运行时使用动态加载 (通过在某个插件上使用dlopen(3) ),您需要知道相关函数的类型和签名(由dlsym(3)返回)。 程序加载插件总是有其特定的插件约定。 例如,看看用于geany插件和GCC插件的约定(另请参阅这些有关GCC插件的幻灯片 )。

实际上,如果你正在开发你的应用程序接受一些插件,你将定义一组名称,它们的期望类型,签名和角色。 例如

  typedef void plugin_start_function_t (const char*); typedef int plugin_more_function_t (int, double); 

然后声明例如一些变量(或数据结构中的字段)以命名约定指向它们

  plugin_start_function_t* plustart; // app_plugin_start in plugins #define NAME_plustart "app_plugin_start" plugin_more_function_t* plumore; // app_plugin_more in plugins #define NAME_plumore "app_plugin_more" 

然后加载插件并设置这些指针,例如

  void* plugdlh = dlopen(plugin_path, RTLD_NOW); if (!plugdlh) { fprintf(stderr, "failed to load %s: %s\n", plugin_path, dlerror()); exit(EXIT_FAILURE; } 

然后检索符号:

  plustart = dlsym(plugdlh, NAME_plustart); if (!plustart) { fprintf(stderr, "failed to find %s in %s: %s\n", NAME_plustart, plugin_path, dlerror(); exit(EXIT_FAILURE); } plumore = dlsym(plugdlh, NAME_plumore); if (!plumore) { fprintf(stderr, "failed to find %s in %s: %s\n", NAME_plumore, plugin_path, dlerror(); exit(EXIT_FAILURE); } 

然后适当使用plustartplumore函数指针。

在你的插件中,你需要编码

 extern "C" void app_plugin_start(const char*); extern "C" int app_plugin_more (int, double); 

并给他们两个定义。 插件应该被编译为位置独立的代码 ,例如

  g++ -Wall -fPIC -O -g pluginsrc1.c -o pluginsrc1.pic.o g++ -Wall -fPIC -O -g pluginsrc2.c -o pluginsrc2.pic.o 

并与之挂钩

  g++ -shared pluginsrc1.pic.o pluginsrc2.pic.o -o yourplugin.so 

您可能想要将额外的共享库链接到您的插件。

你通常应该链接你的主程序(一个加载插件)和-rdynamic链接标志(因为你想让你的主程序的一些符号对你的插件是可见的)。

另请阅读C ++ dlopen mini howto