从 dlopen 加载的函数可以调用加载它的可执行文件中的函数吗?

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

设置

假设我在 C/C++ 中编写了一个程序并希望允许插件加载。典型的解决方案是将插件编写为:

plugin.c

int my_plugin_fn() {
    return 7;
}

并使用gcc -fpic -shared -o plugin.so plugin.c

之类的东西编译它

然后,在加载这个插件的主程序中,我们可能有:

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;
}

我用 gcc -o loader loader.c -ldl 编译了 loader.c,在 运行 之后,正如预期的那样,输出是 Plugin object returned: 7

问题

假设我们想在我们的主程序(loader.c)中添加插件可以使用的功能。例如,

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);
}

以与以前相同的方式编译和 运行 这些文件会生成 Could not open shared object: ./loader_v2: symbol lookup error: ./plugin_v2.so: undefined symbol: times_2

有没有办法使用 dlopen() 从加载它们的程序中调用函数来加载插件?

执行此类操作的最佳方法是使用函数指针。您可以将函数指针传递给库函数,库函数随后会调用它。

所以库函数看起来像这样:

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

dlsym 的调用将如下所示:

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

你会像这样调用库函数:

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

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

是的,但是您要从主可执行文件调用的函数必须从中 exportednot默认。您可以使用 nm -D loader.

查看从主二进制文件导出的符号

您可以通过 link 使用 -rdynamic 标志导出主可执行文件中定义的 所有 函数。

一些 link 用户,最著名的是 GNU-ld 的较新版本,GOLD 和 LLD,支持 --export-dynamic-symbol 标志,它允许 有选择地 仅导出您需要的符号。

在您的情况下,您将 link 您的 loader 可执行文件 -Wl,--export-dynamic-symbol=times_2