Can function loaded from dlopen call a function from the executable that loaded it?

Viewed 646

Setup

Suppose I wrote a program in C/C++ and wanted to allow plugin loading. The typical solution would be to write the plugin as:

plugin.c

int my_plugin_fn() {
    return 7;
}

And compile it using something like gcc -fpic -shared -o plugin.so plugin.c

Then, in the main program that loads this plugin, we might have:

loader.c

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

int main() {
    void *plugin_handle = dlopen("./plugin.so", RTLD_LAZY);
    if (!plugin_handle) {
        printf("Could not open shared object: %s\n", dlerror());
        return -1;
    }

    int (*fn)() = dlsym(plugin_handle, "my_plugin_fn");
    char *err = dlerror();
    if (err) {
        printf("Could not resolve symbol: %s\n", err);
        return -1;
    }

    printf("Plugin object returned: %d\n", fn());

    return 0;
}

I compiled loader.c with gcc -o loader loader.c -ldl, and after running it, the output was Plugin object returned: 7, as expected.

Question

Suppose we want to add functions in our main program (loader.c) that plugins can use. For example,

loader_v2.c

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

int times_2(int x) {
    return 2*x;
}

int main() {
    void *plugin_handle = dlopen("./plugin_v2.so", RTLD_LAZY);
    if (!plugin_handle) {
        printf("Could not open shared object: %s\n", dlerror());
        return -1;
    }

    int (*fn)() = dlsym(plugin_handle, "my_plugin_fn");
    char *err = dlerror();
    if (err) {
        printf("Could not resolve symbol: %s\n", err);
        return -1;
    }

    printf("Plugin object returned: %d\n", fn());

    return 0;
}

plugin_v2.c

extern int times_2(int);

int my_plugin_fn() {
    return times_2(7);
}

Compiling and running these files in the same way as before produces Could not open shared object: ./loader_v2: symbol lookup error: ./plugin_v2.so: undefined symbol: times_2.

Is there a way to have plugins loaded using dlopen() call functions from the program that loaded them?

2 Answers

Is there a way to have plugins loaded using dlopen() call functions from the program that loaded them?

Yes, but the function that you want to call from the main executable must be exported from it, which does not happen by default. You can see which symbols are exported from your main binary with nm -D loader.

You can export all functions defined in the main executable by linking it with the -rdynamic flag.

Some linkers, most notably newer versions of GNU-ld, GOLD and LLD, support the --export-dynamic-symbol flag, which allows one to selectively export just the symbol(s) you need.

In your case, you would link your loader executable with -Wl,--export-dynamic-symbol=times_2.

The best way to do something like this is with a function pointer. You would pass the function pointer into the library function, which would subsequently call it.

So the library function would look like this:

int my_plugin_fn(int (*cb)(int)) {
    return cb(7);
}

The call to dlsym would then look like this:

int (*fn)(int (*)(int)) = dlsym(plugin_handle, "my_plugin_fn");

And you would call the library function like this:

printf("Plugin object returned: %d\n", fn(times_2));
Related