Search code examples
c++cshared-librariesldd

Programatically determine shared libraries in use by running application


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).


Solution

  • 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.