记录使用dlclose后so无法卸载问题

记录使用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 

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/commit/bd7de94d7fe8a0ba7742e90b1d6a09baa468bb58)
目前(2022年10月24日),go语言支持dlclose在golang的github中依然是一个open状体的bug:https://github.com/golang/go/issues/11100

参考资料

https://stackoverflow.com/questions/45967961/how-to-unload-all-the-dependent-shared-libraries-from-a-process
https://www.coder.work/article/1518255
https://docs.oracle.com/cd/E19683-01/816-0210/6m6nb7mcs/index.html

你可能感兴趣的:(C语言,c语言,golang)