与具有依赖项的动态库链接

考虑以下场景:

  • 共享库 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方法看起来要好得多。

再次感谢。

54934 次浏览

It looks like you are most of the way there already. Well done with your investigation. Let's see if I can help clear up the 'why' behind it.

Here's what the linker is doing. When you link your executable ('main' above) it has some symbols (functions and other things) that are unresolved. It will look down the list of libraries that follow, trying to resolve unresolved symbols. Along the way, it finds that some of the symbols are provided by libB.so, so it notes that they are now resolved by this library.

However, it also discovers that some of those symbols use other symbols that are not yet resolved in your executable, so it now needs to resolve those as well. Without linking against libA.so, your application would be incomplete. Once it links against libA.so, all symbols are resolved and linking is complete.

As you saw, the use of -unresolved-symbols-in-shared-libs, doesn't fix the problem. It just defers it so that those symbols are resolved at run time. That's what -rpath is for: to specify the libraries to be searched at run time. If those symbols can't be resolved then, your app will fail to start.

It's not an easy thing to figure out library dependencies because a symbol could be provided by more than one library and be satisfied by linking against any one of them.

There is another description of this process here: Why does the order in which libraries are linked sometimes cause errors in GCC?

For dynamic linking only with direct dependencies you can use -Wl,--as-needed with adding the libs after -Wl,--as-needed:

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

For checking the direct dependencies you should use readelf instead of ldd because ldd also shows the indirect dependencies.

$ readelf -d main | grep library
0x0000000000000001 (NEEDED)             Shared library: [libB.so]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

ldd also shows the indirect dependencies:

$ 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)

If you use cmake, you can add the following lines to include only direct dependencies:

set(CMAKE_EXE_LINKER_FLAGS    "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")

Another option is to use libtool

If you change the g++ call to libtool --mode=compile g++ to compile the source code and then libtool --mode=link g++ to create the application off of libB, then libA will be linked automatically.

This is an interesting post - I was banging my head with this as well, but I think you miss a point here..

The idea is as follows, right ?

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

Let's further consider that ..

  • In a.cpp (and only there) you define a class / variable, let's call it "symA"
  • In b.cpp (and only there) you define a class / variable, let's call it "symB".
  • symB uses symA
  • main.cpp uses symB

Now, libB.so and libA.so have been compiled as you described above. After that, your first option should work, i.e.:

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

I guess that your problem originates from the fact that

in main.cpp you also refer to symA

Am I correct?

If you use a symbol in your code, then that symbol must be found in an .so file

The whole idea of inter-referencing shared libraries (i.e. creating APIs), is that the symbols in the deeper layers are hidden (think of peeling onions) and not used. .. i.e. don't refer to symA in your main.cpp, but only to symB instead (and let symB to refer symA only).