一、什么是符号引入,为啥要导入?
在linux内核中,有分为内核程序与模块程序,内核是主题,而模块可以动态从内核地添加与卸载。
如果模块程序中需要使用内核模块中定义的函数或者变量,那么内核模块就需要将它们导出(引入的反向操作)。
同理,如果不同模块之间也需要相互使用对方定义的东西,那么都要导出它们。
不导出的危害
如果不导出,但是又有别的模块需要使用它们,虽然该模块在编译时正常,(产生未解决的引用问题)但是当加入到内核中却没有解决这个未解决的引用问题(也就是没有找到该符号的地址)那么就会报错。
如何导出?
EXPORT_SYMBOL//允许被所有模块以及内核使用
EXPORT_SYMBOL_GPL//不支持给不支持GPL的模块使用
EXPORT_SYMBOL_GPL_FUTURE//支持给所有模块使用,但是不支持GPL会报警
二、如何导入?
在此之前我们先了解一下有关内核模块加载的知识
内核模块
在编译好之后使用file命令可以发现其实就是ELF格式的文件(Linux下的可执行文件格式),它允许重定位。
我们先了解一下它的结构。!](https://img-blog.csdnimg.cn/3d4ec9d98ee64e9fa70ef4663a606a3a.png)
其中ELF header描绘的是Section header entry的基本信息(地址等),而Section header entry描绘的是它对应的Section对应的基本信息(地址等),Section就是ELF文件的主题部分了。
insmod之后,模块是如何被加载到内核中的?
1.insmod触发read系统调用,调用到了相应文件系统的APL,将模块的ELF加载到某块用户空间中
2.sys_init_module系统调用,完成剩下的模块加载任务
sys_init_module
在详细了解如何加载模块到内核之前我们先了解一个结构体,它是内核模块在内核中的抽象
struct module {
enum module_state state;//模块的状态,在模块导入过程中有不同的状态
/* Member of list of modules */
struct list_head list;//内核模块被内核用链表管理起来
/* Unique handle for this module */
char name[MODULE_NAME_LEN];//模块名称
/* Exported symbols */
const struct kernel_symbol *syms;//这个就是导出的符号,里面包含符号名称与地址
/* Kernel parameters. */
struct kernel_param *kp;//模块参数
/* Startup function. */
int (*init)(void);//模块初始化指针
//用于管理相互依赖的模块
/* What modules depend on me? */
struct list_head source_list;
/* What modules do I depend on? */
struct list_head target_list;
......
......
......
};
1.调用load_module
1.vmalloc在内核中分配一大块内存
2.利用copy_from_user将前面用户空间的ELF文件拷贝到内核空间(叫做HDR,但是HDR也不是最终的地址)
2.两次改写
第一次:
1.将ELF从用户空间拷贝到内核空间后,我们需要改写Section header entry中的地址(但是Section的内容没有改动,注意)
2.利用.gnu.linkonce.this_module Section初始化mod对象(struct moudle结构体)
第二次:
1.将最终搬移好的ELF中的Section分为CORE与INIT两种,(其中.gnu.linkonce.this_module Section在CORE中)
2.搬移好之后再次改写Section header entry中的地址,并且Mod中的地址也要改变。
3.将INIT Section删除
模块的导入是如何实现的?
那就要从EXPORT_SYMBOL的宏说起了,简言之,这些宏的作用就是生成了kernel_sysmbol结构体对象(包含了符号名与符号地址)
重要的是:
这个对象在编译之后是被放入_ksymtab Section中的,而这个Section也会被搬移到CORE Section中。
(_ksymtab Section是由kernel_sysmbol数组构成的)
别的模块如何查找到它的地址?
前面我们知道了模块在内核中有一个抽象struct module通过这个模块与模块以及内核就会产生联系,借用simplify_symbols函数以及find_symbol就能找到对应的地址,如果找不到就报错。
这样真的可以了吗?
我们知道在两次搬移HDR过程中,都没有改写Section中的内容,而_ksymtab Section中包含kernel_sysmbol,它里面又包含符号的地址,所以这样会造成错误。
重定位:
其实在ELF文件中还有一个与_ksymtab Section对应的 .rel_ksymtab Section,利用这个Section以及其它的一些Section在第二次搬移之后将_ksymtab Section中的地址修改为正确地址。