2012-06-15 104 views
52

考虑以下情形:动态链接库具有依赖性

  • 共享库libA.so,无依赖性。
  • 共享库libB.so,libA.so作为其依赖项。

我想编译一个与libB链接的二进制文件。 我应该只用libB还是用libA链接二进制文件?

是否有任何方法只与直接依赖链接,让从运行时依赖的解决未解决的符号?

我担心库libB实现可能会在将来发生变化,引入其他依赖关系(例如libC,libD,libE)。我会遇到问题吗?

换句话说:

  • 力霸文件:a.cpp啊
  • libB文件:b.cpp BH
  • 主程序文件:main.cpp中

当然, b.cpp包含ah和main.cpp包含bh

编译命令:

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的未解析符号。但这听起来有点奇怪。

非常感谢。

- 更新注释:

当我链接的二进制文件,链接器将尝试解决从主和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方法看起来好多了。

再次感谢。

回答

27

看起来你已经是大部分路线了。你的调查做得很好。让我们看看我能否帮助理清它背后的“为什么”。

以下是链接器正在做的事情。当你连接你的可执行文件('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

我知道这是很久以前,我忘记标记为已解决。非常感谢您的回答。] – Marcus

+0

谢谢大家的惊人的问题,和一个合适的答案!我有几乎完全相同的情况。我试图将openCV共享对象包装在自定义共享对象中。可以说C.so是我的习惯,所以CV.so就是一些openCV。我已经成功地链接了C.so和CV.so,并且我想将一个main.cpp(你可以!)链接到C.so,但是除了使用C.so的对象之外,main.cpp使用了一个在CV.so中定义的对象'cv :: Mat'。在这种情况下,main.cpp运行时是否需要链接C.so和CV.so?如果有人对此有所了解,我很乐意。谢谢! :) –

+0

为什么链接程序在生成libB.so时不能解析所有需要的libA.so符号? 主应用程序不应链接到libA,因为它应该是透明的,例如,间接由libB屏蔽? – Wilson

11

动态链接只能用直接的依赖以后还可以-Wl,--as-needed使用-Wl,--as-needed与添加库

gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA 

为了检查直接依赖你应该使用readelf而不是ldd因为ldd al因此显示间接依赖关系。

$ 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}") 
+0

对不起,我已将您的示例作为c程序而不是C++。因此我使用了gcc编译器。但它也适用于g ++。 – user1047271

+0

这是非常有用的信息。我不知道使用redelf而不是ldd。非常感谢。 – Marcus

1

另一种选择是使用libtool

如果更改g++调用libtool --mode=compile g++编译源代码,然后libtool --mode=link g++创造过的libB的应用程序,然后libA将自动链接。

0

这是一个有趣的帖子 - 我敲我的头,这为好,但我觉得你错过这里的一个点..

的思路如下,对不对?

main.cpp =(depends)=> libB.so =(depends)=> libA.so 

让我们进一步考虑..

  • 在a.cpp(只有那里)你定义一个类/变量,我们把它称为 “司马”
  • 在b.cpp(和只有那里)你定义了一个类/变量,我们称之为“symB”。
  • SYMB使用司马
  • main.cpp中使用SYMB

现在,如果上述libB.so和libA.so已经编译。在这之后,你的第一选择应该工作,即:

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

我猜你的问题的事实,

在main.cpp中还指司马

上午起源我纠正?

如果您在代码中使用一个符号,那么该符号必须在.so文件发现

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

+0

你误解了。显然你需要解决直接需要的符号。问题与您的依赖可能需要的符号有关。开发库供第三方使用时,图书馆用户必须为您的图书馆使用的功能提供实现,这是很常见的。你使用一个存根进行开发,完全没有依赖关系,集成者将开发一个库来替换存根,然后将所有东西链接在一起。加载器必须解决所有的依赖关系。然而,你的编译脚本(和你的用户)必须考虑它。 – Marcus