3

我编写了一个示例 hello.ko 内核模块:

#include <linux/module.h>      /* Needed by all modules */
#include <linux/kernel.h>      /* Needed for KERN_INFO */

int init_module(void)
{
        printk(KERN_INFO "Hello world.\n");
        return 0;
}

void cleanup_module(void)
{
        printk(KERN_INFO "Goodbye world 1.\n");
}

在这里,我使用了“printk”方法,它是 Linux 公开的内核 API。我可以在“/proc/kallsyms”中看到 Linux 导出的符号。我很想知道 gcc/ld 如何链接被调用的内核 API?gcc/ld 是否从“/proc/kallsyms”或其他文件中获取内核方法的地址并执行链接?如果是,gcc/ld 如何知道这一点?我没有任何选择可以说明这一点。

4

1 回答 1

8

Linux 内核的模块加载器基本上包含一个专用的运行时链接器。.ko 文件实际上是一个与任何其他文件一样的目标文件,因此它带有一个符号表。如果你nm在它上面运行(nm <path/to/some_module.ko>),你会看到很多标记为“U”的符号,即“未定义”。这包括模块使用的核心内核函数的符号,例如printk__kmallockfree等,但在许多情况下还包括其他模块实现的符号。

加载模块时,内核运行模块的未定义符号并在(运行时)符号表中查找它们,修补相关的内存位置,就像任何其他链接器一样。insmod如果符号表中还没有任何未定义的符号,加载将失败(通过使用而不是易于演示,modprobe因为它不会加载依赖项)。它还将模块导出的任何符号添加到符号表中以供其他模块使用,跟踪依赖关系,因此您不能抽出另一个模块使用的模块。Ilya Matveychikov已在评论中链接到模块加载器中的相关代码,如果您想了解所有血腥细节,这将有所帮助。

于 2012-06-12T16:55:02.723 回答