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

3

32

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.

Diophantus answered 7/9, 2010 at 21:19 Comment(2)
How about you just stick a wrapper function/macro in your code?Potoroo
@Seamus : I prefer not to use macros. I don't really have a problem. I am asking just to increase SO knowledge and learn some new trick.Diophantus
F
63

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
Forsterite answered 7/9, 2010 at 21:35 Comment(4)
It looks that function that calls malloc cannot be dynamically linked. For example, I wrote a tools.c, which contains a function perform_alloc() which calls malloc. Then if I make a libtools.so first and link it dynamically by -ltools, -Wl,-wrap,malloc will not work.Bannasch
As answered in #3826608 it is required to prefix the function definitions with extern "C" when using this code in a C++ program.Statistician
Does the function have to be called __wrap_malloc? Or can it be named differently? What I mean is when wrapping system calls, do we have to name the functions same as the actual call?Paragon
@return0: as man ld shows, symbols must have the prefixes __wrap_ and __real_.Forsterite
A
3

Symbols are resolved by the linker in the order you list them on the command line so if you listed your library before the standard library you'd have precidence. For gcc you'd need to specify

gcc <BLAH> -nodefaultlibs <BLAH BLAH> -lYOUR_LIB <OTHER_LIBS>

This way your libraries would be searched and found first.

Alerion answered 7/9, 2010 at 21:39 Comment(0)
I
0

For linux and GNU libc, the library has built-in support for intercepting and reimplementing any function in the library.

If you define you own version of ANY libc function, and link it before libc (so have it part of your executable, or in a library linked before -lc on the link command or even loaded with LD_PRELOAD if libc is dynamically linke), it will be called instead of the libc version (even calls in other function in libc itself). You can then call the function with a __libc_ prefix to get the actual version in the library (though you will need to declare that symbol yourself.) For example:

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       extern int __libc_open(const char *pathname, int flags, mode_t mode);

       int open(const char *pathname, int flags, mode_t mode) {
           return __libc_open(pathname, flags, mode);
       }
Inspissate answered 15/8, 2023 at 22:37 Comment(3)
I've successfully employed this technique for malloc/free, but no luck with open/close. Your example code does not compile on GCC 9.4.0 nor on onlinegdb.com (error: conflicting types for ‘open’). If I fix the conflicting definition then the error changes to "undefined reference to `__libc_open'". If this is due to some recent change in libc, can you perhaps update your answer?Toon
@itaych: those errors probably come from using some other libc, not GNU libc. As far as I know, only Linux uses GNU libc, and only if you don't install some other libc.Inspissate
I have never installed any other libc. My local system is Ubuntu 20.04.4 LTS, GCC 9.4.0, and the output of the script at https://mcmap.net/q/454440/-how-to-check-libc-version says "glibc 2.31". I also have GCC 8.3.0 which reports glibc 2.29. At onlinegdb.com the values of __ GLIBC __ and __ GLIBC_MINOR __ are 2 and 35 respectively and "linux" is defined. So these are three different compilers (one of which you are welcome to test for yourself), all using Linux and GNU libc and none of them work for me. On what environment does your code compile?Toon

© 2022 - 2024 — McMap. All rights reserved.