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?