如何在 C 中创建模块

发布于 2024-10-08 17:10:09 字数 210 浏览 7 评论 0原文

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

I have an interface with which I want to be able to statically link modules. For example, I want to be able to call all functions (albeit in seperate files) called FOO or that match a certain prototype, ultimately make a call into a function in the file without a header in the other files. Dont say that it is impossible since I found a hack that can do it, but I want a non hacked method. (The hack is to use nm to get functions and their prototypes then I can dynamically call the function). Also, I know you can do this with dynamic linking, however, I want to statically link the files. Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

内心旳酸楚 2024-10-15 17:10:09

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

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

然后将一个表放入主程序中,列出所有这些表:

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

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

Put a table of all functions into each translation unit:

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

Then put a table into the main program listing all these tables:

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

Then, at run time, search through the tables, and lookup the corresponding function pointer.

ˉ厌 2024-10-15 17:10:09

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

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

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

另一种方法是使用一些链接器技巧。这就是 Linux 内核的初始化过程。作为 init 代码的函数用限定符 __init 进行标记。它在 linux/init.h 中定义如下:

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

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

为了调用函数,每个模块都会声明一个 initcall 函数以及一些其他宏 core_initcall(func)、arch_initcall(func) 等(也在 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 技巧的一个很好的参考。

This is somewhat common in writing test code. e.g., you want to call all functions that start with test_. So you have a shell script that grep's through all your .C files and pulls out the function names that match test_.*. Then that script generates a test.c file that contains a function that calls all the test functions.

e.g., generated program would look like:

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

Another way to do it would be to use some linker tricks. This is what the Linux kernel does for its initialization. Functions that are init code are marked with the qualifier __init. This is defined in linux/init.h as follows:

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

This causes the linker to put that function in the section .init.text. The kernel will reclaim memory from that section after the system boots.

For calling the functions, each module will declare an initcall function with some other macros core_initcall(func), arch_initcall(func), et cetera (also defined in linux/init.h). These macros put a pointer to the function into a linker section called .initcall.

At boot-time, the kernel will "walk" through the .initcall section calling all of the pointers there. The code that walks through looks like this:

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

The symbols __initcall_start, __initcall_end, etc. get defined in the linker script.

In general, the Linux kernel does some of the cleverest tricks with the GCC pre-processor, compiler and linker that are possible. It's always been a great reference for C tricks.

遮了一弯 2024-10-15 17:10:09

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

显然,您需要某种机制来注册可用的函数。动态链接将提供这样的功能。

You really need static linking and, at the same time, to select all matching functions at runtime, right? Because the latter is a typical case for dynamic linking, i'd say.

You obviusly need some mechanism to register the available functions. Dynamic linking would provide just this.

隐诗 2024-10-15 17:10:09

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

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

I really don't think you can do it. C isn't exactly capable of late-binding or the sort of introspection you seem to be requiring.

Although I don't really understand your question. Do you want the features of dynamically linked libraries while statically linking? Because that doesn't make sense to me... to static link, you need to already have the binary in hand, which would make dynamic loading of functions a waste of time, even if you could easily do it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文