获取动态加载库的地址空间

时间:2018-12-16 15:59:44

标签: c linux ld

有人可以帮助我了解如何获取动态加载的库的地址空间吗?

上下文: 我正在使用dlopen加载共享库,而在其他时候,我想跟踪malloc调用,但仅跟踪由该库触发的调用。

我现在正在做的是在我拥有的malloc钩子中,我正在遍历整个调用栈(由backtrace获得)并使用dladdr进行检查每个函数指针以查看它是否来自我的共享库,但这非常慢。

我认为,如果我只关心手动加载的一个库,就可以得到它的地址空间,就像它在内存映射输出中一样:

$ cat /proc/2049/maps | head
00400000-007a8000 r-xp 00000000 08:01 526896    /usr/bin/python3.5
009a8000-009aa000 r--p 003a8000 08:01 526896    /usr/bin/python3.5
...

并查看该地址空间中是否包含来自调用堆栈的函数地址?我猜应该会快得多...我该怎么办?也许我可以使用.text获得lib的dlsym符号地址(因为我只关心可执行代码的地址),但是如何计算大小呢?

1 个答案:

答案 0 :(得分:1)

在插入的函数中,可以使用uintptr_t caller_address = (uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0));获取呼叫者的地址。

要找出哪些地址来自哪个ELF文件,请解析/proc/self/maps或使用dl_iterate_phdr()。例如:

#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <link.h>
#include <string.h>
#include <stdio.h>

static int iterator(struct dl_phdr_info *info, size_t size, void *data)
{
    const char  *name;
    size_t       headers, h;

    /* Empty name refers to the binary itself. */
    if (!info->dlpi_name || !info->dlpi_name[0])
        name = (const char *)data;
    else
        name = info->dlpi_name;

    headers = info->dlpi_phnum;
    for (h = 0; h < headers; h++)
        if ((info->dlpi_phdr[h].p_type == PT_LOAD) &&
            (info->dlpi_phdr[h].p_memsz > 0) &&
            (info->dlpi_phdr[h].p_flags)) {
            const uintptr_t  first = info->dlpi_addr + info->dlpi_phdr[h].p_vaddr;
            const uintptr_t  last  = first + info->dlpi_phdr[h].p_memsz - 1;

            /* Addresses first .. last, inclusive, belong to binary 'name'. */
            printf("%s: %lx .. %lx\n", name, (unsigned long)first, (unsigned long)last);
        }

    return 0;
}

int main(int argc, char *argv[])
{
    if (dl_iterate_phdr(iterator, argv[0])) {
        fprintf(stderr, "dl_iterate_phdr() failed.\n");
        exit(EXIT_FAILURE);
    }

    ...