与具有依赖关系的dynamic库链接
考虑以下情况:
- 共享库libA.so,没有依赖关系。
- 共享库libB.so,libA.so作为其依赖项。
我想编译一个与libB链接的二进制文件。 我应该只与libB或与libA二进制链接?
有没有什么办法只与直接依赖链接,让从运行时的依赖解决未解决的符号?
我担心库libB实现可能会在将来发生变化,引入其他依赖(例如,libC,libD,libE)。 我会遇到问题吗?
换一种说法:
- libA文件:a.cpp啊
- libB文件:b.cpp bh
- 主程序文件:main.cpp
当然,b.cpp包括啊,main.cpp包含bh
编译命令:
g++ -fPIC a.cpp -c g++ -shared -o libA.so ao g++ -fPIC b.cpp -c -I. g++ -shared -o libB.so bo -L. -lA
我应该使用哪个下面的选项?
g++ main.cpp -o main -I. -L. -lB
要么
g++ main.cpp -o main -I. -L. -lB -lA
我不能使用第一个选项。 链接器抱怨库libA中未parsing的符号。 但是这听起来有点奇怪。
非常感谢。
– 更新评论:
当我链接二进制文件时,链接器将尝试parsingmain和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
不应该用于这个目的。 强制在给定目录中find库是有用的。 -unresolved-symbol
方法看起来好多了。
再次感谢。
它看起来像你已经在那里大部分的方式。 做好你的调查。 让我们看看我能否帮助理清它背后的“为什么”。
这是链接器正在做的事情。 当你链接你的可执行文件(上面的'main')时,它有一些未解决的符号(函数和其他东西)。 它会查看下面的库列表,试图解决未解决的符号。 一路上,它发现一些符号是由libB.so提供的,所以它指出现在这个库已经解决了。
但是,它也会发现其中一些符号使用了其他符号,这些符号在您的可执行文件中尚未parsing,所以现在也需要解决这些符号。 没有链接反对libA.so,您的应用程序将不完整。 一旦链接到libA.so,所有的符号被parsing,链接完成。
如您所见,使用-unresolved-symbols-in-shared-libs
不能解决问题。 它只是推迟它,以便这些符号在运行时解决。 这就是-rpath
的作用:在运行时指定要search的库。 如果这些符号无法parsing,那么您的应用程序将无法启动。
找出库依赖关系并不是一件容易的事情,因为一个符号可以由多个库提供,并且可以通过与其中任何一个链接来满足。
这里有另外一个关于这个过程的描述: 为什么库链接的顺序有时会导致GCC错误?
对于只有直接依赖关系的dynamic链接,您可以使用-Wl,--as-needed
在-Wl 之后添加库-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}")
另一个select是使用libtool
如果将g++
调用改为libtool --mode=compile g++
编译源代码,然后libtool --mode=link g++
从libB
创build应用程序,那么libA
将自动链接。
这是一个有趣的post – 我也用这个敲了敲头,但是我想你在这里错过了一点。
这个想法如下,对不对?
main.cpp =(depends)=> libB.so =(depends)=> libA.so
我们进一步考虑一下..
- 在a.cpp(并且只有那里)你定义一个类/variables,我们称之为“symA”
- 在b.cpp中(只有那里)你定义一个类/variables,我们称之为“symB”。
- symB使用symA
- main.cpp使用symB
现在,libB.so和libA.so已经被编译,如上所述。 之后,你的第一个select应该工作,即:
g++ main.cpp -o main -I. -L. -lB
我想你的问题来自这个事实
在main.cpp中你也可以参考symA
我对么?
如果您在代码中使用了符号,则必须在.so文件中find该符号
相互引用共享库(即创buildAPI)的整个想法是,更深层次中的符号是隐藏的(想到剥洋葱)而不使用。 ..也就是说,不要在你的main.cpp中引用symA,而只是在symB中引用(而symB只能引用symA)。