linux-kernel - 编译器/链接器如何解析内核 API,例如从 linux 上的模块调用的 'printk'

标签 linux-kernel linux-device-driver kernel kernel-module

我已经编写了一个示例 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 如何知道这一点?我找不到任何可以说明这一点的选项。

最佳答案

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

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

关于linux-kernel - 编译器/链接器如何解析内核 API,例如从 linux 上的模块调用的 'printk',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10946081/

相关文章:

linux - PCIe NIC 如何工作以及它如何与主机通信?

c - Linux DMA : Using the DMAengine for scatter-gather transactions

linux - 如何从内核写入只读文件?

linux - 检查虚拟内存地址的有效性

linux - 为什么根据 LDD3 device->kobj->parent 等于 &device->parent->kobj?

linux - Systemtap 不显示内核函数中的所有局部变量

linux - 我需要在多线程读取调用中保护 fd 吗?

android - Android Q中访问/proc/net/tcp

c - 构建linux内核模块

linux - 更改控制台参数后无提示