如何解决库链接出现符号重定义问题

下面是一组和GPT的对话,通过逐个排查,我们知道只有一种情况会导致符号重定义。以下假设在 CMake 配置环境。

Q:如果一个静态库a,链接了b,这个时候 c 链接了 ab ,会导致 b 符号重复定义么?

A:
任何库(静态或动态)本身都不链接其他库。他们可能有对其他库的依赖性,但实际上并不包含其他库的代码。

在你给出的情况下,假设你有三个静态库ab和一个可执行目标c

a使用了库b中的一些符号(函数或变量);
目标c链接了库a和库b
在这种情况下,不会出现符号重复定义的问题,因为库a并不会“包含”库b,只是有对它的依赖性。链接器在链接可执行文件c时,它会从库a和库b中提取所需的对象文件,如果同一符号在两个库中都找到了,链接器会选择其中一个(具体选择哪个取决于链接器的行为)。

然而需要注意的是,静态库的链接顺序是重要的。在上面的例子中,由于库a依赖于库b,所以在链接目标c时,应该先链接库a,然后再链接库b,这样链接器才能正确解析库a中对库b的依赖。以下是在CMakeLists.txt中进行链接的示例:

target_link_libraries(c a b)

如果链接命令写成了 target_link_libraries(c b a),那么可能会出现未定义的符号错误。这是因为当链接器处理库b时,它并不知道后面还有对这些符号的引用。

Q: 如果一个静态库a,链接了b,这个时候可执行程序 c 链接了 ab ,会导致 b 符号重复定义么?

A:
在你的情况下,即使静态库a有对静态库b的依赖性,并且可执行程序c同时链接了库a和库b,通常也不会出现符号重复定义的问题。

这是因为静态库并没有真正地“链接”其他静态库。当静态库a被编译时,它可能需要库b中的符号(例如函数或变量),但是库b的代码并没有被合并到库a中,库a只是有对库b的依赖性。

然后,当链接可执行程序c时,链接器将查看c以及所有传递的库(在本例中为ab)所需的所有符号,并从这些库中拉取需要的对象文件来满足这些需求。这意味着每个必要的符号只会被包含一次,而不会被重复包含。

请注意,虽然不会出现符号重复定义的问题,但可以会出现未解析的符号错误。这可能发生在库b先于库a被链接时。由于库a依赖库b,库b应该在库a之后链接,以便链接器能正确解析所有符号。所以正确的链接顺序应该是:

target_link_libraries(c a b)

而不是:

target_link_libraries(c b a)

Q: 如果一个动态库 a 链接了 b,这个时候可执行程序 c 链接了 ab,会导致 b 符号重复定义么?

A:
在你的情况下,动态库(Shared Library)a 依赖于另一个动态库 b,而可执行程序c同时链接了ab,这通常不会导致符号重复定义的问题。

对于动态库来说,它们在编译时并不包含其依赖库的代码。相反,动态库在运行时才解析它们的依赖关系。也就是说,如果动态库a依赖于动态库b,那么当程序加载库a时,操作系统(通过动态链接器)将负责加载库b(如果尚未加载)。

因此,即使可执行程序c同时链接了ab,也只有一份库b的实例被加载到内存中。库ac中引用库b的符号都将解析到这同一份实例,所以不会有符号重复定义的问题。

然而,请注意确保动态库的正确路径,特别是在运行可执行程序c时,库ab必须能够被找到。一般来说,这需要将它们放置在标准库路径下,或者修改环境变量(如LD_LIBRARY_PATH在Linux下,DYLD_LIBRARY_PATH在MacOS下),以包含这些库的路径。

Q: 如果一个动态库a 链接了静态库 b,这个时候可执行程序 c 链接了 ab,会导致 b 符号重复定义么?

A:
在你的情况下,动态库 a 链接了静态库 b ,然后可执行程序 c 又链接了动态库 a 和静态库 b 。这种情况可能会导致符号重复定义。

当一个动态库(如 a )链接一个静态库(如 b )时,静态库 b 中的所有对象代码实际上都会被包含到动态库 a 中。所以当你分别将动态库 a 和静态库 b 链接到可执行文件 c 时,因为动态库 a 已经包含了静态库 b 的所有代码,所以相同的符号实际上被包含了两次,这就可能导致符号重复定义。

解决这个问题的一种方法是只在一个地方链接静态库 b。也就是说,要么只在动态库 a 中链接静态库 b(然后在可执行程序 c 中只链接动态库 a ),要么在动态库 a 和可执行程序 c 中都不链接静态库 b(而是只在链接可执行程序 c 时链接静态库 b )。这样可以确保静态库 b 的代码只被包含一次,从而避免符号重复定义。

--end--

posted @ 2023-09-21 13:18  ffl  阅读(494)  评论(0编辑  收藏  举报