Function interposition in Linux without dlsym
Asked Answered
C

4

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?

Coed answered 15/6, 2009 at 21:22 Comment(0)
S
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.

Surfperch answered 15/6, 2009 at 21:28 Comment(3)
This is great. I had never heard of the --wrap option, but this is exactly what I need. Thank you.Coed
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?Coed
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.Surfperch
C
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.
Cockeye answered 15/6, 2009 at 21:36 Comment(0)
B
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.

Bergama answered 14/8, 2009 at 3:34 Comment(0)
C
0

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

Celibate answered 21/6, 2011 at 19:32 Comment(1)
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.Coed

© 2022 - 2024 — McMap. All rights reserved.