clinuxsystem-callslibc

How do I reimplement (or wrap) a syscall function on Linux?


Suppose I want to completely take over the open() system call, maybe to wrap the actual syscall and perform some logging. One way to do this is to use LD_PRELOAD to load a (user-made) shared object library that takes over the open() entry point.

The user-made open() routine then obtains the pointer to the glibc function open() by dlsym()ing it, and calling it.

The solution proposed above is a dynamic solution, however. Suppose I want to link my own open() wrapper statically. How would I do it? I guess the mechanism is the same, but I also guess there will be a symbol clash between the user-defined open() and the libc open().

Please share any other techniques to achieve the same goal.


Solution

  • You can use the wrap feature provided by ld. From man ld:

    --wrap symbol Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol.

    Any undefined reference to __real_symbol will be resolved to symbol.

    So you just have to use the prefix __wrap_ for your wrapper function and __real_ when you want to call the real function. A simple example is:

    malloc_wrapper.c:

    #include <stdio.h>
    void *__real_malloc (size_t);
    
    /* This function wraps the real malloc */
    void * __wrap_malloc (size_t size)
    {
        void *lptr = __real_malloc(size);
        printf("Malloc: %lu bytes @%p\n", size, lptr);
        return lptr;
    }
    

    Test application testapp.c:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        free(malloc(1024)); // malloc will resolve to __wrap_malloc
        return 0;
    }
    

    Then compile the application:

    gcc -c malloc_wrapper.c
    gcc -c testapp.c
    gcc -Wl,-wrap,malloc testapp.o malloc_wrapper.o -o testapp
    

    The output of the resulting application will be:

    $ ./testapp
    Malloc: 1024 bytes @0x20d8010