构建可执行文件时涉及 .so 库时未定义引用错误

Undefined reference to error when .so libraries are involved while building the executable

我有一个 .so 库,在构建它时我没有遇到任何未定义的引用错误。 但是现在我正在使用 .so 文件构建一个可执行文件,我可以在链接阶段看到未定义的引用错误,如下所示:

xy.so: undefined reference to `MICRO_TO_NANO_ULL'

我参考了 this and this 但无法真正理解动态链接。

同时阅读 here 导致更多混乱:

Dynamic linking is accomplished by placing the name of a sharable library in the executable image. Actual linking with the library routines does not occur until the image is run, when both the executable and the library are placed in memory. An advantage of dynamic linking is that multiple programs can share a single copy of the library.

我的问题是:

Doesn't dynamic linking means that when I start the executable using ./executable_name then if the linker not able to locate the .so file on which executable depends it should crash?

是的。如果 .so 文件在 运行 时不存在。

What actually is dynamic linking if all external entity references are resolved while building? Is it some sort of pre-check performed by dynamic linker? Else dynamic linker can make use of LD_LIBRARY_PATH to get additional libraries to resolve the undefined symbols.

它允许升级库并让应用程序仍然能够使用该库,并且它通过加载库的一个副本而不是在每个使用它的应用程序中加载一个副本来减少内存使用。

linker 只是创建对这些符号的引用,以便以后可以使用底层变量或函数。它不会 link 将变量和函数直接放入可执行文件中。

动态 linker 不会引入任何库,除非在可执行文件中指定了这些库(或通过扩展可执行文件所依赖的任何库)。如果您提供一个 LD_LIBRARY_PATH 目录,其中的 .so 文件的版本与可执行文件所需的版本完全不同,可执行文件可能会崩溃。


在您的情况下,似乎没有找到所需的宏定义,并且编译器正在使用隐式声明规则。您可以通过使用 -pedantic -pedantic-errors 编译代码轻松解决此问题(假设您使用的是 GCC)。

编译共享库时似乎缺少 #define。这个错误

xy.so: undefined reference to `MICRO_TO_NANO_ULL'

意思是

#define MICRO_TO_NANO_ULL(sec) ((unsigned long long)sec * 1000)

应该存在,但没有。

然后编译器假定它是一个外部函数并为其创建一个(未定义的)符号,而它应该在编译时由预处理器宏解析。

如果您包含了正确的文件(宏名称的 grep)或在源文件的顶部放置了适当的定义,那么链接器错误应该会消失。

Doesn't dynamic linking means that when I start the executable using ./executable_name then if the linker not able to locate the .so file on which executable depends it should crash?

它会崩溃。崩溃的时间确实取决于您从 .so 文件中调用某个导出函数的方式。 您可以使用 dlopen dlysm 和 co 自己通过函数指针检索所有导出的函数。在这种情况下,如果找不到导出的方法,程序将在第一次调用时崩溃。

如果可执行文件只是从共享 object(它的一部分是 header)调用导出的方法,动态链接器会使用要在其可执行文件中调用的方法的信息(请参阅第二个答案)并在找不到库或符号不匹配的情况下崩溃。

What actually is dynamic linking if all external entity references are resolved while building? Is it some sort of pre-check performed by dynamic linker? Else dynamic linker can make use of LD_LIBRARY_PATH to get additional libraries to resolve the undefined symbols.

您需要区分实际链接和动态链接。从实际链接开始: 在链接静态库的情况下,实际链接将从使用它的 executable/library 中要调用的方法中复制所有代码。 链接动态库时,您不会复制代码,而是复制符号。这些符号包含指向动态库中实际代码的偏移量或其他信息。如果可执行文件确实调用了动态库未导出的方法,则它在实际链接部分已经失败。

现在,当启动您的可执行文件时,OS 会在某个时候尝试将共享 object 加载到代码实际所在的内存中。如果它没有找到它,或者如果它是不可行的(即:可执行文件链接到使用不同导出的库),它可能仍然会在运行时失败。

Doesn't dynamic linking means that when I start the executable using ./executable_name then if the linker not able to locate the .so file on which executable depends it should crash?

不,链接器将退出并显示 "No such file or directory" 消息。

想象一下:

  • 您的可执行文件在某处存储了它需要的共享库列表。
  • 链接器,把它当作一个普通的程序。
  • 链接器打开您的可执行文件。
  • 链接器读取此列表。对于每个文件。
    • 它试图在链接器路径中找到这个文件。
    • 如果它找到文件,它 "loads" 它。
    • 如果找不到文件,它会从 open() 调用中获取 errnoNo Such file or directory。然后打印一条消息,指出找不到库并终止您的可执行文件。
  • 当 运行 连接可执行文件时,链接器会在共享库中动态搜索符号。
    • 当它找不到符号时,它会打印一些消息并且可执行文件终止。

例如,您可以设置 LD_DEBUG=all 来检查链接器正在做什么。您还可以在 strace 下检查您的可执行文件以查看所有 open 调用。

What actually is dynamic linking if all external entity references are resolved while building?

动态链接是当您 运行 可执行文件然后链接器加载每个共享库时。

构建时,您的编译器会很友好地为您检查,您在程序中使用的所有符号都存在于共享库中。这只是为了安全。例如,您可以使用 ex 禁用此检查。 --unresolved-symbols=ignore-in-shared-libs.

Is it some sort of pre-check performed by dynamic linker?

是的。

Else dynamic linker can make use of LD_LIBRARY_PATH to get additional libraries to resolve the undefined symbols.

LD_LIBRARY_PATH 只是一个以逗号分隔的路径列表,用于搜索共享库。 LD_LIBRARY_PATH 中的路径只是在标准路径之前处理。就这样。它没有 "additional libraries",它有额外的路径来搜索库 - 库保持不变。