重复符号后如何可靠地使用dlsym?

Velos:

晚上好,我目前正在基于Plux.net模型的C ++ / Linux插件系统上工作

为简单起见,我基本上用extern C声明一个符号(将其称为pluginInformation)(解开),我的插件管理器在预配置的导入文件(.so)中查找该符号。

问题是主应用程序声明了相同的符号,不仅声明它具有任何依赖关系,而且它具有的任何依赖项也都具有该符号。(因为在该pluginInformation中,模块可以发布插头和/或插槽)。

因此,当我的PluginManager启动时,它首先尝试在主程序中查找符号(将NULL传递给dlopen),然后尝试在其任何依赖项中查找该符号(使用dl_iterate_phdr)。最后,它将dlopen一组配置导入(它将读取.so的路径,以便用户配置,dlopen它们,最后dlsym pluginInformation符号)。

在所有模块中找到的pluginInformation集合然后用于构建扩展名三。

如果我在主程序中声明该符号并使用dlopen加载导入,则该符号将起作用(只要在倾斜导入时传递标志RTLD_DEEPBIND即可)。

但是对于应用程序依赖项,我没有选择传递标志的选项(我可以,但是它没有做任何事情),因为此.sos是在应用程序启动时加载的。

现在,当我尝试使用从依赖项中获得的任何符号(启动时加载的符号)时,都会遇到分段错误。我认为问题是符号表中有多个具有相同名称的符号,奇怪的是,它似乎可以正确识别出多个符号,甚至为我提供了.so的正确路径。声明,但是一旦我访问该符号,就会发生分段错误。如果仅在主程序或依赖项之一中声明该符号,则一切正常。

如何使用dlsym管理主程序和分层导入之间的重复符号?

我一直在考虑保持乱码,然后尝试查找符号表中的符号,但是我不确定这是否有可能(以编程方式在模块中列出所有符号)。

PD:对不起,我没有发布任何代码,但是我现在不在家里,我希望对我试图做的事情的描述足够清楚,否则我明天可以发布一些代码。

动物标称:

这是另一种方法。

该应用程序本身导出一个或多个插件项注册功能。例如:

int register_plugin_item(const char *const text,
                         const char *const icon,
                         void (*enter)(void *),
                         void (*click)(void *),
                         void (*leave)(void *),
                         void *data);

每登记项目中,有两个串槽(texticon),三个功能时隙(enterclick,和leave),和调用时被提供给用作参数的不透明参考。

(请注意,-rdynamic在编译主应用程序(实现上述功能的目标文件)时,需要使用编译器选项,以确保链接器将register_plugin_item符号添加到动态符号表中。)

每个插件register_plugin_item()都在构造函数(在库加载时自动运行)中为其所需的每个项目调用该函数。对于该功能,可能首先要检查其运行的环境,以确定要注册的功能,或者为每个插件项目使用哪些优化的功能变体,这可能并且通常是有用的。

这是一个简单的示例插件。请注意所有符号都是static,因此插件不会污染动态符号表,也不会引起任何符号冲突。

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

extern int register_plugin_item(const char *const,
                                const char *const,
                                void (*enter)(void *),
                                void (*click)(void *),
                                void (*leave)(void *),
                                void *);

static void enter(void *msg)
{
    fprintf(stderr, "Plugin: Enter '%s'\n", (char *)msg);
}

static void leave(void *msg)
{
    fprintf(stderr, "Plugin: Leave '%s'\n", (char *)msg);
}

static void click(void *msg)
{
    fprintf(stderr, "Plugin: Click '%s'\n", (char *)msg);
}

static void init(void) __attribute__((constructor));
static void init(void)
{
    register_plugin_item("one", "icon-one.gif",
                         enter, leave, click,
                         "1");

    register_plugin_item("two", "icon-two.gif",
                         enter, leave, click,
                         "2");
}

上面的插件导出两个项目。为了进行测试,请至少创建上述的几个变体;您会看到即使插件使用相同的(静态)变量和函数名称,也不会出现符号冲突。

这是一个示例应用程序,它加载指定的插件并测试每个注册的项目:

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

struct item {
    struct item *next;
    const char  *text;
    const char  *icon;
    void        *data;
    void       (*enter)(void *);
    void       (*leave)(void *);
    void       (*click)(void *);
};

static struct item *list = NULL;

int register_plugin_item(const char *const text,
                         const char *const icon,
                         void (*enter)(void *),
                         void (*click)(void *),
                         void (*leave)(void *),
                         void *data)
{
    struct item *curr;

    curr = malloc(sizeof *curr);
    if (!curr)
        return ENOMEM;

    curr->text = text;
    curr->icon = icon;
    curr->data = data;
    curr->enter = enter;
    curr->leave = leave;
    curr->click = click;

    /* Prepend to list */
    curr->next = list;
    list = curr;

    return 0;
}

int main(int argc, char *argv[])
{
    int          arg;
    void        *handle;
    struct item *curr;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s PLUGIN.so ... \n", argv[0]);
        fprintf(stderr, "\n");
        fprintf(stderr, "Please supply full plugin paths, unless\n");
        fprintf(stderr, "the plugins reside in a standard library directory,\n");
        fprintf(stderr, "or in a directory listed in LD_LIBRARY_PATH.\n");
        fprintf(stderr, "\n");
        return 1;
    }

    for (arg = 1; arg < argc; arg++) {

        handle = dlopen(argv[arg], RTLD_NOW);
        if (handle != NULL)
            fprintf(stderr, "%s: Loaded.\n", argv[arg]);
        else
            fprintf(stderr, "%s.\n", dlerror());

        /* Note: We deliberately "leak" the handle,
         *       so that the plugin is not unloaded. */
    }

    for (curr = list; curr != NULL; curr = curr->next) {
        if (curr->text)
            printf("Item '%s':\n", curr->text);
        else
            printf("Unnamed item:\n");

        if (curr->icon)
            printf("\tIcon is '%s'\n", curr->icon);
        else
            printf("\tNo icon\n");

        if (curr->data)
            printf("\tCustom data at %p\n", curr->data);
        else
            printf("\tNo custom data\n");

        if (curr->enter)
            printf("\tEnter handler at %p\n", curr->enter);
        else
            printf("\tNo enter handler\n");

        if (curr->click)
            printf("\tClick handler at %p\n", curr->click);
        else
            printf("\tNo click handler\n");

        if (curr->leave)
            printf("\tLeave handler at %p\n", curr->leave);
        else
            printf("\tNo leave handler\n");

        if (curr->enter || curr->click || curr->leave) {
            printf("\tTest calls:\n");
            if (curr->enter)
                curr->enter(curr->data);
            if (curr->click)
                curr->click(curr->data);
            if (curr->leave)
                curr->leave(curr->data);
            printf("\tTest calls done.\n");
        }
    }

    return 0;
}

如果应用程序是app.c,并且您有插件plugin-foo.cplugin-bar.c,则可以使用例如编译它们。

gcc -W -Wall -rdynamic app.c -ldl -o app

gcc -W -Wall -fpic -c plugin-foo.c
gcc -shared -Wl,-soname,plugin-foo.so plugin-foo.o -o plugin-foo.so

gcc -W -Wall -fpic -c plugin-bar.c
gcc -shared -Wl,-soname,plugin-bar.so plugin-bar.o -o plugin-bar.so

并使用例如运行

./app --help

./app ./plugin-foo.so

./app ./plugin-foo.so ./plugin-bar.so

请注意,如果多次定义同一个插件,则构造函数仅对该库执行一次。没有重复的注册。


插件和应用程序之间的接口完全取决于您。在此示例中,只有一个功能。一个实际的应用程序可能会有更多。该应用程序还可以导出其他功能,例如用于插件以查询应用程序配置。

设计一个好的界面是一个完全不同的主题,并且绝对应至少与实现时一样多地考虑。

Plux.NET插件平台也允许插件导出自己的插槽。这种替代方法可以通过多种方式实现这一点。其中之一是导出插件注册功能-即注册插件而不是单个项目-该功能需要一个函数指针:

int register_plugin(const char *const name,
                    int (*extend)(const char *const, ...));

如果插件提供了插槽,则它将提供自己的注册功能作为extend功能指针。该应用程序还导出功能,例如

int plugin_extend(const char *const name, ...);

插件可以用来调用其他插件的注册功能。plugin_extend()在主应用程序中的实现涉及搜索extend已经注册的合适功能,然后对其进行调用。)

在实现方面,允许插件导出插槽会使实现变得相当复杂。特别是,插件导出的插槽何时以及何时可用?是否有特定的插件必须加载顺序,以确保导出所有可能的插槽?如果存在循环依赖关系会怎样?插件应该在注册开始之前指定他们依赖的其他插件吗?

如果每个插件是一个单独的实体,不导出自己的任何插槽,而仅将其插入主应用程序插槽,则可以避免实现过程中的大多数复杂性。

但是,检查注册项目的顺序可能是您可能需要考虑的细节。上面的示例程序使用一个链表,该链表中的项与注册顺序相比以相反顺序结束,并且注册顺序与在命令行上首次指定插件文件名的顺序相同。如果你有一个插件目录,这是自动扫描(例如,使用opendir()/ readdir()/ dlopen()/ closedir()循环),则该插件登记顺序是半随机(取决于文件系统;仅当插件被添加或移除,通常变化的)。

更正?有什么问题吗 注释?

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

ObservableList:如何可靠地检测到setAll?

如何使用Go编程语言可靠地unlink()Unix域套接字

如何使电子托盘点击事件可靠地工作?

如何不使用window.navigator可靠地检测浏览器?

如何使用webdriver和Chrome在localhost上可靠地设置cookie?

如何使SKAction(name :)可靠地工作?

如何使用运行Bash的前台子进程可靠地使用陷阱

可靠地将类型符号(ITypeSymbol)与Roslyn进行比较

如何可靠地获取Wikipedia信息框中使用的图像?

如何可靠地检测到对nullptr的支持?

如何在docker-compose中可靠地使用本地Maven镜像代理?

如何使用GHC在机器附近可靠地影响生成的代码?

如何从git钩可靠地找到根git目录?

如何可靠地离线构建复杂的Java项目

如何使用PowerShell可靠地将项目复制到mtp设备?

如何可靠地写入OPC UA服务器?

如何在Eclipse中可靠地设置编译后字节码增强构建器?

无论使用硒的方法如何,都无法可靠地选择元素

如何可靠地确定C#中字符的宽度?

如何可靠地引用过程(使用标签或类似的东西)?

如何最可靠地跟踪条形码

如何可靠地确定最近7天未使用的所有文件夹?

从SQLite可靠地获取数据而无需重复

在Rails中,使用嵌套路由时如何可靠地查找特定参数?

如何可靠地使Chrome Devtools与Cordova + Crosswalk一起使用

无论当前窗口如何,如何使用“ Space”热键可靠地暂停PotPlayer?

您如何能可靠地返回不可连接的值?

如何可靠地从Guava LoadingCache删除记录?

如何使用 python opencv 可靠地仅选择 maksed 液滴图像的外轮廓?