9

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.

Lorraine
  • 1,189
  • 14
  • 30

2 Answers2

5

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.

Kristina
  • 15,859
  • 29
  • 111
  • 181
  • There's little or no real distinction between ELF programs and ELF shared libraries. In fact, some shared libraries contain a `main()` function and can be run as programs. There is no particular reason why an ELF "program" could not be loaded as a shared library. All the same, I'm sure you're right that the problem is that the symbol in question is not exported. – John Bollinger May 08 '16 at 22:43
  • 5
    `There's no point in using dlsym to get symbols from the same image as your dlsym call` and `I'm not sure why you'd want to do that.` are very counterproductive things to say. There are use-cases. – Qix - MONICA WAS MISTREATED Aug 03 '18 at 05:39
  • I don't think they are, for shared libraries interposition is a use case but if you're the main binary your symbols (strong aliases) take precedence over anything if you want to interpose something which makes dlsym on self redundant. Unless you want to go into the edge case territory of `LD_PRELOAD` libraries or lazy loaded dynamic libs hooking explicitly exported symbols by an executable, anything can be done through attributes that influence how static linker treats some symbols. If there are other use cases, feel free to add them as an answer, I'm genuinely curious myself as I'm not aware. – Kristina Aug 04 '18 at 00:48
  • @KristinaBrooks There do exist certain valid cases, e.g. by the symbol resolution `RTDyldObjectLinkingLayer` used in LLVM ORC JIT, where the symbol is known statically (not in an external dynamic library) but referenced in some LLVM IR generated at runtime. – FrankHB Jun 17 '21 at 19:52
1

I faced the similar issue in my code.

I did the following to export symbols

  #ifndef EXPORT_API
  #define EXPORT_API __attribute__ ((visibility("default")))
  #endif 

Now for each of the function definition I used the above attribute.

For example the earlier code was

     int func() { printf(" I am a func %s ", __FUNCTION__ ) ;

I changed to

     EXPORT_API int func() { printf(" I am a func %s ", __FUNCTION__ ) ;

Now it works.

dlsym gives no issues after this.

Hope this works for you as well.

AnotherDeveloper
  • 2,161
  • 2
  • 23
  • 27