Gcc 链接具有依赖项的动态库
考虑以下场景:Gcc 链接具有依赖项的动态库,gcc,dll,dependencies,g++,Gcc,Dll,Dependencies,G++,考虑以下场景: 共享库libA.so,没有依赖项 共享库libB.so,其依赖项为libA.so 我想编译一个与libB链接的二进制文件。 我应该只使用libB链接二进制文件还是使用libA链接二进制文件 有没有办法只与直接依赖项链接,让运行时从依赖项中解析未解析的符号 我担心库libB实现将来可能会改变,引入其他依赖项(例如libC、libD、libE)。我会有问题吗 换言之: libA文件:a.cpp a.h libB文件:b.cpp b.h 主要程序文件:main.cpp 当然,b
- 共享库libA.so,没有依赖项
- 共享库libB.so,其依赖项为libA.so
- libA文件:a.cpp a.h
- libB文件:b.cpp b.h
- 主要程序文件:main.cpp
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
或
我不能使用第一个选项。链接器抱怨库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
不应用于此目的。强制在给定目录中找到库是很有用的。-未解析符号
方法看起来更好
再次感谢。看来你已经完成了大部分工作。你的调查做得很好。让我们看看我能不能帮你弄清楚背后的原因 下面是链接器正在做的事情。当你链接你的可执行文件('main'在上面)时,它有一些未解析的符号(函数和其他东西)。它将查找下面的库列表,试图解析未解析的符号。在此过程中,它发现一些符号是由libB.so提供的,因此它注意到它们现在由这个库解析 但是,它还发现其中一些符号使用了在可执行文件中尚未解析的其他符号,因此它现在也需要解析这些符号。如果不链接libA.so,您的应用程序将不完整。一旦它链接到libA.so,所有符号都被解析,链接就完成了 如您所见,在共享lib中使用
-未解析符号并不能解决问题。它只是将其延迟,以便在运行时解析这些符号。这就是-rpath
的作用:指定要在运行时搜索的库。如果这些符号无法解析,那么您的应用程序将无法启动
弄清楚库依赖关系不是一件容易的事,因为一个符号可以由多个库提供,并且可以通过与其中任何一个库进行链接而得到满足
这里还有对该过程的另一种描述:对于仅具有直接依赖关系的动态链接,您可以根据需要使用-Wl,--as-needed
并在-Wl,--as-needed
之后添加libs:
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)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
如果使用cmake,则可以添加以下行以仅包含直接依赖项:
$ 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)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
另一个选择是使用
如果您将g++
调用更改为libtool--mode=compile g++
以编译源代码,然后libtool--mode=link g++
以在libB
上创建应用程序,那么libA
将自动链接。这是一篇有趣的帖子-我也用它来敲我的头,但我认为你在这里漏掉了一点
想法如下,对吗
main.cpp =(depends)=> libB.so =(depends)=> libA.so
我们进一步考虑…
- 在a.cpp中(仅在那里)定义一个类/变量,我们称之为“symA”
- 在b.cpp中(仅在这里)定义了一个类/变量,我们称之为“symB”
- symB使用symA
- main.cpp使用符号
现在,libB.so和libA.so已按照上述方式编译。在这之后,您的第一个选项应该有效,即:
g++ main.cpp -o main -I. -L. -lB
我想你的问题源于这样一个事实
在main.cpp中,您还可以参考symA
我说得对吗
如果在代码中使用符号,则必须在.so文件中找到该符号
交互引用共享库(即创建API)的整体思想是,深层中的符号是隐藏的(想想剥皮的洋葱),不使用。
.. i、 e.不要在main.cpp中引用symA,而只引用symB(让symB只引用symA)。我知道这是很久以前的事了,我忘了将其标记为已解决。非常感谢你们的回答。]谢谢你们提出的令人惊讶的问题,以及一个合适的答案!我有几乎完全相同的情景。我正在尝试将openCV共享对象包装到自定义共享对象中。假设C.so是我的自定义so,CV.so是一些openCV so。我已经成功地将C.so链接到了CV.so,我想将main.cpp(你明白了!)链接到C.so,但是除了使用C.so中的对象之外,main.cpp还使用了一个在CV.so中定义的对象“CV::Mat”。。在这种情况下,要运行main.cpp,是否需要链接aga