Search code examples
clinuxdlsym

dlsym returns NULL, even though the symbol exists


I am using dlsym to look up symbols in my program, but it always returns NULL, which I am not expecting. According to the manpage, dlsym may return NULL if there was an error somehow, or if the symbol indeed is NULL. In my case, I am getting an error. I will show you the MCVE I have made this evening.

Here is the contents of instr.c:

#include <stdio.h>

void * testing(int i) {
    printf("You called testing(%d)\n", i);
    return 0;
}

A very simple thing containing only an unremarkable example function.

Here is the contents of test.c:

#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

typedef void * (*dltest)(int);

int main(int argc, char ** argv) {

    /* Declare and set a pointer to a function in the executable */
    void * handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
    dlerror();
    dltest fn = dlsym(handle, "testing");

    if(fn == NULL) {
        printf("%s\n", dlerror());
        dlclose(handle);
        return 1;
    }
    dlclose(handle);
    return 0;
}

As I step through the code with the debugger, I see the dlopen is returning a handle. According to the manpage, If filename is NULL, then the returned handle is for the main program. So if I link a symbol called testing into the main program, dlsym should find it, right?

Here is the way that I am compiling and linking the program:

all: test

instr.o: instr.c
    gcc -ggdb -Wall -c instr.c

test.o: test.c 
    gcc -ggdb -Wall -c test.c

test: test.o instr.o
    gcc -ldl -o test test.o instr.o 

clean:
    rm -f *.o test

And when I build this program, and then do objdump -t test | grep testing, I see that the symbol testing is indeed there:

08048632 g     F .text  00000020              testing

Yet the output of my program is the error:

./test: undefined symbol: testing

I am not sure what I am doing wrong. I would appreciate if someone could shed some light on this problem.


Solution

  • I don't think you can do that, dlsym works on exported symbols. Because you're doing dlsym on NULL (current image), even though the symbols are present in the executable ELF image, they're not exported (since it's not a shared library).

    Why not call it directly and let the linker take care of it? There's no point in using dlsym to get symbols from the same image as your dlsym call. If your testing symbol was in a shared library that you either linked against or loaded using dlopen then you would be able to retrieve it.

    I believe there's also a way of exporting symbols when building executables (-Wl,--export-dynamic as mentioned in a comment by Brandon) but I'm not sure why you'd want to do that.