17

Can I get list of all functions names from a shared library (Linux only) programmatically when I am using dl_open()?

I want something like this:

std::vector<std::string> list_all_functions(void *dl) { //... what can I do here? } int main() { void * dl = dl_open("./mylib.so", RTLD_NOW); auto functions = list_all_functions(dl); //... dl_close(dl); return 0; } 

Example library (mylib.so)

Header (.h):

extern "C" { int sum (int a, int b); } 

Source (.c):

int sum (int a, int b) { return a + b; } 

Dirty hack that I know: use nm or objdump utility

4
  • 2
    check out man elf for the ELF API. There are various implementations such as fedorahosted.org/elfutils or sourceforge.net/p/elftoolchain/wiki/libelf Commented Aug 12, 2014 at 17:29
  • Even if you got the list of names, how would you call them if you don't know what parameters they take? Commented Aug 12, 2014 at 17:37
  • @MarkB , in my project, all functions have same signature, but there is other method to call function without specified signature Commented Aug 12, 2014 at 17:43
  • 1
    @AlexBar for your own project you can create a special function that lists all available ones in a module. Commented Feb 2, 2016 at 16:48

2 Answers 2

11

UPDATE | TL;DR :

I actually found shorter way:

 auto library = dlopen("/path/to/lib.so", RTLD_LAZY | RTLD_GLOBAL); const char * libname = "lib.so"; struct link_map * map = nullptr; dlinfo(library, RTLD_DI_LINKMAP, &map); Elf64_Sym * symtab = nullptr; char * strtab = nullptr; int symentries = 0; for (auto section = map->l_ld; section->d_tag != DT_NULL; ++section) { if (section->d_tag == DT_SYMTAB) { symtab = (Elf64_Sym *)section->d_un.d_ptr; } if (section->d_tag == DT_STRTAB) { strtab = (char*)section->d_un.d_ptr; } if (section->d_tag == DT_SYMENT) { symentries = section->d_un.d_val; } } int size = strtab - (char *)symtab; for (int k = 0; k < size / symentries; ++k) { auto sym = &symtab[k]; // If sym is function if (ELF64_ST_TYPE(symtab[k].st_info) == STT_FUNC) { //str is name of each symbol auto str = &strtab[sym->st_name]; printf("%s\n", str); } } 

OLD

I believe author does not need this anymore but maybe someone need actual code and here it is (based on previous answer)

First, we need a callback for dl_iterate_phdr():

static int callback(struct dl_phdr_info *info, size_t size, void *data) { // data is copy of 2nd arg in dl_iterate_phdr // you can use it for your lib name as I did const char * libname = (const char *)data; // if current elf's name contains your lib if (strstr(info->dlpi_name, libname)) { printf("loaded %s from: %s\n", libname, info->dlpi_name); for (int j = 0; j < info->dlpi_phnum; j++) { // we need to save dyanmic section since it contains symbolic table if (info->dlpi_phdr[j].p_type == PT_DYNAMIC) { Elf64_Sym * symtab = nullptr; char * strtab = nullptr; int symentries = 0; auto dyn = (Elf64_Dyn *)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); for (int k = 0; k < info->dlpi_phdr[j].p_memsz / sizeof(Elf64_Dyn); ++k) { if (dyn[k].d_tag == DT_SYMTAB) { symtab = (Elf64_Sym *)dyn[k].d_un.d_ptr; } if (dyn[k].d_tag == DT_STRTAB) { strtab = (char*)dyn[k].d_un.d_ptr; } if (dyn[k].d_tag == DT_SYMENT) { symentries = dyn[k].d_un.d_val; } } int size = strtab - (char *)symtab; // for each string in table for (int k = 0; k < size / symentries; ++k) { auto sym = &symtab[k]; auto str = &strtab[sym->st_name]; printf("%s\n", str); } break; } } } return 0; } 

Next, we call dl_iterate_phdr():

int main() { auto library = dlopen("/path/to/library.so", RTLD_LAZY | RTLD_GLOBAL); const char * libname = "library.so"; dl_iterate_phdr(callback, (void*)libname); return 0; } 

If you need to store those names somewhere, you can pass a pointer to your container, restore it with cast and write there.

For my example library:

#include "simple_lib.h" #include <cstdio> void __attribute__ ((constructor)) initLibrary(void) { printf("Library is initialized\n"); } void __attribute__ ((destructor)) cleanUpLibrary(void) { printf("Library is exited\n"); } void make_number() { printf("1\n"); } 

Prints this:

Library is initialized _ITM_deregisterTMCloneTable puts __gmon_start__ _ITM_registerTMCloneTable __cxa_finalize _Z11initLibraryv make_number _Z14cleanUpLibraryv Library is exited 
Sign up to request clarification or add additional context in comments.

1 Comment

#include <elf.h> #include <link.h> for Elf64_Sym and link_map
6

There is no libc function to do that. However, you can write one yourself (or copy/paste the code from a tool like readelf).

On Linux, dlopen() returns the address of a link_map structure, which has a member named l_addr that points to the base address of the loaded shared object (assuming your system doesn't randomize shared library placement, and that your library has not been prelinked).

On Linux, a way to find the base address (the address of Elf*_Ehdr) is to use dl_iterate_phdr() after dlopen()ing the library.

Having the ELF header, you should be able to iterate over a list of exported symbols (the dynamic symbol table), by first locating the Elf*_Phdr of type PT_DYNAMIC, and then locating DT_SYMTAB, DT_STRTAB entries, and iterating over all symbols in the dynamic symbol table. Use /usr/include/elf.h to guide you.

Additionally, you could use libelf, that I don't know very well personally.

However, note that you'll get a list of defined functions, but you'll have no idea how to call them.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.