转自:http://blog.csdn.net/chrovery/article/details/51088425
- 转自http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27717694&id=3971861
- 一、前言
- 对于现在编译的一些module要insmod在系统上时,可能会报各种各样的错误。这些错误仔细研读内核源码,都能找出原因。2.6 内核以前的insmod部分主要依赖于modutils源码包,在用户层基本将工作完成,加载过程参考前一篇文章。2.6 内核以后的做法是将大部分的原来用户级操作纳入内核中来处理,无论是逻辑上还是代码量上都比原来减少了许多,通过busybox中的insmod命令与内核接入。把这套源代码弄明白,就不会出现加载模块时出现的各种各样的错误,可以写一些patch代码,修正这些内核要检查的项,比如vermagic和crc等等。
- 二、相关结构
- 1.模块依赖关系
- struct module_use{
- struct list_head source_list;
- struct list_head target_list;
- struct module*source,*target;
- };
- 2.模块状态
- enum module_state{
- MODULE_STATE_LIVE,/*Normal state.*/
- MODULE_STATE_COMING,/*Full formed,running module_init.*/
- MODULE_STATE_GOING,/*Going away.*/
- MODULE_STATE_UNFORMED,/*Still setting it up.*/
- };
- 3.模块计数
- struct module_ref{
- unsigned long incs;
- unsigned long decs;
- }__attribute((aligned(2*sizeof(unsigned long))));
- 4.模块结构
- struct module
- {
- enum module_state state;
- /*Member of list of modules*/
- struct list_head list;
- /*Unique handleforthis module*/
- char name[MODULE_NAME_LEN];
- /*Sysfs stuff.*/
- struct module_kobject mkobj;
- struct module_attribute*modinfo_attrs;
- constchar*version;
- constchar*srcversion;
- struct kobject*holders_dir;
- /*Exported symbols*/
- conststruct kernel_symbol*syms;
- constunsigned long*crcs;
- unsignedintnum_syms;
- /*Kernel parameters.*/
- struct kernel_param*kp;
- unsignedintnum_kp;
- /*GPL-only exported symbols.*/
- unsignedintnum_gpl_syms;
- conststruct kernel_symbol*gpl_syms;
- constunsigned long*gpl_crcs;
- #ifdef CONFIG_UNUSED_SYMBOLS
- /*unused exported symbols.*/
- conststruct kernel_symbol*unused_syms;
- constunsigned long*unused_crcs;
- unsignedintnum_unused_syms;
- /*GPL-only,unused exported symbols.*/
- unsignedintnum_unused_gpl_syms;
- conststruct kernel_symbol*unused_gpl_syms;
- constunsigned long*unused_gpl_crcs;
- #endif
- #ifdef CONFIG_MODULE_SIG
- /*Signature was verified.*/
- bool sig_ok;
- #endif
- /*symbols that will be GPL-onlyinthe near future.*/
- conststruct kernel_symbol*gpl_future_syms;
- constunsigned long*gpl_future_crcs;
- unsignedintnum_gpl_future_syms;
- /*Exception table*/
- unsignedintnum_exentries;
- struct exception_table_entry*extable;
- /*Startupfunction.*/
- int(*init)(void);
- /*Ifthisisnon-NULL,vfree after init()returns*/
- void*module_init;
- /*Hereisthe actual code+data,vfree'donunload.*/
- void*module_core;
- /*Here are the sizes of the initandcore sections*/
- unsignedintinit_size,core_size;
- /*The size of the executable codeineachsection.*/
- unsignedintinit_text_size,core_text_size;
- /*Size of RO sections of the module(text+rodata)*/
- unsignedintinit_ro_size,core_ro_size;
- /*Arch-specific module values*/
- struct mod_arch_specific arch;
- unsignedinttaints;/*same bits as kernel:tainted*/
- #ifdef CONFIG_GENERIC_BUG
- /*SupportforBUG*/
- unsigned num_bugs;
- struct list_head bug_list;
- struct bug_entry*bug_table;
- #endif
- #ifdef CONFIG_KALLSYMS
- Elf_Sym*symtab,*core_symtab;
- unsignedintnum_symtab,core_num_syms;
- char*strtab,*core_strtab;
- /*Section attributes*/
- struct module_sect_attrs*sect_attrs;
- /*Notes attributes*/
- struct module_notes_attrs*notes_attrs;
- #endif
- char*args;
- #ifdef CONFIG_SMP
- /*Per-cpu data.*/
- void __percpu*percpu;
- unsignedintpercpu_size;
- #endif
- #ifdef CONFIG_TRACEPOINTS
- unsignedintnum_tracepoints;
- struct tracepoint*const*tracepoints_ptrs;
- #endif
- #ifdef HAVE_JUMP_LABEL
- struct jump_entry*jump_entries;
- unsignedintnum_jump_entries;
- #endif
- #ifdef CONFIG_TRACING
- unsignedintnum_trace_bprintk_fmt;
- constchar**trace_bprintk_fmt_start;
- #endif
- #ifdef CONFIG_EVENT_TRACING
- struct ftrace_event_call**trace_events;
- unsignedintnum_trace_events;
- #endif
- #ifdef CONFIG_FTRACE_MCOUNT_RECORD
- unsignedintnum_ftrace_callsites;
- unsigned long*ftrace_callsites;
- #endif
- #ifdef CONFIG_MODULE_UNLOAD
- /*What modules dependonme?*/
- struct list_head source_list;
- /*What modulesdoI dependon?*/
- struct list_head target_list;
- /*Whoiswaitingforustobe unloaded*/
- struct task_struct*waiter;
- /*Destructionfunction.*/
- void(*exit)(void);
- struct module_ref __percpu*refptr;
- #endif
- #ifdef CONFIG_CONSTRUCTORS
- /*Constructor functions.*/
- ctor_fn_t*ctors;
- unsignedintnum_ctors;
- #endif
- };
- 5.ELF文件信息结构
- struct load_info{
- Elf_Ehdr*hdr;//指向elf头
- unsigned longlen;
- Elf_Shdr*sechdrs;//指向节区头
- char*secstrings;//指向字符串节区中节区名称所在的地址
- char*strtab;//指向字符串节区的内容所在地址
- unsigned long symoffs;
- unsigned long stroffs;
- struct _ddebug*debug;
- unsignedintnum_debug;
- bool sig_ok;//模块签名检查
- struct{
- unsignedintsym;//模块的符号表节区索引号
- unsignedintstr;//模块的字符串节区索引号
- unsignedintmod;//模块的".gnu.linkonce.this_module"节区索引号
- unsignedintvers;//模块的"__versions"节区索引号
- unsignedintinfo;//模块的.modinfo节区索引号
- unsignedintpcpu;
- }index;
- };
- 三、源代码解析
- //busybox中insmod.c文件中
- intinsmod_main(intargc UNUSED_PARAM,char**argv)
- {
- char*filename;
- intrc;
- IF_FEATURE_2_4_MODULES(
- getopt32(argv,INSMOD_OPTS INSMOD_ARGS);
- argv+=optind-1;
- );
- //取得要加载模块的路径名
- filename=*++argv;
- if(!filename)
- bb_show_usage();
- rc=bb_init_module(filename,parse_cmdline_module_options(argv,0));
- if(rc)
- bb_error_msg("can't insert '%s': %s",filename,moderror(rc));
- return rc;
- }
- char*FAST_FUNC parse_cmdline_module_options(char**argv,intquote_spaces)
- {
- char*options;
- intoptlen;
- options=xzalloc(1);
- optlen=0;
- //遍历模块名后边的模块参数
- while(*++argv){
- constchar*fmt;
- constchar*var;
- constchar*val;
- var=*argv;
- //为options分配空间
- options=xrealloc(options,optlen+2+strlen(var)+2);
- fmt="%.*s%s ";
- //若'='存在于var中,则返回'='在var中出现的第一个位置的指针,否则返回字符串var末尾的空字符。
- val=strchrnul(var,'=');
- if(quote_spaces){//为0
- if(*val){/*has var=val format.skip'='*/
- val++;
- if(strchr(val,''))
- fmt="%.*s"%s" ";
- }
- }
- //模块参数按格式存入options,"%.*s%s "中“*”对应(int)(val-var),第一个s对应var,表示在var字符串中去除(int)(val-var)个字符显示,即=前边的字符被去除。实际上只是将val字符串(即=后边的字符串)存入options指针指向的地址。
- optlen+=sprintf(options+optlen,fmt,(int)(val-var),var,val);
- }
- return options;
- }
- intFAST_FUNC bb_init_module(constchar*filename,constchar*options)
- {
- size_t image_size;
- char*image;
- intrc;
- bool mmaped;
- if(!options)
- options="";
- //若是2.6以前的版本调用bb_init_module_24(),这种处理就是以前modutils对模块的加载处理。这里要研究的是2.6以后的模块加载处理
- #ifENABLE_FEATURE_2_4_MODULES
- if(get_linux_version_code()<KERNEL_VERSION(2,6,0))
- return bb_init_module_24(filename,options);
- #endif
- image_size=INT_MAX-4095;//初始化为文件的最大值
- mmaped=0;
- //把模块文件映射进内存,并返回映射空间的大小image_size
- image=try_to_mmap_module(filename,&image_size);
- if(image){
- mmaped=1;
- }else{
- errno=ENOMEM;/*may be changed by e.g.open errors below*/
- image=xmalloc_open_zipped_read_close(filename,&image_size);
- if(!image)
- return-errno;
- }
- errno=0;
- //系统调用,对应内核函数是sys_init_module()函数,进入到内核空间
- init_module(image,image_size,options);
- rc=errno;
- if(mmaped)
- munmap(image,image_size);
- else
- free(image);
- return rc;
- }
- void*FAST_FUNC try_to_mmap_module(constchar*filename,size_t*image_size_p)
- {
- void*image;
- struct stat st;
- intfd;
- //打开模块.ko文件,获得文件描述符
- fd=xopen(filename,O_RDONLY);
- //由文件描述符取得文件状态
- fstat(fd,&st);
- image=NULL;
- //文件的size是否超过设定的文件最大值
- if(st.st_size<=*image_size_p){
- size_t image_size=st.st_size;//文件size
- //以只读的方式将.ko文件映射到内存中,返回在内存中的起始地址
- image=mmap(NULL,image_size,PROT_READ,MAP_PRIVATE,fd,0);
- if(image==MAP_FAILED){
- image=NULL;
- }
- //检查一下.ko文件的开头是否为ELF标准格式
- elseif(*(uint32_t*)image!=SWAP_BE32(0x7f454C46)){
- munmap(image,image_size);
- image=NULL;
- }else{
- *image_size_p=image_size;//返回文件size
- }
- }
- close(fd);
- return image;
- }
- //sys_init_module(void __user*umod,unsigned longlen,constchar __user*uargs)
- //umod:是一个指针,指向用户地址空间中的区域,模块的二进制代码位于其中。
- //len:该区域的长度。
- //uargs:是一个指针,指定了模块的参数。
- SYSCALL_DEFINE3(init_module,void __user*,umod,unsigned long,len,constchar __user*,uargs)
- {
- interr;
- struct load_info info={};
- //是否有权限加载模块:capable(CAP_SYS_MODULE)
- err=may_init_module();
- if(err)
- returnerr;
- pr_debug("init_module: umod=%p, len=%lu, uargs=%p ",umod,len,uargs);
- //将用户空间的.ko文件的内存映像(elf格式)拷贝到内核空间,并填充info结构中
- err=copy_module_from_user(umod,len,&info);
- if(err)
- returnerr;
- //模块的主要操作
- return load_module(&info,uargs,0);
- }
- staticintcopy_module_from_user(constvoid __user*umod,unsigned longlen,struct load_info*info)
- {
- interr;
- //模块文件映射到用户空间的size(即.ko文件本身的size)
- info->len=len;
- if(info->len<sizeof(*(info->hdr)))
- return-ENOEXEC;
- err=security_kernel_module_from_file(NULL);
- if(err)
- returnerr;
- //在内核空间为模块分配内存,并将该内存的起始地址付给成员hdr,即该成员现在指向的是整个模块内存
- info->hdr=vmalloc(info->len);
- if(!info->hdr)
- return-ENOMEM;
- //将用户空间的.ko文件的内存映像(elf格式)拷贝到内核空间info->hdr处
- if(copy_from_user(info->hdr,umod,info->len)!=0){
- vfree(info->hdr);
- return-EFAULT;
- }
- return 0;
- }
- staticintload_module(struct load_info*info,constchar __user*uargs,intflags)
- {
- struct module*mod;
- longerr;
- err=module_sig_check(info);//检查证书
- if(err)
- goto free_copy;
- err=elf_header_check(info);//检查elf头
- if(err)
- goto free_copy;
- //布置模块,并分配相关的内存,把相关节区复制到最终镜像中
- mod=layout_and_allocate(info,flags);
- if(IS_ERR(mod)){
- err=PTR_ERR(mod);
- goto free_copy;
- }
- //因为前边已将模块镜像复制到了内核空间的内存中,module对象也指向对应的位置,然后将此module对象添加到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 ",mod->name);
- add_taint_module(mod,TAINT_FORCED_MODULE,LOCKDEP_STILL_OK);
- }
- #endif
- //为节区pcpu分配空间,用于多处理器,此处不考虑
- err=percpu_modalloc(mod,info);
- if(err)
- goto unlink_mod;
- //模块计数加1,并初始化模块链表
- err=module_unload_init(mod);
- if(err)
- goto unlink_mod;
- //找到其余节区地址,初始化module对象相关指针
- find_module_sections(mod,info);
- //检查license和version
- err=check_module_license_and_versions(mod);
- if(err)
- goto free_unload;
- //根据.modinfo段设置模块信息
- setup_modinfo(mod,info);
- //根据前边设置的模块在内核的起始地址,节区的起始地址已经更新,但是节区中符号的地址还未进行更新,这里根据节区的内存地址更新符号地址
- 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 the instruction cache,no care
- flush_module_icache(mod);
- //把可选参数从用户空间复制到内核空间
- mod->args=strndup_user(uargs,~0UL>>1);
- if(IS_ERR(mod->args)){
- err=PTR_ERR(mod->args);
- goto free_arch_cleanup;
- }
- //处理用于debug节区
- dynamic_debug_setup(info->debug,info->num_debug);
- //确认是否有重定义符号,并且设置模块状态为正在运行
- err=complete_formation(mod,info);
- if(err)
- goto ddebug_cleanup;
- /*Moduleisreadytoexecute:parsing args maydothat.*/
- //分析参数是否有效
- err=parse_args(mod->name,mod->args,mod->kp,mod->num_kp,-32768,32767,unknown_module_param_cb);
- if(err<0)
- goto bug_cleanup;
- //sysfs文件系统相关,在sysfs中创建模块相应的项
- err=mod_sysfs_setup(mod,info,mod->kp,mod->num_kp);
- if(err<0)
- goto bug_cleanup;
- //释放临时模块镜像和其他临时辅助内存区
- free_copy(info);
- /**/
- trace_module_load(mod);
- //调用do_init_module开始运行模块
- 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);
- returnerr;
- }
- staticintmodule_sig_check(struct load_info*info)
- {
- //#define MODULE_SIG_STRING"~Module signature appended~ "
- interr=-ENOKEY;
- constunsigned long markerlen=sizeof(MODULE_SIG_STRING)-1;
- constvoid*mod=info->hdr;
- //elf文件长度是否大于签名字符串长度,并且比较该elf格式的模块文件是否在文件末尾有signature string。若有的话则将该文件长度减去该字符串长度,并确认该签名是module类型。
- if(info->len>markerlen&&memcmp(mod+info->len-markerlen,MODULE_SIG_STRING,markerlen)==0)
- {
- info->len-=markerlen;//模块长度减去签名字符串的长度
- err=mod_verify_sig(mod,&info->len);//验证模块签名
- }
- if(!err){
- info->sig_ok=true;
- return 0;
- }
- /*Nothaving a signatureisonly anerrorifwe're strict.*/
- if(err<0&&fips_enabled)
- panic("Module verification failed with error %d in FIPS mode ",err);
- if(err==-ENOKEY&&!sig_enforce)
- err=0;
- returnerr;
- }
- struct module_signature{
- u8 algo;/*Public-key crypto algorithm[enum pkey_algo]*/
- u8 hash;/*Digest algorithm[enum pkey_hash_algo]*/
- u8 id_type;/*Key identifier type[enum pkey_id_type]*/
- u8 signer_len;/*Length of signer's name*/
- u8 key_id_len;/*Length of key identifier*/
- u8 __pad[3];
- __be32 sig_len;/*Length of signature data*/
- };
- intmod_verify_sig(constvoid*mod,unsigned long*_modlen)
- {
- //mod是模块文件起始地址,_modlen是模块文件的大小(去掉了最后的签名字符串长度)
- struct public_key_signature*pks;
- struct module_signature ms;
- struct key*key;
- constvoid*sig;
- size_t modlen=*_modlen,sig_len;
- intret;
- pr_devel("==>%s(,%zu) ",__func__,modlen);
- //检查长度
- if(modlen<=sizeof(ms))
- return-EBADMSG;
- //如果.ko文件最后有签名字符串的话,则签名字符串之上有一个module_signature结构,读出这个结构的内容
- memcpy(&ms,mod+(modlen-sizeof(ms)),sizeof(ms));
- modlen-=sizeof(ms);//相应修改模块文件长度
- sig_len=be32_to_cpu(ms.sig_len);//获得签名数据长度
- if(sig_len>=modlen)
- return-EBADMSG;
- modlen-=sig_len;//模块文件长度再减去签名数据长度
- if((size_t)ms.signer_len+ms.key_id_len>=modlen)
- return-EBADMSG;
- //模块文件长度再减去签名人名字长度和key长度
- modlen-=(size_t)ms.signer_len+ms.key_id_len;
- *_modlen=modlen;//去掉签名验证的信息后返回模块真实size
- //文件指针移到签名信息处
- sig=mod+modlen;
- /*Forthe moment,only support RSAandX.509 identifiers*/
- if(ms.algo!=PKEY_ALGO_RSA||ms.id_type!=PKEY_ID_X509)
- return-ENOPKG;
- if(ms.hash>=PKEY_HASH__LAST||!pkey_hash_algo[ms.hash])
- return-ENOPKG;
- //将signer name和key identifier从文件sig处读出来,返回key
- key=request_asymmetric_key(sig,ms.signer_len,sig+ms.signer_len,ms.key_id_len);
- if(IS_ERR(key))
- return PTR_ERR(key);
- //Digest the module contents.
- pks=mod_make_digest(ms.hash,mod,modlen);
- if(IS_ERR(pks)){
- ret=PTR_ERR(pks);
- goto error_put_key;
- }
- //Extract an MPIarrayfrom the signature data.This represents the actual signature.Eachraw MPIisprefaced by a BE 2-byte value indicating the size of the MPIinbytes.RSA signatures only have one MPI,so currently we only read one.
- ret=mod_extract_mpi_array(pks,sig+ms.signer_len+ms.key_id_len,sig_len);
- if(ret<0)
- goto error_free_pks;
- //Initiate the use of an asymmetric keytoverify a signature
- //key:The asymmetric keytoverify against
- //sig:The signaturetocheck
- ret=verify_signature(key,pks);
- pr_devel("verify_signature() = %d ",ret);
- error_free_pks:
- mpi_free(pks->rsa.s);
- kfree(pks);
- error_put_key:
- key_put(key);
- pr_devel("<==%s() = %d ",__func__,ret);
- return ret;
- }
- staticintelf_header_check(struct load_info*info)
- {
- if(info->len<sizeof(*(info->hdr)))
- return-ENOEXEC;
- //检查elf文件头,以及文件类型(.ko文件必是可重定位文件),架构以及节区大小是否设置正确
- if(memcmp(info->hdr->e_ident,ELFMAG,SELFMAG)!=0||info->hdr->e_type!=ET_REL
- ||!elf_check_arch(info->hdr)||info->hdr->e_shentsize!=sizeof(Elf_Shdr))
- return-ENOEXEC;
- //检查节区的偏移地址
- if(info->hdr->e_shoff>=info->len
- ||(info->hdr->e_shnum*sizeof(Elf_Shdr)>info->len-info->hdr->e_shoff))
- return-ENOEXEC;
- return 0;
- }
- static struct module*layout_and_allocate(struct load_info*info,intflags)
- {
- struct module*mod;
- interr;
- //设置info结构,并检查module_layout的crc值,并返回一个存储在.gnu.linkonce.this_module节区中的struct module结构,该module的起始地址正是.gnu.linkonce.this_module节区起始地址
- mod=setup_load_info(info,flags);
- if(IS_ERR(mod))
- returnmod;
- //检查.modinfo节区中的信息,包括version magic
- err=check_modinfo(mod,info,flags);
- if(err)
- return ERR_PTR(err);
- //什么也不做,返回0
- err=module_frob_arch_sections(info->hdr,info->sechdrs,info->secstrings,mod);
- if(err<0)
- return ERR_PTR(err);
- //移除SHF_ALLOC标志
- info->sechdrs[info->index.pcpu].sh_flags&=~(unsigned long)SHF_ALLOC;
- //只有节区头部设置了SHF_ALLOC标志,才最终存于内存中,且内存分为init部分和core部分
- layout_sections(mod,info);
- //为symbol section以及跟它相关的string table布置位置,更新相关size
- layout_symtab(mod,info);
- //为mod指向的临时镜像中标记了SHF_ALLOC节区分配内存,并从临时镜像复制到最终的位置,并且修改节区的起始地址
- err=move_module(mod,info);
- if(err)
- return ERR_PTR(err);
- //因为前边已将模块靠诶到最终的内存位置,所以各个节区的起始地址已经改变,之前mod指向的地址已经无效,所以重新将新的“.gnu.linkonce.this_module”节区的起始地址(指向一个module对象)赋给mod对象
- mod=(void*)info->sechdrs[info->index.mod].sh_addr;
- //扫描检查内存泄露???
- kmemleak_load_module(mod,info);
- returnmod;
- }
- static struct module*setup_load_info(struct load_info*info)
- {
- unsignedinti;
- interr;
- struct module*mod;
- //找到节区头部表开始地址
- info->sechdrs=(void*)info->hdr+info->hdr->e_shoff;
- //找到节区名称的字符串节区的起始地址
- info->secstrings=(void*)info->hdr+info->sechdrs[info->hdr->e_shstrndx].sh_offset;
- //根据elf格式的.ko文件拷贝到内核空间内存中的虚拟地址为基地址(即(void*)info->hdr),重写节区在内存映像中节区第一个字节应处的位置
- err=rewrite_section_headers(info);
- if(err)
- return ERR_PTR(err);
- /*遍历所有节区找到符号表(类型为 SHT_SYMTAB 的唯一段)和相关的符号字符串表的位置,
- 前者的 sh_link 即为后者的段索引*/
- for(i=1;i<info->hdr->e_shnum;i++){
- if(info->sechdrs[i].sh_type==SHT_SYMTAB){
- //找到符号表节区.symtab索引
- info->index.sym=i;
- //找到符号表字符串节区索引,即.strtab节区
- info->index.str=info->sechdrs[i].sh_link;
- //字符串节区内容开始地址
- info->strtab=(char*)info->hdr+info->sechdrs[info->index.str].sh_offset;
- break;
- }
- }
- //在.gnu.linkonce.this_module节区中,有一个struct module的实例,此结构大部分成员是NULL,编译器只是初始化了name,init,exit和arch等几个成员。
- info->index.mod=find_sec(info,".gnu.linkonce.this_module");
- if(!info->index.mod){
- printk(KERN_WARNING"No module found in object ");
- return ERR_PTR(-ENOEXEC);
- }
- //mod指向 struct module的实例,该实例中提供了模块的名称和指向初始化以及清理函数的指针,但其他成员仍然初始化为NULL或 0.将该模块的地址暂时设为临时映像中节区给出的地址,后边会对module的地址再做出修正.
- mod=(void*)info->sechdrs[info->index.mod].sh_addr;
- if(info->index.sym==0){
- printk(KERN_WARNING"%s: module has no symbols (stripped?) ",mod->name);
- return ERR_PTR(-ENOEXEC);
- }
- //查找".data..percpu"节区索引号
- info->index.pcpu=find_pcpusec(info);
- //检查模块中“module_layout”符号的crc值,若失败则打印著名模块加载错误“Exec formaterror”
- if(!check_modstruct_version(info->sechdrs,info->index.vers,mod))
- return ERR_PTR(-ENOEXEC);
- returnmod;
- }
- staticintrewrite_section_headers(struct load_info*info,intflags)
- {
- unsignedinti;
- info->sechdrs[0].sh_addr=0;//节区0地址是0,表示此节区不出现在内存映像中
- for(i=1;i<info->hdr->e_shnum;i++){
- Elf_Shdr*shdr=&info->sechdrs[i];
- //判断节区size
- if(shdr->sh_type!=SHT_NOBITS&&info->len<shdr->sh_offset+shdr->sh_size){
- printk(KERN_ERR"Module len %lu truncated ",info->len);
- return-ENOEXEC;
- }
- //在elf文件拷贝到内核空间的基地址基础上重新设置节区在内核空间的起始地址,shdr->sh_addr表示节区在内存映像中节区第一个字节应处的位置
- shdr->sh_addr=(size_t)info->hdr+shdr->sh_offset;
- #ifndef CONFIG_MODULE_UNLOAD
- /*Don't load.exitsections*/
- if(strstarts(info->secstrings+shdr->sh_name,".exit"))
- shdr->sh_flags&=~(unsigned long)SHF_ALLOC;
- #endif
- }
- //找到__versions和.modinfo节区,并记录节区索引
- if(flags&MODULE_INIT_IGNORE_MODVERSIONS)
- info->index.vers=0;
- else
- info->index.vers=find_sec(info,"__versions");
- info->index.info=find_sec(info,".modinfo");
- //这两个节区清楚SHF_ALLOC标志,表示此节区在进程执行过程中不占用内存
- info->sechdrs[info->index.info].sh_flags&=~(unsigned long)SHF_ALLOC;
- info->sechdrs[info->index.vers].sh_flags&=~(unsigned long)SHF_ALLOC;
- return 0;
- }
- static inlineintcheck_modstruct_version(Elf_Shdr*sechdrs,unsignedintversindex,struct module*mod)
- {
- constunsigned long*crc;
- //查找系统内核中“module_layout”符号,并返回该符号的crc值
- if(!find_symbol(VMLINUX_SYMBOL_STR(module_layout),NULL,&crc,true,false))
- BUG();
- //系统中“module_layout”符号的crc值与模块中的“module_layout”符号crc值是否一致
- return check_version(sechdrs,versindex,VMLINUX_SYMBOL_STR(module_layout),mod,crc,NULL);
- }
- //查找符号使用的结构体
- struct find_symbol_arg{
- //输入
- constchar*name;//要查找的符号名
- bool gplok;//如果为真,则表示内核模块支持GPL许可
- bool warn;//警告信息标志
- //输出
- struct module*owner;//该符号所属的模块
- constunsigned long*crc;//该符号的crc值
- conststruct kernel_symbol*sym;//符号结构
- };
- conststruct kernel_symbol*find_symbol(constchar*name,struct module**owner,constunsigned long**crc,bool gplok,bool warn)
- {
- //设置查找符号参数结构
- struct find_symbol_arg fsa;
- fsa.name=name;
- fsa.gplok=gplok;
- fsa.warn=warn;
- //查找符号,填写符号参数结构的输出部分
- if(each_symbol_section(find_symbol_in_section,&fsa)){
- if(owner)//NULL,下边不需赋值
- *owner=fsa.owner;
- if(crc)//得到该符号的crc值
- *crc=fsa.crc;
- //返回以name命名的内核符号结构
- return fsa.sym;
- }
- //没有找到内核符号,返回NULL
- pr_debug("Failed to find symbol %s ",name);
- returnNULL;
- }
- bool each_symbol_section(bool(*fn)(conststruct symsearch*arr,struct module*owner,void*data),void*data)
- {
- struct module*mod;
- //第一部分在内核导出的符号表中查找对应的符号,找到就返回对应的符号信息,否则,再进行第二部分查找.
- /*struct symsearch{
- conststruct kernel_symbol*start,*stop;
- constunsigned long*crcs;
- enum{
- NOT_GPL_ONLY,
- GPL_ONLY,
- WILL_BE_GPL_ONLY,
- }licence;
- bool unused;
- };
- */
- staticconststruct symsearch arr[]={
- {__start___ksymtab,__stop___ksymtab,
- __start___kcrctab,NOT_GPL_ONLY,false},
- {__start___ksymtab_gpl,__stop___ksymtab_gpl,
- __start___kcrctab_gpl,GPL_ONLY,false},
- {__start___ksymtab_gpl_future,__stop___ksymtab_gpl_future,
- __start___kcrctab_gpl_future,WILL_BE_GPL_ONLY,false},
- #ifdef CONFIG_UNUSED_SYMBOLS
- {__start___ksymtab_unused,__stop___ksymtab_unused,
- __start___kcrctab_unused,NOT_GPL_ONLY,true},
- {__start___ksymtab_unused_gpl,__stop___ksymtab_unused_gpl,
- __start___kcrctab_unused_gpl,GPL_ONLY,true},
- #endif
- };
- if(each_symbol_in_section(arr,ARRAY_SIZE(arr),NULL,fn,data))
- returntrue;
- //在内核导出的符号表中没有找到对应的符号,则在系统已加载的模块中查找
- list_for_each_entry_rcu(mod,&modules,list){
- struct symsearch arr[]={
- {mod->syms,mod->syms+mod->num_syms,
- mod->crcs,NOT_GPL_ONLY,false},
- {mod->gpl_syms,mod->gpl_syms+mod->num_gpl_syms,
- mod->gpl_crcs,GPL_ONLY,false},
- {mod->gpl_future_syms,mod->gpl_future_syms+mod->num_gpl_future_syms,
- mod->gpl_future_crcs,WILL_BE_GPL_ONLY,false},
- #ifdef CONFIG_UNUSED_SYMBOLS
- {mod->unused_syms,mod->unused_syms+mod->num_unused_syms,
- mod->unused_crcs,NOT_GPL_ONLY,true},
- {mod->unused_gpl_syms,mod->unused_gpl_syms+mod->num_unused_gpl_syms,
- mod->unused_gpl_crcs,GPL_ONLY,true},
- #endif
- };
- //若模块状态为MODULE_STATE_UNFORMED,则此模块的符号不可用
- if(mod->state==MODULE_STATE_UNFORMED)
- continue;
- if(each_symbol_in_section(arr,ARRAY_SIZE(arr),mod,fn,data))
- returntrue;
- }
- returnfalse;
- }
- static bool each_symbol_in_section(conststruct symsearch*arr,unsignedintarrsize,struct module*owner,bool(*fn)(conststruct symsearch*syms,struct module*owner,void*data),void*data)
- {
- unsignedintj;
- //调用find_symbol_in_section()对每个数组指定的符号地址范围进行查找
- for(j=0;j<arrsize;j++){
- if(fn(&arr[j],owner,data))//调用find_symbol_in_section()
- returntrue;
- }
- returnfalse;
- }
- static bool find_symbol_in_section(conststruct symsearch*syms,struct module*owner,void*data)
- {
- struct find_symbol_arg*fsa=data;
- struct kernel_symbol*sym;
- //在范围内查找符号名为fsa->name的内核符号
- sym=bsearch(fsa->name,syms->start,syms->stop-syms->start,sizeof(struct kernel_symbol),cmp_name);
- //若找到内核符号,则对其进行check是否有效
- if(sym!=NULL&&check_symbol(syms,owner,sym-syms->start,data))
- returntrue;
- returnfalse;
- }
- void*bsearch(constvoid*key,constvoid*base,size_t num,size_t size,int(*cmp)(constvoid*key,constvoid*elt))
- {
- size_t start=0,end=num;
- intresult;
- while(start<end){//折半查找
- size_tmid=start+(end-start)/2;
- //调用cmp_name()函数比较符号名是否一致
- result=cmp(key,base+mid*size);
- if(result<0)
- end=mid;
- elseif(result>0)
- start=mid+1;
- else
- return(void*)base+mid*size;
- }
- returnNULL;
- }
- staticintcmp_name(constvoid*va,constvoid*vb)
- {
- constchar*a;
- conststruct kernel_symbol*b;
- a=va;b=vb;
- return strcmp(a,b->name);
- }
- static bool check_symbol(conststruct symsearch*syms,struct module*owner,unsignedintsymnum,void*data)
- {
- struct find_symbol_arg*fsa=data;
- if(!fsa->gplok){//若未设置gplok,则必须为GPL许可
- if(syms->licence==GPL_ONLY)
- returnfalse;
- if(syms->licence==WILL_BE_GPL_ONLY&&fsa->warn){
- printk(KERN_WARNING"Symbol %s is being used "
- "by a non-GPL module, which will not "
- "be allowed in the future ",fsa->name);
- }
- }
- #ifdef CONFIG_UNUSED_SYMBOLS
- if(syms->unused&&fsa->warn){
- printk(KERN_WARNING"Symbol %s is marked as UNUSED, "
- "however this module is using it. ",fsa->name);
- printk(KERN_WARNING"This symbol will go away in the future. ");
- printk(KERN_WARNING
- "Please evalute if this is the right api to use and if "
- "it really is, submit a report the linux kernel "
- "mailinglist together with submitting your code for "
- "inclusion. ");
- }
- #endif
- fsa->owner=owner;//符号所属模块
- //#define symversion(base,idx)((base!=NULL)?((base)+(idx)):NULL)
- fsa->crc=symversion(syms->crcs,symnum);//符号的crc值
- fsa->sym=&syms->start[symnum];//返回的符号结构
- returntrue;
- }
- staticintcheck_version(Elf_Shdr*sechdrs,unsignedintversindex,constchar*symname,struct module*mod,constunsigned long*crc,conststruct module*crc_owner)
- {
- unsignedinti,num_versions;
- struct modversion_info*versions;
- //若系统中的该符号crc值为0,则直接返回1完事
- if(!crc)
- return 1;
- //若该模块的elf格式文件中没有__versions节区,则尝试强制加载模块
- //但是try_to_force_load()函数的实现依赖于CONFIG_MODULE_FORCE_LOAD宏是否定义。而该宏默认是没有定义的,所以这里会返回失败,看来内核并不推荐强制加载模块。
- if(versindex==0)
- return try_to_force_load(mod,symname)==0;
- //找到模块“__versions”节区在内存映像中的起始地址。相当于节区的contents内容
- versions=(void*)sechdrs[versindex].sh_addr;
- num_versions=sechdrs[versindex].sh_size/sizeof(struct modversion_info);
- for(i=0;i<num_versions;i++){
- if(strcmp(versions[i].name,symname)!=0)//在此节区中找到要比较的符号
- continue;
- //比较该模块中的符号crc值和现在系统上的内核符号的crc值是否一致
- if(versions[i].crc==maybe_relocated(*crc,crc_owner))
- return 1;
- pr_debug("Found checksum %lX vs module %lX ",maybe_relocated(*crc,crc_owner),versions[i].crc);
- goto bad_version;
- }
- //若在“__versions”节区没有找到要比较的符号,则会给出加载模块时常见错误:“no symbol version for”
- printk(KERN_WARNING"%s: no symbol version for %s ",mod->name,symname);
- return 0;
- bad_version:
- //如果比较符号的额crc值不一致,则会给出加载模块时常见错误“disagrees about version of symbol”
- printk("%s: disagrees about version of symbol %s ",mod->name,symname);
- return 0;
- }
- staticinttry_to_force_load(struct module*mod,constchar*reason)
- {
- #ifdef CONFIG_MODULE_FORCE_LOAD
- //若选项CONFIG_MODULE_FORCE_LOAD打开,则检查tainted_mask是否设置了TAINT_FORCED_MODULE标志,若没有给出警告信息
- if(!test_taint(TAINT_FORCED_MODULE))
- printk(KERN_WARNING"%s: %s: kernel tainted. ",mod->name,reason);
- //设置mod->taints和tainted_mask的TAINT_FORCED_MODULE标志,表示强制加载该模块,并返回正确值0
- add_taint_module(mod,TAINT_FORCED_MODULE,LOCKDEP_NOW_UNRELIABLE);
- return 0;
- #else
- //若选项CONFIG_MODULE_FORCE_LOAD未打开,则直接返回错误
- return-ENOEXEC;
- #endif
- }
- staticintcheck_modinfo(struct module*mod,struct load_info*info,intflags)
- {
- //从模块.modinfo节区中获得version magic
- constchar*modmagic=get_modinfo(info,"vermagic");
- interr;
- if(flags&MODULE_INIT_IGNORE_VERMAGIC)
- modmagic=NULL;
- //若version magic为0,则尝试强制加载
- if(!modmagic){
- err=try_to_force_load(mod,"bad vermagic");
- if(err)
- returnerr;
- }
- //与内核的vermagic是否一致,若不一致,给出著名错误:“version magic...should be...”,返回错误码
- elseif(!same_magic(modmagic,vermagic,info->index.vers)){
- printk(KERN_ERR"%s: version magic '%s' should be '%s' ",mod->name,modmagic,vermagic);
- return-ENOEXEC;
- }
- //返回.modinfo节区中intree=“...”的内容,若不存在设置标志TAINT_OOT_MODULE
- if(!get_modinfo(info,"intree"))
- add_taint_module(mod,TAINT_OOT_MODULE,LOCKDEP_STILL_OK);
- //返回.modinfo节区中staging=“...”的内容,存在设置标志TAINT_CRAP
- if(get_modinfo(info,"staging")){
- add_taint_module(mod,TAINT_CRAP,LOCKDEP_STILL_OK);
- printk(KERN_WARNING"%s: module is from the staging directory,"" the quality is unknown, you have been warned. ",mod->name);
- }
- //取出.modinfo节区的license字段指定的license类型,并对此license检查
- set_license(mod,get_modinfo(info,"license"));
- return 0;
- }
- static char*get_modinfo(struct load_info*info,constchar*tag)
- {
- char*p;
- unsignedinttaglen=strlen(tag);
- //找到模块.modinfo节区
- Elf_Shdr*infosec=&info->sechdrs[info->index.info];
- unsigned long size=infosec->sh_size;
- //查找.modinfo节区中的内容,返回"*tag"字符串=后边的内容
- for(p=(char*)infosec->sh_addr;p;p=next_string(p,&size))
- {
- if(strncmp(p,tag,taglen)==0&&p[taglen]=='=')
- return p+taglen+1;
- }
- returnNULL;
- }
- static inlineintsame_magic(constchar*amagic,constchar*bmagic,bool has_crcs)
- {
- //从字符串中依次取出以“ ”结尾的字符串段进行比较
- if(has_crcs){
- amagic+=strcspn(amagic," ");
- bmagic+=strcspn(bmagic," ");
- }
- return strcmp(amagic,bmagic)==0;
- }
- static void set_license(struct module*mod,constchar*license)
- {
- if(!license)
- license="unspecified";
- //比较模块的license类型是否是内核指定的GPL类型,若不是则设置TAINT_PROPRIETARY_MODULE标志
- if(!license_is_gpl_compatible(license)){
- if(!test_taint(TAINT_PROPRIETARY_MODULE))
- printk(KERN_WARNING"%s: module license '%s' taints kernel. ",mod->name,license);
- add_taint_module(mod,TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);
- }
- }
- static inlineintlicense_is_gpl_compatible(constchar*license)
- {
- return(strcmp(license,"GPL")==0
- ||strcmp(license,"GPL v2")==0
- ||strcmp(license,"GPL and additional rights")==0
- ||strcmp(license,"Dual BSD/GPL")==0
- ||strcmp(license,"Dual MIT/GPL")==0
- ||strcmp(license,"Dual MPL/GPL")==0);
- }
- static void layout_sections(struct module*mod,struct load_info*info)
- {
- //注意:节的复制是按照原来ELF的顺序,将所有标志包含SHF_ALLOC的节都复制到相应的分配空间(module_core/module_init),例外的情况是SHT_NOBITS,也就是BSS段,文件中没有分配空间,因此不需要复制.
- //sections函数首先为标记了SHF_ALLOC的section定义了四种类型:code,read-only data,read-write data和small data.带有SHF_ALLOC的section必定属于四类中的一类。函数遍历section header table中的所有项,将section name不是以".init"开始的section划归为COREsection.以".init"开始的section划归为INIT section.
- static unsigned longconstmasks[][2]={
- {SHF_EXECINSTR|SHF_ALLOC,ARCH_SHF_SMALL},//code
- {SHF_ALLOC,SHF_WRITE|ARCH_SHF_SMALL},//read only data
- {SHF_WRITE|SHF_ALLOC,ARCH_SHF_SMALL},//read-write data
- {ARCH_SHF_SMALL|SHF_ALLOC,0}//small data
- };
- unsignedintm,i;
- //遍历所有节区初始化sh_entsize成员
- //某些节区中包含固定大小的项目,如符号表。对于这类节区,此成员sh_entsize给出每个表项的长度字节数。如果节区中并不包含固定长度表项的表格,此成员取值为 0。
- for(i=0;i<info->hdr->e_shnum;i++)
- info->sechdrs[i].sh_entsize=~0UL;
- //划分为两部分:CORE INIT
- //第1部分CORE:查找标志中含有SHF_ALLOC的section
- for(m=0;m<ARRAY_SIZE(masks);++m){
- for(i=0;i<info->hdr->e_shnum;++i){
- Elf_Shdr*s=&info->sechdrs[i];
- //找到节区名
- constchar*sname=info->secstrings+s->sh_name;
- //含有SHF_ALLOC的section需要加载到最终的内存
- //含有SHF_ALLOC的section并且不以init开头的节区划分到CORE部分
- if((s->sh_flags&masks[m][0])!=masks[m][0]
- ||(s->sh_flags&masks[m][1])
- ||s->sh_entsize!=~0UL||strstarts(sname,".init"))
- continue;
- //把由于对齐产生的偏移保存到节区的sh_entsize字段,后边mod通过sh_entsize就可以找到该节区存储的位置。并把符合要求的节区大小加到mod->core_size
- s->sh_entsize=get_offset(mod,&mod->core_size,s,i);
- }
- //由于我们节的复制是按顺序的,而.text节是第一个节,因此mod->module_core实际上指向的就是.text段。而mod->core_text_size中也包含了.text节的大小.
- switch(m){
- case0://可执行的段,代码段都一样
- mod->core_size=debug_align(mod->core_size);
- mod->core_text_size=mod->core_size;
- break;
- case1://只读段
- mod->core_size=debug_align(mod->core_size);
- mod->core_ro_size=mod->core_size;
- break;
- case3://所有段
- mod->core_size=debug_align(mod->core_size);
- break;
- }
- }
- //第2部分INIT
- for(m=0;m<ARRAY_SIZE(masks);++m){
- for(i=0;i<info->hdr->e_shnum;++i){
- Elf_Shdr*s=&info->sechdrs[i];
- constchar*sname=info->secstrings+s->sh_name;
- //含有SHF_ALLOC的section需要加载到最终的内存
- //含有SHF_ALLOC的section并且以init开头的划分到INIT部分
- if((s->sh_flags&masks[m][0])!=masks[m][0]
- ||(s->sh_flags&masks[m][1])
- ||s->sh_entsize!=~0UL
- ||!strstarts(sname,".init"))
- continue;
- //把由于对齐产生的偏移保存到节区的sh_entsize字段,并把符合要求的节区大小加到mod->init_size
- s->sh_entsize=(get_offset(mod,&mod->init_size,s,i)|INIT_OFFSET_MASK);
- }
- switch(m){
- case0://代码段
- mod->init_size=debug_align(mod->init_size);
- mod->init_text_size=mod->init_size;
- break;
- case1://只读段
- mod->init_size=debug_align(mod->init_size);
- mod->init_ro_size=mod->init_size;
- break;
- case3://所有段
- mod->init_size=debug_align(mod->init_size);
- break;
- }
- }
- }
- static long get_offset(struct module*mod,unsignedint*size,Elf_Shdr*sechdr,unsignedintsection)
- {
- //#define ALIGN(x,a)__ALIGN_KERNEL((x),(a))
- //#define __ALIGN_KERNEL(x,a)__ALIGN_KERNEL_MASK(x,(typeof(x))(a)-1)
- //#define __ALIGN_KERNEL_MASK(x,mask)(((x)+(mask))&~(mask))
- long ret;
- *size+=arch_mod_section_prepend(mod,section);//空函数,返回0
- ret=ALIGN(*size,sechdr->sh_addralign?:1);//返回*size字节对齐后的值
- *size=ret+sechdr->sh_size;//把当前节区的size也加到*size上
- return ret;
- }
- static void layout_symtab(struct module*mod,struct load_info*info)
- {
- Elf_Shdr*symsect=info->sechdrs+info->index.sym;//找到符号表节区头部
- Elf_Shdr*strsect=info->sechdrs+info->index.str;//找到字符串表节区头部
- constElf_Sym*src;
- unsignedinti,nsrc,ndst,strtab_size=0;
- //符号节区设置SHF_ALLOC标志,表示在内存占空间
- symsect->sh_flags|=SHF_ALLOC;
- //设置符号表节区每个表项的长度字节数,并把符号表的size加到mod->init_size
- symsect->sh_entsize=get_offset(mod,&mod->init_size,symsect,info->index.sym)|INIT_OFFSET_MASK;
- pr_debug(" %s ",info->secstrings+symsect->sh_name);
- src=(void*)info->hdr+symsect->sh_offset;//符号表节区内容起始地址
- nsrc=symsect->sh_size/sizeof(*src);//符号表项个数
- //计算该模块包含的所有符号名长度所占的空间
- for(ndst=i=0;i<nsrc;i++){
- //is_core_symbol检查符号是否有效且属于SHF_ALLOC
- if(i==0||is_core_symbol(src+i,info->sechdrs,info->hdr->e_shnum)){
- strtab_size+=strlen(&info->strtab[src[i].st_name])+1;
- ndst++;
- }
- }
- //将mod->core_size按符号节区对齐约束对齐后得到的值返回给info->symoffs
- info->symoffs=ALIGN(mod->core_size,symsect->sh_addralign?:1);
- //将符号表节区内容(即ndst个符号表项)添加到core部分的后边,并重新设置core部分的size。
- info->stroffs=mod->core_size=info->symoffs+ndst*sizeof(Elf_Sym);
- //core部分再把符号名的字符串所占空间加上
- mod->core_size+=strtab_size;
- //注意:这里只是为符号项和符号的名称长度分配了空间,还并没有将内容拷贝过来,在下边的add_kallsyms函数中进行拷贝!!!
- //把字符串节区加到模块的init部分
- strsect->sh_flags|=SHF_ALLOC;//字符串节区也设置上需要内存空间标志
- strsect->sh_entsize=get_offset(mod,&mod->init_size,strsect,info->index.str)|INIT_OFFSET_MASK;
- pr_debug(" %s ",info->secstrings+strsect->sh_name);
- }
- static bool is_core_symbol(constElf_Sym*src,constElf_Shdr*sechdrs,unsignedintshnum)
- {
- constElf_Shdr*sec;
- //未定义的符号,或者符号索引号大于符号总个数,或者符号名为空则直接返回0
- if(src->st_shndx==SHN_UNDEF||src->st_shndx>=shnum||!src->st_name)
- returnfalse;
- //找到符号所在的节区,若该节区不占内存,则也返回0
- sec=sechdrs+src->st_shndx;
- if(!(sec->sh_flags&SHF_ALLOC)
- #ifndef CONFIG_KALLSYMS_ALL
- ||!(sec->sh_flags&SHF_EXECINSTR)
- #endif
- ||(sec->sh_entsize&INIT_OFFSET_MASK))
- returnfalse;
- returntrue;
- }
- staticintmove_module(struct module*mod,struct load_info*info)
- {
- inti;
- void*ptr;
- //在内核空间为模块的core部分分配空间,返回地址
- ptr=module_alloc_update_bounds(mod->core_size);
- //但是要扫描这个指针所分配的内存的内容。分配数据结构那么该结构本身不打印,但是会扫描结构内部的成员变量,是否引用其他指针。
- kmemleak_not_leak(ptr);
- if(!ptr)
- return-ENOMEM;
- memset(ptr,0,mod->core_size);
- mod->module_core=ptr;//地址赋给module_core成员
- //在内核空间为init section分配内存,初始化后存储在module对象的module_init成员中
- if(mod->init_size){
- //为模块的init部分分配空间,返回地址
- ptr=module_alloc_update_bounds(mod->init_size);
- kmemleak_ignore(ptr);
- if(!ptr){
- module_free(mod,mod->module_core);
- return-ENOMEM;
- }
- memset(ptr,0,mod->init_size);
- mod->module_init=ptr;//地址赋给module_init
- }else
- mod->module_init=NULL;
- /*Transfereachsection which specifies SHF_ALLOC*/
- pr_debug("final section addresses: ");
- //遍历所有节区,拷贝需要占用内存(标志为SHF_ALLOC的节区)的段到init section 或core section,并且调整各个节区的运行时地址
- for(i=0;i<info->hdr->e_shnum;i++){
- void*dest;
- Elf_Shdr*shdr=&info->sechdrs[i];
- if(!(shdr->sh_flags&SHF_ALLOC))
- continue;
- //如果节区首部的sh_entsize的最高位设置的话,表示该段属于init section,则从module_init开始的内存中获取当前段应该存储的地址,否则从module_core开始的内存中获取当前段应该存储的地址。
- if(shdr->sh_entsize&INIT_OFFSET_MASK)
- dest=mod->module_init+(shdr->sh_entsize&~INIT_OFFSET_MASK);
- else
- dest=mod->module_core+shdr->sh_entsize;
- //将当前节区的内容从ELF文件头拷贝到指定的段(init section或core section)中
- if(shdr->sh_type!=SHT_NOBITS)
- memcpy(dest,(void*)shdr->sh_addr,shdr->sh_size);
- //更改节区的运行时地址,sh_addr原先存储的地址是相对于ELF文件头的地址,现在是在内核空间分配新的内存空间后,节区新的运行地址(即节区内容的起始地址)。
- shdr->sh_addr=(unsigned long)dest;
- pr_debug(" 0x%lx %s ",(long)shdr->sh_addr,info->secstrings+shdr->sh_name);
- }
- return 0;
- }
- static void*module_alloc_update_bounds(unsigned long size)
- {
- //在内核空间中分配size大小的空间,返回起始地址
- void*ret=module_alloc(size);
- //更新模块边界值
- if(ret){
- mutex_lock(&module_mutex);
- if((unsigned long)ret<module_addr_min)
- module_addr_min=(unsigned long)ret;
- if((unsigned long)ret+size>module_addr_max)
- module_addr_max=(unsigned long)ret+size;
- mutex_unlock(&module_mutex);
- }
- return ret;
- }
- staticintadd_unformed_module(struct module*mod)
- {
- interr;
- struct module*old;
- mod->state=MODULE_STATE_UNFORMED;
- again:
- mutex_lock(&module_mutex);
- //在内核中查找此模块是否已加入链表
- old=find_module_all(mod->name,strlen(mod->name),true);
- if(old!=NULL){
- if(old->state==MODULE_STATE_COMING||old->state==MODULE_STATE_UNFORMED){
- /*Waitincaseit failstoload.*/
- mutex_unlock(&module_mutex);
- err=wait_event_interruptible(module_wq,finished_loading(mod->name));
- if(err)
- goto out_unlocked;
- goto again;
- }
- err=-EEXIST;
- goto out;
- }
- //若还没有,则挂入全局模块链表modules
- list_add_rcu(&mod->list,&modules);
- err=0;
- out:
- mutex_unlock(&module_mutex);
- out_unlocked:
- returnerr;
- }
- staticintmodule_unload_init(struct module*mod)
- {
- //初始化多处理下用于引用计数的refptr成员
- mod->refptr=alloc_percpu(struct module_ref);
- if(!mod->refptr)
- return-ENOMEM;
- //初始化module对象的链表
- INIT_LIST_HEAD(&mod->source_list);
- INIT_LIST_HEAD(&mod->target_list);
- //模块计数加1
- __this_cpu_write(mod->refptr->incs,1);
- /*Backwards compatibility macros put refcount during init.*/
- mod->waiter=current;
- return 0;
- }
- static void find_module_sections(struct module*mod,struct load_info*info)
- {
- mod->kp=section_objs(info,"__param",sizeof(*mod->kp),&mod->num_kp);
- mod->syms=section_objs(info,"__ksymtab",sizeof(*mod->syms),&mod->num_syms);
- mod->crcs=section_addr(info,"__kcrctab");
- mod->gpl_syms=section_objs(info,"__ksymtab_gpl",sizeof(*mod->gpl_syms),&mod->num_gpl_syms);
- mod->gpl_crcs=section_addr(info,"__kcrctab_gpl");
- mod->gpl_future_syms=section_objs(info,"__ksymtab_gpl_future",
- sizeof(*mod->gpl_future_syms),&mod->num_gpl_future_syms);
- mod->gpl_future_crcs=section_addr(info,"__kcrctab_gpl_future");
- #ifdef CONFIG_UNUSED_SYMBOLS
- mod->unused_syms=section_objs(info,"__ksymtab_unused",sizeof(*mod->unused_syms),&mod->num_unused_syms);
- mod->unused_crcs=section_addr(info,"__kcrctab_unused");
- mod->unused_gpl_syms=section_objs(info,"__ksymtab_unused_gpl",sizeof(*mod->unused_gpl_syms),&mod->num_unused_gpl_syms);
- mod->unused_gpl_crcs=section_addr(info,"__kcrctab_unused_gpl");
- #endif
- #ifdef CONFIG_CONSTRUCTORS
- mod->ctors=section_objs(info,".ctors",sizeof(*mod->ctors),&mod->num_ctors);
- #endif
- #ifdef CONFIG_TRACEPOINTS
- mod->tracepoints_ptrs=section_objs(info,"__tracepoints_ptrs",sizeof(*mod->tracepoints_ptrs),&mod->num_tracepoints);
- #endif
- #ifdef HAVE_JUMP_LABEL
- mod->jump_entries=section_objs(info,"__jump_table",sizeof(*mod->jump_entries),&mod->num_jump_entries);
- #endif
- #ifdef CONFIG_EVENT_TRACING
- mod->trace_events=section_objs(info,"_ftrace_events",sizeof(*mod->trace_events),&mod->num_trace_events);
- #endif
- #ifdef CONFIG_TRACING
- mod->trace_bprintk_fmt_start=section_objs(info,"__trace_printk_fmt",sizeof(*mod->trace_bprintk_fmt_start),&mod->num_trace_bprintk_fmt);
- #endif
- #ifdef CONFIG_FTRACE_MCOUNT_RECORD
- /*sechdrs[0].sh_sizeisalways zero*/
- mod->ftrace_callsites=section_objs(info,"__mcount_loc",sizeof(*mod->ftrace_callsites),&mod->num_ftrace_callsites);
- #endif
- mod->extable=section_objs(info,"__ex_table",sizeof(*mod->extable),&mod->num_exentries);
- if(section_addr(info,"__obsparm"))
- printk(KERN_WARNING"%s: Ignoring obsolete parameters ",mod->name);
- info->debug=section_objs(info,"__verbose",sizeof(*info->debug),&info->num_debug);
- }
- static void*section_objs(conststruct load_info*info,constchar*name,size_t object_size,unsignedint*num)
- {
- //根据节区名找到节区索引值
- unsignedintsec=find_sec(info,name);
- //计算节区项目数
- *num=info->sechdrs[sec].sh_size/object_size;
- //返回节区起始地址
- return(void*)info->sechdrs[sec].sh_addr;
- }
- staticintcheck_module_license_and_versions(struct module*mod)
- {
- if(strcmp(mod->name,"ndiswrapper")==0)
- add_taint(TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);
- /*driverloader was caught wrongly pretendingtobe under GPL*/
- if(strcmp(mod->name,"driverloader")==0)
- add_taint_module(mod,TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);
- /*lve claimstobe GPL but upstream won't provide source*/
- if(strcmp(mod->name,"lve")==0)
- add_taint_module(mod,TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);
- #ifdef CONFIG_MODVERSIONS
- if((mod->num_syms&&!mod->crcs)||(mod->num_gpl_syms&&!mod->gpl_crcs)
- ||(mod->num_gpl_future_syms&&!mod->gpl_future_crcs)
- #ifdef CONFIG_UNUSED_SYMBOLS
- ||(mod->num_unused_syms&&!mod->unused_crcs)
- ||(mod->num_unused_gpl_syms&&!mod->unused_gpl_crcs)
- #endif
- ){
- return try_to_force_load(mod,"no versions for exported symbols");
- }
- #endif
- return 0;
- }
- 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,
- };
- static void setup_modinfo(struct module*mod,struct load_info*info)
- {
- struct module_attribute*attr;
- inti;
- for(i=0;(attr=modinfo_attrs[i]);i++){
- if(attr->setup)
- attr->setup(mod,get_modinfo(info,attr->attr.name));
- }
- }
- staticintsimplify_symbols(struct module*mod,conststruct load_info*info)
- {
- Elf_Shdr*symsec=&info->sechdrs[info->index.sym];
- Elf_Sym*sym=(void*)symsec->sh_addr;
- unsigned long secbase;
- unsignedinti;
- intret=0;
- conststruct kernel_symbol*ksym;
- //遍历模块所有符号
- for(i=1;i<symsec->sh_size/sizeof(Elf_Sym);i++){
- constchar*name=info->strtab+sym[i].st_name;
- //不同符号类型必须进行不同的处理。
- switch(sym[i].st_shndx){
- caseSHN_COMMON://标注了一个尚未分配的公共块符号
- DEBUGP("Common symbol: %s ",name);
- printk("%s: please compile with -fno-common ",mod->name);
- ret=-ENOEXEC;
- break;
- caseSHN_ABS://完全定义的符号是最容易的,因为什么也不需要做。
- DEBUGP("Absolute symbol: 0x%08lx ",(long)sym[i].st_value);
- break;
- caseSHN_UNDEF://未定义的符号,会进行crc检查
- //会调用check_version检查crc值是否一致,若一致返回内核中的同名符号
- ksym=resolve_symbol_wait(mod,info,name);
- //如果符号已经解决(crc一致),则没有问题,将内核中的符号地址赋给模块中的符号地址
- if(ksym&&!IS_ERR(ksym)){
- sym[i].st_value=ksym->value;//使用内核中的符号值
- break;
- }
- //如果符号定义为弱的,也没有问题
- if(!ksym&&ELF_ST_BIND(sym[i].st_info)==STB_WEAK)
- break;
- printk(KERN_WARNING"%s: Unknown symbol %s (err %li) ",mod->name,name,PTR_ERR(ksym));
- ret=PTR_ERR(ksym)?:-ENOENT;
- break;
- //解决其他符号时(符号通过st_shndx成员指向固定的节区),根据节区新的起始地址
- default:
- if(sym[i].st_shndx==info->index.pcpu)
- secbase=(unsigned long)mod_percpu(mod);
- else
- secbase=info->sechdrs[sym[i].st_shndx].sh_addr;
- //根据符号所在节区的起始地址更改符号地址
- sym[i].st_value+=secbase;
- break;
- }
- }
- return ret;
- }
- staticconststruct kernel_symbol*resolve_symbol_wait(struct module*mod,conststruct load_info*info,constchar*name)
- {
- conststruct kernel_symbol*ksym;
- char owner[MODULE_NAME_LEN];
- //调用resolve_symbol()函数
- if(wait_event_interruptible_timeout(module_wq,!IS_ERR(ksym=resolve_symbol(mod,info,name,owner))||PTR_ERR(ksym)!=-EBUSY,30*HZ)<=0){
- printk(KERN_WARNING"%s: gave up waiting for init of module %s. ",mod->name,owner);
- }
- return ksym;
- }
- staticconststruct kernel_symbol*resolve_symbol(struct module*mod,conststruct load_info*info,constchar*name,char ownername[])
- {
- struct module*owner;
- conststruct kernel_symbol*sym;
- constunsigned long*crc;
- interr;
- mutex_lock(&module_mutex);
- //从内核中查找符号名为name的符号,并返回该符号的crc值
- sym=find_symbol(name,&owner,&crc,!(mod->taints&(1<<TAINT_PROPRIETARY_MODULE)),true);
- if(!sym)
- goto unlock;
- //检查该内核中的符号和模块中此未定义的符号的crc值是否一致
- if(!check_version(info->sechdrs,info->index.vers,name,mod,crc,owner)){
- sym=ERR_PTR(-EINVAL);
- goto getname;
- }
- //更新模块的依赖关系,即修正模块的source_list和target_list链表
- err=ref_module(mod,owner);
- if(err){
- sym=ERR_PTR(err);
- goto getname;
- }
- getname:
- strncpy(ownername,module_name(owner),MODULE_NAME_LEN);
- unlock:
- mutex_unlock(&module_mutex);
- return sym;
- }
- staticintapply_relocations(struct module*mod,conststruct load_info*info)
- {
- unsignedinti;
- interr=0;
- //遍历所有的节区
- for(i=1;i<info->hdr->e_shnum;i++){
- //对于重定位节区来说,其sh_info指向重定位所适用的节区的节区头部索引
- //像.rel.text节区对应.text节区
- unsignedintinfosec=info->sechdrs[i].sh_info;
- //如果当前节区附加的节区的索引大于节区的数目,则info不是一个有效的索引,不做处理。
- if(infosec>=info->hdr->e_shnum)
- continue;
- //如果节区在执行过程中不占内存,则 不需要进行处理。
- if(!(info->sechdrs[infosec].sh_flags&SHF_ALLOC))
- continue;
- //此节区包含重定位表项,其中没有补齐,进行符号重定位,则调用apply_relocate来处理
- if(info->sechdrs[i].sh_type==SHT_REL)
- err=apply_relocate(info->sechdrs,info->strtab,info->index.sym,i,mod);
- //此节区包含重定位表项,其中有补齐,进行符号重定位,则调用apply_relocate_add来处理
- elseif(info->sechdrs[i].sh_type==SHT_RELA)
- err=apply_relocate_add(info->sechdrs,info->strtab,info->index.sym,i,mod);
- if(err<0)
- break;
- }
- returnerr;
- }
- intapply_relocate(Elf32_Shdr*sechdrs,constchar*strtab,unsignedintsymindex,unsignedintrelsec,struct module*me)
- {
- //sechdrs表示节区头部表起始地址,strtab表示字符串节区的内容,symindex表示符号节区在节区头部表的索引值,relsec表示重定位节区的索引值(例如:.rel.text节区),该节区的内容是重定位结构的表项
- unsignedinti;
- //找到重定位节区中的重定位表项起始地址
- Elf32_Rel*rel=(void*)sechdrs[relsec].sh_addr;
- Elf32_Sym*sym;
- uint32_t*location;
- DEBUGP("Applying relocate section %u to %u ",relsec,sechdrs[relsec].sh_info);
- //遍历重定位节区中的重定位表项
- for(i=0;i<sechdrs[relsec].sh_size/sizeof(*rel);i++){
- //r_offset指定在此节区的区间r_offset处需要重新填写这个符号(.r_info指定)的绝对地址。找到重定位的位置,重定位节区relsec的sh_info表示重定位所适用的节区的节区头部索引值
- //像.rel.text节区对应.text节区
- location=(void*)sechdrs[sechdrs[relsec].sh_info].sh_addr+rel[i].r_offset;
- //找到重定位的符号,ELF32_R_SYM(rel[i].r_info)表示这个符号在在符号表节区的内容中的索引值
- sym=(Elf32_Sym*)sechdrs[symindex].sh_addr+ELF32_R_SYM(rel[i].r_info);
- //sym->st_value是符号的绝对地址
- switch(ELF32_R_TYPE(rel[i].r_info)){//重定位类型
- caseR_386_32://绝对地址修正
- //在要被修正地址处loc,把该地址下的值加上符号的绝对地址,等于这个位置处的取值就是符号地址了,一般来说*location初始值是0,加上符号绝对地址就能在此位置处找到符号地址了
- *location+=sym->st_value;
- break;
- caseR_386_PC32://相对地址修正
- /*Add the value,subtract its position*/
- *location+=sym->st_value-(uint32_t)location;
- break;
- default:
- pr_err("%s: Unknown relocation: %u ",me->name,ELF32_R_TYPE(rel[i].r_info));
- return-ENOEXEC;
- }
- }
- return 0;
- }
- staticintpost_relocation(struct module*mod,conststruct load_info*info)
- {
- //对模块的异常表进行排序
- sort_extable(mod->extable,mod->extable+mod->num_exentries);
- //因为mod对象已经复制到了内核中的最终位置,拷贝模块的percpu数据到mod对象的内存地址处。
- percpu_modcopy(mod,(void*)info->sechdrs[info->index.pcpu].sh_addr,info->sechdrs[info->index.pcpu].sh_size);
- //初始化module对象中中字符串表、符号表相关的成员,初始化core部分中的字符串表和符号表
- add_kallsyms(mod,info);
- /*Arch-specific module finalizing.*/
- return module_finalize(info->hdr,info->sechdrs,mod);//x86是空函数
- }
- static void add_kallsyms(struct module*mod,conststruct load_info*info)
- {
- unsignedinti,ndst;
- constElf_Sym*src;
- Elf_Sym*dst;
- char*s;
- Elf_Shdr*symsec=&info->sechdrs[info->index.sym];//符号表节区
- //符号表节区开始地址
- mod->symtab=(void*)symsec->sh_addr;
- //符号表项个数
- mod->num_symtab=symsec->sh_size/sizeof(Elf_Sym);
- //找到字符串节区
- mod->strtab=(void*)info->sechdrs[info->index.str].sh_addr;
- //遍历符号表节区中的符号表项,获取符号的属性(STB_LOCAL,STB_GLOBAL...)
- for(i=0;i<mod->num_symtab;i++)
- mod->symtab[i].st_info=elf_type(&mod->symtab[i],info);
- //将core部分存储符号项的偏移地址info->symoffs加上core部分的起始地址得到存储符号项的地址。将符号表项的地址付给core_symtab成员,符号名称起始地址付给core_strtab成员
- mod->core_symtab=dst=mod->module_core+info->symoffs;
- mod->core_strtab=s=mod->module_core+info->stroffs;
- src=mod->symtab;//符号表节区开始地址
- //将符号表节区的符号项(若干的Elf_Sym结构)内容拷贝到core部分指定地址处
- //将符号项中符号的名称拷贝到core部分指定地址处,并重新设置符号项中名称在符号字符串中的索引值st_name
- for(ndst=i=0;i<mod->num_symtab;i++){
- if(i==0||is_core_symbol(src+i,info->sechdrs,info->hdr->e_shnum)){
- //符号项拷贝到mod->module_core+info->symoffs
- dst[ndst]=src[i];
- //重新设置符号名在mod->core_strtab中的索引值
- dst[ndst++].st_name=s-mod->core_strtab;
- //符号名称拷贝到mod->module_core+info->stroffs
- s+=strlcpy(s,&mod->strtab[src[i].st_name],KSYM_NAME_LEN)+1;
- }
- }
- mod->core_num_syms=ndst;//符号个数
- }
- staticintcomplete_formation(struct module*mod,struct load_info*info)
- {
- interr;
- mutex_lock(&module_mutex);
- //确认是否有重定义符号
- err=verify_export_symbols(mod);
- if(err<0)
- goto out;
- //模块bug相关操作
- module_bug_finalize(info->hdr,info->sechdrs,mod);
- //设置模块的状态
- mod->state=MODULE_STATE_COMING;
- out:
- mutex_unlock(&module_mutex);
- returnerr;
- }
- staticintverify_export_symbols(struct module*mod)
- {
- unsignedinti;
- struct module*owner;
- conststruct kernel_symbol*s;
- struct{
- conststruct kernel_symbol*sym;
- unsignedintnum;
- }arr[]={
- {mod->syms,mod->num_syms},
- {mod->gpl_syms,mod->num_gpl_syms},
- {mod->gpl_future_syms,mod->num_gpl_future_syms},
- #ifdef CONFIG_UNUSED_SYMBOLS
- {mod->unused_syms,mod->num_unused_syms},
- {mod->unused_gpl_syms,mod->num_unused_gpl_syms},
- #endif
- };
- //遍历模块中的符号,在内核中检查是否已经导出,若已经导出该符号,则给出著名error:“exports duplicate symbol”
- for(i=0;i<ARRAY_SIZE(arr);i++){
- for(s=arr[i].sym;s<arr[i].sym+arr[i].num;s++){
- if(find_symbol(s->name,&owner,NULL,true,false)){
- printk(KERN_ERR"%s: exports duplicate symbol %s"" (owned by %s) ",mod->name,s->name,module_name(owner));
- return-ENOEXEC;
- }
- }
- }
- return 0;
- }
- staticintdo_init_module(struct module*mod)
- {
- intret=0;
- current->flags&=~PF_USED_ASYNC;
- //内核通知
- blocking_notifier_call_chain(&module_notify_list,MODULE_STATE_COMING,mod);
- //SetROandNX regionsforcore
- set_section_ro_nx(mod->module_core,mod->core_text_size,mod->core_ro_size,mod->core_size);
- //SetROandNX regionsforinit
- set_section_ro_nx(mod->module_init,mod->init_text_size,mod->init_ro_size,mod->init_size);
- //调用模块构造函数
- do_mod_ctors(mod);
- //启动模块
- if(mod->init!=NULL)
- ret=do_one_initcall(mod->init);
- if(ret<0){
- /*Init routine failed:abort.Trytoprotect 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 ""%s: loading module anyway... ",__func__,mod->name,ret,__func__);
- dump_stack();
- }
- //模块初始化完成,更改状态,通知内核
- mod->state=MODULE_STATE_LIVE;
- blocking_notifier_call_chain(&module_notify_list,MODULE_STATE_LIVE,mod);
- if(current->flags&PF_USED_ASYNC)
- async_synchronize_full();
- mutex_lock(&module_mutex);
- /*Drop initial reference.*/
- module_put(mod);
- rim_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);
- 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;
- }
- int__init_or_module do_one_initcall(initcall_t fn)
- {
- intcount=preempt_count();
- intret;
- char msgbuf[64];
- if(initcall_debug)
- ret=do_one_initcall_debug(fn);
- else
- ret=fn();//执行模块的init_module函数
- msgbuf[0]=0;
- if(preempt_count()!=count){
- sprintf(msgbuf,"preemption imbalance ");
- preempt_count()=count;
- }
- if(irqs_disabled()){
- strlcat(msgbuf,"disabled interrupts ",sizeof(msgbuf));
- local_irq_enable();
- }
- WARN(msgbuf[0],"initcall %pF returned with %s ",fn,msgbuf);
- return ret;
- }