Я пытаюсь перехватить вызовы io_getevents (и другие вызовы aio), написав общую библиотеку и используя ее с LD_PRELOAD перед запуском двоичного файла.
Что я заметил, так это то, что «настоящая» функция io_getevents, которая должна вызываться, отличается от той, которую я получаю с помощью dlsym и RTLD_NEXT.
Я написал минимальный пример проблемы.
#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;
}
А вот и вывод
$ 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
Не используя dlsym / dlopen, я попробовал следующее
#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;
}
И запустил его следующим образом -
$ 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. Почему один из них использует адрес 650, а другой 750?
Q2. Похоже, мне нужно использовать тот, который заканчивается на 650. Когда я использовал LD_PRELOAD и перехватил функцию io_getevents и отправил ее по адресу 750, это не сработало. Чтобы это исправить, я жестко закодировал адрес, используя dlInfo.dli_fbase + 0x650
. Есть ли лучший способ сделать это?