Linux环境C语言dlopen加载so库文件提示undefined symbol问题

Linux环境C语言dlopen加载so库文件提示undefined symbol问题

出错场景

近期在做由AIXLinux的迁移工作,迁移到Linux系统的C程序gcc编译无报错信息,但在程序运行时加载so动态库文件报错。

dlopen() error: /app/home/slib/1000.so undefined symbol: fill_space

fill_space() 是我们编写的一个功能简单的公函,编译后在静态库libcom.a库供其他程序调用。报错的C程序在AIX使用cc编译、运行均正常,迁移到Linux使用gcc编译后,运行时报undefined symbol错误。

问题分析

查询了一些资料,发现GCC/LD编译链接时,对 依赖库的输入顺序 敏感,即 被依赖的库 需要放在后面。

LD对依赖库的 输入顺序 敏感
LD在链接生成目标文件时,会从左到右扫描输入的依赖库。但是,当依赖库之间也有依赖关系时,必须将"依赖别人的库"放在"被别人依赖的库"的前面,否则,会链接失败!

我们项目中所用的依赖库较多,在AIX程序使用cc编译,对依赖库之间的依赖关系并不敏感,所以忽略了依赖库的顺序问题。

解决步骤

  1. 查看so库undefined symbol的符号
    ldd1 脚本查看so文件(dlopen加载失败的so文件)

ldd脚本查看库文件
3. 确认undefined symbol符号是否真的不存在
nm2命令查看a文件(fill_space函数所在的库文件)
nm命令查看库文件
2. 修改makefile文件,调整依赖库顺序,将-lcom往后移
如果依赖库比较多,可以先使用gcc命令 调整-l库的顺序后直接编译
再使用ldd1 脚本查看so文件进行验证(顺序调整合适后再修改makefile文件)

// gcc
gcc -shared -fPIC -o /app/home/src/1000.so /app/home/src/1000.o -L/usr/lib -L/app/home/lib -lcmd -lapp -lcom

// ldd
ldd -r 1000.so
  1. 顺便提一句,关于链接库的两个环境变量 LD_LIBRARY_PATH LIBRARY_PATH
# LD_LIBRARY_PATH
# 程序加载运行期间 查找动态链接库的路径
# 将项目的slib目录添加进来
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/app/home/slib

# LIBRARY_PATH
# 程序编译期间 查找动态链接库时指定查找共享库的路径
# 例如,指定gcc编译需要用到的动态链接库的目录,即相当于 gcc -L选项
export LIBRARY_PATH=$LIBRARY_PATH:/app/home/lib

参考资料

链接器(Unix平台)是如何解析(resolve)对程序库的引用的(参考1,p556)。
在符号解析(symbol resolution)阶段,链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合:(1)集合E是将被合并到一起组成可执行文件的所有目标文件集合;(2)集合D是所有之前已被加入E的目标文件定义的符号集合;(3)集合U是未解析符号(unresolved symbols,即那些被E中目标文件引用过但在D中还不存在的符号)的集合。一开始,E、D、U都是空的。
(1): 对命令行中的每一个输入文件f,链接器确定它是目标文件还是库文件,如果它是目标文件,就把f加入到E,并把f中未解析的符号和已定义的符号分别加入到U、D集合中,然后处理下一个输入文件。
(2): 如果f是一个库文件,链接器会尝试把U中的所有未解析符号与f中各目标模块定义的符号进行匹配。如果某个目标模块m定义了一个U中的未解析符号,那么就把m加入到E中,并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的所有目标模块重复这个过程直至到达一个不动点(fixed point),此时U和D不再变化。而那些未加入到E中的f里的目标模块就被简单地丢弃,链接器继续处理下一输入文件。
(3): 当扫描完所有输入文件时如果U非空或者有同名的符号被多次加入D,链接器报告错误信息并退出。否则,它把E中的所有目标文件合并在一起生成可执行文件。

  • 第一次发文,如有 错误之处 还请多多指正

  1. ldd命令 ↩︎ ↩︎

  2. nm命令 ↩︎

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 环境下动态加载,可以使用动态链接(Dynamic Linking)实现。动态链接是一种可执行文件,它在运行时才会被加载到内存,并且可以在多个程序之间共享。这样,可以减小每个程序的内存占用,同时也方便了代码的维护和更新。 Linux 系统提供了一些函数来实现动态加载,包括 `dlopen()`、`dlsym()`、`dlclose()` 等。其中,`dlopen()` 函数用于打开一个动态链接,`dlsym()` 函数用于获取动态链接中的符号,`dlclose()` 函数用于关闭动态链接。 下面是一个简单的动态加载的示例: ```c #include <stdio.h> #include <dlfcn.h> int main() { void *handle; double (*cosine)(double); char *error; handle = dlopen("libm.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); return 1; } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); return 1; } printf("%f\n", (*cosine)(2.0)); dlclose(handle); return 0; } ``` 上面的代码中,首先使用 `dlopen()` 函数打开了名为 `libm.so` 的动态链接,并将其句柄保存在 `handle` 变量中。然后使用 `dlsym()` 函数获取了名为 `cos` 的符号,并将其保存在 `cosine` 变量中。最后,调用了 `cosine` 函数,并将结果打印出来。最后,使用 `dlclose()` 函数关闭了动态链接。 需要注意的是,在使用动态链接时,需要确保文件的路径正确,并且需要保证文件与应用程序的架构一致。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值