共享库加载失败问题排查。gcc编译器生成共享库时不检查符号的依赖项。gcc编译器生成可执行程序时,会多链接一些无用的额库
2014-11-04 19:44
633 查看
原贴: 点击打开链接
导引:
共享库加载失败问题排查。
共享库的多个名字的解释。
编译器生成共享库时不检查符号的依赖项,这往往导致编译出的so在动态加载时因为出现未定义符号而加载失败。
编译器生成可执行程序时,会多链接一些无用的额库,导致程序在没有部署相应库的机器上运行不起来。
加载通过dlopen加载so失败,为何?怎么解决?
(本节也介绍了gcc的链接生成共享库的特点:即使没有指定符号依赖的共享库,也不会报错!)
方法1:
1)ldd -r xxx.so
2)此时应该会提示:
a)某些xxx.so依赖的so没有找到,(这个情况解决较简单)
解决办法:
ldd显示依赖的so都是动态库的soname,soname是指向动态库realname的一个软链,
在硬盘里搜索realname的动态库的所在位置,然后手动建一个soname软链指向realname,放在/usr/lib目录下。
b)或者以及存在某些未定义的符号undefined symbol(这个情况解决较复杂)。
解决办法:
为什么会存在未定义的符号?
在编译链接生成xxx.so时,根本就没有链接未定义符号所在的so。所以,编译其也不知到符号所在的so的soname是什么。
此时就需要检查你的makefile文件。
ps:生成so时,即使不链接其依赖的库,也会编译过去不报错!!!
ps:生成so时,即使链接其依赖的库,但如果链接错了文件,也会编译过去不报错!!!suse系统gcc4.1版本实验。
我遇到的情况是:链接的linkername对应的文件是个0字节文件,居然也没报错。结果导致了很多符号未定义
比如 -lmysqlclient, 结果发现 mysqlclient.so是个0字节的文件,不是软链,居然也编译过去了。
通过修正makefile中实际引用到的 mysqlclient.so软链,使其指向正确的库,libmysqlclient.so -> libmysqlclient.so.15.0.0*
重新编译搞定。
编译器生成共享库时特点
不检查符号的依赖项,,即使不链接其依赖的库,也会编译过去不报错,这往往导致编译出的so在动态加载时因为出现未定义符号而加载失败。
编译器生成可执行程序时特点
会严格检查符号是否都有定义,但是,对于未使用到的共享库,如果你链接了,即使任何符号都未使用到,也会被链进去!!
gcc errtest.c -o errtest -L. -lerr -lcrypt =====<crypt是估计加的,未使用到
nemo@ubuntu:~/code/linux_unleashed/chap24$ ldd -r errtest
linux-gate.so.1 => (0x00638000)
liberr.so.1 => not found
libcrypt.so.1 => /lib/tls/i686/cmov/libcrypt.so.1 (0x00fc8000) =====<crypt被链进来了!!!
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00985000)
实际开发中经常遇到这样的情况,我的CGI没有使用mysql共享库,但是由于我的makefile里面链接的mysql的库,导致我的cgi运行时也必须加载mysql库,
如果把cgi放在一个没有部署mysql共享库的机器上,就会运行不起来!!!
linux动态库的三个名字 soname realname linkername详解
为什么一个动态库会有如此多的名字,而且soname和linkername都是软链,实际的文件是realname
对于从windows下转行过来的同学会对此很不解,本人就非常的疑惑,今日得以领悟,记录下自己的认识。如有错误请批评指正。
下面从动态库的版本控制的角度讲起。
a)windows下的dll的版本控制:
在windows下,一个dll都具有一个文件名,比如xxx.dll, 同时该dll也具有一些属性:文件版本等。可以通过选中dll-属性-详细信息查看dll的版本版权等很多信息。
当dll的功能更新时,只要接口没变,那么只需更新dll的版本号即可,可以发布一个和之前dll同名的xxx.dll, 直接覆盖掉原来的dll就可实现新功能的升级。
exe可执行文件里面记录的依赖的dll仅记录了dll的文件名,所以dll升级后,exe也就自动链接升级后的dll了。
b)linux下的so的版本控制:
linux下的so文件不具有像windows下的额外属性。为了标识一个so的版本,gcc链接生成so目标时一般都采用libxxx.so.1.0.0的方式。
这样把so的版本信息记录在文件名里面。这个带版本信息的文件名就是realname。
那么exe可执行文件如何记录自己依赖的so呢,如果记录的是so的realname,那么so有新版本升级时,新的so必定具有不同的realname,exe就无法自动使用更新的so了。
好在linux下有软链这样的机制可以解决该问题。exe中不必记录依赖的so的realname,exe中记下一个指向实际realname的软链即可,这个软链就是soname啦。soname一般采用这样的名字 libxxx.so.1(保留大版本号1)
当有一个功能更新的so发布时,只需修改soname软链,指向升级后的realname文件即可。
比如我发布了xxx动态库的升级版 libxxx.so.1.0.1, 使软链libxxx.so.1指向libxxx.so.1.0.1即可。
soname是我们在编译其他程序时,往其他程序的二进制映像里面写入的共享库的名字。
那么什么是linkname呢?顾名思义,就是编译代码时的链接阶段使用的,比如我有一个程序需要链接libxxx.so.1.0.0库,
makefile需这样写 -lxxx.so.1.0.0,这r样写实在很长也很丑,而且如果后续libxxx有更新时,必须修改makefile文件才能链接到新的库上。
所以出现了一个新的链接到realname的软链,这个软链就叫 linkername。通常lingkername是不带任何版本信息的,取名如下 libxxx.so
这样makefile就变成了 -lxxx.so
这其中有一个问题,动态库的使用者(比如exe)是通过linkername链接的,而lingkername指向的是so的realname,我们前文说为了解决升级so重新编译的问题,exe文件里面记录的是其依赖的so的soname名字,而不是真实的realname,
那么exe是如何知道realname对应的soname呢?
答案在realname文件里面,在编译链接生成realname时, 同事也指定了其对应的soname,这个soname存储在realname的文件里面。
eg 如下编译命令:
gcc -g -Wall -shared -Wl,-soname,liberr.so.1 -o liberr.so.1.0.0 liberr.o -lc // liberr.so.1是soname,将记录在生成的liberr.so.1.0.0文件里面
可以通过如下工具查看一个realname的so的对应的soname:
readelf -d ./liberr.so.1.0.0
Dynamic section at offset 0xf10 contains 22 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000e (SONAME) Library soname: [liberr.so.1] ===============< 其soname
参考资料:
Linux 动态库剖析
http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/
文中给出了so动态加载的示例代码 dlopen dlsym等
以及一些工具的使用比如ldd等。
也谈共享库
http://bigwhite.blogbus.com/logs/88871474.html
文中介绍了
1)soname,realname,linkername,很好很详细
2)介绍了exe加载so时的搜索so的路径顺序:
a)编译exe程序是指定的-rpath
b)环境变量LD_LIBRARY_PATH
c)ldconfig配置的缓存中的路径
d)系统默认路径/lib和/usr/lib。
nm 命令详解
T 才是库中导出的符号
U 指的是在上边的T对应的函数中使用了,却未定义的函数或符号
所以, 用nm查看.a或.so文件导出符号的时候,只需查看具有T标识的行即可:
如下命令
nm a.out|grep -w -i t|grep your_fun_name
导引:
共享库加载失败问题排查。
共享库的多个名字的解释。
编译器生成共享库时不检查符号的依赖项,这往往导致编译出的so在动态加载时因为出现未定义符号而加载失败。
编译器生成可执行程序时,会多链接一些无用的额库,导致程序在没有部署相应库的机器上运行不起来。
加载通过dlopen加载so失败,为何?怎么解决?
(本节也介绍了gcc的链接生成共享库的特点:即使没有指定符号依赖的共享库,也不会报错!)
方法1:
1)ldd -r xxx.so
2)此时应该会提示:
a)某些xxx.so依赖的so没有找到,(这个情况解决较简单)
解决办法:
ldd显示依赖的so都是动态库的soname,soname是指向动态库realname的一个软链,
在硬盘里搜索realname的动态库的所在位置,然后手动建一个soname软链指向realname,放在/usr/lib目录下。
b)或者以及存在某些未定义的符号undefined symbol(这个情况解决较复杂)。
解决办法:
为什么会存在未定义的符号?
在编译链接生成xxx.so时,根本就没有链接未定义符号所在的so。所以,编译其也不知到符号所在的so的soname是什么。
此时就需要检查你的makefile文件。
ps:生成so时,即使不链接其依赖的库,也会编译过去不报错!!!
ps:生成so时,即使链接其依赖的库,但如果链接错了文件,也会编译过去不报错!!!suse系统gcc4.1版本实验。
我遇到的情况是:链接的linkername对应的文件是个0字节文件,居然也没报错。结果导致了很多符号未定义
比如 -lmysqlclient, 结果发现 mysqlclient.so是个0字节的文件,不是软链,居然也编译过去了。
通过修正makefile中实际引用到的 mysqlclient.so软链,使其指向正确的库,libmysqlclient.so -> libmysqlclient.so.15.0.0*
重新编译搞定。
编译器生成共享库时特点
不检查符号的依赖项,,即使不链接其依赖的库,也会编译过去不报错,这往往导致编译出的so在动态加载时因为出现未定义符号而加载失败。
编译器生成可执行程序时特点
会严格检查符号是否都有定义,但是,对于未使用到的共享库,如果你链接了,即使任何符号都未使用到,也会被链进去!!
gcc errtest.c -o errtest -L. -lerr -lcrypt =====<crypt是估计加的,未使用到
nemo@ubuntu:~/code/linux_unleashed/chap24$ ldd -r errtest
linux-gate.so.1 => (0x00638000)
liberr.so.1 => not found
libcrypt.so.1 => /lib/tls/i686/cmov/libcrypt.so.1 (0x00fc8000) =====<crypt被链进来了!!!
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00985000)
实际开发中经常遇到这样的情况,我的CGI没有使用mysql共享库,但是由于我的makefile里面链接的mysql的库,导致我的cgi运行时也必须加载mysql库,
如果把cgi放在一个没有部署mysql共享库的机器上,就会运行不起来!!!
linux动态库的三个名字 soname realname linkername详解
为什么一个动态库会有如此多的名字,而且soname和linkername都是软链,实际的文件是realname
对于从windows下转行过来的同学会对此很不解,本人就非常的疑惑,今日得以领悟,记录下自己的认识。如有错误请批评指正。
下面从动态库的版本控制的角度讲起。
a)windows下的dll的版本控制:
在windows下,一个dll都具有一个文件名,比如xxx.dll, 同时该dll也具有一些属性:文件版本等。可以通过选中dll-属性-详细信息查看dll的版本版权等很多信息。
当dll的功能更新时,只要接口没变,那么只需更新dll的版本号即可,可以发布一个和之前dll同名的xxx.dll, 直接覆盖掉原来的dll就可实现新功能的升级。
exe可执行文件里面记录的依赖的dll仅记录了dll的文件名,所以dll升级后,exe也就自动链接升级后的dll了。
b)linux下的so的版本控制:
linux下的so文件不具有像windows下的额外属性。为了标识一个so的版本,gcc链接生成so目标时一般都采用libxxx.so.1.0.0的方式。
这样把so的版本信息记录在文件名里面。这个带版本信息的文件名就是realname。
那么exe可执行文件如何记录自己依赖的so呢,如果记录的是so的realname,那么so有新版本升级时,新的so必定具有不同的realname,exe就无法自动使用更新的so了。
好在linux下有软链这样的机制可以解决该问题。exe中不必记录依赖的so的realname,exe中记下一个指向实际realname的软链即可,这个软链就是soname啦。soname一般采用这样的名字 libxxx.so.1(保留大版本号1)
当有一个功能更新的so发布时,只需修改soname软链,指向升级后的realname文件即可。
比如我发布了xxx动态库的升级版 libxxx.so.1.0.1, 使软链libxxx.so.1指向libxxx.so.1.0.1即可。
soname是我们在编译其他程序时,往其他程序的二进制映像里面写入的共享库的名字。
那么什么是linkname呢?顾名思义,就是编译代码时的链接阶段使用的,比如我有一个程序需要链接libxxx.so.1.0.0库,
makefile需这样写 -lxxx.so.1.0.0,这r样写实在很长也很丑,而且如果后续libxxx有更新时,必须修改makefile文件才能链接到新的库上。
所以出现了一个新的链接到realname的软链,这个软链就叫 linkername。通常lingkername是不带任何版本信息的,取名如下 libxxx.so
这样makefile就变成了 -lxxx.so
这其中有一个问题,动态库的使用者(比如exe)是通过linkername链接的,而lingkername指向的是so的realname,我们前文说为了解决升级so重新编译的问题,exe文件里面记录的是其依赖的so的soname名字,而不是真实的realname,
那么exe是如何知道realname对应的soname呢?
答案在realname文件里面,在编译链接生成realname时, 同事也指定了其对应的soname,这个soname存储在realname的文件里面。
eg 如下编译命令:
gcc -g -Wall -shared -Wl,-soname,liberr.so.1 -o liberr.so.1.0.0 liberr.o -lc // liberr.so.1是soname,将记录在生成的liberr.so.1.0.0文件里面
可以通过如下工具查看一个realname的so的对应的soname:
readelf -d ./liberr.so.1.0.0
Dynamic section at offset 0xf10 contains 22 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x0000000e (SONAME) Library soname: [liberr.so.1] ===============< 其soname
参考资料:
Linux 动态库剖析
http://www.ibm.com/developerworks/cn/linux/l-dynamic-libraries/
文中给出了so动态加载的示例代码 dlopen dlsym等
以及一些工具的使用比如ldd等。
也谈共享库
http://bigwhite.blogbus.com/logs/88871474.html
文中介绍了
1)soname,realname,linkername,很好很详细
2)介绍了exe加载so时的搜索so的路径顺序:
a)编译exe程序是指定的-rpath
b)环境变量LD_LIBRARY_PATH
c)ldconfig配置的缓存中的路径
d)系统默认路径/lib和/usr/lib。
nm 命令详解
T 才是库中导出的符号
U 指的是在上边的T对应的函数中使用了,却未定义的函数或符号
所以, 用nm查看.a或.so文件导出符号的时候,只需查看具有T标识的行即可:
如下命令
nm a.out|grep -w -i t|grep your_fun_name
相关文章推荐
- 共享库加载失败问题排查。gcc编译器生成共享库时不检查符号的依赖项。gcc编译器生成可执行程序时,会多链接一些无用的额库。
- GCC/LD编译链接潜规则 (第四弹) : ld 会把所有通过-l指定的动态库全部链接进最终的目标程序中, 无论是否真的用到(导致生产环境加载失败)
- QT下生成可执行程序的方法及一些问题解决办法:
- 使用gcc 5 编译c++11编写的程序 出现的符号找不到的链接问题。
- 未能加载文件或程序集或它的某一个依赖项(针对资源问题,x86文件在x64上编译失败)
- [转]小问题: windows组件调用失败,无法加载安装安装程序:Wbemupgd.dll
- 抽取VS文件组成类GCC的编译器,并编译C程序为dll动态链接库
- 近几天 用微芯力科的板子 调试usb虚拟串口的程序,发现一些问题以及解决方法。和大家共享
- 小问题: windows组件调用失败,无法加载安装安装程序:Wbemupgd.dll
- 编写简单的连接MongoDB数据库C++程序 && 解决编译C++程序时链接MongoDB动态库失败的问题
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。问题的解决方法!
- 解决方法:对性能监视器计数器注册表值执行系统配置检查失败。有关详细信息,请参阅自述文件或 SQL Server 联机丛书中的“如何在 SQL Server 2005 中为安装程序增加计数器注册表项值”
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。问题的解决方法!
- 程序链接关于静态链接,动态链接,共享库,ABI的一些记录(os学习)
- “未能加载文件或程序集“×××”或它的某一个依赖项。试图加载格式不正确的程序”问题的解决
- gcc链接动态库时,两个动态库中符号重名的问题
- UserControl 的一个值得注意的问题 [属性" * "的代码生成失败.错误是:"程序集"*.Version=1.0.0.0,Culture=neutral,..........无标记为序列化"
- 如何解决 SQL Server 安装程序中的 COM+ 系统配置检查失败问题
- 解决"未能加载文件或程序集,或它的某一个依赖项,试图加载格式不正确的程序"问题一法
- linux下gcc编译可执行程序时,添加动态链接库加载路径