How in C to get the name of the shared library a symbol is loaded from programatically at runtime?

Viewed 246

I load a symbol from a shared library like this:

void *sym = dlsym(RTLD_DEFAULT, "printf");

That will tell me whether the symbol printf exists in a loaded shared library, and if it does, what its address is, but it will not tell me which shared library it was loaded from. Is there a way to determine that?

Note I want to find the answer programatically at runtime, so solutions using debuggers, the LD_DEBUG environment variable, etc., are not what I am looking for.

I am particularly looking for solutions for Linux and macOS.

1 Answers

Here's an example of how to do this with the dladdr function:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <assert.h>

void show_sym(const char *sym_name) {
        void *sym_ptr = dlsym(RTLD_DEFAULT,sym_name);
        printf("SYMBOL %s ADDRESS %p\n", sym_name, sym_ptr);
        if (sym_ptr == NULL)
                return;
        Dl_info info;
        int rc = dladdr(sym_ptr,&info);
        assert(rc != 0);
        printf("SHARED LIBRARY NAME: %s\n", info.dli_fname);
        printf("SHARED LIBRARY BASE ADDRESS: %p\n", info.dli_fbase);
        printf("NEAREST SYMBOL: %s\n", info.dli_sname);
        printf("NEAREST SYMBOL ADDRESS: %p\n", info.dli_saddr);
}

int main(int argc, char** argv) {
        if (argc != 2) {
                fprintf(stderr,"usage: %s SYMBOL-NAME\n", argv[0]);
                return 1;
        }
        show_sym(argv[1]);
        return 0;
}

I tested the above code and it works on both macOS and Linux. (On Linux you have to link against -ldl.) Although I haven't tested it, the same (or similar) code likely works on other Unix-like platforms which implement dladdr, such as Solaris (which is where dladdr comes from originally), FreeBSD, OpenBSD, NetBSD, DragonFly BSD, HP/UX. (And the IBM mainframe operating system z/TPF too, which few would call "Unix-like".)

It is possible to do the same thing on Windows, but the API is completely different (basically turn the address into a HMODULE using GetModuleHandleEx and then call GetModuleFileName). There are some other Unix-likes which support this but not using the dladdr API, e.g. AIX lacks dladdr but has an API loadquery which reports at what memory addresses shared libraries are loaded and so can be indirectly used to work this out (indeed, here is some sample code to do that, although I haven't tested that code myself.)

Related