考虑以下情况:
我想编译一个与libB链接的二进制文件。 我应该仅将二进制文件与libB或libA链接吗?
有没有办法只链接直接依赖项,让运行时依赖项中未解析符号的解析?
我担心库libB实现将来可能会发生变化,引入其他依赖项(例如libC,libD,libE)。我会遇到问题吗?
换句话说:
当然,b.cpp包含a.h,main.cpp包含b.h。
编译命令:
g++ -fPIC a.cpp -c
g++ -shared -o libA.so a.o
g++ -fPIC b.cpp -c -I.
g++ -shared -o libB.so b.o -L. -lA
我应该使用哪种波纹管选项?
g++ main.cpp -o main -I. -L. -lB
或
g++ main.cpp -o main -I. -L. -lB -lA
我无法使用第一个选项。链接器抱怨库libA中未解析的符号。但这听起来有点奇怪。
非常感谢。
- 更新了评论
当我链接二进制文件时,链接器将尝试解析main和libB中的所有符号。但是,libB具有来自libA的未定义符号。这就是链接器抱怨的原因。
这也是我需要与libA联系的原因。 但是我发现了一种忽略共享库中未解析符号的方法。 看起来我应该使用以下命令行来执行此操作:
g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs
看起来仍然可以使用-rpath
选项。
但是我需要更好地理解它。
使用-Wl,-unresolved-symbols=ignore-in-shared-libs
选项时是否有人知道任何可能的陷阱?
- 更新了评论2:
-rpath
不应用于此目的。强制在给定目录中找到库是很有用的。 -unresolved-symbol
方法看起来好多了。
再次感谢。
答案 0 :(得分:37)
看起来你已经完成了大部分工作。你的调查做得好。让我们看看我是否可以帮助清理它背后的“原因”。
这是链接器正在做的事情。当你链接你的可执行文件(上面的'main')时,它有一些未解决的符号(函数和其他东西)。它将查看后面的库列表,尝试解析未解析的符号。在此过程中,它发现某些符号是由libB.so提供的,因此它注意到它们现在由此库解析。
但是,它还发现其中一些符号使用了可执行文件中尚未解析的其他符号,因此现在也需要解析这些符号。如果没有链接libA.so,您的应用程序将是不完整的。一旦链接到libA.so,就会解析所有符号并完成链接。
如您所见,使用-unresolved-symbols-in-shared-libs
并不能解决问题。它只是推迟它,以便在运行时解析这些符号。这就是-rpath
的用途:指定在运行时搜索的库。如果这些符号无法解决,那么您的应用将无法启动。
找出库依赖关系并不容易,因为符号可以由多个库提供,并且可以通过链接任何一个库来满足。
此过程还有另一种说明:Why does the order in which libraries are linked sometimes cause errors in GCC?
答案 1 :(得分:15)
对于仅与直接相关性进行动态链接,您可以使用 -Wl,--as-needed
并在 -Wl,--as-needed
后添加库:
gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA
要检查直接依赖关系,您应该使用 readelf 而不是 ldd ,因为ldd还会显示间接依赖关系。
$ readelf -d main | grep library
0x0000000000000001 (NEEDED) Shared library: [libB.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
ldd还显示了间接依赖关系:
$ LD_LIBRARY_PATH=. ldd ./main
linux-vdso.so.1 (0x00007fff13717000)
libB.so => ./libB.so (0x00007fb6738ed000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000)
libA.so => ./libA.so (0x00007fb6732e8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000)
如果您使用 cmake ,则可以添加以下行以仅包含直接依赖项:
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
答案 2 :(得分:1)
另一种选择是使用libtool
如果您将g++
调用更改为libtool --mode=compile g++
以编译源代码,然后libtool --mode=link g++
更改为libB
创建应用程序,则libA
将自动链接。
答案 3 :(得分:0)
这是一篇有趣的帖子 - 我也是这样想的,但我想你在这里错过了一点......
这个想法如下,对吧?
main.cpp =(depends)=> libB.so =(depends)=> libA.so
让我们进一步考虑......
现在,如上所述编译了libB.so和libA.so。之后,您的第一个选项应工作,即:
g++ main.cpp -o main -I. -L. -lB
我猜你的问题源于
这个事实 在main.cpp中你也可以参考symA
我说错了吗?
如果在代码中使用符号,则必须在.so文件中找到该符号
相互引用共享库(即创建API)的整个想法是,更深层中的符号被隐藏(想到剥洋葱)而不使用。 ..即不要在你的main.cpp中引用symA,而是仅引用symB(并让symB仅引用symA)。