我想在 linux 内核 3.2.x 中添加一个特定的新系统调用,但作为可加载的内核模块(因为我不想一次又一次地重新编译内核)
我阅读了互联网上以及 SO 上的许多帖子,有些地方声称将系统调用实现为可加载模块是不可能的,而另一些地方则说这是可能的。
是哪一个?如果可以的话,如何实现呢?
答案1
这是不可能的,因为系统调用表(称为sys_call_table
)是一个静态大小的数组。它的大小在编译时由注册的系统调用的数量决定。这意味着没有空间容纳另一个。
arch/x86/kernel/syscall_64.c
您可以检查文件中sys_call_table
定义的x86 架构的实现。它的大小正好是__NR_syscall_max+1
.__NR_syscall_max
定义为arch/x86/kernel/asm-offsets_64.c
(sizeof(syscalls) - 1
它是最后一个系统调用的编号),其中syscall
是包含所有系统调用的表。
一种可能的解决方案是重复使用一些现有的(或已弃用的系统调用号,如果您的体系结构有的话,请参见sys_setaltroot
示例)系统调用号,因为这不需要更多的内存空间。某些架构的系统调用表中也可能存在漏洞(例如 x86 的 64 位版本),因此您也可以使用它。
如果您正在开发新的系统调用并且只想避免在试验时重新启动,则可以使用此技术。您必须定义新的系统调用,在系统调用表中找到现有条目,然后从模块中替换它。
从内核模块执行此操作并不简单,因为从版本 2.6 开始,内核不会导出sys_call_table
到模块(导出此符号的最后一个内核版本是2.5.41
)。
解决此问题的一种方法是更改内核以将sys_call_table
符号导出到模块。为此,您必须将以下两行添加到kernel/kallsyms.c
(不要在生产机器上这样做):
extern void *sys_call_table;
EXPORT_SYMBOL(sys_call_table);
另一种技术是动态查找系统调用表。您遍历内核内存,将每个字与指向已知系统调用函数的指针进行比较。由于您知道该系统调用在表中的偏移量,因此您可以计算表的起始地址。
答案2
不幸的是,您无法将系统调用作为可加载模块添加到内核。每次添加新的系统调用时,您都必须忍受编译内核的痛苦。