Programatically determine shared libraries in use by running application

4.2k views Asked by At

Is it possible to (and, if so, how does one) determine the shared libraries of an application that are used by an application at runtime? Basically, can I programmatically obtain the the output of ldd? Preferred C/C++ solution does not just jump to execute ldd on the command-line.

Consider the following: I have a driver application that calls doAction() from a shared library libfoo. I compile the application once and then set LD_LIBRARY_PATH to an appropriate directory containing a libfoo with the doAction() symbol defined. This way, I can have multiple implementations of doAction() in different libfoos but only ever compile an application once.

A real world example would be a professor having a class of students implement doAction(). Instead of compiling a test harness against each student's implementation of doAction(), the students submit a shared library and the professor can simply change LD_LIBRARY_PATH to evaluate each student.

My goal in obtaining the library currently being used is to perform an md5sum on the library at runtime to ensure I'm calling the correct library. In the contrived example, all students would submit the md5sum of their library and the professor could match the running executable + shared library (database lookup, log to file, ...) to the student, to prevent an accident in setting LD_LIBRARY_PATH effecting another student's grade (forgot to change LD_LIBRARY_PATH to David's directory and ran again with Bill's libfoo).

5

There are 5 answers

4
Useless On BEST ANSWER

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.

0
rob mayoff On

If you're using Linux, you can use the dl_iterate_phdr function:

The dl_iterate_phdr() function allows an application to inquire at run time to find out which shared objects it has loaded.

http://linux.die.net/man/3/dl_iterate_phdr

3
Basile Starynkevitch On

At runtime, it is not an application, it is a process.

If the process has pid 1234, you can get its memory map by reading /proc/1234/maps (or /proc/1234/smaps which is more detailed). That map lists in particular mmap-ed files (notably shared libraries). From inside the application, read /proc/self/maps

Try

   grep so /proc/self/maps

to have an idea of what I mean.

By the way, if you have an address, the dladdr function gives information about the nearest symbol and shared object...

addenda

And as Rob Mayoff answered, dl_iterate_phdr is probably the best solution on Linux

0
FatalError On

If this is Linux (I doubt there's a generic POSIX way to do this but I could be wrong), you may be interested in the contents of /proc/(pid)/maps. This gives the mapped memory ranges for your process and you could search for which of the ranges your md5sum() function's address falls in.

0
Ross Rogers On

If you're in linux/unix, you could use strace like strace -o strace.log -f students_binary . Strace traces all system calls, including the calls to open a library. Then you could parse strace.log for all openings of any file and perform the md5sum on all open files.