问题
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 |
回答1:
There are (at least) two bugs in your program:
- you are subtracting
load_addr
, but that only works on systems wherelibm.so.6
is prelinked. On systems where it is not prelinked, your program crashes trying to printSONAME
. 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.
来源:https://stackoverflow.com/questions/17620751/use-dlinfo-to-print-all-symbols-in-a-library