使用未定义符号的共享库的Linux共享库

18

有两个共享库liba.so和libb.so。 liba.so使用了libb.so。 所有c文件都使用-fPIC编译。 链接使用-shared。 当我们在liba.so上调用dlopen时,它无法找到libb.so中的符号...我们会得到“未定义的符号”错误。 我们可以无错误地dlopen libb.so。 我们知道liba正在找libb,因为我们没有得到文件未找到错误。 删除libb.so时我们会得到文件未找到错误。 我们尝试过-lutil但没有运气。

任何想法吗?

哦,是的。 gcc 4.1.2

更新:我们在链接liba时使用rpath以便它可以找到libb。

ldd liba.so返回:

linux-gate.so.1 => (0xffffe000)  
libb.so => ./libb.so (0xf6ef9000)  <-------- LIBB 
libutil.so.1 => /lib/libutil.so.1 (0xf6ef5000)  
libdl.so.2 => /lib/libdl.so.2 (0xf6ef1000)  
libm.so.6 => /lib/libm.so.6 (0xf6ec9000)  
libpthread.so.0 => /lib/libpthread.so.0 (0xf6eb1000)  
librt.so.1 => /lib/librt.so.1 (0xf6ea8000)  
libc.so.6 => /lib/libc.so.6 (0xf6d62000)  
/lib/ld-linux.so.2 (0x007d0000)   

在 libb 的末尾没有 .# 是有意义的吗?


2
你的意思是:你创建了两个库(-fPIC -shared),liba.so和libb.so。liba.so与libb.so动态链接(或应该是这样...)并使用它。在程序X中,您尝试对libb.so进行dlopen,一切正常;另一个测试程序Y尝试dlopen liba.so,但失败了,尽管您知道liba.so正确地找到了libb.so,因为您尝试删除libb.so并引发了另一个问题...您用于dlopen的选项是什么? - ShinTakezou
你做得很好。现在我们没有使用任何选项,因为dlopen是从一些我们无法控制的程序中调用的。 - johnnycrash
命令 ldd liba.so 的输出是什么? - el.pescado - нет войне
ldd 命令显示 libb.so => ./libb.so (0xf6ef9000) 等信息。所有其他行的 so 名称后面都有一个额外的 .#,例如 "libutil.so.1 => /lib/libutil.so.1 (0xf6ef5000)"。libb.so 后面没有 .# 是否有意义? - johnnycrash
1
在这种情况下,您应该检查符号的定义 - 它是已定义还是仅声明。 - Dmitry Yudakov
显示剩余2条评论
2个回答

30

您可以使用ldd命令轻松检查期望的libb.so位置:

 $ ldd liba.so
    linux-gate.so.1 =>  (0xb77b0000)
    libb.so.1 => not found
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb75b6000)
    libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7572000)
    libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb742b000)
    /lib/ld-linux.so.2 (0xb77b1000)
如果出现“未找到”的情况,则需要将libb.so的路径添加到/etc/ld.so.conf或shell变量LD_LIBRARY_PATH中。
另一种方法是在liba.so本身中设置rpath - 这基本上是硬编码其路径,因此当启动二进制文件时,动态链接器就会知道在哪里搜索共享库。
如果没有设置rpath,它将首先在LD_LIBRARY_PATH中搜索,然后搜索/etc/ld.so.conf(或/etc/ld.so.conf.d/)中提到的路径。在添加到ls.so.conf后,请不要忘记执行/sbin/ldconfig 动态链接器根据其soname(如果已设置)搜索依赖的共享库 - 如果没有设置soname(例如使用-Wl,-soname,libb.so.1),则会按照库的名称进行搜索。
示例:libb.so.1.0是您的实际库,具有soname - libb.so.1。通常应该有以下文件结构:
libb.so -> libb.so.1
libb.so.1 -> libb.so.1.0
libb.so.1.0

其中libb.solibb.so.1是符号链接。

通常在构建某些应用程序或其他依赖于libb.so的库时,您会链接到libb.so

gcc -shared -Wl,-soname,liba.so.1 -o liba.so.1.2 -L/libb/path -lb

当应用程序启动时(或执行dlopen操作 - 在您的情况下),动态链接器会搜索具有名为libb.so.1的文件 - 依赖库的soname,如果设置了soname而不是libb.so

这就是为什么您需要那个符号链接libb.so.1,指向实际库的原因。

如果使用ld.so.confldconfig,它会创建名称为soname的符号链接,指向库文件,如果缺少此符号链接。

您可以查看ld-linux手册页面以获取更多有用信息。


如果找到库但某些符号丢失,请尝试使用-Wl,--no-undefined选项构建libb.so

gcc -shared -Wl,-soname,libb.so.1 -Wl,--no-undefined -o libb.so.1.2

如果您忘记定义某个符号,它应该会提示错误。


我们确实使用了rpath。我们相当确定它找到了库,因为我们尝试删除libb后运行应用程序,结果出现文件未找到错误。当我们使用libb运行时,会出现未定义符号错误。 - johnnycrash
我可能没有理解关于soname的部分...你能再解释一下吗? - johnnycrash
我会试着搞一下看看会发生什么。此外,liba使用libb。应用程序调用dlopen(liba),并出现有关liba中使用的在libb中的符号的错误。 - johnnycrash
是的,我的错误,我混淆了依赖关系,现在应该已经修复了。 - Dmitry Yudakov
顺便提一下,我假设 libb 无法找到,但也有可能只是没有定义某些符号。请参阅我的最后一次编辑关于 no-undefined 选项的说明。 - Dmitry Yudakov

3
请勿忘记库的顺序(所有-lxxx参数)在链接所有对象和库以生成可执行文件时至关重要(至少在gcc中)。

简单的例子:

LIBS=-L. -ltest1 -ltest2

OBJS=code1.o code2.o

gcc $(LIBS) $(OBJS) -o mysoft

有些情况下会失败,而

gcc $(OBJS) -o mysoft $(LIBS)

则不会。


1
嗨,你的回答解救了我的编译。我在其他地方查找过,没有人提到-o标志的顺序(只有对象和库的顺序),虽然在你的示例中,你将库移动到-o之后以及对象之后。你知道这是否重要吗? - fuzzyTew
这对我起作用了,但我不明白为什么? - Cyclonecode
因为链接器从左到右搜索,并在进行搜索时记录未解析的符号。对于循环依赖的库,您还需要多次指定每个库。更多信息请参见此处:https://dev59.com/Z3VD5IYBdhLWcg3wO5AD - Vincent Fenet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接