Difference between revisions of "PTRACE LD PRELOAD comparison"
(Move page from old installation)
Latest revision as of 17:00, 22 April 2019
Most C compiled programs do not communicate directly with the kernel. Instead, the program calls a function defined inside the runtime library (on Linux, typically, glibc), which then proceeds to perform whatever preparations are necessary (such as copying the arguments into registers) and trigger the system call. The connection between the program and the runtime library's functions is performed when the program is loaded by the run time linker (on Linux, typically called /lib/ld-linux.so.6 or similar name).
If an environment variable named LD_PRELOAD is defined, any shared object linked by it is loaded by the dynamic linker first, before the dependencies explicitly stated in the program file. As a result, if this shared object has functions bearing the same name as functions defined in the run time library, the former's functions will be called. This allows a library to "wrap" the run time library's original functions, intercepting system calls. A shared object implementing LD_PRELOAD has the option to call the original system call, not call it, or even call several unrelated system calls, all using a standard C function.
Programming an LD_PRELOAD library, while not trivial, is a relatively straight forward task.
Ptrace is the name of a system call that handles the case where one process acts as a debugger for another. The debugger process can attach to another process, or the debugee can ask for its parent to become its debugger. The debugger then controls the debugee, allowing it to query its state, control the delivery of signals, and to single step it, run it until a signal, or run it until the debugee performs a system call.
While the function itself is fairly well documented, this documentation is very superficial. Almost everything about working with ptrace is extremely platform dependent. For example, the ptrace interface defines a command for retrieving a process' CPU registers. While this is a standard command, which registers exist is CPU dependent, the way they are numbered depends on the platform, and what each register means depends on both. Theoretically, you can write a cross platform program that manipulates the registers, but practically, anything you do has to be both operating system and CPU specific.
The theory of wrapping system calls with ptrace is this: Run the program "until syscall". When a system call arrives, the debugger gets called twice. Once right before the kernel starts processing the system call, and one immediately after it finished. Ptrace allows you to change all the registers involved in the system call (including the one determining which system call is being carried out), but does not allow you to cancel the system call. Ptrace also does not allow you to generate a system call out of the blue.
Of course, fakeroot-ng has to be able to do all three, plus much more. This is achieved by a host of tricks, described in the technical details page.