How can a shared library (.so) call a function that is implemented in its loader code?

You have two options, from which you can choose:

Option 1: export all symbols from your executable.
This is simple option, just when building executable, add a flag -Wl,--export-dynamic. This would make all functions available to library calls.

Option 2: create an export symbol file with list of functions, and use -Wl,--dynamic-list=exported.txt. This requires some maintenance, but more accurate.

To demonstrate: simple executable and dynamically loaded library.

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

void exported_callback() /*< Function we want to export */
{
    printf("Hello from callback!\n");
}

void unexported_callback() /*< Function we don't want to export */
{
    printf("Hello from unexported callback!\n");
}

typedef void (*lib_func)();

int call_library()
{
   void     *handle  = NULL;
   lib_func  func    = NULL;
   handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL);
   if (handle == NULL)
   {
       fprintf(stderr, "Unable to open lib: %s\n", dlerror());
       return -1;
   }
   func = dlsym(handle, "library_function");

   if (func == NULL) {
       fprintf(stderr, "Unable to get symbol\n");
      return -1;
   }

   func();
   return 0;
}

int main(int argc, const char *argv[])
{
    printf("Hello from main!\n");
    call_library();
    return 0;
}

Library code (lib.c):

#include <stdio.h>
int exported_callback();

int library_function()
{
    printf("Hello from library!\n");
    exported_callback();
    /* unexported_callback(); */ /*< This one will not be exported in the second case */
    return 0;
}

So, first build the library (this step doesn’t differ):

 gcc -shared -fPIC lib.c -o libprog.so

Now build executable with all symbols exported:

 gcc -Wl,--export-dynamic main.c -o prog.exe -ldl

Run example:

 $ ./prog.exe
 Hello from main!
 Hello from library!
 Hello from callback!

Symbols exported:

 $ objdump -e prog.exe -T | grep callback
 00000000004009f4 g    DF .text  0000000000000015  Base        exported_callback
 0000000000400a09 g    DF .text  0000000000000015  Base        unexported_callback

Now with the exported list (exported.txt):

{
    extern "C"
    {
       exported_callback;
    };
};

Build & check visible symbols:

$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl
$ objdump -e prog.exe -T | grep callback
0000000000400774 g    DF .text  0000000000000015  Base        exported_callback

Leave a Comment