Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use dlinfo to print all symbols in a library

Tags:

c

dlopen

I have a C++ class that uses dlopen to load a library. As an exercise I was trying to dump all of the symbol names from the loaded library.

I've used dlinfo to load the linkmap via RTDL_DI_LINKMAP:

struct link_map
  {
    ElfW(Addr) l_addr;                /* Base address shared object is loaded at.  */
    char *l_name;                     /* Absolute file name object was found in.  */
    ElfW(Dyn) *l_ld;                  /* Dynamic section of the shared object.  */
    struct link_map *l_next, *l_prev; /* Chain of loaded objects.  */
  };

This provides me a linked list of libraries that have been loaded by the dlopen call. I then thought that I could process the l_ld dynamic sections, get the symbol lookup table and find all the symbol names. However, I cannot work out how the dynamic SYMTAB section is supposed to be used. Casting the DT_SYMTAB to a ElfW(Sym) doesn't seem to result in anything. What is the SYMTAB pointing too? Do I have a bad offset? I have included where I have got so far below.


#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

#ifdef __cplusplus
# define __STDC_FORMAT_MACROS
#endif

#include <inttypes.h>

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

///////////////////////////////////////////////////////////////////////////////
static const ElfW(Dyn) *
FindTag(const ElfW(Dyn) * dyn, const ElfW(Sxword) tag) {
  for (; dyn->d_tag != DT_NULL; ++dyn) {
    if (dyn->d_tag == tag) {
      return dyn;
    }
  }
  return 0;
}

///////////////////////////////////////////////////////////////////////////////
static size_t
FindVal(const ElfW(Dyn) * dyn, const ElfW(Sxword) tag) {
  for (; dyn->d_tag != DT_NULL; ++dyn) {
    if (dyn->d_tag == tag) {
      return dyn->d_un.d_val;
    }
  }
  assert(false);
}

///////////////////////////////////////////////////////////////////////////////
static const void *
FindPtr(const ElfW(Addr) load_addr,
           const ElfW(Dyn) * dyn, const ElfW(Sxword) tag) {
  for (; dyn->d_tag != DT_NULL; ++dyn) {
    if (dyn->d_tag == tag) {
      return (const void *)(dyn->d_un.d_ptr - load_addr);
    }
  }
  assert(false);
}

///////////////////////////////////////////////////////////////////////////////
#define Title(...) printf("-------------------------------------------------" \
  "------------------------------\n" __VA_ARGS__)

///////////////////////////////////////////////////////////////////////////////
int main(const int argc, const char * const * const argv) {
  assert((argc == 2) && (argc == 2));
  const char * const lib = argv[1];
  Title("Loading: %s\n", lib);
  void * const handle = dlopen(lib, RTLD_LAZY);
  assert(handle != 0);
#ifdef _GNU_SOURCE
  // Get the link map
  const struct link_map * link_map = 0;
  const int ret = dlinfo(handle, RTLD_DI_LINKMAP, &link_map);
  const struct link_map * const loaded_link_map = link_map;
  assert(ret == 0);
  assert(link_map != 0);
  Title("Libraries:\n");
  while (link_map->l_prev) {
    link_map = link_map->l_prev;
  }
  while (link_map) {
    printf(" - %s (0x%016" PRIX64 ")\n", link_map->l_name, link_map->l_addr);
    link_map = link_map->l_next;
  }
  // Process the dynamic sections
  const ElfW(Dyn) * const dyn_start = loaded_link_map->l_ld;
  const ElfW(Addr) load_addr = loaded_link_map->l_addr;
  Title("Dynamic Sections (%s):\n", loaded_link_map->l_name);
  printf("|%-16s|%-10s|%-12s|%-16s|%-16s|\n", "Tag", "Tag", "Value", "Ptr",
    "Offset");
  for (const ElfW(Dyn) * dyn = dyn_start; dyn->d_tag != DT_NULL; ++dyn) {
    switch (dyn->d_tag) {
      #define print(tag) \
        printf("|%-16s|0x%-8" PRIx64 "|%12" PRIu64 "|%-16p|%-16p|\n", \
          tag, dyn->d_tag, dyn->d_un.d_val, (const void *)(dyn->d_un.d_ptr), \
          (const void *)(dyn->d_un.d_ptr - link_map->l_addr)); \
        break
      #define case(tag) case tag: print(#tag)
      #define default(tag) default: print(#tag)
      case (DT_NEEDED);           /* Name of needed library */
      case (DT_PLTRELSZ);         /* Size in bytes of PLT relocs */
      case (DT_PLTGOT);           /* Processor defined value */
      case (DT_HASH);             /* Address of symbol hash table */
      case (DT_STRTAB);           /* Address of string table */
      case (DT_SYMTAB);           /* Address of symbol table */
      case (DT_RELA);             /* Address of Rela relocs */
      case (DT_RELASZ);           /* Total size of Rela relocs */
      case (DT_RELAENT);          /* Size of one Rela reloc */
      case (DT_STRSZ);            /* Size of string table */
      case (DT_SYMENT);           /* Size of one symbol table entry */
      case (DT_INIT);             /* Address of init function */
      case (DT_FINI);             /* Address of termination function */
      case (DT_SONAME);           /* Name of shared object */
      case (DT_RPATH);            /* Library search path (deprecated) */
      case (DT_SYMBOLIC);         /* Start symbol search here */
      case (DT_REL);              /* Address of Rel relocs */
      case (DT_RELSZ);            /* Total size of Rel relocs */
      case (DT_RELENT);           /* Size of one Rel reloc */
      case (DT_PLTREL);           /* Type of reloc in PLT */
      case (DT_DEBUG);            /* For debugging; unspecified */
      case (DT_TEXTREL);          /* Reloc might modify .text */
      case (DT_JMPREL);           /* Address of PLT relocs */
      case (DT_BIND_NOW);         /* Process relocations of object */
      case (DT_INIT_ARRAY);       /* Array with addresses of init fct */
      case (DT_FINI_ARRAY);       /* Array with addresses of fini fct */
      case (DT_INIT_ARRAYSZ);     /* Size in bytes of DT_INIT_ARRAY */
      case (DT_FINI_ARRAYSZ);     /* Size in bytes of DT_FINI_ARRAY */
      case (DT_RUNPATH);          /* Library search path */
      case (DT_FLAGS);            /* Flags for the object being loaded */
      case (DT_ENCODING);         /* Start of encoded range */
        /* This is a duplicate value Have submitted this as a possible bug:
         * http://sourceware.org/bugzilla/show_bug.cgi?id=15733
         */
//      case (DT_PREINIT_ARRAY);    /* Array with addresses of preinit fct*/
      case (DT_PREINIT_ARRAYSZ);  /* size in bytes of DT_PREINIT_ARRAY */
      case (DT_NUM);              /* Number used */
      case (DT_LOOS);             /* Start of OS-specific */
      case (DT_HIOS);             /* End of OS-specific */
      case (DT_LOPROC);           /* Start of processor-specific */
      case (DT_HIPROC);           /* End of processor-specific */
      case (DT_PROCNUM);          /* Most used by any processor */
      case (DT_GNU_HASH);         /* GNU-style hash table.  */
      case (DT_VERDEF);           /* Address of version definition table */
      case (DT_VERDEFNUM);        /* Number of version definitions */
      case (DT_VERNEED);          /* Address of table with needed versions */
      case (DT_VERNEEDNUM);       /* Number of needed versions */
      case (DT_VERSYM);           /* The versioning entry types. */
      case (DT_RELACOUNT);
      case (DT_CHECKSUM);
      case (DT_GNU_PRELINKED);    /* Prelinking timestamp */
      default(UNKNOWN);
      #undef print
      #undef case
    }
  }
  // Some aliases
  #define GetTag(tag) FindTag(dyn_start, tag)
  #define GetVal(tag) FindVal(dyn_start, tag)
  #define GetPtr(tag) FindPtr(load_addr, dyn_start, tag)
  #define IterTag(tag) \
    for (const ElfW(Dyn) * dyn = GetTag(tag); dyn; dyn = FindTag(++dyn, tag))
  // Get the string table
  const size_t strtabsize = GetVal(DT_STRSZ);
  const char * const strtab = (const char * const)GetPtr(DT_STRTAB);
  Title("String Table: %p (%" PRIu64")\n", strtab, strtabsize);
  // Get the so name
  Title("SO Name: %s\n", &strtab[GetVal(DT_SONAME)]);
  // Get the needed libraries
  Title("Needed:\n");
  IterTag(DT_NEEDED) {
    const size_t index = dyn->d_un.d_val;
    assert(index < strtabsize);
    printf(" - %s\n", &strtab[dyn->d_un.d_val]);
  }
  // Get the symbol table
  typedef ElfW(Sym) SymEnt;
  const size_t symentsize = GetVal(DT_SYMENT);
  const SymEnt * const symtab = (const SymEnt*)GetVal(DT_SYMTAB);
  const SymEnt * syment = symtab;
  Title("Symbols:\n");
  printf("|%-16s|%-10s|%-10s|%-8s|%-16s|%-8s|\n", "Name", "Type",
    "Visibility", "Section", "Addr", "Size");
  while (syment->st_shndx != STN_UNDEF) {
    assert(syment->st_name);
    assert(syment->st_name < strtabsize);
    printf("|%-16s|%10u|%10u|%8u|%-16p|%8" PRIu64"|\n",
      &strtab[syment->st_name], syment->st_info, syment->st_other,
      syment->st_shndx, (const void*)(syment->st_value), syment->st_size);
    syment = (const SymEnt*)((const uint8_t*)(syment) + symentsize);
  }
#else
# warning Not using GNU extensions
#endif
  dlclose(handle);
  return 0;
}

Which produces the following ./main libm.so:

-------------------------------------------------------------------------------
Loading: libm.so
-------------------------------------------------------------------------------
Libraries:
 -  (0x0000000000000000)
 -  (0x00007FFF7A6FE000)
 - /lib64/libdl.so.2 (0x0000000000000000)
 - /lib64/libstdc++.so.6 (0x0000000000000000)
 - /lib64/libm.so.6 (0x0000000000000000)
 - /lib64/libgcc_s.so.1 (0x0000000000000000)
 - /lib64/libc.so.6 (0x0000000000000000)
-------------------------------------------------------------------------------
Dynamic Sections (/lib64/libm.so.6):
|Tag             |Tag       |Value       |Ptr             |Offset          |
|DT_NEEDED       |0x1       |        3127|0xc37           |0xc37           |
|DT_SONAME       |0xe       |        3137|0xc41           |0xc41           |
|DT_INIT         |0xc       |259952497752|0x3c86605458    |0x3c86605458    |
|DT_FINI         |0xd       |259952921100|0x3c8666ca0c    |0x3c8666ca0c    |
|DT_INIT_ARRAY   |0x19      |259955596424|0x3c868f9c88    |0x3c868f9c88    |
|DT_INIT_ARRAYSZ |0x1b      |           8|0x8             |0x8             |
|DT_FINI_ARRAY   |0x1a      |259955596432|0x3c868f9c90    |0x3c868f9c90    |
|DT_FINI_ARRAYSZ |0x1c      |           8|0x8             |0x8             |
|DT_HASH         |0x4       |259953492808|0x3c866f8348    |0x3c866f8348    |
|DT_GNU_HASH     |0x6ffffef5|259952476800|0x3c86600280    |0x3c86600280    |
|DT_STRTAB       |0x5       |259952492008|0x3c86603de8    |0x3c86603de8    |
|DT_SYMTAB       |0x6       |259952482024|0x3c866016e8    |0x3c866016e8    |
|DT_STRSZ        |0xa       |        3194|0xc7a           |0xc7a           |
|DT_SYMENT       |0xb       |          24|0x18            |0x18            |
|DT_PLTGOT       |0x3       |259955597288|0x3c868f9fe8    |0x3c868f9fe8    |
|DT_PLTRELSZ     |0x2       |         552|0x228           |0x228           |
|DT_PLTREL       |0x14      |           7|0x7             |0x7             |
|DT_JMPREL       |0x17      |259952497200|0x3c86605230    |0x3c86605230    |
|DT_RELA         |0x7       |259952496216|0x3c86604e58    |0x3c86604e58    |
|DT_RELASZ       |0x8       |         984|0x3d8           |0x3d8           |
|DT_RELAENT      |0x9       |          24|0x18            |0x18            |
|DT_VERDEF       |0x6ffffffc|259952496040|0x3c86604da8    |0x3c86604da8    |
|DT_VERDEFNUM    |0x6ffffffd|           4|0x4             |0x4             |
|DT_FLAGS        |0x1e      |          16|0x10            |0x10            |
|DT_VERNEED      |0x6ffffffe|259952496168|0x3c86604e28    |0x3c86604e28    |
|DT_VERNEEDNUM   |0x6fffffff|           1|0x1             |0x1             |
|DT_VERSYM       |0x6ffffff0|259952495202|0x3c86604a62    |0x3c86604a62    |
|DT_RELACOUNT    |0x6ffffff9|          32|0x20            |0x20            |
|DT_CHECKSUM     |0x6ffffdf8|  2098136911|0x7d0f074f      |0x7d0f074f      |
|DT_GNU_PRELINKED|0x6ffffdf5|  1370399848|0x51aea468      |0x51aea468      |
-------------------------------------------------------------------------------
String Table: 0x3c86603de8 (3194)
-------------------------------------------------------------------------------
SO Name: libm.so.6
-------------------------------------------------------------------------------
Needed:
 - libc.so.6
-------------------------------------------------------------------------------
Symbols:
|Name            |Type      |Visibility|Section |Addr            |Size    |
like image 711
Matt Clarkson Avatar asked Jul 12 '13 17:07

Matt Clarkson


1 Answers

There are (at least) two bugs in your program:

  • you are subtracting load_addr, but that only works on systems where libm.so.6 is prelinked. On systems where it is not prelinked, your program crashes trying to print SONAME.
  • your symbol printing loop: while (syment->st_shndx != STN_UNDEF) { is incorrect, because shared libraries start with just such a symbol, e.g.

    readelf -Ws /lib/x86_64-linux-gnu/libm.so.6 | head
    
    Symbol table '.dynsym' contains 415 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND   <<<-- your loop stops here
         1: 00000000000053c8     0 SECTION LOCAL  DEFAULT   11
         2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.2.5 (5)
         3: 0000000000000000     0 TLS     GLOBAL DEFAULT  UND errno@GLIBC_PRIVATE (6)
         4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strtod@GLIBC_2.2.5 (5)
         5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.2.5 (5)
         6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __get_cpu_features@GLIBC_PRIVATE (6)
    

Instead, you should loop until you've looped over all the symbols (I believe the only way you can calculate the number of symbols is by decoding DT_HASH or DT_GNU_HASH).

When I adjust your source for the two problems above, I get:

Needed:
 - libc.so.6
-------------------------------------------------------------------------------
Symbols:
|Name            |Type      |Visibility|Section |Addr            |Size    |
|                |         0|         0|       0|(nil)           |       0|
|                |         3|         0|      11|0x53c8          |       0|
|__errno_location|        18|         0|       0|(nil)           |       0|
|errno           |        22|         0|       0|(nil)           |       0|
|strtod          |        18|         0|       0|(nil)           |       0|
|strlen          |        18|         0|       0|(nil)           |       0|
|__get_cpu_features|        18|         0|       0|(nil)           |       0|
|__assert_fail   |        18|         0|       0|(nil)           |       0|
|fputs           |        18|         0|       0|(nil)           |       0|
|strtof          |        18|         0|       0|(nil)           |       0|

... which matches readelf output.

like image 200
Employed Russian Avatar answered Oct 15 '22 04:10

Employed Russian