2

我有一个接口,我希望能够静态链接模块。例如,我希望能够调用称为 FOO 或匹配某个原型的所有函数(尽管在单独的文件中),最终在文件中调用一个函数,而在其他文件中没有标题。不要说这是不可能的,因为我找到了一个可以做到的黑客,但我想要一个非黑客的方法。(hack 是使用 nm 来获取函数及其原型,然后我可以动态调用该函数)。另外,我知道您可以通过动态链接来做到这一点,但是,我想静态链接文件。有任何想法吗?

4

4 回答 4

5

将所有函数的表放入每个翻译单元:

struct functions MOD1FUNCS[]={
     {"FOO", foo},
     {"BAR", bar},
     {0, 0}
};

然后在主程序中放一张表,列出所有这些表:

struct functions* ALLFUNCS[]={
    MOD1FUNCS,
    MOD2FUNCS,
    0
};

然后,在运行时,搜索表,并查找相应的函数指针。

于 2010-12-20T22:28:53.040 回答
4

这在编写测试代码时有些常见。例如,您想调用所有以 test_ 开头的函数。所以你有一个 shell 脚本,它通过 grep 遍历你的所有 .C 文件并提取与 test_.* 匹配的函数名。然后该脚本生成一个 test.c 文件,其中包含一个调用所有测试函数的函数。

例如,生成的程序如下所示:

int main() {
   initTestCode();
   testA();
   testB();
   testC();
}

另一种方法是使用一些链接器技巧。这就是 Linux 内核对其初始化所做的事情。初始化代码的函数用限定符 __init 标记。这在 linux/init.h 中定义如下:

#define __init          __section(.init.text) __cold notrace

这会导致链接器将该函数放在 .init.text 节中。系统启动后,内核将从该部分回收内存。

为了调用这些函数,每个模块将声明一个带有其他一些宏 core_initcall(func)、arch_initcall(func) 等的 initcall 函数(也在 linux/init.h 中定义)。这些宏将指向函数的指针放入名为 .initcall 的链接器部分。

在启动时,内核将“遍历” .initcall 部分,调用那里的所有指针。遍历的代码如下所示:

extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];

static void __init do_initcalls(void)
{
        initcall_t *fn;

        for (fn = __early_initcall_end; fn < __initcall_end; fn++)
                do_one_initcall(*fn);

        /* Make sure there is no pending stuff from the initcall sequence */
        flush_scheduled_work();
}

符号 __initcall_start、__initcall_end 等在链接描述文件中定义。

一般来说,Linux 内核使用 GCC 预处理器、编译器和链接器做了一些可能的最聪明的技巧。它一直是 C 技巧的一个很好的参考。

于 2010-12-20T22:59:36.590 回答
0

您确实需要静态链接,同时在运行时选择所有匹配的函数,对吗?因为后者是动态链接的典型案例,我会说。

您显然需要一些机制来注册可用功能。动态链接将提供这一点。

于 2010-12-20T22:27:26.960 回答
0

我真的不认为你能做到。C 并不完全能够进行后期绑定或您似乎需要的那种内省。

虽然我不太明白你的问题。静态链接时你想要动态链接库的特性吗?因为这对我来说没有意义......对于静态链接,您需要已经拥有二进制文件,这会使函数的动态加载浪费时间,即使您可以轻松地做到这一点。

于 2010-12-20T22:29:00.903 回答