Выбор правильной функции io_getevents из libaio.so.1 с использованием dlsym - PullRequest
0 голосов
/ 16 ноября 2018

Я пытаюсь перехватить вызовы 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. Есть ли лучший способ сделать это?

...