8

I'm currently working on a project where I need to track the usage of several system calls and low-level functions like mmap, brk, sbrk. So far, I've been doing this using function interposition: I write a wrapper function with the same name as the function I'm replacing (mmap for example), and I load it in a program by setting the LD_PRELOAD environment variable. I call the real function through a pointer that I load with dlsym.

Unfortunately, one of the functions I want to wrap, sbrk, is used internally by dlsym, so the program crashes when I try to load the symbol. sbrk is not a system call in Linux, so I can't simply use syscall to call it indirectly.

So my question is, how can I call a library function from a wrapper function of the same name without using dlsym? Is there any compiler trick (using gcc) that lets me refer to the original function?

Jay Conrod
  • 28,943
  • 19
  • 98
  • 110

4 Answers4

15

see ld's option --wrap symbol. From the man page:

--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.

This can be used to provide a wrapper for a system function. The wrapper function should be called "__wrap_symbol". If it wishes to call the system function, it should call "__real_symbol".

Here is a trivial example:

void *
__wrap_malloc (size_t c)
{
    printf ("malloc called with %zu\n", c);
    return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead. The call to "__real_malloc" in
"__wrap_malloc" will call the real "malloc" function.

You may wish to provide a "__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as "__wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".

The other option is to possibly look at the source for ltrace, it is more or less does the same thing :-P.

Here's an idea though. You could have your LD_PRELOAD'ed library change the PLT entries to point to your code. This you technically the sbrk() function is still callable from your code nativly.

Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • This is great. I had never heard of the --wrap option, but this is exactly what I need. Thank you. – Jay Conrod Jun 15 '09 at 21:48
  • To clarify, do the --wrap flags need to be passed when linking the executable or when linking the LD_PRELOAD library containing the wrappers? Also, would you consider providing more information about modifying the PLT entries of the executable? – Jay Conrod Jun 15 '09 at 22:25
  • The designed use case is for you to link the *target* app with --wrap. It may be possible to make it work for the LD_PRELOAD case as well, I'm not sure, I'll have to test. – Evan Teran Jun 16 '09 at 01:46
2

You can examine function invocation unobtrusively using tools such as:

  • gdb
  • ltrace
  • systemtap

These tools allow a monitor program to inform you when a function is called, and allow you to interrogate the arguments.

The main differences are:

  • gdb is interactive, but powerful
  • ltrace simple to use, but you can only print the function name
  • systemtap is not interactive, but it can be very fast, and is powerful.
Alex Brown
  • 41,819
  • 10
  • 94
  • 108
0

If you are running a host system with glibc, the libc has some internal back end to the runtime dynamic linker that I used some time ago. If I recall correctly, I think it's called '__libc_dlsym'. (To check, "$ readelf -s /usr/lib/libc.a | grep dlsym" should help.) Declare it as an externally linked function with the same arguments and return value that dlsym has and use it to wrap dlsym itself.

0

Does truss not work on your system? It works perfectly for this kind of things here on Solaris.

Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48
  • That sounds exactly like `strace` in Linux. This works fine if you only need to get a list of calls that were made. In this project, I needed to add some functionality to the wrappers, and I think interposition is the only way to go for that. – Jay Conrod Jun 22 '11 at 17:08