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
hasatexit (close_stdout);
as its initialisation code. When it finishes, it closes stdout (i.e.close(1)
), so yourcout
,printf
orwrite(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 withls
ordf
. I started withstrace /bin/true
andstrace /bin/ls
and compared latest system calls. It shownclose(1)
andclose(2)
forls
, but none fortrue
. After that things started to make sense and I just had to verify that destructor is called.If the program exits via
_exit
(POSIX) or_Exit
(C99) or abnormal program termination (abort
, fatal signals, etc.) then there's no way destructors could be called. I don't see any way around this.Like others said, a program might call via
_exit()
,_Exit()
orabort()
and your destructors won't even notice. To solve these cases you could override these functions by just writing a wrapper like the following the example below:But this wouldn't solve all the possibilities of a program escaping without your library's knowledge, because those are not the only exit points an application could have. It could also trigger the
exit
system call via thesyscall()
function and to avoid it you would have to wrap it too.Another way a program could exit is by receiving an unhandled signal, so you should also handle (or wrap?) all signals that could trigger the death of a program. Read the
signal(2)
man page for more information but please be aware that signals likeSIGKILL
(9) cannot be handled and an application could destroy itself by callingkill()
. With that being said and unless you don't expect to handle insane applications written by crazy monkeys you should wrapkill()
too.Another system call you'd have to wrap is
execve()
.Anyway, a system call (like
_exit
) could also be triggered directly via an assemblyint 0x80
instruction or the obsolete_syscallX()
macro. How'd you wrap it if not from outside the application (likestrace
orvalgrind
)? Well, if you expect this kind of behaviour in you programs I suggest you drop theLD_PRELOAD
technique and start thinking about doing likestrace
andvalgrind
do (usingptrace()
from another process) or creating a Linux kernel module to trace it.