为什么在预加载到达世币时没有调用共享库 fini 函数?

why isn't shared library fini function called when preloaded to dash?

提问人:Mark Galeck 提问时间:3/7/2023 最后编辑:Mark Galeck 更新时间:3/11/2023 访问量:115

问:

我使用的是最新的 Ubuntu Linux。

下面是一个共享库,其中包含在加载和卸载时调用的函数:

shared.c:

#include <fcntl.h>
#include <sys/stat.h>

void init() {
    open("init", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
}

void fini() {
    open("fini", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
}

这是用

gcc -shared -fPIC -Wl,-init=init -Wl,-fini=fini shared.c -o shared.so

然后我这样做:

$ rm init fini
$ LD_PRELOAD=$PWD/shared.so  dash /dev/null
$ echo $?
0
$ ls init
init
$ ls fini
ls: cannot access 'fini': No such file or directory

所以。。。加载函数被调用,但卸载函数没有被调用

如果我用 替换 ,则两者都被调用。dashbash

使用没有区别。__attribute__((destructor))

为什么不调用卸载功能?dash

根据 Marco Bonelli 的要求添加:

$ file $(which dash)
/usr/bin/dash: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f7ab02fc1b8ff61b41647c1e16ec9d95ba5de9f0, for GNU/Linux 3.2.0, stripped

$ ldd $(which dash)
        linux-vdso.so.1 (0x00007ffd931c0000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6b19e84000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6b1a0ec000)
linux 内核 共享库 ld-preload

评论

0赞 Marco Bonelli 3/7/2023
你能添加和的输出吗?只是为了了解它是什么样的可执行文件。file $(which dash)ldd $(which dash)
0赞 Mark Galeck 3/8/2023
@MarcoBonelli ??It's - Linux 系统的标准外壳dash
0赞 Marco Bonelli 3/8/2023
我很清楚是什么。但是,我不知道的是您的系统上安装了哪种ELF可执行文件。这些信息将是相关的,可以帮助回答您的问题。dashdash
0赞 Mark Galeck 3/8/2023
@MaroBonelli哦,我明白了,对不起,看到上面添加的内容。您是否在系统上没有看到问题?我认为这一定发生在任何最近的 Linux 上,我只是不明白为什么。
0赞 Marco Bonelli 3/8/2023
谢谢。似乎你是一个动态的ELF可执行文件,所以理论上加载器应该尊重......我假设的输出是一样的,对吧?检查 和 在这两种情况下,您是否看到加载程序正在打开/读取文件。dashLD_PRELOADldd $(shared.so)strace -f dash -c "> /dev/null"strace -f dash -c "touch /dev/null"shared.so

答:

1赞 Philippe 3/8/2023 #1

我应该把它作为一个评论,但我把它放在一个答案上,用于格式化。

问题不在于 和 之间的区别,因为在破折号/触摸运行中,shared.so 被加载了两次,一次用于破折号,一次用于触摸。 仅用 .redirectiontouchfinitouch

您可以看到这两次运行的差异:

$ LD_PRELOAD=$PWD/shared.so  dash  /dev/null
# fini is not called
$ LD_PRELOAD=$PWD/shared.so  touch /dev/null
# fini is called

所以这与.dash

希望这能为您的调查提供更多材料。

评论

0赞 Mark Galeck 3/11/2023
感谢您指出这一点,我将根据您的观察修改和简化问题
2赞 KamilCuk 3/11/2023 #2

为什么在预加载到达世币时没有调用共享库 fini 函数?

因为 dash 调用 here 会调用 exit_group syscall here 会立即终止程序。_exit

我们再举一个小例子:

# Makefile
all:
        gcc -shared -fPIC -Wl,-init=init -Wl,-fini=fini -Wl,-soname,shared.so shared.c -o shared.so
        gcc main.c -o main.out
        LD_PRELOAD=$(PWD)/shared.so ./main.out /dev/null

# main.c
#include <unistd.h>
int main() {
        _exit(0);
}

# shared.c
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
void out(const char *str) {
        int fd = open("/dev/tty", O_WRONLY);
        write(fd, str, strlen(str));
        close(fd);
}
void init() {
        out("\nstart\n\n");
}
void fini() {
        out("\nstop\n\n");
}

执行时不输出停止:

$ make
gcc -shared -fPIC -Wl,-init=init -Wl,-fini=fini -Wl,-soname,shared.so shared.c -o shared.so
gcc main.c -o main.out
LD_PRELOAD=..../shared.so ./main.out /dev/null

start

评论

0赞 Mark Galeck 3/11/2023
等。。。所以,当我查看官方手册页文档并谈到该选项时,这基本上毫无价值?因为如果主程序使用,则该函数将被忽略。换句话说,有什么意义 - 我把一些需要运行的代码放在那里,但主程序可以很容易地击败它,我的代码将无法运行。ld-fini_exit()-fini-fini
0赞 KamilCuk 3/11/2023
关键是立即退出。的重点是添加一个 finish 函数来退出。 主程序可以随时调用或执行分段故障。_exitfinithe main program can easily defeatabort()
0赞 Mark Galeck 3/11/2023
那么,我如何确保我的“完成”代码始终在预加载我的库的主程序退出后运行。没有办法吗??
0赞 Mark Galeck 3/11/2023
是的,我明白了。我搞砸了......如果我不能依靠我的代码完成执行,这对我来说是一个障碍。
0赞 KamilCuk 3/11/2023
how do I ensure that my "finishing"将程序包装在父程序中。.使用进程管理器、服务编排器 - 或 等。bash -c 'trap "echo finishing task" EXIT; ./yourprogram'systemdsystemd-run