【问题标题】:Why including an h file with external vars and funcs results in undefined references为什么包含带有外部变量和函数的 h 文件会导致未定义的引用
【发布时间】:2014-05-12 19:09:14
【问题描述】:

如果我希望在运行时使用 dlopen 解析这些外部变量怎么办?

我试图理解为什么在 C 可执行程序中包含一个带有共享库外部变量和函数的 h 文件会导致未定义/未解析。 (链接时)

如果我只想在运行时解析这些符号,为什么我必须在 gcc 链接中添加“-lsomelib”标志。

链接时链接器需要这些定义解析来做什么。为什么使用 dlopen 时不能等待运行时的解析。

谁能帮我理解这个?

【问题讨论】:

  • 我的问题不是关于共享库,而是更多关于从 h 文件中解析外部。如果有时我只想使用动态链接并且无论如何只能在运行时解析符号,为什么我总是必须向 gcc 提供符号定义(使用“-lthelib”)?
  • 我开始阅读这篇关于共享库的 47 页论文。这很有趣,并且可能回答了我关于这个过程的许多问题。但与此同时... :)

标签: c linux gcc extern dlopen


【解决方案1】:

以下内容可能有助于理解: 有 3 种链接类型:

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

另请参阅:http://www.ibm.com/developerworks/library/l-dynamic-libraries/Difference between shared objects (.so), static libraries (.a), and DLL's (.so)?

【讨论】:

  • 是的,但我要问的是:为什么动态链接/加载在编译/链接过程中需要任何解析,而不仅仅是在运行机器上的运行时。链接器做什么需要符号的任何定义/解析,如果无论如何这是在运行时使用正在运行的机器上的共享库完成的?
  • 动态链接需要确保你正在调用的函数存在于某个地方,因为系统会为你做所有事情。那是他们需要解决的时候。对于动态加载,我从来没有使用过,我真的帮不上忙。但据我了解,我认为您不需要在链接时解决
  • 同样从我提供的链接之一 (ibm.com/developerworks/library/l-dynamic-libraries) 用于在 linux 系统中动态加载,您只链接用于在运行时打开 lib 和加载函数的系统库。跨度>
  • 没关系。但是为什么我必须解决我包含的 h 文件中的外部问题。如果我不添加“-lhfileSymbolsResolutionLib”,则 gcc 操作会导致错误!!!
【解决方案2】:

头文件(例如,某些 #include 指令引用的 *.h 文件)与 C 或 C++ compiler 相关。 linker 不知道source files(这是编译器的输入),而只知道assembler 产生的object files(在executable and linkable format 中,即ELF)

库文件(由-lfoo 提供)仅在链接 时相关。编译器不知道库。

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

请注意g++ 程序用于编译和链接。 (实际上它是一个驱动程序:它启动一些 cc1plus -C++ 编译器 - 将 C++ 代码编译为汇编文件,一些 as - 汇编器 - 将汇编文件汇编成目标文件,还有一些ld -链接器-链接object fileslibraries)。

g++ -v 运行g++ 以了解它在做什么,即它正在运行什么程序。

如果您不链接所需的库,则在链接时,某些引用仍未解析(因为某些目标文件包含外部引用和relocation)。

(使用链接时间优化会稍微复杂一些,我们可以忽略)

另请阅读 Program Library HowToLevine's book linkers and loadersDrepper's paper: how to write shared libraries

如果你在运行时使用dynamic loading(通过在某些插件上使用dlopen(3)),你需要知道相关函数的类型和签名(由dlsym(3)返回)。加载插件的程序总是有其特定的插件约定。有关示例,请查看用于 geany pluginsGCC plugins 的约定(另请参阅 these slides 关于 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);

并给他们两个下一个定义。该插件应编译为position independent code,例如与

 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

【讨论】:

  • 谢谢,但 .h 文件引入了外部全局变量和函数,这些变量和函数稍后(在链接中)会创建“未解决/未定义”错误
  • 我可能在这里错过了一些东西。你说“如果你不链接所需的库,在链接时,一些引用仍未解决”,我问:如果它们仍未解决怎么办?这不就是动态链接的全部意义吗?直到运行时才解决?
  • 不,运行时链接是针对一组已知库的,您需要将它们提供给g++
  • dlopen 可以打开我给它的任何库。已知或未知。所以仍然不清楚为什么 gcc/g++ 需要知道关于稍后与 dlopen 一起使用的库的知识。如果我不包含带有 funcs 和全局变量的 h 文件,那么我不需要添加任何 -l 选项,因为在 dlopen 之前无需解决任何问题。
  • 不,dlopen 正在打开一个 fixedknown 库,如其第一个参数中所述。按照我给你的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-17
  • 2011-11-21
  • 2020-01-31
  • 2018-12-31
  • 1970-01-01
  • 2016-09-18
  • 2018-08-14
相关资源
最近更新 更多