C – How to handle different behaviors of dlsym() in OSX and Linux (generic)

How to handle different behaviors of dlsym() in OSX and Linux (generic)… here is a solution to the problem.

How to handle different behaviors of dlsym() in OSX and Linux (generic)

I recently added some dynamic plugin behavior in an OSX program that works as designed. However, attempting the same operation on Linux (general) fails because the dlsym() library call cannot resolve symbols in the same way that works fine in OSX.

It’s clear from reading the man dlsym that system calls are implemented very differently in both architectures, but I assumed that the most basic case would be the same, but apparently not.

The following (virtual) example works fine in OSX but not in Linux.

Is there some workaround to make it parse symbols the same way in Linux?

// Needed to make RTLD_DEFAULT available on GNU/Linux
#define _GNU_SOURCE

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

 Pointer to function returning int
typedef int (*pfi_t)(void);

 "Plugin" dummy function for illustrative purpose
int get_result(void) { return 42; }

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

 Resolve "plugin" function using the default handle which should,
     according to documentation, search in global symbols and dependencies.
    pfi_t res_fnc = (pfi_t) dlsym(RTLD_DEFAULT, "get_result");

if (NULL == res_fnc) {
        fprintf(stderr, "Cannot resolve symbol 'get_result'.\n");
        return EXIT_FAILURE;
    } else {
        fprintf(stdout, "Resolved symbol 'get_result'. get_result() = %d\n", (*res_fnc)());
    };

return EXIT_SUCCESS;
}

Run the result on OSX (Monteray 12.5, Apple clang version 13.1.6).

The symbol 'get_result' has been resolved. get_result() = 42

But on Linux (tested on Ubuntu 22.04.1 and Ubuntu 18.04.6) it runs as follows:

The symbol 'get_result' could not be resolved.

Note: On Ubuntu 18.x, -ldl must be chained to resolve dlsym().

Solution

The workaround is to tell the linker to export all global symbols to a dynamic symbol table using the gcc -wl, -e, or -wl,--export-dynamic option>. For better control, –wl,--export-dynamic-symbol=get_result tells the linker to export a single global symbol get_result. Global pattern matching is used for values after =. Alternatively, –wl,--export-dynamic-symbol-list=FILE gets the symbols to export from the file FILE that contains glob patterns.

Edit: @yugr states that the GCC option -rdynamic is equivalent to –wl,--export-dynamic.

Related Problems and Solutions