- 了解module 加载过程
1.module加载
调用过程分两步:
- 用户空间调用insmod命令(busybox)将*.ko文件读取到用户空间的一段内存中;
- 然后通过系统调用sys_init_module进入内核态,该函数分配内核存储空间(kernel memory)给*.ko模块,将该模块内容拷贝到这块存储空间里。
1.1.函数sys_init_module:
该函数包含三个参数,且都是由insmod经用户空间传递过来:
- umod:指向用户空间*.ko文件映像数据的内存地址;
- len:该文件的数据大小;
- uargs:传给模块的参数在用户空间下的内存地址。
3860 SYSCALL_DEFINE3(init_module, void __user *, umod,
3861 unsigned long, len, const char __user *, uargs)
3862 {
3863 int err;
3864 struct load_info info = { };
3865
3866 err = may_init_module();
3869
3870 pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
3871 umod, len, uargs);
3872
3873 err = copy_module_from_user(umod, len, &info);
3876
3877 return load_module(&info, uargs, 0);
3878 }
3879
1.2.load_module
3666 static int load_module(struct load_info *info, const char __user *uargs,
3667 int flags)
3668 {
3669 struct module *mod;
3670 long err = 0;
3671 char *after_dashes;
3672
3673 err = elf_header_check(info);
3677 err = setup_load_info(info, flags);
3686 err = module_sig_check(info, flags);
3673 if (!check_modstruct_version(info, info->mod)) {
3674 err = -ENOEXEC;
3675 goto free_copy;
3676 }
3690 err = rewrite_section_headers(info, flags);
3750 err = simplify_symbols(mod, info);
}
分析如下:
第3673行:err = elf_header_check(info); 负责检查elf文件格式,变量unsigned char e_ident[EI_NIDENT] 保存elf文件头部识别字符,是否匹配#define ELFMAG “\177ELF”。
204 typedef struct elf32_hdr{
205 unsigned char e_ident[EI_NIDENT];
206 Elf32_Half e_type;
207 Elf32_Half e_machine;
208 Elf32_Word e_version;
209 Elf32_Addr e_entry; /* Entry point */
210 Elf32_Off e_phoff;
211 Elf32_Off e_shoff;
212 Elf32_Word e_flags;
213 Elf32_Half e_ehsize;
214 Elf32_Half e_phentsize;
215 Elf32_Half e_phnum;
216 Elf32_Half e_shentsize;
217 Elf32_Half e_shnum;
218 Elf32_Half e_shstrndx;
219 } Elf32_Ehdr;
第3677行:setup_load_info,ELF文件一般包含两个字符串表:
- 保存各section名称的字符串
- 保存符号表中各个符号名称的字符串
该函数实现功能:获取两个字符串表的基地址。
2935 static int setup_load_info(struct load_info *info, int flags)
2936 {
2937 unsigned int i;
2938
2939 /* Set up the convenience variables */
2940 info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
2941 info->secstrings = (void *)info->hdr
2942 + info->sechdrs[info->hdr->e_shstrndx].sh_offset;
2943
2944 /* Try to find a name early so we can log errors with a module name */
2945 info->index.info = find_sec(info, ".modinfo");
2946 if (!info->index.info)
2947 info->name = "(missing .modinfo section)";
2948 else
2949 info->name = get_modinfo(info, "name");
2950
2951 /* Find internal symbols and strings. */
2952 for (i = 1; i < info->hdr->e_shnum; i++) {
2953 if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
2954 info->index.sym = i;
2955 info->index.str = info->sechdrs[i].sh_link;
2956 info->strtab = (char *)info->hdr
2957 + info->sechdrs[info->index.str].sh_offset;
2958 break;
2959 }
2960 }
2961
2967 info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
2973 /* This is temporary: point mod into copy of data. */
2974 info->mod = (void *)info->hdr + info->sechdrs[info->index.mod].sh_offset;
2975
2980 if (!info->name)
2981 info->name = info->mod->name;
2982
2983 if (flags & MODULE_INIT_IGNORE_MODVERSIONS)
2984 info->index.vers = 0; /* Pretend no __versions section! */
2985 else
2986 info->index.vers = find_sec(info, "__versions");
2988 info->index.pcpu = find_pcpusec(info);
2990 return 0;
2991 }
第3673行:check_modstruct_version,检查struct_module符号的CRC校验码。若校验码不相等,则提示“disagrees about version of symbol struct_module”。
第3690行:第一次遍历section header table中所有entry,将每个entry中的sh_addr改写为:shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset;
1.3.模块之间的依赖关系:
系统运行中不只加载一个模块,模块可以随时添加进系统,也可以随时被卸载。这些内核模块之间并不是完全相对独立的,比如当一个模块引用到另一个模块中导出的符号时,这两个模块间就建立了依赖关系。因此,依赖关系只存在于模块与模块之间,模块与内核之间不构成依赖关系。内核必须能跟踪模块间的依赖关系。