dlsym与-fPIC,以及objcopy

简介:
本文的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给耽误了不少时间。
目录
相关文章
|
10月前
|
开发者
在代码维护中,Qwen-coder可以通过多种方式提升编程效率和代码质量
Qwen-coder在代码维护中发挥着重要作用,帮助开发者提高代码质量,减少错误,提升开发效率。这些功能使得Qwen-coder成为代码维护和开发过程中的有力助手。
406 52
|
12月前
|
监控 安全 网络安全
EAP:无线网络安全的强大卫士
EAP:无线网络安全的强大卫士
491 0
|
Kubernetes Linux 网络安全
在K8S中,nodePort默认端口范围是多少? 为什么是这个端口范围?
在K8S中,nodePort默认端口范围是多少? 为什么是这个端口范围?
Springboot最全权限集成Redis-前后端分离-springsecurity-jwt-Token3
Springboot最全权限集成Redis-前后端分离-springsecurity-jwt-Token3
181 68
|
Shell Perl
在awk中,`-v` 参数用于从命令行导入环境变量或定义自定义变量
在awk中,`-v` 参数用于从命令行导入环境变量或定义自定义变量
720 1
|
机器学习/深度学习 人工智能 自然语言处理
OpenAI 推出 GPT-4o,免费向所有人提供GPT-4级别的AI ,可以实时对音频、视觉和文本进行推理,附使用详细指南
GPT-4o不仅提供与GPT-4同等程度的模型能力,推理速度还更快,还能提供同时理解文本、图像、音频等内容的多模态能力,无论你是付费用户,还是免费用户,都能通过它体验GPT-4了
729 1
|
人工智能 机器人 测试技术
【编程】 打桩测试的原则及举例示范(详细讲解)
【编程】 打桩测试的原则及举例示范(详细讲解)
|
Go 内存技术
【Jlink】JLink Commander调试方法
上面的信息连可以看到当前运行的PC指针,再可以结合生成的map文件,就可以看到当前运行的函数。例如上面运行的PC指针为0x01000E72,下图是固件的map文件,查看map文件对应地址的函数为SEGGER_RTT_Write。用来读取内存的数据,参数为内存的地址和读取的长度。也可以直接用mem命令按照8位来读取。常用的命令有halt,go,mem(mem8,mem16, mem32), write(write1, write2, write4 )用来写入对应的内存地址,参数为内存的地址和写入的数据。
2880 45
【Jlink】JLink Commander调试方法
|
Shell
Shell 整数值操作符(大小等与、-eq、-ne、-ge、-le、-gt、-lt)
Shell 整数值操作符(大小等与、-eq、-ne、-ge、-le、-gt、-lt)
1818 0