Linux内核---40.模块加载过程分析

一. 自己写的一个insmod小程序
1. 下面是一个类似于insmod的程序
绝大多数代码是从busybox中的insmod.c中弄过来的,不过效果还是有的.
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <unistd.h>
  6. #include <fcntl.h>
  7. #include <sys/mman.h>
  8. #include <errno.h>
  9. #include <sys/syscall.h>
  10. #define INT_MAX     ((int)(~0U>>1))
  11. void* try_to_mmap_module(const char *filename, size_t *image_size_p)
  12. {
  13.     void *image;
  14.     struct stat st;
  15.     int fd;
  16.     fd = open(filename, O_RDONLY);
  17.     fstat(fd, &st);
  18.     image = NULL;
  19.     /* st.st_size is off_t, we can't just pass it to mmap */
  20.     if (st.st_size <= *image_size_p) {
  21.         size_t image_size = st.st_size;
  22.         image = mmap(NULL, image_size, PROT_READ, MAP_PRIVATE, fd, 0);
  23.         if (image == MAP_FAILED) 
  24.             image = NULL;
  25.          else 
  26.             /* Success. Report the size */
  27.             *image_size_p = image_size;        
  28.     }
  29.     close(fd);
  30.     return image;
  31. }

  32. int main(int argc, char **argv)
  33. {
  34.     char *filename;
  35.     int rc;
  36.     size_t image_size;
  37.     char *image;

  38.     filename = *++argv;

  39.     image_size = INT_MAX - 4095;
  40.     image = try_to_mmap_module(filename, &image_size);
  41.     if (image == NULL) {
  42.         printf("mmap error\n");
  43.         return -1;
  44.     }
  45.     //loads the relocated module image into kernel space and runs the module's init function.
  46.     printf("next init_module\n");
  47.     rc = init_module(image, image_size, "");   //它只是调用了 init_module,为了简单不可带参数
  48.     if(rc)
  49.     {
  50.         printf("init_module failed %d:%s\n", errno, strerror(errno));
  51.         munmap(image, image_size);
  52.         return rc;
  53.     }
  54.     printf("init module sucess\n");
  55.     return rc;
  56. }
Makefile
  1. CC=arm-none-linux-gnueabi-gcc
  2. inm: inm.c
  3.     $(CC) --o $@ $<
2. 实验结果如下:
  1. root@OK6410:/work/hello# ../inm ./hello.ko 
  2. init module sucess
  3. root@OK6410:/work/hello# lsmod 
  4. hello 2045 0 - Live 0xbf004000

二. linux内核的模块装载过程
为何只调用一个init_module就会把模块装载到内核呢?下面就分析一下它的过程
在kernel/module.c中
  1. SYSCALL_DEFINE3(init_module, void __user *, umodunsigned long, len, const char __user *, uargs)
  2. {
  3.     struct module *mod;
  4.     int ret = 0;
  5.     if (!capable(CAP_SYS_MODULE) || modules_disabled)   //判断是否有权限
  6.         return -EPERM;

  7.     mod = load_module(umod, len, uargs);                //主要过程都在这儿
  8.    
  9.     blocking_notifier_call_chain(&module_notify_list,  MODULE_STATE_COMING, mod);  //通知有新的模块来了

  10.     set_section_ro_nx(mod->module_core, mod->core_text_size, mod->core_ro_size, mod->core_size);

  11.     set_section_ro_nx(mod->module_init, mod->init_text_size, mod->init_ro_size, mod->init_size);

  12.     do_mod_ctors(mod);
  13.     if (mod->init != NULL)
  14.         ret = do_one_initcall(mod->init);               //执行模块的init函数
  15.     if (ret < 0) {
  16.         mod->state = MODULE_STATE_GOING;
  17.         synchronize_sched();
  18.         module_put(mod);
  19.         blocking_notifier_call_chain(&module_notify_list,   MODULE_STATE_GOING, mod);
  20.         free_module(mod);
  21.         wake_up(&module_wq);
  22.         return ret;
  23.     }
  24.     if (ret > 0)
  25.         dump_stack();    

  26.     mod->state = MODULE_STATE_LIVE;
  27.     wake_up(&module_wq);
  28.     blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_LIVE, mod);
  29.     async_synchronize_full();

  30.     mutex_lock(&module_mutex);
  31.     module_put(mod);
  32.     trim_init_extable(mod);
  33. #ifdef CONFIG_KALLSYMS
  34.     mod->num_symtab = mod->core_num_syms;
  35.     mod->symtab = mod->core_symtab;
  36.     mod->strtab = mod->core_strtab;
  37. #endif
  38.     unset_module_init_ro_nx(mod);
  39.     module_free(mod, mod->module_init);
  40.     mod->module_init = NULL;
  41.     mod->init_size = 0;
  42.     mod->init_ro_size = 0;
  43.     mod->init_text_size = 0;
  44.     mutex_unlock(&module_mutex);
  45.     return 0;
  46. }
注: SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
其中SYSCALL_DEFINE3是定义在include/linux/syscalls.h
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...)     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...)     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

还有一个宏 __SC_DECL*也是定义在include/linux/syscalls.h中,
它的作用是: 去掉两个参数之间的逗号,这TMD还是递归的!!
#define __SC_DECL1(t1, a1)    t1 a1
#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
所以是终展开如下:
asmlinkage long sys_init_module(void __user* umod, unsinged long len, const char __user* uargs) 
为什么是SYSCALL_DEFINE3?  后面的3是代表有3个参数.
2. 在load_module中完成大部分操作
init_module
    --> load_module
  1. static struct module *load_module(void __user *umodunsigned long len, const char __user *uargs)
  2. {
  3.     struct load_info info = { NULL, };

  4.     copy_and_check(&info, umod, len, uargs);            //1.申请一个hello.ko大小的内存,并检查hello.ko是否合法  
  5.     struct module* mod = layout_and_allocate(&info);    //2. 解析模块hello.ko,并把含SHF_ALLOC的数据加载到新内存中      
  6.     module_unload_init(mod);                            //3.   
  7.     find_module_sections(mod, &info);                   //4. 
  8.     check_module_license_and_versions(mod);             //5.  
  9.     setup_modinfo(mod, &info);                          //6.
  10.     simplify_symbols(mod, &info);                       //7.
  11.     apply_relocations(mod, &info);                      //8.
  12.     post_relocation(mod, &info);                        //9.
  13.     flush_module_icache(mod);                           //10.
  14.     mod->args = strndup_user(uargs, ~0UL >> 1);
  15.     mod->state = MODULE_STATE_COMING;
  16.     mutex_lock(&module_mutex);
  17.     if (find_module(mod->name)) {
  18.         err = -EEXIST;
  19.         goto unlock;
  20.     }

  21.     if (!mod->taints || mod->taints == (1U<<TAINT_CRAP))
  22.         dynamic_debug_setup(info.debug, info.num_debug);

  23.     err = verify_export_symbols(mod);

  24.     module_bug_finalize(info.hdr, info.sechdrs, mod);
  25.     list_add_rcu(&mod->list, &modules);
  26.     mutex_unlock(&module_mutex);
  27.    
  28.     err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);

  29.     err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp);

  30.     kfree(info.strmap);
  31.     free_copy(&info);

  32.     trace_module_load(mod);
  33.     return mod;
  34. }
下面以hello.ko的加载为例分析一下:

  1. 0000000: 7f45 4c46 0101 0100 0000 0000 0000 0000    //e_ident
  2. 0000010: 0100  2800   0100 0000   0000 0000   0000 0000 
  3.          type mach  version    entry      phoff
  4. 0000020: 2cb7 0000    0000 0005   3400    0000       0000    2800
  5.          shoff       flags     ehsize phentsize phnum  shensize
  6. 0000030: 2400     2100
  7.          shnum   shstrndx
e_shoff = 0xb72c  = 46892 --> section header table在文件中的偏移是46892
e_shentsize = 0x28 = 40  --> section header table 每个entry是40Byte
e_shnum = 0x24 =36 --> 有36个section header table
e_shstrndx = 0x21 = 33 --> 说明.shstrtab这个section在section_header_table中的第33项

2.1 第1步申请内存,并把ko数据从用户区copy到内核区

root@OK6410:/work/hello# ls -l hello.ko 
-rw-rw-r--    1 1000     1000         62173 Aug  6 13:58 hello.ko
init_module
    --> load_module
        --> copy_and_check
  1. static int copy_and_check(struct load_info *infoconst void __user *umod, unsigned long len, const char __user *uargs)
  2. {
  3.     int err;
  4.     Elf_Ehdr *hdr;

  5.     if (len < sizeof(*hdr))
  6.         return -ENOEXEC;
  7.     //根据hello.ko的大小来申请内存
  8.     if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)   //这儿的len就是hello.ko的size=62173
  9.         return -ENOMEM;

  10.     if (copy_from_user(hdr, umod, len) != 0) {                //将hello.ko整个文件由用户空间copy到内核空间
  11.         err = -EFAULT;
  12.         goto free_hdr;
  13.     }
  14.     if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0           //检查ident字段的四个字节是不是'\177ELF'
  15.      || hdr->e_type != ET_REL                                //e_type 必须是1, 说明这是relocatable file
  16.      || !elf_check_arch(hdr)                                 //检查e_machine=0x28代表arm
  17.      || hdr->e_shentsize != sizeof(Elf_Shdr)) {              //elf的section_header 是不是等于sizeof(Elf_Shdr)
  18.         err = -ENOEXEC;
  19.         goto free_hdr;
  20.     }

  21.     if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {  //检查长度: e_shoff是section_header_table的起始
  22.         err = -ENOEXEC;                     //hdr->e_shnum * sizeof(Elf_Shdr) 是section_header_table的大小                            
  23.         goto free_hdr;
  24.     }

  25.     info->hdr = hdr;                            //保存在info中
  26.     info->len = len;
  27.     return 0;
  28. }
2.2 第2步申请内存,并把ko数据从用户区copy到内核区
init_module

    --> load_module
        -->  layout_and_allocate
  1. static struct module *layout_and_allocate(struct load_info *info)
  2. {
  3.     /* Module within temporary copy. */
  4.     struct module *mod;
  5.     Elf_Shdr *pcpusec;   

  6.     mod = setup_load_info(info);   //1.更新每一个section的实际地址,并返回gnu.linkonce.this_module这个section的地址
  7.     check_modinfo(mod, info);      //2.检查mofinfo这个section中的内容

  8.     //这个函数是空的
  9.     module_frob_arch_sections(info->hdr, info->sechdrs, info->secstrings, mod);
  10.     //没有pcpu这个section, pcpupcpusec->sh_size=0
  11.     pcpusec = &info->sechdrs[info->index.pcpu];
  12.     if (pcpusec->sh_size) {
  13.         /* We have a special allocation for this section. */
  14.         err = percpu_modalloc(mod, pcpusec->sh_size, pcpusec->sh_addralign);
  15.         if (err)
  16.             goto out;
  17.         pcpusec->sh_flags &= ~(unsigned long)SHF_ALLOC;
  18.     }
  19.     //3.这儿的len就是hello.ko的size=62173
  20.     layout_sections(mod, info);
  21.     //为符号名称字符串表分配内存
  22.     info->strmap = kzalloc(BITS_TO_LONGS(info->sechdrs[info->index.str].sh_size)* sizeof(long), GFP_KERNEL);
  23.     //4. 如果配置了选项CONFIG_KALLSYMS,就要把符号名称的字符串表加载到内存
  24.     layout_symtab(mod, info);

  25.     //5. 把hello.ko中需要加载到内存的加载到内存
  26.     err = move_module(mod, info);

  27.     //.gnu.linkonce.this_module的section的地址也要相应改变
  28.     mod = (void *)info->sechdrs[info->index.mod].sh_addr;
  29.     kmemleak_load_module(mod, info);
  30.     return mod;
  31. }
2.2.1 HDR的第一次修改
init_module
    --> load_module
        -->  layout_and_allocate
                -->  setup_load_info
更新每一个section的实际地址,并返回gnu.linkonce.this_module这个section的地址
  1. static struct module *setup_load_info(struct load_info *info)
  2. {
  3.     unsigned int i;
  4.     int err;
  5.     struct module *mod;
  6.     //获取section header table基地址
  7.     info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
  8.     //获取.shstrtab这个section的地址
  9.     info->secstrings = (void *)info->hdr + info->sechdrs[info->hdr->e_shstrndx].sh_offset;

  10.     err = rewrite_section_headers(info);     //更新sh_addr的地址
  11.     //遍历查找符号名称字符串表
  12.     for (= 1; i < info->hdr->e_shnum; i++) {
  13.         if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
  14.             info->index.sym = i;
  15.             info->index.str = info->sechdrs[i].sh_link;
  16.             info->strtab = (char *)info->hdr + info->sechdrs[info->index.str].sh_offset;
  17.             break;
  18.         }
  19.     }
  20.     //遍历,并找出gnu.linkonce.this_module这个section在section_table中的地址.
  21.     //如果找不到,则说明这不是一个模块,返加error
  22.     info->index.mod = find_sec(info, ".gnu.linkonce.this_module");   
  23.     //找到gnu.linkonce.this_module这个section的地址
  24.     mod = (void *)info->sechdrs[info->index.mod].sh_addr;
  25.     
  26.     //如果strip了hello.ko,那么在加载时找不到符号,也会报错
  27.     if (info->index.sym == 0)
  28.         return ERR_PTR(-ENOEXEC);
  29.     
  30.     info->index.pcpu = find_pcpusec(info);

  31.     if (!check_modstruct_version(info->sechdrs, info->index.vers, mod))
  32.         return ERR_PTR(-ENOEXEC);

  33.     return mod; //返回gnu.linkonce.this_module这个section的地址
  34. }
init_module
    --> load_module
        -->  layout_and_allocate
                -->  setup_load_info
                    -->  rewrite_section_headers
因为hello.ko己经被读取到了内核空间的某一个内存处,要想访问每个section的地址需要通过sh_addr
但是现在sh_addr这个值己经不准了,所以需要用新地址来更新一下
  1. static int rewrite_section_headers(struct load_info *info)
  2. {
  3.     unsigned int i;
  4.     info->sechdrs[0].sh_addr = 0;

  5.     for (= 1; i < info->hdr->e_shnum; i++) {
  6.         Elf_Shdr *shdr = &info->sechdrs[i]
  7.         if (shdr->sh_type != SHT_NOBITS && info->len < shdr->sh_offset + shdr->sh_size)
  8.             return -ENOEXEC;        

  9.         //现在己经把hello.ko读到了内核空间的某一个地址处,sh_addr是指向section的地址需要更新
  10.         //在section_header_table中修改每一个secion的地址
  11.         shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset;

  12. #ifndef CONFIG_MODULE_UNLOAD  //如果配了这个宏说明,模块加载后不可卸载
  13.        //模块都不可卸载了, exit这个section也没有加载的必要了
  14.         if (strstarts(info->secstrings+shdr->sh_name, ".exit"))
  15.             shdr->sh_flags &= ~(unsigned long)SHF_ALLOC;      
  16. #endif
  17.     }
  18.     info->index.vers = find_sec(info, "__versions");        //查找version这个section的地址
  19.     info->index.info = find_sec(info, ".modinfo");          //查找modinfo这个section的地址
  20.     info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;   //把version 与 modinfo这两个section
  21.     info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;   //标记为最终不可见
  22.     return 0;
  23. }
init_module
    --> load_module
        -->  layout_and_allocate
                -->  setup_load_info
                    -->  find_sec
//遍历整个section_header_table找到name与参数相同的section,返回这个section在section_header_table中的索引
  1. static unsigned int find_sec(const struct load_info *info, const char *name)
  2. {
  3.     //info->secstrings是.shstrtab的地址, shdr->sh_name是每一个section的名字在.shstrtab中的偏移
  4.     for (= 1; i < info->hdr->e_shnum; i++) {
  5.         Elf_Shdr *shdr = &info->sechdrs[i];
  6.         if ((shdr->sh_flags & SHF_ALLOC) && strcmp(info->secstrings + shdr->sh_name, name) == 0)
  7.             return i;
  8.     }
  9.     return 0;
  10. }
2.2.2 检查section中的版本号
init_module
    --> load_module
        -->  layout_and_allocate
                -->  check_modinfo
检查mofinfo这个section中的内容
  1. static int check_modinfo(struct module *mod, struct load_info *info)
  2. {
  3.     const char *modmagic = get_modinfo(info, "vermagic");
  4.     int err;
  5.     //检查编译模块的系统版本与当前运行的系统版本是否一致,hello.ko中vermagic=3.0.1    
  6.     if (!modmagic) {
  7.         err = try_to_force_load(mod, "bad vermagic");
  8.         if (err)
  9.             return err;
  10.     } else if (!same_magic(modmagic, vermagic, info->index.vers)) {
  11.         printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
  12.          mod->name, modmagic, vermagic);
  13.         return -ENOEXEC;
  14.     }

  15.     if (get_modinfo(info, "staging")) {
  16.         add_taint_module(mod, TAINT_CRAP);
  17.         printk(KERN_WARNING "%s: module is from the staging directory,"
  18.          " the quality is unknown, you have been warned.\n",
  19.          mod->name);
  20.     }

  21.     //检查license是不是GPL
  22.     set_license(mod, get_modinfo(info, "license"));

  23.     return 0;
  24. }
init_module
    --> load_module
        -->  layout_and_allocate
                -->  check_modinfo
                   -->  get_modinfo
  1. static char *get_modinfo(struct load_info *info, const char *tag)
  2. {
  3.     unsigned int taglen = strlen(tag);
  4.     //在函数rewrite_section_headers中有: info->index.info = find_sec(info, ".modinfo");
  5.    //这儿是获取modinfo这个section在section_table中的地址 
  6.     Elf_Shdr *infosec = &info->sechdrs[info->index.info];
  7.     unsigned long size = infosec->sh_size;           //modinfo这个section的长度
  8.     //在modinfo中每一项都以\0结束
  9.     for (= (char *)infosec->sh_addr; p; p = next_string(p, &size)) {
  10.         if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
  11.             return p + taglen + 1;    //返回等号后面的第一个字节的地址,既结果
  12.     }
  13.     return NULL;
  14. }
下面是用objdump打印的modinfo
Linux内核---40.模块加载过程分析_第1张图片
从上图可以看出每一个字段的结束都是 00

2.2.3 划分为两部分CORE 与 INIT
init_module
    --> load_module
        -->  layout_and_allocate
                --> layout_sections
  1. static void layout_sections(struct module *mod, struct load_info *info)
  2. {
  3.     static unsigned long const masks[][2] = {
  4.         { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
  5.         { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
  6.         { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
  7.         { ARCH_SHF_SMALL | SHF_ALLOC, 0 }
  8.     };
  9.     unsigned int m, i;

  10.     for (= 0; i < info->hdr->e_shnum; i++)
  11.         info->sechdrs[i].sh_entsize = ~0UL;
  12.     //划分为两部分: CORE  INIT

  13.     //a. 第1部分CORE:  查找标志中含有SHF_ALLOC的section
  14.     for (= 0; m < ARRAY_SIZE(masks); ++m) {
  15.         for (= 0; i < info->hdr->e_shnum; ++i) {
  16.             Elf_Shdr *= &info->sechdrs[i];
  17.             const char *sname = info->secstrings + s->sh_name;
  18.             //含有SHF_ALLOC的section需要加载到最终的内存
  19.             //含有SHF_ALLOC的section并且不以init开头的划分到CORE部分
  20.             if ((s->sh_flags & masks[m][0]) != masks[m][0] || (s->sh_flags & masks[m][1]) 
  21.              || s->sh_entsize != ~0UL || strstarts(sname, ".init"))
  22.                 continue;
  23.             s->sh_entsize = get_offset(mod, &mod->core_size, s, i);    //sh_entsize是core+init的size
  24.         }
  25.         switch (m) {
  26.         case 0:  //可执行的段,代码段都一样
  27.             mod->core_size = debug_align(mod->core_size);
  28.             mod->core_text_size = mod->core_size;                      
  29.             break;
  30.         case 1:  //只读段
  31.             mod->core_size = debug_align(mod->core_size);
  32.             mod->core_ro_size = mod->core_size;
  33.             break;
  34.         case 3:  //所有段
  35.             mod->core_size = debug_align(mod->core_size);
  36.             break;
  37.         }
  38.     }
  39.     //b. 第2部分INIT
  40.     for (= 0; m < ARRAY_SIZE(masks); ++m) {
  41.         for (= 0; i < info->hdr->e_shnum; ++i) {
  42.             Elf_Shdr *= &info->sechdrs[i];
  43.             const char *sname = info->secstrings + s->sh_name;
  44.           //含有SHF_ALLOC的section需要加载到最终的内存
  45.           //含有SHF_ALLOC的section并且以init开头的划分到INIT部分
  46.             if ((s->sh_flags & masks[m][0]) != masks[m][0]
  47.              || (s->sh_flags & masks[m][1])
  48.              || s->sh_entsize != ~0UL
  49.              || !strstarts(sname, ".init"))
  50.                 continue;
  51.             s->sh_entsize = (get_offset(mod, &mod->init_size, s, i) | INIT_OFFSET_MASK);
  52.         }
  53.         switch (m) {
  54.         case 0: 
  55.             mod->init_size = debug_align(mod->init_size);
  56.             mod->init_text_size = mod->init_size;
  57.             break;
  58.         case 1: 
  59.             mod->init_size = debug_align(mod->init_size);
  60.             mod->init_ro_size = mod->init_size;
  61.             break;
  62.         case 3: 
  63.             mod->init_size = debug_align(mod->init_size);
  64.             break;
  65.         }
  66.     }
  67. }
注: 这儿为什么要区分init与core呢?  因为init部分的内存在使用完之后,马上就会被释放,而core部分的内存则会一直存在于内存中
2.2.4 加载符号名称字符串表
当配置了内核选项CONFIG_KALLSYMS时,就需要把符号名称字符串表加载到内存中去
因为这个section的标志位中不含 SHF_ALLOC,所以需要单独加载
  1. static void layout_symtab(struct module *mod, struct load_info *info)
  2. {
  3.     Elf_Shdr *symsect = info->sechdrs + info->index.sym;
  4.     Elf_Shdr *strsect = info->sechdrs + info->index.str;
  5.     const Elf_Sym *src;
  6.     unsigned int i, nsrc, ndst;

  7.     /* Put symbol section at end of init part of module. */
  8.     symsect->sh_flags |= SHF_ALLOC;
  9.     symsect->sh_entsize = get_offset(mod, &mod->init_size, symsect,
  10.                      info->index.sym) | INIT_OFFSET_MASK;
  11.     DEBUGP("\t%s\n", info->secstrings + symsect->sh_name);

  12.     src = (void *)info->hdr + symsect->sh_offset;
  13.     nsrc = symsect->sh_size / sizeof(*src);
  14.     for (ndst = i = 1; i < nsrc; ++i, ++src)
  15.         if (is_core_symbol(src, info->sechdrs, info->hdr->e_shnum)) {
  16.             unsigned int j = src->st_name;

  17.             while (!__test_and_set_bit(j, info->strmap)
  18.              && info->strtab[j])
  19.                 ++j;
  20.             ++ndst;
  21.         }

  22.     /* Append room for core symbols at end of core part. */
  23.     info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
  24.     mod->core_size = info->symoffs + ndst * sizeof(Elf_Sym);

  25.     /* Put string table section at end of init part of module. */
  26.     strsect->sh_flags |= SHF_ALLOC;
  27.     strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
  28.                      info->index.str) | INIT_OFFSET_MASK;
  29.     DEBUGP("\t%s\n", info->secstrings + strsect->sh_name);

  30.     /* Append room for core symbols' strings at end of core part. */
  31.     info->stroffs = mod->core_size;
  32.     __set_bit(0, info->strmap);
  33.     mod->core_size += bitmap_weight(info->strmap, strsect->sh_size);
  34. }


2.2.5 HDR视图的第二次转移
为core与ini区分配新的内存,并把core与init部分copy到新内存中
  1. static int move_module(struct module *mod, struct load_info *info)
  2. {
  3.     int i;
  4.     void *ptr;
  5.     //对core区分配内存
  6.     ptr = module_alloc_update_bounds(mod->core_size);  
  7.     kmemleak_not_leak(ptr);             //检查内存泄漏
  8.     memset(ptr, 0, mod->core_size);     //将对core区内存清0     
  9.     mod->module_core = ptr;             //将core内存指针记录在变量module_core中
  10.    
  11.     //对init区分配内存
  12.     ptr = module_alloc_update_bounds(mod->init_size);
  13.     kmemleak_ignore(ptr);              //检查内存泄漏
  14.     memset(ptr, 0, mod->init_size);    //将对init区内存清0
  15.     mod->module_init = ptr;            //将init内存指针记录在变量module_init中    
  16.     
  17.     //将core与init区copy到新分配的内存中去
  18.     for (= 0; i < info->hdr->e_shnum; i++) {
  19.         void *dest;
  20.         Elf_Shdr *shdr = &info->sechdrs[i];

  21.         if (!(shdr->sh_flags & SHF_ALLOC))
  22.             continue;

  23.         if (shdr->sh_entsize & INIT_OFFSET_MASK)
  24.             dest = mod->module_init  + (shdr->sh_entsize & ~INIT_OFFSET_MASK);
  25.         else
  26.             dest = mod->module_core + shdr->sh_entsize;

  27.         if (shdr->sh_type != SHT_NOBITS)
  28.             memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
  29.         //sectin再次移动,那么section_header_table中指向section的指针也要相应改变
  30.         shdr->sh_addr = (unsigned long)dest;   
  31.     }
  32.     return 0;
  33. }

附:
1. 关于c中的转义
类似于 \t \b
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main ( int argc, char *argv[] )
  4. {
  5.     printf("0x%x\n", '\177');
  6.     return EXIT_SUCCESS;
  7. }
  8. gcc -o test test.c
  9. 结果是: '\177' = 0x7f
\OOO :  8进制
\xXX : 16进制
2. 关于gcc中的条件表达式
表达式a?b:c, 省略了中间的b,是什么意思呢?
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main ( int argc, char *argv[] )
  4. {
  5.     int a=2;
  6.     printf("result=%d\n", a?:1);
  7.     return 0;
  8. }
这是gcc的一个扩展:  a?:c  ==  a?a:c 
参考文章:
http://gcc.gnu.org/onlinedocs/gcc/Conditionals.html#Conditionals

你可能感兴趣的:(linux内核)