dlsym与-fPIC,以及objcopy

简介: 本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net     这两天做的工作当中,遇到了这样一个需求。
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
    

这两天做的工作当中,遇到了这样一个需求。在Linux环境下,要为一个daemon程序的一个动态库进行升级,不不打断这个daemon的运行。这个动态库的函数会被daemon的多个线程调用。在升级时,对于已经使用了这个动态库的线程要毫无影响,直到这样的线程再次调用动态库的API时,再使用新的动态库。换句话说,在升级时,新旧两个动态库可以同时被这个daemon的线程调用。

为了实现这个功能,毫无疑问,不能让daemon自动加载动态库,而是要通过使用dlopen和dlsym来加载动态库,以及获得相应的symbol。对于动态库的句柄和指向其symbol的指针,需要使用引用计数,来检查是否还有线程在引用它们。当更新动态库时,对于其句柄和那些指针使用RCU机制进行释放。这样就保证了更新动态库时,已被引用的旧的动态库资源仍然可以继续使用。直到所有的资源都被解引用时,资源才会被释放。

为了验证自己的思路,我写了一个简单的程序来测试同时加载两个含有同样symbol的动态库,是否可以完全正常工作。下面是这个程序的简单示意。

void *lib_handle1 = dlopen("libmy_lib.so", RTLD_NOW);
if (NULL == lib_handle1) {
//error handler
}
void *fp1 = dlsym(lib_handle1, "my_api");
if (NULL == fp1) {
//error handler
}
void *lib_handle2 = dlopen("libmy_new_lib.so", RTLD_NOW);
if (NULL  == lib_handle2) {
//error handler
}
void *fp2 = dlsym(lib_handle2, "my_api");
if (NULL == fp2) {
//error handler
}

首先要说明的是,第二次调用dlopen时,第二个动态的名字必须与第一个不同,否则程序会认为该动态库已经加载,那么第二个dlopen就会直接返回lib_handle1的地址。这在man手册中也有清晰的说明。
我这里的第二个动态库的名字已经与第一个有了区别。第二次dlopen返回了一个新的加载地址,但是第二个dlsym却返回了与fp1相同的地址。也就是说,程序根本没有取得libmy_new_lib.so中的my_api的地址。

我上网搜索了一下,没有找到准确的解释。但是有人说因为symbol也已经被加载了,所以同名的symbol不会被第二次加载。

于是,我想将libmy_new_lib.so中的my_api改名——当然不能通过修改源代码的方式。于是我使用objcopy来修改它的名字。具体命令是objcopy --redefine-sym my_api=my_new_api libmy_lib.so libmy_new_lib.so。修改后使用objdump查看,发现已经成功修改了。

可是在测试中,却发现dlsym失败,报告找不到my_new_api。于是我又开始研究这个问题,最终发现objcopy的修改symbol名字的功能不支持动态库,可man手册上并没有写到。大家可以上网搜到,甚至可以找到objcopy的开发mailist中,有人就已经发现了这个问题,但是不知道为什么一直没有解决。

绕了半天,又回到了原点。最后又思索了半天,发现了最终的解决方案。在编译参数中使用“-fPIC”即可解决这个问题。在man手册中,对于-fPIC的解释如下:
-fPIC
If supported for the target machine, emit position-independent code, suitable for dynamic linking and  avoiding any limit on the size of the global offset table. This option makes a difference on the m68k,  PowerPC and SPARC.  Position-independent code requires special support, and therefore works only on certain machines.
When this flag is set, the macros "__pic__" and "__PIC__" are defined to 2.

当使用-fPIC时,生成的代码时与位置无关的代码。那么在加载动态库时,就不会加载到固定位置,那么每个symbol都可以加载成功。当没有-fPIC时,程序会发现该位置已有对应的symbol,自然就不会二次加载了。

这么一个小问题也耽误了我近一个下午的时间。原因有二:一是对gcc的编译选项不够熟悉;二是被objcopy的bug给耽误了不少时间。
相关文章
|
安全 测试技术 Linux
安装配置Samba服务器(CentOS7)
假设我们有这样一个需求 共享名     路径         权限 Mealkey_Share   /smb/docs    所有人员包括来宾均可以访问 Group     /smb/tech    仅允许特定组的用户进行读写访问   特定组的组名为RD,目前的有zyy一人...
3464 0
|
10月前
|
缓存 Linux
Linux 清空缓存命令
某些时候需要把linux 的缓存清理一下。使用时需要区分参数的不同
301 0
|
存储 网络协议 Java
内存池组件以及根据nginx内存池源码设计实现简易内存池
内存池组件以及根据nginx内存池源码设计实现简易内存池
内存池组件以及根据nginx内存池源码设计实现简易内存池
|
11月前
|
供应链 项目管理 智能硬件
|
11月前
|
存储 缓存 安全
Linux下线程同步(带你了解什么是互斥锁、死锁、读写锁、条件变量、信号量等)
Linux下线程同步(带你了解什么是互斥锁、死锁、读写锁、条件变量、信号量等)
126 0
|
并行计算 PyTorch 算法框架/工具
如何将自己定义的函数,也传给cuda进行处理?
要将自己定义的函数传递到CUDA进行处理,需要使用PyTorch提供的CUDA扩展功能。具体来说,可以使用torch.cuda.jit模块中的@torch.jit.script装饰器将Python函数转换为Torch脚本,并使用.cuda()方法将其移动到GPU上。
603 0
|
安全 Shell 网络安全
Fiddler - 夜神模拟器证书安装App抓包
文章目录 Fiddler- 夜神模拟器证书安装App抓包 前言 一、软件安装 1.Openssl安装 1.1下载安装 1.2配置环境变量 1.3查看openssl版本,输入命令:openssl version 2.夜神模拟器安装 1.1 下载安装 1.2工具准备,MT管理器 3.Fiddler安装 1.1下载安装 二、Fiddler 安装证书 1.安装证书并导出 2.格式化证书 3.通过MT管理器将证书配置到模拟器中 4.模拟器WiFi填入Fiddler代理的IP和端口 5.抓包示例
783 0
Fiddler - 夜神模拟器证书安装App抓包
TypeError: unsupported operand type(s) for -=: 'Retry' and 'int'
TypeError: unsupported operand type(s) for -=: 'Retry' and 'int'
353 0
|
Ubuntu Linux Shell
root用户和普通用户
root用户和普通用户
435 0
将华为欧拉系统OpenEuler运行在你的树莓派上,用上国产系统
将华为欧拉系统OpenEuler运行在你的树莓派上,用上国产系统
将华为欧拉系统OpenEuler运行在你的树莓派上,用上国产系统