与具有依赖关系的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)。