关于c:记录使用dlclose后so无法卸载问题

38次阅读

共计 1977 个字符,预计需要花费 5 分钟才能阅读完成。

记录应用 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
[email protected]:~# 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,如下所示:

[email protected]:~# 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 命令查看下:

[email protected]:~# 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…

正文完
 0