Choosing the right io_getevents function from libaio.so.1 using dlsym

303 Views Asked by At

I'm trying to intercept calls to io_getevents (and other aio calls) by writing a shared library and using that with LD_PRELOAD before running a binary.

What I've noticed is that the "actual" io_getevents function that should be called is not the same as the one I get with dlsym and RTLD_NEXT.

I've written a minimal example of the problem.

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <libaio.h>

void print_dl_info(void *fn) {
    Dl_info dlInfo;
    if(!dladdr(fn, &dlInfo)) {
        fprintf(stderr, "dlInfo failed: %s\n", dlerror());
        return;
    }

    printf("dlInfo name %s, base %p, sname %s, saddr %p\n",
        dlInfo.dli_fname, dlInfo.dli_fbase, dlInfo.dli_sname, dlInfo.dli_saddr);
}

int main() {
    void *handle;
    void *fn;

    // Opening the shared library directly
    handle = dlopen("libaio.so.1",  RTLD_NOW);
    if (handle == NULL) {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        return 1;
    }

    fn = dlsym(handle, "io_getevents");
    if (fn == NULL) {
        fprintf(stderr, "dlsym failed: %s\n", dlerror());
        return 1;
    }

    printf("When opening libaio.so.1 directly\n");
    print_dl_info(fn);
    dlclose(handle);

    // Just using RTLD_NEXT (this is what I was using with LD_PRELOAD)
    // It gives a different function address.
    fn = dlsym(RTLD_NEXT, "io_getevents");
    printf("When using RTLD_NEXT\n");
    print_dl_info(fn);

    io_getevents(NULL, 0, 0, NULL, NULL);
    return 0;
}

And here's the output

$ gcc test3.c -ldl -laio
$ ./a.out
When opening libaio.so.1 directly
dlInfo name /lib/x86_64-linux-gnu/libaio.so.1, base 0x7fc9a1bbb000, sname io_getevents, saddr 0x7fc9a1bbb650
When using RTLD_NEXT
dlInfo name /lib/x86_64-linux-gnu/libaio.so.1, base 0x7fc9a1bbb000, sname io_getevents, saddr 0x7fc9a1bbb770



$ nm -D /lib/x86_64-linux-gnu/libaio.so.1
00000000000006a0 T io_cancel
00000000000006d0 T io_cancel
00000000000006c0 T io_destroy
0000000000000650 T io_getevents
0000000000000770 T io_getevents
0000000000000590 T io_queue_init
00000000000005b0 T io_queue_release
00000000000005d0 T io_queue_run
0000000000000710 T io_queue_wait
00000000000005c0 T io_queue_wait
00000000000006b0 T io_setup
0000000000000690 T io_submit
0000000000000000 A LIBAIO_0.1
0000000000000000 A LIBAIO_0.4
                 U __stack_chk_fail

Without using dlsym/dlopen, I tried the following

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <libaio.h>

int main(int argc, char **argv) {
    io_context_t ctx;
    // Using gdb to print its address
    io_getevents(ctx, 0, 0, NULL, NULL);
    return 0;
}

And ran it as follows -

$ gcc -g test1.c -laio
$ gdb a.out
(gdb) set step-mode on
(gdb) b 7
Breakpoint 1 at 0x400575: file test1.c, line 7.
(gdb) r
Starting program: a.out

Breakpoint 1, main (argc=1, argv=0x7fffffffe5b8) at test1.c:9
9        io_getevents(ctx, 0, 0, NULL, NULL);
(gdb) s
0x00007ffff7bd5650 in io_getevents () from /lib/x86_64-linux-gnu/libaio.so.1

Q1. Why is it that one of them uses the address 650 and the other 750?

Q2. It looks like I need to use the one ending with 650. When I used LD_PRELOAD and intercepted an io_getevents function and sent it to the 750 address, it didn't work. To fix this, I hardcoded the address using dlInfo.dli_fbase + 0x650. Is there a better way to do it?

0

There are 0 best solutions below