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

描述

不知大家有没有想过,在一个内核模块代码中,会用到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_initvser_exit分别是模块的入口函数和出口函数,使用nm命令查看模块目标文件的符号信息时,可以看到vser_exitvser_init的符号类型是t,表示它们是 函数

printk的 符号类型是U,表示它是一个 未决符号 。意思是说在编译阶段不知道这个符号的地址,因为它被定义在其他文件中,没有放在模块代码一起编译。

那printk函数的地址问题怎么解决呢?答案是用EXPORT_SYMBOL宏将printk导出即可。

EXPORT_SYMBOL导出符号

大致原理:利用EXPORT_SYMBOL宏生成一个特定的结构并放在ELF文件的一个特定段中,在 内核的启动过程中,会将符号的确切地址填充到这个结构的特定成员中

模块加载时,加载程序将去处理未决符号,在特殊段中搜索符号的名字,如果找到,则将获得的地址填充在被加载模块的相应段中,这样符号的地址就可以确定。

使用这种方式处理未决符号,其实相当于把链接的过程推后,进行了动态链接,和普通的应用程序使用共享库函数的道理是类似的 。可以发现,内核将会有大量的符号导出,为模块提供了丰富的基础设施。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分