不知大家有没有想过,在一个内核模块代码中,会用到printk函数,而这个函数不是我们实现的,它是内核代码的一部分,但我们为什么能够编译通过呢?
我们的代码之所以能够编译通过,是因为对模块的编译 仅仅是编译,并没有链接 。
编译出来的.ko文件是一个普通的ELF文件 ,使用file命令和nm命令,我们可以看到相关的信息:
# file vser.ko
vser.ko ELF 32-bit LSB relocatable, Intel 80386, vserion 1 (SYSV), BuildID[sha1]=0x09ca747e6f75c65v19a5da9102113v98d7cea24, not stripped
# nm vser.ko
......
00000004 d port
U printk
00000000 t vser_exit
00000000 t vser_init
vser_init和vser_exit分别是模块的入口函数和出口函数,使用nm命令查看模块目标文件的符号信息时,可以看到vser_exit和vser_init的符号类型是t,表示它们是 函数 。
而printk的 符号类型是U,表示它是一个 未决符号 。意思是说在编译阶段不知道这个符号的地址,因为它被定义在其他文件中,没有放在模块代码一起编译。
那printk函数的地址问题怎么解决呢?答案是用EXPORT_SYMBOL宏将printk导出即可。
EXPORT_SYMBOL导出符号
大致原理:利用EXPORT_SYMBOL宏生成一个特定的结构并放在ELF文件的一个特定段中,在 内核的启动过程中,会将符号的确切地址填充到这个结构的特定成员中 。
模块加载时,加载程序将去处理未决符号,在特殊段中搜索符号的名字,如果找到,则将获得的地址填充在被加载模块的相应段中,这样符号的地址就可以确定。
使用这种方式处理未决符号,其实相当于把链接的过程推后,进行了动态链接,和普通的应用程序使用共享库函数的道理是类似的 。可以发现,内核将会有大量的符号导出,为模块提供了丰富的基础设施。
-
内核
+关注
关注
4文章
1436浏览量
42491 -
Linux
+关注
关注
88文章
11628浏览量
217970 -
函数
+关注
关注
3文章
4406浏览量
66838 -
编译
+关注
关注
0文章
688浏览量
34944
发布评论请先 登录
Linux内核学习笔记:printk调试
printk()函数的总结
linux内核打印函数printk的方法
如何配置和使用Linux内核printk功能
你知道Linux内核调试关键技术之一的printk?
Linux中的Printk与dmesg功能
使用LPNDR_ReadGlobalInfo函数块读取IP地址和MAC地址
C语言如何获得自身定义函数的实际地址和大小吗
Linux内核pr_xx()函数封装
Linux内核printk日志级别全解析:从参数解读到实操配置

printk函数的地址问题怎么解决
评论