C中静态链接模块的插件系统?

时间:2011-10-04 14:02:19

标签: c plugins static-libraries static-linking

我需要编写一个插件系统,它可以在Linux上使用静态链接的模块。 我不希望核心(主函数)显式调用模块的init函数。

对于我想要实现的目标,我能想到的最接近的类比是Linux内核。 在那里可以静态编译和链接未知数量的模块/插件,但模块是在动态加载时启动的。

我有这个

芯/ main.c中:

 int main(void) { return 0; }

芯/ pluginregistrar.h:

#ifndef PLUGIN_REGISTRAR_H
#define PLUGIN_REGISTRAR_H

#include <stdio.h>

#define module_init(pInitor) \
    static inline funInitor __inittest(void) \
        { fprintf(stdout, "test\n"); return pInitor; }
    int init_module(void) __attribute__((alias(#pInitor)));

typedef void (*funInitor)(void);

#endif

adap1 / main.c中:

#include "pluginregistrar.h"

void adap1Init(void) { fprintf(stdout, "test1\n"); }

module_init(adap1Init);

adap2 / main.c中:

#include "pluginregistrar.h"

void adap2Init(void) { fprintf(stdout, "test2\n"); }

module_init(adap2Init);

到目前为止,但我不知道如何让核心实际启动已经完成module_init的模块。

这里有人可以给我一个指针吗? (没有双关语)

编辑: 我将core / main.c改为

extern int init_module(void);
int main(void) {
  init_module();
  return 0;
}

并且它没有显示“自适应”的调用,它是给链接器的库列表中的第一个。

2 个答案:

答案 0 :(得分:1)

如果您正在使用GCC,则可以使用__attribute__((constructor))说明符在启动时(main之前)为每个“模块”运行一段代码。 (也适用于clang和ICC。)

例如:

$ cat t.c
#include <stdio.h>
#ifdef AAA
static void  __attribute__((constructor)) myinit()
{
    printf("%s\n", AAA);
}
#else
int main()
{
    printf("bye\n");
    return 0;
}
#endif
$ gcc -DAAA=\"hello\" -o  m.o -c t.c 
$ gcc -DAAA=\"there\" -o  n.o -c t.c 
$ gcc -o t.o -c t.c
$ gcc -o foo m.o n.o t.o
$ ./foo
there
hello
bye

(代码仅供参考。)

一旦你拥有了它,你就会非常好。让“构造函数”函数执行模块需要做的任何事情来初始化自己,并“注册”到您的插件框架中。 (带有一堆函数指针的结构,添加到链表或类似的东西会起作用。)

请注意,链接顺序将决定您的插件初始化顺序,这是一堆蠕虫 - 如果您的模块相互依赖,事情变得非常棘手。确保你有尽可能少的全局变量。

更新

如果您需要使用静态库而不是普通的.o文件,则需要一些额外的链接器魔法。

假设上面已经运行:

$ ar cru libm.a m.o
$ ar cru libn.a n.o
$ gcc -o foo t.c -Wl,-whole-archive libn.a libm.a -Wl,-no-whole-archive
$ ./foo
hello
there
bye

在这种情况下,我不完全确定你是否可以依赖(反向)链接顺序。

或者:

$ ar cru libmn.a m.o n.o
$ gcc -o foo t.c -Wl,-whole-archive libmn.a -Wl,-no-whole-archive
$ ./foo 
there
hello
bye

(在这里我不知道你会得到什么样的构造函数。)

答案 1 :(得分:0)

程序中只有一个入口点,默认为main。如果我理解正确的话,你建议的是有多个入口点。据我所知,这在标准C中是不可能的。

如果静态加载库(意味着它们被传递到链接器中,无论它们是否包含在生成的可执行文件中),那么您必须在头文件中导出它们的初始化程序,并显式调用{{1在每一个上。

如果您动态加载库(使用dlfcn.hmodule_init / dlopen之类的机制),那么一旦确定了什么,就可以根据需要调用dlsym要加载的模块以及何时加载它们。

在任何一种情况下,如果您希望它运行,必须调用 module_init。你可以重构隐藏它,但在某些时候它必须被调用。