Consider the following library which can be preloaded before any program execution:
// 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;
The problem is that, while the constructor of the global variable goodbye
is always called, the destructor is not called for some programs, like ls
:
$ LD_PRELOAD=./preload.so ls Hello
For some other programs, the destructor is called as expected:
$ LD_PRELOAD=./preload.so man Hello What manual page do you want? Goodbye!
Can you explain why the destructor is not called in the first case? EDIT: the above question has been already answered, that is a program might well use _exit(), abort() to exit.
However:
Is there a way to force a given function being called when a preloaded program exits?
ls
has atexit (close_stdout);
as its initialisation code. When it finishes, it closes stdout (i.e. close(1)
), so your cout
, printf
or write(1, ...
operations will not print anything. It doesn't mean destructor isn't called. You can verify this by e.g. creating a new file in your destructor.
http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c#n1285 here is the line in GNU coreutils ls.
It is not just ls
, most of coreutils do that. Unfortunately, I don't know exact reason why they prefer to close it.
Side note on how this could be found (or at least what I did) - may help next time or with program with no source code access:
Destructor message is printed with /bin/true
(simplest program I could think of), but isn't printed with ls
or df
. I started with strace /bin/true
and strace /bin/ls
and compared latest system calls. It shown close(1)
and close(2)
for ls
, but none for true
. After that things started to make sense and I just had to verify that destructor is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With