使用依赖项链接动态库

时间:2012-06-15 17:34:41

标签: 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.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方法看起来好多了。

再次感谢。

4 个答案:

答案 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

让我们进一步考虑......

  • 在a.cpp中(只有那里)你定义一个类/变量,我们称之为“symA”
  • 在b.cpp(并且只在那里)你定义一个类/变量,我们称之为“symB”。
  • symB使用symA
  • main.cpp使用symB

现在,如上所述编译了libB.so和libA.so。之后,您的第一个选项工作,即:

g++ main.cpp -o main -I. -L. -lB

我猜你的问题源于

这个事实 在main.cpp中

你也可以参考symA

我说错了吗?

如果在代码中使用符号,则必须在.so文件中找到该符号

相互引用共享库(即创建API)的整个想法是,更深层中的符号被隐藏(想到剥洋葱)而不使用。 ..即不要在你的main.cpp中引用symA,而是仅引用symB(并让symB仅引用symA)。