Search code examples
clinuxgnuglibc

Linux dladdr1(): Unable to get absolute pathname of the matched file


As per the man page extra_info contains absolute pathname. I am not sure in case I understood the man page correctly, but I am not able to get the absolute path name of the file. Here is what I had tried:

Source code:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <link.h>

#include <stdio.h>

int  main2(int i)
{
        return 2+i;
}

int main(void)
{
    Dl_info i={0};
    int r;
//  struct link_map ei_={0}, *ei=&ei_;
    struct link_map *ei=0;
    void *ptr = (void*)main2;

    r = dladdr1(ptr, &i, (void**)&ei, RTLD_DL_LINKMAP);

    if(r)
    {
        printf("name = %s [%s]\n", i.dli_sname, ei->l_name);
    }

    return 1;
}

Compiled like this:

gcc -g3 -rdynamic -ldl dlerr.c

Result:

name = main2 []   

gdb session:

24              printf("name = %s [%s]\n", i.dli_sname, ei->l_name);
(gdb)
name = main2 []
27          return 1;
(gdb) p i
$1 = {dli_fname = 0x7fffffffe5a7 "/home/user/learn/dlerr/a.out", dli_fbase = 0x400000,
  dli_sname = 0x400674 "main2", dli_saddr = 0x4008bd <main2>}
(gdb) p *ei
$2 = {l_addr = 0, l_name = 0x7ffff7ffe6d8 "", l_ld = 0x600e08, l_next = 0x7ffff7ffe6e0,
  l_prev = 0x0}
(gdb)

notes: info parameter gives required path currently but in a complex project we often find relative paths getting printed while debugging (not tried this API). So when man page says extra_info gives absolute path, I would like to depend on it. Again *info and *extra_info are not mutually exclusive wrt returning the path names at least as per man page. (gcc version is 4.8.x).


Solution

  • So when man page says extra_info gives absolute path

    The man page doesn't say that. It says:

    RTLD_DL_LINKMAP
              Obtain a pointer to the link map for the matched file.  The
              extra_info argument points to a pointer to a link_map
              structure (i.e., struct link_map **), defined in <link.h> as:
      ... copy of struct link_map from link.h ...
    

    The man page is correct: you get a pointer to link_map. The link.h is also correct, but incomplete.

    What's happening is that in the list of link_map entries, there are special entires for ELF objects that were not loaded by the linker; namely the main executable and (on non-ancient versions of glibc) the vdso.

    Since these entries were loaded by the kernel, the link_map doesn't contain their full pathname.

    info parameter gives required path currently but in a complex project we often find relative paths getting printed

    The path to main executable isn't known by the loader, so it can't tell you where that executable is.

    Usually you could find that path by treating the first entry in the link_map list specially: e.g. use readlink(/proc/self/exe).

    This fails in corner cases: when the /proc isn't mounted, or when the main executable has been deleted before your code gets to execute, but these conditions are quite rare.