记录使用dlclose后so无法卸载问题
问题描述
有一个类似插件的功能,使用dlopen方式加载一个so,升级so的时候,先dlclose,然后再dlopen加载。这样可以做到更换so的时候不用重新启动程序。本来一切运行的比较好,但有一个so比较奇怪,升级so后,某些函数无法使用新的so里面的实现,还是旧的so中的实现。
问题定位
在gdb中使用info sharedlibrary命令查看加载的so,再使用lsof查看如下:
(gdb) info sharedlibrary
0x00007fff2132a2c0 0x00007fff213bc5f8 Yes /xxxx/xxx/libxxx.so
root@probe:~# lsof |grep libxxx.so
nginx 3391245 root mem REG 253,0 8604544 5250054 /xxx/xxx/libxxx.so
从这些信息中可以看到,目前正在使用的so的inode是5250054,而是用stat查看现在so的inode信息,发现inode是5377891,如下所示:
root@probe:~# stat /xxx/xxx/libxxx.so
File: /apisec/modules/component/sensitive_data/libs/libdi_rechk.so
Size: 8604544 Blocks: 16808 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 5377891 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-10-21 17:01:57.404283135 +0800
Modify: 2022-10-21 15:48:29.000000000 +0800
Change: 2022-10-21 17:00:23.423822609 +0800
两者inode不一样,说明使用的不是同一个so文件,因为旧的so已经被删除。但程序中已经dlclose并重新dlopen了,于是从网上查找资料,有些资料中都说查看下是否有NODELET标记。于是使用readelf命令查看下:
root@probe:~# readelf -d libxxx.so |grep NODELETE
0x000000006ffffffb (FLAGS_1) Flags: NODELETE
so中有此标识,则动态加载程序已被告知不要卸载库,所以调用dlclose后,不会从进程中卸载此so。
手动测试
自己写一个test.c的测试例子,代码如下:
#include <stdio.h>
int test()
{
printf("this is test function.\n");
return 0;
}
使用如下编译命令编译:
gcc -fpic -shared -o libtest.so test.c
然后使用readelf命令查看是否有标识:
gcc -fpic -shared -o libtest.so test.c
发现没有任何输出。
重新使用以下命令编译并使用readelf命令查看:
gcc -fpic -shared -znodelete -o libtest.so test.c
readelf -d libtest.so |grep NODELETE
0x000000006ffffffb (FLAGS_1) Flags: NODELETE
可以看到,添加了-znodelete选项后,编译出的so中已经有了NODELETE选项。
-znodelete选项是链接器程序ld的一个参数,使用ld --help可以看到-znodelete选项的作用。有了此选项后,程序会常驻在进程中,在调用dlclose后,不会从进程中删除。除了nodelete选项,还有nodlopen等其它选项。
为什么这个so会添加-znodelete选项?
从开发so的人员那里了解到,其并没有手动添加这个选项,他的so是用go语言开发的,使用go build -buildmode=c-shared编译的c语言的so。
于是从网上搜索相关问题,发现go语言生成的so中,目前不支持dlclose,使用的方式就是添加-znodelete选项。(https://github.com/golang/go/...)
目前(2022年10月24日),go语言支持dlclose在golang的github中依然是一个open状体的bug:https://github.com/golang/go/...
参考资料
https://stackoverflow.com/que...
https://www.coder.work/articl...
https://docs.oracle.com/cd/E1...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。