模块是向内核添加新功能的一种方法,当需要为内核编写设备驱动程序,扩展内核功能时,就可以通过编写内核模块扩展内核,模块可以动态的加载和移除,所以无须重新编译内核或者重启操作系统,现在5.X内核源码和之前研究的2.6.X关于内核模块的改动比较大,所以基于当前最新的5.X内核源码重新研究下内核模块的实现。
- 模块的内核表示
Module结构的成员较多,这里我提取了部分重要的成员注释说明
struct module {
enum module_state state; //模块的状态
//链接到内核模块链表的元素
struct list_head list;
//模块名称
char name[MODULE_NAME_LEN];
//导出到内核的模块符号
const struct kernel_symbol *syms;
const s32 *crcs;
unsigned int num_syms;
//模块核心代码在地址空间的位置信息
struct module_layout core_layout __module_layout_align;
//模块初始化代码地址信息
struct module_layout init_layout;
//模块初始化函数
int (*init)(void);
//模块是否会污染内核
unsigned long taints;
//本模块被其它模块依赖的链表
struct list_head source_list;
//本模块依赖哪些其它模块
struct list_head target_list;
//模块销毁时调用的函数
void (*exit)(void);
//模块被引用的次数
atomic_t refcnt;
}
- 插入内核模块过程
内核执行流程:
- 用户空间insmod发起加载模块系统调用
-
init_module函数,它复制模块二进制代码到内核地址空间,初始化load_info结构体(包含模块二进制内容)
-
load_module 检查模块有效性(elf头部检查、签名检查),解决引用、修复重定位项等
-
do_init_module 设置module状态等信息,并执行模块初始化函数,至此模块已加载完毕
init_module说明
SYSCALL_DEFINE3(init_module, void __user *, umod,
unsigned long, len, const char __user *, uargs)
{
int err;
struct load_info info = { };
//如果禁止模块加载则返回错误代码并停止加载过程
err = may_init_module();
if (err)
return err;
pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
umod, len, uargs);
//从用户空间拷贝模块二进制代码到内核空间
err = copy_module_from_user(umod, len, &info);
if (err)
return err;
//执行模块实际加载工作
return load_module(&info, uargs, 0);
}
load_module说明
static int load_module(struct load_info *info, const char __user *uargs,
int flags)
{
struct module *mod;
long err = 0;
char *after_dashes;
//模块签名验证
err = module_sig_check(info, flags);
if (err)
goto free_copy;
//检查ELF 头部和区段有效性
err = elf_validity_check(info);
if (err) {
pr_err("Module has invalid ELF structures\n");
goto free_copy;
}
//设置模块结构各个区段在内核地址空间的位置
err = setup_load_info(info, flags);
if (err)
goto free_copy;
//检查模块是否在内核黑名单内,如果是则禁止加载
if (blacklisted(info->name)) {
err = -EPERM;
pr_err("Module %s is blacklisted\n", info->name);
goto free_copy;
}
//设置所有节区在内核临时内存地址空间的位置
err = rewrite_section_headers(info, flags);
if (err)
goto free_copy;
//检查模块版本
if (!check_modstruct_version(info, info->mod)) {
err = -ENOEXEC;
goto free_copy;
}
//为内核模块分配内存
mod = layout_and_allocate(info, flags);
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
}
audit_log_kern_module(mod->name);
//判断模块是否已经加载,若没有则将模块添加到内核模块链表中
err = add_unformed_module(mod);
if (err)
goto free_module;
#ifdef CONFIG_MODULE_SIG
mod->sig_ok = info->sig_ok;
if (!mod->sig_ok) {
pr_notice_once("%s: module verification failed: signature "
"and/or required key missing - tainting "
"kernel\n", mod->name);
add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
}
#endif
/* To avoid stressing percpu allocator, do this once we're unique. */
err = percpu_modalloc(mod, info);
if (err)
goto unlink_mod;
//初始化模块依赖引用链表并增加引用计数
err = module_unload_init(mod);
if (err)
goto unlink_mod;
init_param_lock(mod);
/*
* Now we've got everything in the final locations, we can
* find optional sections.
*/
err = find_module_sections(mod, info);
if (err)
goto free_unload;
err = check_module_license_and_versions(mod);
if (err)
goto free_unload;
/* Set up MODINFO_ATTR fields */
setup_modinfo(mod, info);
//修复模块未解决引用
err = simplify_symbols(mod, info);
if (err < 0)
goto free_modinfo;
//处理重定位
err = apply_relocations(mod, info);
if (err < 0)
goto free_modinfo;
err = post_relocation(mod, info);
if (err < 0)
goto free_modinfo;
flush_module_icache(mod);
//拷贝模块参数到module->args
mod->args = strndup_user(uargs, ~0UL >> 1);
if (IS_ERR(mod->args)) {
err = PTR_ERR(mod->args);
goto free_arch_cleanup;
}
dynamic_debug_setup(mod, info->debug, info->num_debug);
/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
ftrace_module_init(mod);
//模块已准备完成,设置模块状态标识等
err = complete_formation(mod, info);
if (err)
goto ddebug_cleanup;
err = prepare_coming_module(mod);
if (err)
goto bug_cleanup;
/* Module is ready to execute: parsing args may do that. */
after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
-32768, 32767, mod,
unknown_module_param_cb);
if (IS_ERR(after_dashes)) {
err = PTR_ERR(after_dashes);
goto coming_cleanup;
} else if (after_dashes) {
pr_warn("%s: parameters '%s' after `--' ignored\n",
mod->name, after_dashes);
}
//链接到/sysfs文件系统
err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
if (err < 0)
goto coming_cleanup;
if (is_livepatch_module(mod)) {
err = copy_module_elf(mod, info);
if (err < 0)
goto sysfs_cleanup;
}
//释放加载模块过程中分配的临时内存
free_copy(info);
/* Done! */
trace_module_load(mod);
return do_init_module(mod);
do_init_module说明
static noinline int do_init_module(struct module *mod)
{
int ret = 0;
struct mod_initfree *freeinit;
//为内核初始化代码分配内存
freeinit = kmalloc(sizeof(*freeinit), GFP_KERNEL);
if (!freeinit) {
ret = -ENOMEM;
goto fail;
}
freeinit->module_init = mod->init_layout.base;
/*
* We want to find out whether @mod uses async during init. Clear
* PF_USED_ASYNC. async_schedule*() will set it.
*/
current->flags &= ~PF_USED_ASYNC;
do_mod_ctors(mod);
//启动模块,如果存在初始化函数则执行它
if (mod->init != NULL)
ret = do_one_initcall(mod->init);
if (ret < 0) {
goto fail_free_freeinit;
}
if (ret > 0) {
pr_warn("%s: '%s'->init suspiciously returned %d, it should "
"follow 0/-E convention\n"
"%s: loading module anyway...\n",
__func__, mod->name, ret, __func__);
dump_stack();
}
//设置模块运行标识
mod->state = MODULE_STATE_LIVE;
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_LIVE, mod);
至此模块已加载到Linux内核,以后再分享将如何使用内核提供的模块机制来编写自己的内核模块,内核模块编写这块资料还是比较多,而且内核关于这块的变动不大,各个内核版本几乎是兼容的。