标签:
看本文前,先看着篇文章,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。
标签:
原文地址:http://blog.csdn.net/jltxgcy/article/details/45536025