Search code examples
clinuxld

Get the address space of a dynamic loaded library


Can anyone help me with finding out how can I obtain the address space of a dynamically loaded library?

The context: I'm loading a shared library using dlopen and at some other point I want to track down malloc calls, but only these which were triggered by this library.

What I'm doing right now is in the malloc hook I have, I'm going through the whole callstack (obtained by backtrace) and using dladdr I'm checking each function pointer to see if it comes from my shared library or not, but it's extremely slow.

I thought that maybe if I care only about the one lib which I'm loading manually I can just get its address space, like it is in the memory maps output:

$ 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
...

and see if the function address from the callstack is contained in this address space? It should be much faster I guess... How can I do that? Maybe I can get .text symbol address of the lib using dlsym (since I care only on address of the executable code), but how calculate the size then?


Solution

  • In your interposed functions, you can use uintptr_t caller_address = (uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0)); to obtain the address of the caller.

    To find out which addresses are from which ELF files, either parse /proc/self/maps, or use dl_iterate_phdr(). For example:

    #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);
        }
    
        ...