Linux内核源代码情景分析-insmod
看本文前,先看着篇文章,Linux字符设备驱动。
insmod,大体上所做的事,有这么一些:
1、打开待安装模块并将其读入到用户空间。所谓“模块”就是经过编译但未经连接的.o文件。
2、模块中必定有一些在模块内部无法落实的符号(函数名或变量名),对这些符号的引用必须连接到内核中的相应符号。为此目的,需要通过系统调用query_module向内核询问这些符号在内核中的地址。如果内核允许"移出"这些符号的地址,就会返回有关的"符号表“。有些符号可能并不属于内核本身,而属于已经安装的其他模块。
至此在用户空间中完成了单向连接的模块镜像。后面init_module中参数mod_user,前半部分是module结构(里面的指针指向了后半部分的数据),后半部分是name大小+模块镜像(里面包含init_module和cleanup_module)+其他参数(例如deps)的集合体。
3、然后,通过系统调用create_module在内核中创建一个module数据结构,并且"预订"所需的系统(内核)空间。
4、最后,通过系统调用init_module,把用户空间中完成了单向连接的模块映像装入内核空间,再调用模块中一个名为init_module的函数(在Linux字符设备驱动一文中就是freg_init)。
接下来,我们解释query_module,代码如下:
asmlinkage long sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,//name_user为查询对象所在模块的模块名,查询的结果通过缓冲区buf返回 size_t *ret) { struct module *mod; int err; lock_kernel(); if (name_user == NULL) mod = &kernel_module; else { long namelen; char *name; if ((namelen = get_mod_name(name_user, &name)) < 0) {//把name从用户空间拷贝到系统空间 err = namelen; goto out; } err = -ENOENT; if (namelen == 0)//如果为0,则指向内核本身 mod = &kernel_module; else if ((mod = find_module(name)) == NULL) {//在module_list中寻找相应的module结构 put_mod_name(name); goto out; } put_mod_name(name); } switch (which) { case 0: err = 0; break; case QM_MODULES: err = qm_modules(buf, bufsize, ret); break; case QM_DEPS: err = qm_deps(mod, buf, bufsize, ret);//所依赖模块的个数,buf为这些模块的模块名 break; case QM_REFS: err = qm_refs(mod, buf, bufsize, ret); break; case QM_SYMBOLS: err = qm_symbols(mod, buf, bufsize, ret); break; case QM_INFO: err = qm_info(mod, buf, bufsize, ret); break; default: err = -EINVAL; break; } out: unlock_kernel(); return err; }
struct module * find_module(const char *name) { struct module *mod; for (mod = module_list; mod ; mod = mod->next) { if (mod->flags & MOD_DELETED) continue; if (!strcmp(mod->name, name)) break; } return mod; }
static int qm_deps(struct module *mod, char *buf, size_t bufsize, size_t *ret) { size_t i, space, len; if (mod == &kernel_module) return -EINVAL; if (!MOD_CAN_QUERY(mod)) if (put_user(0, ret)) return -EFAULT; else return 0; space = 0; for (i = 0; i < mod->ndeps; ++i) { const char *dep_name = mod->deps[i].dep->name;//所依赖的module的名字;mod->deps[i]指向了module_ref len = strlen(dep_name)+1; if (len > bufsize) goto calc_space_needed; if (copy_to_user(buf, dep_name, len)) return -EFAULT; buf += len; bufsize -= len; space += len; } if (put_user(i, ret)) return -EFAULT; else return 0; calc_space_needed: space += len; while (++i < mod->ndeps) space += strlen(mod->deps[i].dep->name)+1; if (put_user(space, ret)) return -EFAULT; else return -ENOSPC; }
各种数据结构如下:
struct module_symbol { unsigned long value; const char *name; }; struct module_ref { struct module *dep; /* "parent" pointer */ struct module *ref; /* "child" pointer */ struct module_ref *next_ref; }; /* TBD */ struct module_persist; struct module { unsigned long size_of_struct; /* == sizeof(module) */ struct module *next; const char *name;//模块名 unsigned long size;//模块的大小 union { atomic_t usecount; long pad; } uc; /* Needs to keep its size - so says rth */ unsigned long flags; /* AUTOCLEAN et al */ unsigned nsyms;//符号数 unsigned ndeps;//这个module所依赖module的个数 struct module_symbol *syms;//描述一个符号,包括符号名及其所在的地址 struct module_ref *deps;//这个module所依赖的module,因为数量是固定的,所以是个数组 struct module_ref *refs;//谁引用了这个module int (*init)(void);//init_module void (*cleanup)(void);//cleanup_module const struct exception_table_entry *ex_table_start; const struct exception_table_entry *ex_table_end; #ifdef __alpha__ unsigned long gp; #endif /* Members past this point are extensions to the basic module support and are optional. Use mod_member_present() to examine them. */ const struct module_persist *persist_start; const struct module_persist *persist_end; int (*can_unload)(void); int runsize; /* In modutils, not currently used */ const char *kallsyms_start; /* All symbols for kernel debugging */ const char *kallsyms_end; const char *archdata_start; /* arch specific data for module */ const char *archdata_end; const char *kernel_data; /* Reserved for kernel internal use */ }
asmlinkage unsigned long sys_create_module(const char *name_user, size_t size)//name_user为module的名字,size为module结构大小加上完成了单向连接的模块镜像大小 { char *name; long namelen, error; struct module *mod; if (!capable(CAP_SYS_MODULE)) return -EPERM; lock_kernel(); if ((namelen = get_mod_name(name_user, &name)) < 0) {//把name从用户空间拷贝到系统空间 error = namelen; goto err0; } if (size < sizeof(struct module)+namelen) { error = -EINVAL; goto err1; } if (find_module(name) != NULL) {//在module_list中寻找相应的module结构 error = -EEXIST; goto err1; } if ((mod = (struct module *)module_map(size)) == NULL) {//申请了module结构 error = -ENOMEM; goto err1; } memset(mod, 0, sizeof(*mod)); mod->size_of_struct = sizeof(*mod);//module结构的大小 mod->next = module_list; mod->name = (char *)(mod + 1);//name存放在紧跟module结构后 mod->size = size;//module结构大小+name大小+完成了单向连接的模块镜像大小+其他参数,如上面所示的mod_user memcpy((char*)(mod+1), name, namelen+1);//name复制到指定的位置(紧跟module后) put_mod_name(name); module_list = mod; //链入到module_list中 error = (long) mod; goto err0; err1: put_mod_name(name); err0: unlock_kernel(); return error; }
asmlinkage long sys_init_module(const char *name_user, struct module *mod_user)//mod_user在上面我们已经介绍过了,是用户空间中module结构 { struct module mod_tmp, *mod; char *name, *n_name, *name_tmp = NULL; long namelen, n_namelen, i, error; unsigned long mod_user_size; struct module_ref *dep; if (!capable(CAP_SYS_MODULE)) return -EPERM; lock_kernel(); if ((namelen = get_mod_name(name_user, &name)) < 0) {//把name从用户空间拷贝到系统空间 error = namelen; goto err0; } if ((mod = find_module(name)) == NULL) {//在module_list中寻找相应的module结构,我们刚刚创建了 error = -ENOENT; goto err1; } /* Check module header size. We allow a bit of slop over the size we are familiar with to cope with a version of insmod for a newer kernel. But don‘t over do it. */ if ((error = get_user(mod_user_size, &mod_user->size_of_struct)) != 0)//从用户空间拷贝module结构的大小到mod_user_size goto err1; if (mod_user_size < (unsigned long)&((struct module *)0L)->persist_start || mod_user_size > sizeof(struct module) + 16*sizeof(void*)) {//对大小做了个检查 printk(KERN_ERR "init_module: Invalid module header size.\n" KERN_ERR "A new version of the modutils is likely " "needed.\n"); error = -EINVAL; goto err1; } /* Hold the current contents while we play with the user‘s idea of righteousness. */ mod_tmp = *mod;//把刚找到的内核空间module结构mod赋值给mod_tmp name_tmp = kmalloc(strlen(mod->name) + 1, GFP_KERNEL); /* Where‘s kstrdup()? */ if (name_tmp == NULL) { error = -ENOMEM; goto err1; } strcpy(name_tmp, mod->name);//把原来的名字保存在name_tmp中 error = copy_from_user(mod, mod_user, mod_user_size);//把module结构mod_user从用户空间拷贝到内核空间module结构mod if (error) { error = -EFAULT; goto err2; } /* Sanity check the size of the module. */ error = -EINVAL; if (mod->size > mod_tmp.size) { printk(KERN_ERR "init_module: Size of initialized module " "exceeds size of created module.\n"); goto err2; } //mod_bound用来检查由用户提供的指针所指的对象是否落在模块的边界(size以内)内 if (!mod_bound(mod->name, namelen, mod)) { printk(KERN_ERR "init_module: mod->name out of bounds.\n"); goto err2; } if (mod->nsyms && !mod_bound(mod->syms, mod->nsyms, mod)) { printk(KERN_ERR "init_module: mod->syms out of bounds.\n"); goto err2; } if (mod->ndeps && !mod_bound(mod->deps, mod->ndeps, mod)) { printk(KERN_ERR "init_module: mod->deps out of bounds.\n"); goto err2; } if (mod->init && !mod_bound(mod->init, 0, mod)) { printk(KERN_ERR "init_module: mod->init out of bounds.\n"); goto err2; } if (mod->cleanup && !mod_bound(mod->cleanup, 0, mod)) { printk(KERN_ERR "init_module: mod->cleanup out of bounds.\n"); goto err2; } if (mod->ex_table_start > mod->ex_table_end || (mod->ex_table_start && !((unsigned long)mod->ex_table_start >= ((unsigned long)mod + mod->size_of_struct) && ((unsigned long)mod->ex_table_end < (unsigned long)mod + mod->size))) || (((unsigned long)mod->ex_table_start - (unsigned long)mod->ex_table_end) % sizeof(struct exception_table_entry))) { printk(KERN_ERR "init_module: mod->ex_table_* invalid.\n"); goto err2; } if (mod->flags & ~MOD_AUTOCLEAN) { printk(KERN_ERR "init_module: mod->flags invalid.\n"); goto err2; } #ifdef __alpha__ if (!mod_bound(mod->gp - 0x8000, 0, mod)) { printk(KERN_ERR "init_module: mod->gp out of bounds.\n"); goto err2; } #endif if (mod_member_present(mod, can_unload) && mod->can_unload && !mod_bound(mod->can_unload, 0, mod)) { printk(KERN_ERR "init_module: mod->can_unload out of bounds.\n"); goto err2; } if (mod_member_present(mod, kallsyms_end)) { if (mod->kallsyms_end && (!mod_bound(mod->kallsyms_start, 0, mod) || !mod_bound(mod->kallsyms_end, 0, mod))) { printk(KERN_ERR "init_module: mod->kallsyms out of bounds.\n"); goto err2; } if (mod->kallsyms_start > mod->kallsyms_end) { printk(KERN_ERR "init_module: mod->kallsyms invalid.\n"); goto err2; } } if (mod_member_present(mod, archdata_end)) { if (mod->archdata_end && (!mod_bound(mod->archdata_start, 0, mod) || !mod_bound(mod->archdata_end, 0, mod))) { printk(KERN_ERR "init_module: mod->archdata out of bounds.\n"); goto err2; } if (mod->archdata_start > mod->archdata_end) { printk(KERN_ERR "init_module: mod->archdata invalid.\n"); goto err2; } } if (mod_member_present(mod, kernel_data) && mod->kernel_data) { printk(KERN_ERR "init_module: mod->kernel_data must be zero.\n"); goto err2; } /* Check that the user isn‘t doing something silly with the name. */ if ((n_namelen = get_mod_name(mod->name - (unsigned long)mod + (unsigned long)mod_user, &n_name)) < 0) {//把用户空间的module结构mod_user的name拷贝到n_name printk(KERN_ERR "init_module: get_mod_name failure.\n"); error = n_namelen; goto err2; } if (namelen != n_namelen || strcmp(n_name, mod_tmp.name) != 0) {//n_name和原来的mod_tmp.name比较,是否一致 printk(KERN_ERR "init_module: changed module name to " "`%s‘ from `%s‘\n", n_name, mod_tmp.name); goto err3; } //通过了检查 if (copy_from_user((char *)mod+mod_user_size, (char *)mod_user+mod_user_size, mod->size-mod_user_size)) {//把模块镜像(里面包含init_module和cleanup_module)和其他参数(例如deps)的集合体从用户空间拷贝到内核空间module结构mod的后面 error = -EFAULT; goto err3; } if (module_arch_init(mod)) goto err3; /* On some machines it is necessary to do something here to make the I and D caches consistent. */ flush_icache_range((unsigned long)mod, (unsigned long)mod + mod->size); mod->next = mod_tmp.next; mod->refs = NULL; /* Sanity check the module‘s dependents */ for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) { struct module *o, *d = dep->dep; /* Make sure the indicated dependencies are really modules. */ if (d == mod) {//检查所依赖的module不是自身的module printk(KERN_ERR "init_module: self-referential " "dependency in mod->deps.\n"); goto err3; } /* Scan the current modules for this dependency */ for (o = module_list; o != &kernel_module && o != d; o = o->next) ; if (o != d) {//检查所依赖的module必须在module_list中 printk(KERN_ERR "init_module: found dependency that is " "(no longer?) a module.\n"); goto err3; } } /* Update module references. */ for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) { struct module *d = dep->dep;//根据"依赖"关系,确定"引用"关系 dep->ref = mod; dep->next_ref = d->refs; d->refs = dep; /* Being referenced by a dependent module counts as a use as far as kmod is concerned. */ d->flags |= MOD_USED_ONCE; } /* Free our temporary memory. */ put_mod_name(n_name); put_mod_name(name); /* Initialize the module. */ mod->flags |= MOD_INITIALIZING; atomic_set(&mod->uc.usecount,1); if (mod->init && (error = mod->init()) != 0) {//调用init函数,也就是init_module atomic_set(&mod->uc.usecount,0); mod->flags &= ~MOD_INITIALIZING; if (error > 0) /* Buggy module */ error = -EBUSY; goto err0; } atomic_dec(&mod->uc.usecount); /* And set it running. */ mod->flags = (mod->flags | MOD_RUNNING) & ~MOD_INITIALIZING; error = 0; goto err0; err3: put_mod_name(n_name); err2: *mod = mod_tmp; strcpy((char *)mod->name, name_tmp); /* We know there is room for this */ err1: put_mod_name(name); err0: unlock_kernel(); kfree(name_tmp); return error; }mod_bound,用来检查由用户提供的指针所指的对象是否落在模块的边界(size以内)内,代码如下:
#define mod_bound(p, n, m) ((unsigned long)(p) >= ((unsigned long)(m) + ((m)->size_of_struct)) && (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size)对于,mod_bound(mod->name, namelen, mod)来说:
((unsigned long)(mod->name) >= ((unsigned long)(mod) + ((mod)->size_of_struct)) && (unsigned long)((mod->name)+(namelen)) <= (unsigned long)(mod) + (mod)->size)最后sys_init_module,调用了mod->init方法,也就是init_module,对于Linux字符设备驱动,也就是freg_init。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。