Get Functions Names in a Shared Library Programmatically

Get functions names in a shared library programmatically

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.

How can a shared library (.so) call a function that is implemented in its loader code?

You'll need make a register function in your .so so that the executable can give a function pointer to your .so for it's later used.

Like this:

void in_main_func () {
// this is the function that need to be called from a .so
}

void (*register_function)(void(*)());
void *handle = dlopen("libmylib.so");

register_function = dlsym(handle, "register_function");

register_function(in_main_func);

the register_function needs to store the function pointer in a variable in the .so where the other function in the .so can find it.

Your mylib.c would the need to look something like this:

void (*callback)() = NULL;

void register_function( void (*in_main_func)())
{
callback = in_main_func;
}

void function_needing_callback()
{
callback();
}

function in one shared library calling a function in another shared library

If you have called only print1 in your main.c. Then set the path of the libfile2.so in the LD_LIBRARY_PATH. Because it will try to find the dependencies of libfile1.so while linking with main.c.

gcc -o file1.o -c file.c
gcc -o file2.o -c file.c
gcc -o libfile2.so file2.o -shared
gcc -o libfile1.so file1.o -L. -lfile2 -shared
gcc -o main.o -c main.c
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
gcc -o main.exe main.o -L. -lfile1

If you have called both print1 and print2 in main.c then link both libfile1.so and libfile2.so like below.

gcc -o main.o -c main.c
gcc -o main.exe main.o -L$YOUR_LIB_PATH -lfile1 -lfile2

Because all the symbol used in main.c needs to be resolved while generating executable.

Shared Library Discovery

AFAIK, there is no glibc function to enumerate all the public interface functions for a .so file. You can refer to libelf to read all symbols from a dynamic file. Libelf is here http://www.mr511.de/software/. After you find a symbol, you can use dlopen and dlsym to load it.

Programatically determine shared libraries in use by running application

Since it looks like you're using something UNIX-y, just use dlopen instead of dynamically linking your driver app against the missing symbol.

Full sequence is:

  1. iterate over all submitted .so library filenames somehow (maybe you have one directory with studentname.so or something)
  2. load each library
  3. get the entry point function
  4. call it
  5. unload library (optional, I guess)

like so:

void *lib = dlopen(filename, RTLD_LOCAL);
void *libfun = dlsym(lib, "doAction");
if (libfun == NULL)
cout << "student failed by not providing doAction() in " << filename << endl;
else {
void (*doAction)(void) = (void (*)(void)) libfun;
// no, I can't remember the correct syntax for casting to function pointer
cout << "calling " << filename << ":doAction()" << endl;
doAction();
// is there some way to tell if it succeeded?
cout << "unloading " << filename << endl;
dlclose(lib);
}

Notes:

  • if the interface is the same in each case (ie, void (*)()), you could make this configurable by directory name and symbol name, and it'd work for more than one test
  • in fact, if the interface is NOT what you expect, the function pointer cast will do horrible things, so careful with this
  • finally, if the student used C++, their function name symbol will be mangled. Tell them to declare the entry-point as extern "C" void doAction() to avoid that.
  • the RTLD_LOCAL flag should stop anything in one student's library interfering with another (if you don't unload), but there are other flags it may be sensible to add
    • specifically, RTLD_NOW will cause dlopen to fail if the student lib has an unresolved external reference it can't figure out (so you can handle it gracefully, by failing them): otherwise your program may just crash when you call doAction.

Although I think the above is better than the solution you're directly asking for help with, I did also find a reference to dl_iterate_phdr while double-checking the docs. If you're on Linux specifically, and if the dl_phdr_info.dlpi_name is actually the filename ... you might be able to get it that way.

I still think it's much uglier, though.



Related Topics



Leave a reply



Submit