我有一个接口,我希望能够静态链接模块。例如,我希望能够调用称为 FOO 或匹配某个原型的所有函数(尽管在单独的文件中),最终在文件中调用一个函数,而在其他文件中没有标题。不要说这是不可能的,因为我找到了一个可以做到的黑客,但我想要一个非黑客的方法。(hack 是使用 nm 来获取函数及其原型,然后我可以动态调用该函数)。另外,我知道您可以通过动态链接来做到这一点,但是,我想静态链接文件。有任何想法吗?
4 回答
将所有函数的表放入每个翻译单元:
struct functions MOD1FUNCS[]={
{"FOO", foo},
{"BAR", bar},
{0, 0}
};
然后在主程序中放一张表,列出所有这些表:
struct functions* ALLFUNCS[]={
MOD1FUNCS,
MOD2FUNCS,
0
};
然后,在运行时,搜索表,并查找相应的函数指针。
这在编写测试代码时有些常见。例如,您想调用所有以 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 技巧的一个很好的参考。
您确实需要静态链接,同时在运行时选择所有匹配的函数,对吗?因为后者是动态链接的典型案例,我会说。
您显然需要一些机制来注册可用功能。动态链接将提供这一点。
我真的不认为你能做到。C 并不完全能够进行后期绑定或您似乎需要的那种内省。
虽然我不太明白你的问题。静态链接时你想要动态链接库的特性吗?因为这对我来说没有意义......对于静态链接,您需要已经拥有二进制文件,这会使函数的动态加载浪费时间,即使您可以轻松地做到这一点。