3

Imagine that you have an application with a plugin based architecture, where each plugin is a *.so file that is dynamically loaded with dlopen().

The main application can refer to symbols via dlsym(), and so it may call functions of the plugin. How can the plugin call functions of the main application?

I know that the main application could provide a struct full of function pointers, which the plugin could use to call into the application. Is there any easier way than that?

Edit: here's a minimal working example to show what I mean:

app.h:

#ifndef APP_H
#define APP_H
void app_utility(void);
#endif

app.c:

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

#include "app.h"

void app_utility(void)
{
    printf("app_utility()\n");
}

int main(int argc, char **argv)
{
    void *handle;
    void (*plugin_function)(void);
    if (argc < 2) {
        fprintf(stderr, "usage: ./app plugin.so\n");
        exit(1);
    }

    handle = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
    if (!handle) {
        fprintf(stderr, "error loading plugin: %s\n", dlerror());
        exit(1);
    }

    plugin_function = dlsym(handle, "doit");
    if (!plugin_function) {
        fprintf(stderr, "error loading symbol: %s\n", dlerror());
        dlclose(handle);
        exit(1);
    }

    plugin_function();

    dlclose(handle);
    return 0;
}

plugin.c:

#include <stdio.h>

#include "app.h"

void doit(void)
{
    printf("doit()\n");
    app_utility();
    printf("did it!\n");
}

Example usage:

$ gcc -o app app.c -ldl
$ gcc -shared -o plugin.so
$ ./app ./plugin.so
error loading plugin: ./plugin.so: undefined symbol: app_utility
jxh
  • 69,070
  • 8
  • 110
  • 193
brenns10
  • 3,109
  • 3
  • 22
  • 24
  • I would make a common library that both the plugin and application can depend on. This is how Ruby gems work, for example. Each gem links against libruby as does the Ruby interpreter itself. That way you can have compile-time checks of the plugin even though it is loaded and run dynamically. – Max Mar 15 '18 at 19:24
  • 2
    There may be a compiler specific mechanism to compile your main application in a way that its symbols are available to be used by the dynamically loaded library. For GCC, this is `-rdynamic`. – jxh Mar 15 '18 at 19:32
  • @jxh: `-rdynamic` worked for me, I'd be glad to mark your answer accepted if you post it :) – brenns10 Mar 15 '18 at 20:21

1 Answers1

1

There is a option you can pass to when you invoke it to create the executable for the main application: -rdynamic

$ gcc -rdynamic -o app app.c -ldl

The GCC documentation for -rdynamic:

Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of dlopen or to allow obtaining backtraces from within a program.

The ld manual page for --export-dynamic includes this paragraph:

If you use "dlopen" to load a dynamic object which needs to refer back to the symbols defined by the program, rather than some other dynamic object, then you will probably need to use this option when linking the program itself.

jxh
  • 69,070
  • 8
  • 110
  • 193