码迷,mamicode.com
首页 > 系统相关 > 详细

Linux内核源代码情景分析-insmod

时间:2015-05-07 08:45:47      阅读:163      评论:0      收藏:0      [点我收藏+]

标签:

    看本文前,先看着篇文章,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 */
}

    


    再来看,create_module在内核中创建一个module数据结构,并且"预订"所需的系统(内核)空间,代码如下:

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;
}


    最后,我们看init_module,把用户空间中完成了单向连接的模块映像装入内核空间,再调用模块中一个名为init_module的函数,代码如下:

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。

Linux内核源代码情景分析-insmod

标签:

原文地址:http://blog.csdn.net/jltxgcy/article/details/45536025

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!