C语言 让内核模块链接到的标准内核库在哪里?

6ovsh4lw  于 2023-05-22  发布在  其他
关注(0)|答案(3)|浏览(184)

内核模块不能调用libc,因为libc是在用户空间下运行的。
还有一些其他内核指定的API,如printk(),可以使模块正常工作。
据我所知,libc是几个标准c函数obj(s)的集合。
它应该存在一个集合(或库)来包含几个内核标准函数对象。
所以我可以把我的内核模块和这些内核标准库链接起来工作,对吗?
简单来说,我的问题是:
在用户空间中:
aaa.o链接bbb.o以调用myfunc()
aaa.o链接libc.so以调用printf()
在内核空间中:
aaa.ko link bbb.ko call myfunc()?这是问题1
aaa.ko链接xxx调用printk()?xxx叫什么,问题2
谢谢!

6mw9ycah

6mw9ycah1#

内核模块只能调用内核函数(位于内核的固定部分)。它们不使用也不能使用任何外部库。
因此没有内核标准库(包含printk的是内核本身)。
从概念上讲,内核代码使用的是C的freestanding方言;它不使用任何C标准库函数(由于不清楚的原因,Linux内核代码不是用-ffreestandingdialect optiongcc编译的)

vh0rcniy

vh0rcniy2#

你会得到一个.ko文件,它是一个内核对象文件。GCC没有链接它。在运行时,你插入它的内核会这样做。

smdnsysy

smdnsysy3#

问题二

aaa.ko链接xxx调用printk()?xxx叫什么
这仍然是一个动态的运行时重定位,就像在用户空间中一样,只是没有库Map。因此,动态链接器ld.so并不处理这些重定位,而是由insmod(2)函数来处理。实际上,insmod(2)只不过是在.ko中Map并将请求传递给系统调用sys_init_module
/include/linux/syscall.h中,您可以看到init_modulekernel/module.c中定义。我使用5.4.x版本作为参考。如果你浏览代码,你会看到一系列这样的调用:init_module() -> load_module() -> simplify_symbols() -> lookup_symbol()
由于.ko是一个ELF对象,它有一个符号表和重定位部分,就像任何用户空间ELF二进制文件一样。Simplify_symbols遍历模块的符号表,并在运行时解析可重定位的符号,此时可以知道地址。
对于用户态进程,库必须Map到进程内存中。对于内核模块,则相反。模块被Map到所有函数都存在的内核内存中,它们只需要被解析/找到。要解决的问题是,内核在其内存早期的固定位置有一个符号表,不受ASLR的影响。Lookup_symbol使用符号的名称搜索内核符号表,并解析其地址。
这是对该过程的非常简短和简化的描述。您需要掌握的是insmodinit_module进行系统调用,通过搜索内核符号表在运行时解析函数地址。然后,模块代码中的地址用真实的地址(或者更可能的是地址的偏移)来固定。

问题1

aaa.ko链接bbb.ko调用myfunc()
当一个模块被加载到内存中时,它就成为内核代码的一部分。每次在模块中使用EXPORT_SYMBOL()宏都会将这些符号添加到内核符号表中。因此bbb.ko将包含EXPORT_SYMBOL(myfunc)行,这会导致在加载模块时将其添加到内核符号表中。当模块aaa.ko必须解析myfunc()时,它将像问题2中描述的过程一样执行。

人工示例

内核内核默认情况下 * 导出 * 许多有用的函数和变量,这些函数和变量对大多数内核开发人员都很有用,如您提到的printkPrintk是在kernel/printk/printk.c中定义的,你可以看到紧跟着这个函数的是EXPORT_SYMBOL(printk),因为所有的符号都必须显式地全局导出。你可以像这样验证它:

$sudo grep -E '\bprintk\b' /proc/kallsyms  
<some address> T printk

在加载了假设的bbb.ko模块和导出的函数myfunc之后,您还可以在内核符号表中找到它:

$sudo grep -E '\bmyfunc\b' /proc/kallsyms  
<some address> T myfunc  [bbb]

相关问题