模块加载
load_module()
函数调用的功能函数如下:
module_sig_check()
函数用于检查模块的签名验证elf_header_check()
函数用于检查模块的elf头和区段有效性layout_and_allocate()
函数用于分配内核内存空间,把模块相关的节区复制过来audit_log_kern_module()
函数用于检查是否开启了安全审计add_unformed_module()
函数用于判断模块是否已经加载,若没有则将模块添加到内核模块链表中percpu_modalloc()
函数用于申请percpu变量内存,这个变量每个CPU都有一份,无相独立module_unload_init()
函数用于初始化化模块依赖和引用链表,并对模块引用计数加1init_param_lock()
函数用于初始化互斥锁find_module_sections()
函数用于遍历模块中的其它分段check_module_license_and_versions()
函数用于检测模块的CRC是否正确,license授权是否正确setup_modinfo()
函数根据.modinfo段设置模块信息simplify_symbols()
函数用于将模块内存中的静态链接重定位表中的符号,全部指向其真实的内存地址apply_relocations()
函数用于遍历目标文件中的所有内存节区的重定位节区,并遍历每个节区中的每个静态链接重定位表项,对其做静态链接post_relocation()
函数用于对重定位后的percpu变量重新赋值,并将即将加载完成的模块的符号加入到内核模块的符号链表中,如果成功加载此模块且内核配置了CONFIG_KALLSYMS
,那么在/proc/kallsyms下可以看到此模块的符号flush_module_icache()
函数用于刷新模块的init_layout和core_layout的cache。strndup_user()
函数用于复制用户空间的参数到内核空间dynamic_debug_setup()
函数用于处理debug
节区,需要开启内核CONFIG_DYNAMIC_DEBUG
才会启用ftrace_module_init()
函数需要开启相关的ftrace配置complete_formation()
函数用于遍历模块中的所有导出函数,并检查在内核中是否有同名的导出符号,为模块的init_layout/core_layout做RONX保护prepare_coming_module()
函数用于发送模块加载通知链parse_args()
函数用于参数解析与sysfs、livepatch的设置mod_sysfs_setup()
函数用于在sysfs中创建模块相应的项is_livepatch_module()
函数用于检查模块是否是热补丁模块copy_module_elf()
函数用于拷贝模块的elf头free_copy()
函数用于释放最初内核申请的用于保存模块原文件信息的内存trace_module_load()
函数用于加载一些和trace相关的内容,便于后期跟踪调试do_init_module()
函数用于调用模块的初始化函数
static int load_module(struct load_info *info, const char __user *uargs,
int flags)
{
struct module *mod;
long err;
char *after_dashes;
err = module_sig_check(info, flags);
if (err)
goto free_copy;
err = elf_header_check(info);
if (err)
goto free_copy;
/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(info, flags);
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
}
audit_log_kern_module(mod- >name);
/* Reserve our place in the list. */
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;
/* Now module is in final location, initialize linked lists, etc. */
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);
/* Fix up syms, so that st_value is a pointer to location. */
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);
/* Now copy in 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);
/* Finally it's fully formed, ready to start executing. */
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);
}
/* Link in to 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;
}
/* Get rid of temporary copy. */
free_copy(info);
/* Done! */
trace_module_load(mod);
return do_init_module(mod);
sysfs_cleanup:
mod_sysfs_teardown(mod);
coming_cleanup:
mod- >state = MODULE_STATE_GOING;
destroy_params(mod- >kp, mod- >num_kp);
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod);
klp_module_going(mod);
bug_cleanup:
mod- >state = MODULE_STATE_GOING;
/* module_bug_cleanup needs module_mutex protection */
mutex_lock(&module_mutex);
module_bug_cleanup(mod);
mutex_unlock(&module_mutex);
/* we can't deallocate the module until we clear memory protection */
module_disable_ro(mod);
module_disable_nx(mod);
ddebug_cleanup:
dynamic_debug_remove(mod, info- >debug);
synchronize_sched();
kfree(mod- >args);
free_arch_cleanup:
module_arch_cleanup(mod);
free_modinfo:
free_modinfo(mod);
free_unload:
module_unload_free(mod);
unlink_mod:
mutex_lock(&module_mutex);
/* Unlink carefully: kallsyms could be walking list. */
list_del_rcu(&mod- >list);
mod_tree_remove(mod);
wake_up_all(&module_wq);
/* Wait for RCU-sched synchronizing before releasing mod- >list. */
synchronize_sched();
mutex_unlock(&module_mutex);
free_module:
/*
* Ftrace needs to clean up what it initialized.
* This does nothing if ftrace_module_init() wasn't called,
* but it must be called outside of module_mutex.
*/
ftrace_release_mod(mod);
/* Free lock-classes; relies on the preceding sync_rcu() */
lockdep_free_key_range(