LD_PRELOAD无法按预期工作

Mar*_*tin 25 c++ linux gcc ld ld-preload

考虑以下库,可以在任何程序执行之前预先加载:

// g++ -std=c++11 -shared -fPIC preload.cpp -o preload.so
// LD_PRELOAD=./preload.so <command>
#include <iostream>

struct Goodbye {
    Goodbye() {std::cout << "Hello\n";}
    ~Goodbye() {std::cout << "Goodbye!\n";}
} goodbye;
Run Code Online (Sandbox Code Playgroud)

问题是,虽然goodbye总是调用全局变量的构造函数,但是没有为某些程序调用析构函数,例如ls:

$ LD_PRELOAD=./preload.so ls
Hello
Run Code Online (Sandbox Code Playgroud)

对于其他一些程序,析构函数按预期调用:

$ LD_PRELOAD=./preload.so man
Hello
What manual page do you want?
Goodbye!
Run Code Online (Sandbox Code Playgroud)

你能解释为什么在第一种情况下没有调用析构函数吗?编辑:上面的问题已经得到解答,那就是程序可能会使用_exit(),abort()来退出.

然而:

有没有办法在预加载的程序退出时强制调用给定的函数?

kel*_*tar 31

lsatexit (close_stdout);初始化代码.当它完成,它关闭标准输出(即close(1)),所以你的cout,printf或者write(1, ...操作不会显示任何信息.它并不意味着不会调用析构函数.您可以通过在析构函数中创建新文件来验证这一点.

http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c#n1285这里是GNU coreutils ls中的一行.

它不仅仅是ls,大多数coreutils都这样做.不幸的是,我不知道他们为什么喜欢关闭它的确切原因.

关于如何找到它(或者至少我做了什么)的附注 - 可能有助于下次或没有源代码访问的程序:

析构函数消息打印/bin/true(我能想到的最简单的程序),但不打印lsdf.我开始使用strace /bin/truestrace /bin/ls比较最新的系统调用.它显示close(1)close(2)ls,但没有true.之后事情开始变得有意义,我只需要验证析构函数是否被调用.

  • 我根据你的结果进行了一些挖掘:似乎这样做是为了检测`stdout`上的写入失败,然后才能退出失败.不幸的是,`close_stdout`的定义隐藏在gnulib中,但其中的注释非常清楚:http://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/closeout.c#n83 (7认同)

R..*_*R.. 9

如果程序通过_exit(POSIX)或_Exit(C99)退出或程序异常终止(abort致命信号等),则无法调用析构函数.我没有看到任何解决方法.

  • 为什么`/bin/ls` 会这样做? (2认同)
  • 这是一个很好的猜测,但是http://lingrok.org/xref/coreutils/src/ls.c以`exit(exit_status);`结尾(现在指定OP在linux上) (2认同)