Linux 模块加载学习笔记
Linux 3.10
sys_init_module 加载模块的系统调用
SYSCALL_DEFINE3(init_module, void __user *, umod,unsigned long, len, const char __user *, uargs)
{
int err;
struct load_info info = { };
//全局变量modules_disabled决定了是否允许加载模块啊
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);//从用户层复制模块到内核层,并进行SElinux检查
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;
err = module_sig_check(info);//对内核模块签名进行检查。
if (err)
goto free_copy;
err = elf_header_check(info);//检查是否为合法ELF文件
if (err)
goto free_copy;
/* Figure out module layout, and allocate all the memory. */
//使用module_alloc分配内存,即__vmalloc_node_range,分配内存的范围为VMALLOC_START-VMALLOC_END。init节在初始化完后会被释放。
//mod = (void *)info->sechdrs[info->index.mod].sh_addr;mod对象在core的一个节中,节名为.gnu.linkonce.this_module
//info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
mod = layout_and_allocate(info, flags);//从新布局内核模块的地址空间,core和init。
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
}
/* Reserve our place in the list. */
//检查是否有同名模块已加载或正在加载,如果没有同名模块,则将当前模块加到内核的模块列表modules中
err = add_unformed_module(mod);
if (err)
goto free_module;
#ifdef CONFIG_MODULE_SIG
mod->sig_ok = info->sig_ok;
if (!mod->sig_ok) {//模块签名失败但放过的情况
printk_once(KERN_NOTICE
"%s: module verification failed: signature and/or"
" required key missing - tainting kernel\n",
mod->name);
add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_STILL_OK);
}
#endif
/* Now module is in final location, initialize linked lists, etc. */
err = module_unload_init(mod); //根据CONFIG_MODULE_UNLOAD是否定义,是否允许卸载模块
if (err)
goto unlink_mod;
/* Now we've got everything in the final locations, we can
* find optional sections. */
find_module_sections(mod, info);
err = check_module_license_and_versions(mod);
if (err)
goto free_unload;
/* Set up MODINFO_ATTR fields */
setup_modinfo(mod, info); //初始化sysfs文件系统下模块的attr文件,由modinfo_attrs定义
/* 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(info->debug, info->num_debug);
/* Finally it's fully formed, ready to start executing. */
err = complete_formation(mod, info); //导出符号检查与RONX保护
if (err)
goto ddebug_cleanup;
/* Module is ready to execute: parsing args may do that. */
//解析模块参数,其中args是insmod传入的实参,而kp则是模块的实参类型,记录在模块的段__param中
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
-32768, 32767, &ddebug_dyndbg_module_param_cb);
if (err < 0)
goto bug_cleanup;
/* Link in to syfs. */
//初始化sysfs文件系统部分,连接到sysfs文件系统书上,创建相关的节点
err = mod_sysfs_setup(mod, info, mod->kp, mod->num_kp);
if (err < 0)
goto bug_cleanup;
/* Get rid of temporary copy. */
free_copy(info);
/* Done! */
trace_module_load(mod);
return do_init_module(mod); //初始化内核模块
bug_cleanup:
/* module_bug_cleanup needs module_mutex protection */
mutex_lock(&module_mutex);
module_bug_cleanup(mod);
mutex_unlock(&module_mutex);
ddebug_cleanup:
dynamic_debug_remove(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);
wake_up_all(&module_wq);
mutex_unlock(&module_mutex);
free_module:
module_deallocate(mod, info);
free_copy:
free_copy(info);
return err;
}
do_init_module 初始化加载的模块
/* This is where the real work happens */
static int do_init_module(struct module *mod)
{
int ret = 0;
/*
* 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;
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_COMING, mod); // 通知模块通知链,模块加载
/* Set RO and NX regions for core */
set_section_ro_nx(mod->module_core,
mod->core_text_size,
mod->core_ro_size,
mod->core_size);
/* Set RO and NX regions for init */
set_section_ro_nx(mod->module_init,
mod->init_text_size,
mod->init_ro_size,
mod->init_size);
do_mod_ctors(mod);
/* Start the module */
if (mod->init != NULL)
ret = do_one_initcall(mod->init); //调用模块的初始化函数
if (ret < 0) { //初始化函数失败
/* Init routine failed: abort. Try to protect us from
buggy refcounters. */
mod->state = MODULE_STATE_GOING;
synchronize_sched();
module_put(mod);
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod); //通知模块通知链,模块卸载
free_module(mod);
wake_up_all(&module_wq);
return ret;
}
if (ret > 0) {
printk(KERN_WARNING
"%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();
}
/* Now it's a first class citizen! */
mod->state = MODULE_STATE_LIVE;
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_LIVE, mod); 通知模块通知链,模块运行
/*
* We need to finish all async code before the module init sequence
* is done. This has potential to deadlock. For example, a newly
* detected block device can trigger request_module() of the
* default iosched from async probing task. Once userland helper
* reaches here, async_synchronize_full() will wait on the async
* task waiting on request_module() and deadlock.
*
* This deadlock is avoided by perfomring async_synchronize_full()
* iff module init queued any async jobs. This isn't a full
* solution as it will deadlock the same if module loading from
* async jobs nests more than once; however, due to the various
* constraints, this hack seems to be the best option for now.
* Please refer to the following thread for details.
*
* http://thread.gmane.org/gmane.linux.kernel/1420814
*/
if (current->flags & PF_USED_ASYNC)
async_synchronize_full();
mutex_lock(&module_mutex);
/* Drop initial reference. */
module_put(mod);
trim_init_extable(mod);
#ifdef CONFIG_KALLSYMS
mod->num_symtab = mod->core_num_syms;
mod->symtab = mod->core_symtab;
mod->strtab = mod->core_strtab;
#endif
unset_module_init_ro_nx(mod);
module_free(mod, mod->module_init); // 释放模块的init节
mod->module_init = NULL;
mod->init_size = 0;
mod->init_ro_size = 0;
mod->init_text_size = 0;
mutex_unlock(&module_mutex);
wake_up_all(&module_wq);
return 0;
}
mod_sysfs_setup 初始化模块的sysfs部分
static int mod_sysfs_setup(struct module *mod,
const struct load_info *info,
struct kernel_param *kparam,
unsigned int num_params)
{
int err;
//将模块添加到sysfs树上,初始化/sys/module/[mod->name]节点,所属kset为module_kset,ktype为module_ktype
err = mod_sysfs_init(mod);
if (err)
goto out;
mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj);
if (!mod->holders_dir) {
err = -ENOMEM;
goto out_unreg;
}
//在/sys/module/[mod->name]/parameters/目录下添加模块参数文件
err = module_param_sysfs_setup(mod, kparam, num_params);
if (err)
goto out_unreg_holders;
//添加modinfo_attrs里定义的文件,
//uevent,version,srcversion,initstate,coresize,initsize,taint
err = module_add_modinfo_attrs(mod);
if (err)
goto out_unreg_param;
//在该模块所依赖的模块的holders目录下添加指向该模块的软连接
add_usage_links(mod);
//通过sysfs_create_group创建各节区的属性文件
add_sect_attrs(mod, info);
//在notes目录下通过sysfs_create_bin_file创建SHT_NOTE类型节的文件
// /sys/module/foo/notes/.section.name gives contents of SHT_NOTE sections.
add_notes_attrs(mod, info);
//产生一个add 事件
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
return 0;
out_unreg_param:
module_param_sysfs_remove(mod);
out_unreg_holders:
kobject_put(mod->holders_dir);
out_unreg:
kobject_put(&mod->mkobj.kobj);
out:
return err;
}
static struct module_attribute *modinfo_attrs[] = {
&module_uevent,
&modinfo_version,
&modinfo_srcversion,
&modinfo_initstate,
&modinfo_coresize,
&modinfo_initsize,
&modinfo_taint,
#ifdef CONFIG_MODULE_UNLOAD
&modinfo_refcnt,
#endif
NULL,
};
//以modinfo_initstate为例
//文件名为initstate,访问权限为0444,show函数为show_initstate,store函数为空
static struct module_attribute modinfo_initstate =
__ATTR(initstate, 0444, show_initstate, NULL);
static ssize_t show_initstate(struct module_attribute *mattr,
struct module_kobject *mk, char *buffer)
{
const char *state = "unknown";
switch (mk->mod->state) {
case MODULE_STATE_LIVE:
state = "live";
break;
case MODULE_STATE_COMING:
state = "coming";
break;
case MODULE_STATE_GOING:
state = "going";
break;
default:
BUG();
}
return sprintf(buffer, "%s\n", state);
}