<%@LANGUAGE="VBSCRIPT" CODEPAGE="936"%> 青龙山庄—INSMOD的实现原理
 
 
INSMOD的实现原理
 
   问题的提出是前一阵和lgx聊天发现,一个被strip的module也可以被成功的insmod,当时知道一些insmod的原理觉得不太可能,因为一个正常的module文件其实就是标准的ELF格式object文件,如果将他的 symtab strip掉的话,那些printk这类的symbol将不能被正常的解析,理论上是不可能加载成功的,于是做了一个简单的module在turbo7上测试了一把,modutils的版本是2.4.6,出人意料的是竟然成功的被加载,真是觉得真是不可思议,因此觉得有必要研究一下insmod的具体实现,最好的方法当然是go to the source

先说说关于module的几个系统调用,主要有
   sys_create_module
   sys_init_module
   sys_query_module
   sys_delete_module
我们简单的分析一下module的创建过程,有一个重要地数据结构必然要提到,那就是struct module,定义如下。
   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;         // symbol的个数
     unsigned ndeps;
    
     struct module_symbol *syms;   // 此module实现的对外输出的所有symbol
     struct module_ref *deps;
     struct module_ref *refs;
     int (*init)(void);
     void (*cleanup)(void);
     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_opt_member()
     to examine them. */
     const struct module_persist *persist_start;
     const struct module_persist *persist_end;
     int (*can_unload)(void);
   };

   而kernel中有一个全局变量module_list为所有的module的list,在系统初始化是module_list只有一项,为:
   struct module *module_list = &kernel_module;
   kernel_module中的syms为__start___ksymtab即为kernel对外输出的symbol的所有项。
   sys_create_module负责分配空间生成一个module结构,加入module_list中。
   sys_init_module负责将insmod在用户空间的module结构复制到由sys_create_module创建地空间中。

   好了,我们现在分析一下insmod的主要工作流程(modtuils-2.4.6版)

1.get_kernel_info
   get_kernel_info函数负责取得kernel中先以注册的modules,放入module_stat中,并将kernel实现的各个symbol放入ksyms中,个数为ksyms,最终调用new_get_kernel_info

   // type的三种类型,影响get_kernle_info的流程。
   #define K_SYMBOLS 1   /* Want info about symbols */
   #define K_INFO 2    /* Want extended module info */
   #define K_REFS 4    /* Want info about references */
  
   static int new_get_kernel_info(int type)
   {
     struct module_stat *modules;
     struct module_stat *m;
     struct module_symbol *syms;
     struct module_symbol *s;
     size_t ret;
     size_t bufsize;
     size_t nmod;
     size_t nsyms;
     size_t i;
     size_t j;
     char *module_names;
     char *mn;
    
     drop();
    
     // Collect the loaded modules
     module_names = xmalloc(bufsize = 256);
    
     // 取得现有module的名称
     // ret返回个数,module_names返回各个module名称,字符0分割
     while (query_module(NULL, QM_MODULES, module_names, bufsize, &ret))
     {
       if (errno != ENOSPC)
       {
         error("QM_MODULES: %mn");
         return 0;
       }
       module_names = xrealloc(module_names, bufsize = ret);
     }
    
     module_name_list = module_names;
     l_module_name_list = bufsize;
     n_module_stat = nmod = ret;
     module_stat = modules = xmalloc(nmod * sizeof(struct module_stat));
     memset(modules, 0, nmod * sizeof(struct module_stat));
    
     /* Collect the info from the modules */
     // 循环取得各个module的信息,QM_INFO的使用。
     for (i = 0, mn = module_names, m = modules; i < nmod; ++i, ++m, mn += strlen(mn) + 1)
     {
       struct module_info info;
       // info包括module的地址,大小,flag和使用计数器。
       m->name = mn;
       if (query_module(mn, QM_INFO, &info, sizeof(info), &ret))
       {
         if (errno == ENOENT)
         {
           /* The module was removed out from underneath us. */
           m->flags = NEW_MOD_DELETED;
           continue;
         }
         /* else oops */
         error("module %s: QM_INFO: %m", mn);
         return 0;
       }
       m->addr = info.addr;
  
       if (type & K_INFO)   // 取得module的信息
       {
         m->size = info.size;
         m->flags = info.flags;
         m->usecount = info.usecount;
         m->modstruct = info.addr;
       }  // 将info值传给module_stat结构
      
       if (type & K_REFS)   // 取得module的引用关系
       {
         int mm;
         char *mrefs;
         char *mr;
        
         mrefs = xmalloc(bufsize = 64);
         while (query_module(mn, QM_REFS, mrefs, bufsize, &ret))
         {
           if (errno != ENOSPC)
           {
             error("QM_REFS: %m");
             return 1;
           }
           mrefs = xrealloc(mrefs, bufsize = ret);
         }
         for (j = 0,mr=mrefs; j<ret; ++j,mr+=strlen(mr)+1)
         {
           for (mm = 0; mm < i; ++mm)
           {
             if (strcmp(mr, module_stat[mm].name) == 0)
             {
               m->nrefs += 1;
               m->refs=xrealloc(m->refs,m->nrefs *sizeof(struct module_stat **));
               m->refs[m->nrefs - 1] = module_stat + mm;
               break;
             }
           }
         }
         free(mrefs);
       }
  
       if (type & K_SYMBOLS)    /* 取得symbol信息,正是我们要得*/
       {
         syms = xmalloc(bufsize = 1024);
         while (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret))
         {
           if (errno == ENOSPC)
           {
             syms = xrealloc(syms, bufsize = ret);
             continue;
           }
           if (errno == ENOENT)
           {
             /* The module was removed out from underneath us.*/
             m->flags = NEW_MOD_DELETED;
             free(syms);
             goto next;
           }
           else
           {
             error("module %s: QM_SYMBOLS: %m", mn);
             return 0;
           }
         }
         nsyms = ret;
         // syms是module_symbol结构,ret返回symbol个数
         m->nsyms = nsyms;
         m->syms = syms;
        
         /* Convert string offsets to string pointers */
         for (j = 0, s = syms; j < nsyms; ++j, ++s)
           s->name += (unsigned long) syms;
       }
   next:
     }
  
     if (type & K_SYMBOLS)    /* Want info about symbols */
     {
       /* Collect the kernel's symbols. */
       syms = xmalloc(bufsize = 16 * 1024);
       // name为NULL,返回kernel_module的symbol。
       while (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret))
       {
         if (errno != ENOSPC)
         {
           error("kernel: QM_SYMBOLS: %m");
           return 0;
         }
         syms = xrealloc(syms, bufsize = ret);
       }
       //将值返回给nksyms和ksyms两个全局变量存储。
       nksyms = nsyms = ret;
       ksyms = syms;
       /* name原来只是一个结构内的偏移,加上结构地址为真正的字符串地址 */
       for (j = 0, s = syms; j < nsyms; ++j, ++s)
         s->name += (unsigned long) syms;
     }
     return 1;
   }

2.set_ncv_prefix(NULL)
   判断symbolname中是否有前缀,象_smp之类,这里没有。

3.检查是否已有同名的module
   ...
   for (i = 0; i < n_module_stat; ++i)
   {
     if (strcmp(module_stat.name, m_name) == 0)
     {
       error("a module named %s already exists", m_name);
       goto out;
     }
   }  // 判断是否已经存在。
   ...

4.obj_load
   将.o文件读入到struct obj_file结构f中。

   struct obj_file *obj_load (int fp, Elf32_Half e_type, const char *filename)
   {
     struct obj_file *f;
     ElfW(Shdr) *section_headers;
     int shnum, i;
     char *shstrtab;
    
     /* Read the file header. */
     f = arch_new_file();      // 生成新的obj_file结构
     memset(f, 0, sizeof(*f));
     f->symbol_cmp = strcmp;     // 设置symbol名的比较函数就是strcmp
     f->symbol_hash = obj_elf_hash; // 设置计算symbol hash值的函数
     f->load_order_search_start = &f->load_order;  // ??
    
     gzf_lseek(fp, 0, SEEK_SET);
  
     // 取得object文件的ELF头结构。
     if (gzf_read(fp, &f->header, sizeof(f->header)) != sizeof(f->header))
     {
       error("cannot read ELF header from %s", filename);
       return NULL;
     }
     // 判断ELF的magic,是否是ELF文件
     if (f->header.e_ident[EI_MAG0] != ELFMAG0
       || f->header.e_ident[EI_MAG1] != ELFMAG1
       || f->header.e_ident[EI_MAG2] != ELFMAG2
       || f->header.e_ident[EI_MAG3] != ELFMAG3)
     {
       error("%s is not an ELF file", filename);
       return NULL;
     }
     if (f->header.e_ident[EI_CLASS] != ELFCLASSM
       // i386的机器上为ELFCLASS32,表示32bit
       || f->header.e_ident[EI_DATA] != ELFDATAM
       // 此处值为ELFDATA2LSB,表示编码方式
       || f->header.e_ident[EI_VERSION] != EV_CURRENT
       // 此值固定,表示版本
       || !MATCH_MACHINE(f->header.e_machine)
       // 机器类型
     )
     {
       error("ELF file %s not for this architecture", filename);
       return NULL;
     }
     if (f->header.e_type != e_type && e_type != ET_NONE)  // 类型必为ET_REL
     {
       switch (e_type)
       {
       case ET_REL:
         error("ELF file %s not a relocatable object", filename);
         break;
       case ET_EXEC:
         error("ELF file %s not an executable object", filename);
         break;
       default:
         error("ELF file %s has wrong type, expecting %d got %d",
           filename, e_type, f->header.e_type);
         break;
       }
       return NULL;
     }
    
     /* Read the section headers. */
     if (f->header.e_shentsize != sizeof(ElfW(Shdr)))
     {
       error("section header size mismatch %s: %lu != %lu",
         filename,
         (unsigned long)f->header.e_shentsize,
         (unsigned long)sizeof(ElfW(Shdr)));
       return NULL;
     }
    
     shnum = f->header.e_shnum;               // section个数
     f->sections = xmalloc(sizeof(struct obj_section *) * shnum);
     memset(f->sections, 0, sizeof(struct obj_section *) * shnum);
    
     section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);    // section表的大小
     gzf_lseek(fp, f->header.e_shoff, SEEK_SET);
     // 获得section表内容
     if (gzf_read(fp, section_headers, sizeof(ElfW(Shdr))*shnum) != sizeof(ElfW(Shdr))*shnum)
     {
       error("error reading ELF section headers %s: %m", filename);
       return NULL;
     }
    
     /* Read the section data. */
     for (i = 0; i < shnum; ++i)
     {
       struct obj_section *sec;
       f->sections = sec = arch_new_section();//  分配内存给每个section
       memset(sec, 0, sizeof(*sec));
       sec->header = section_headers;   // 设置section表项地址
       sec->idx = i;//section表中第几个
       switch (sec->header.sh_type)      // section的类型
       {
       case SHT_NULL:
       case SHT_NOTE:
       case SHT_NOBITS:
         break;
       case SHT_PROGBITS:
       case SHT_SYMTAB:
       case SHT_STRTAB:
       case SHT_RELM:   // 将以上各种类型的section内容读到结构中。
         if (sec->header.sh_size > 0)
         {
           sec->contents = xmalloc(sec->header.sh_size);
           gzf_lseek(fp, sec->header.sh_offset, SEEK_SET);
           if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size)
           {
             error("error reading ELF section data %s: %m", filename);
             return NULL;
           }
         }
         else
           sec->contents = NULL;
         break;
         // 描述relocation的section
   #if SHT_RELM == SHT_REL
       case SHT_RELA:
         if (sec->header.sh_size)
         {
           error("RELA relocations not supported on this architecture %s", filename);
           return NULL;
         }
         break;
   #else
       case SHT_REL:
         if (sec->header.sh_size)
         {
           error("REL relocations not supported on this architecture %s", filename);
           return NULL;
         }
         break;
   #endif
       default:
         if (sec->header.sh_type >= SHT_LOPROC)
         {
           if (arch_load_proc_section(sec, fp) < 0)
             return NULL;
           break;
         }
         error("can't handle sections of type %ld %s",(long)sec->header.sh_type, filename);
         return NULL;
       }
     }
    
     /* Do what sort of interpretation as needed by each section. */
     // shstrndx存的是section字符串表的索引值,就是第几个section
     // shstrtab就是那个section了。
     shstrtab = f->sections[f->header.e_shstrndx]->contents;
     for (i = 0; i < shnum; ++i)
     {
       struct obj_section *sec = f->sections;
       sec->name = shstrtab + sec->header.sh_name;
     }  // 根据strtab,取得每个section的名字
    
     for (i = 0; i < shnum; ++i)
     {
       struct obj_section *sec = f->sections;
  
       /* .modinfo and .modstring should be contents only but gcc has no
       * attribute for that. The kernel may have marked these sections as
       * ALLOC, ignore the allocate bit.
       */也就是说即使modinfo和modstring有此标志位,也去掉。
       if (strcmp(sec->name, ".modinfo") == 0 || strcmp(sec->name, ".modstring") == 0)
         sec->header.sh_flags &= ~SHF_ALLOC;
       // ALLOC表示此section是否占用内存
       if (sec->header.sh_flags & SHF_ALLOC)
         obj_insert_section_load_order(f, sec);
       // 确定section load的顺序
       // 根据的是flag的类型加权得到优先级
       switch (sec->header.sh_type)
       {
       case SHT_SYMTAB:  // 符号表
       {
         unsigned long nsym, j;
         char *strtab;
         ElfW(Sym) *sym;
        
         if (sec->header.sh_entsize != sizeof(ElfW(Sym)))
         {
           error("symbol size mismatch %s: %lu != %lu",
             filename,
             (unsigned long)sec->header.sh_entsize,
             (unsigned long)sizeof(ElfW(Sym)));
           return NULL;
         }
         // 计算符号表表项个数,nsym也就是symbol个数
         nsym = sec->header.sh_size / sizeof(ElfW(Sym));
         // sh_link是符号字符串表的索引值
         strtab = f->sections[sec->header.sh_link]->contents;
         sym = (ElfW(Sym) *) sec->contents;
        
         /* Allocate space for a table of local symbols. */
         // 为所有符号分配空间
         j = f->local_symtab_size = sec->header.sh_info;
         f->local_symtab = xmalloc(j *= sizeof(struct obj_symbol *));
         memset(f->local_symtab, 0, j);
        
         /* Insert all symbols into the hash table. */
         for (j = 1, ++sym; j < nsym; ++j, ++sym)
         {
           const char *name;
           if (sym->st_name)  // 有值就是strtab的索引值
             name = strtab+sym->st_name;
           // 如果为零,此symbolname是一个section的name,比如.rodata之类的
           else
             name = f->sections[sym->st_shndx]->name;
           // obj_add_symbol将符号加入到f->symbab这个hash表中
           obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
             sym->st_value, sym->st_size);
          
         }
       }
       break;
       }
     }
    
     /* second pass to add relocation data to symbols */
     for (i = 0; i < shnum; ++i)
     {
       struct obj_section *sec = f->sections;
       switch (sec->header.sh_type)
       {
       case SHT_RELM: // 找到描述重定位的section
       {
         unsigned long nrel, j;
         ElfW(RelM) *rel;
         struct obj_section *symtab;
         char *strtab;
         if (sec->header.sh_entsize != sizeof(ElfW(RelM)))
         {
           error("relocation entry size mismatch %s: %lu != %lu",
             filename,
             (unsigned long)sec->header.sh_entsize,
             (unsigned long)sizeof(ElfW(RelM)));
           return NULL;
         }
         // 算出rel有几项,存到nrel中
         nrel = sec->header.sh_size / sizeof(ElfW(RelM));
         rel = (ElfW(RelM) *) sec->contents;
         // rel的section中sh_link相关值是符号section的索引号
         symtab = f->sections[sec->header.sh_link];
         // 而符号section中sh_link是符号字符串section的索引号
         strtab = f->sections[symtab->header.sh_link]->contents;
        
         /* Save the relocate type in each symbol entry. */
         // 存储需要relocate的符号的rel的类型
         for (j = 0; j < nrel; ++j, ++rel)
         {
           ElfW(Sym) *extsym;
           struct obj_symbol *intsym;
           unsigned long symndx;    // 取得这个需relocate的符号索引值
           symndx = ELFW(R_SYM)(rel->r_info);
           if (symndx)
           {
             extsym = ((ElfW(Sym) *) symtab->contents) + symndx;
             if (ELFW(ST_BIND)(extsym->st_info) == STB_LOCAL)
             {  // local类型
               /* Local symbols we look up in the local table to be sure
               we get the one that is really intended. */
               intsym = f->local_symtab[symndx];
             }
             else
             {  // 其他类型,从hash表中取
               /* Others we look up in the hash table. */
               const char *name;
               if (extsym->st_name)
                 name = strtab + extsym->st_name;
               else
                 name = f->sections[extsym->st_shndx]->name;
               intsym = obj_find_symbol(f, name);
             }
             intsym->r_type = ELFW(R_TYPE)(rel->r_info);
           }
         }
       }
       break;
       }
     }
     f->filename = xstrdup(filename);
     return f;
   }

   ...
   struct obj_symbol *
   obj_add_symbol (struct obj_file *f, const char *name, unsigned long symidx,
           int info, int secidx, ElfW(Addr) value, unsigned long size)
   {
     struct obj_symbol *sym;   // 计算符号的hash值
     unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
     int n_type = ELFW(ST_TYPE)(info);
     int n_binding = ELFW(ST_BIND)(info);
    
     // 开始symtab为空的所以肯定找不到,一项一项向里加。
     for (sym = f->symtab[hash]; sym; sym = sym->next)
       if (f->symbol_cmp(sym->name, name) == 0)
       {  // 找到符号对应的值,保存原有的值。
         int o_secidx = sym->secidx;
         int o_info = sym->info;
         int o_type = ELFW(ST_TYPE)(o_info);
         int o_binding = ELFW(ST_BIND)(o_info);
        
         /* A redefinition! Is it legal? */
        
         if (secidx == SHN_UNDEF)
           return sym;
         else if (o_secidx == SHN_UNDEF)
           goto found;
         else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL)
         {
           /* Cope with local and global symbols of the same name
           in the same object file, as might have been created
           by ld -r. The only reason locals are now seen at this
           level at all is so that we can do semi-sensible things
           with parameters. */
          
           struct obj_symbol *nsym, **p;
          
           nsym = arch_new_symbol();//生成一个新的sym加到hash表中
           nsym->next = sym->next;
           nsym->ksymidx = -1;
          
           /* Excise the old (local) symbol from the hash chain. */
           for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
             continue;
           *p = sym = nsym;
           goto found;
         }
         else if (n_binding == STB_LOCAL)
         {
           // Another symbol of the same name has already been defined.
           // Just add this to the local table.
           sym = arch_new_symbol();
           sym->next = NULL;
           sym->ksymidx = -1;//加到本地结构中
           f->local_symtab[symidx] = sym;
           goto found;
         }
         else if (n_binding == STB_WEAK)
           return sym;
         else if (o_binding == STB_WEAK)
           goto found;
           // Don't unify COMMON symbols with object types the programmer
           // doesn't expect.
         else if (secidx == SHN_COMMON
           && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
           return sym;
         else if (o_secidx == SHN_COMMON
           && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
           goto found;
         else
         {
           // Don't report an error if the symbol is coming from
           // the kernel or some external module. */
           if (secidx <= SHN_HIRESERVE)
             error("%s multiply defined", name);
           return sym;
         }
       }
      
       /* Completely new symbol. */
       //开始的时候都会走到这里来
       sym = arch_new_symbol();//创建新的sym结构加入到hash表中
       sym->next = f->symtab[hash];
       f->symtab[hash] = sym;
       sym->ksymidx = -1;
      
       if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1)
       {
         if (symidx >= f->local_symtab_size)
           error("local symbol %s with index %ld exceeds local_symtab_size %ld",
           name, (long) symidx, (long) f->local_symtab_size);
         else
           f->local_symtab[symidx] = sym;//如果是文件内使用的加入到此结构中
       }
      
   found: // 以后用kernel中的symbol解析时会走到此处,value会添上正确的值
       sym->name = name;
       sym->value = value;
       sym->size = size;
       sym->secidx = secidx;
       sym->info = info;
       sym->r_type = 0; /* should be R_arch_NONE for all arch */
       return sym;
   }
   ...

5.比较kernel版本和module的版本
   ...
   /* Version correspondence? */
   k_version = get_kernel_version(k_strversion);
   m_version = get_module_version(f, m_strversion);
   if (m_version == -1)
   {
     error("couldn't find the kernel version the module was compiled for");
     goto out;
   }
   k_crcs = is_kernel_checksummed();  // kernel的symbol是否含有RXXXXXXX的校验
   m_crcs = is_module_checksummed(f); // module的symbol是否含有RXXXXXX的校验
   // 如果都含有,就不必比较version了
   if ((m_crcs == 0 || k_crcs == 0) &&
     strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0)
   {
     if (flag_force_load)      // -f选项会强行加载
     {
       lprintf("Warning: kernel-module version mismatchn"
         "t%s was compiled for kernel version %sn"
         "twhile this kernel is version %s",
         filename, m_strversion, k_strversion);
     }
     else
     {
       if (!quiet)
         error("kernel-module version mismatchn"
         "t%s was compiled for kernel version %sn"
         "twhile this kernel is version %s.",
         filename, m_strversion, k_strversion);
       goto out;
     }
   }
   if (m_crcs != k_crcs)  // 两者不一样
   // 就不使用strcmp比较符号名字,而用ncv_strcmp比较使得printk和pirntk_RXXXXX
   // 是相同的。还有重新创建hash表
   obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
   ...

6.add_kernel_symbols 替换.o中的symbol为ksyms中的符号值
   ...
   static void add_kernel_symbols(struct obj_file *f)
   {
     struct module_stat *m;
     size_t i, nused = 0;
    
     /* 使用已有的module中的symbol */
     for (i = 0, m = module_stat; i < n_module_stat; ++i, ++m)
     {
       if (m->nsyms &&
         add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms))
       {
         m->status = 1 /* used */, ++nused;
       }
       n_ext_modules_used = nused;
       /* 使用kernel export的symbol */
       if (nksyms)
       {
         add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
       }
     }
   }
  
   /*
   * Conditionally add the symbols from the given symbol set
   * to the new module.
   */
   static int add_symbols_from(struct obj_file *f, int idx,
                 struct module_symbol *syms, size_t nsyms)
   {
     struct module_symbol *s;
     size_t i;
     int used = 0;
    
     for (i = 0, s = syms; i < nsyms; ++i, ++s)
     {
     /*
     * Only add symbols that are already marked external.
     * If we override locals we may cause problems for
     * argument initialization.
     * We will also create a false dependency on the module.
     */
       struct obj_symbol *sym;
       //表中是否有需要此名字的的symbol
       sym = obj_find_symbol(f, (char *) s->name);
       if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL)
       {
         sym = obj_add_symbol(f, (char *) s->name, -1,
           ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), idx, s->value, 0);
           /*将hash表中的待解析的symbol的value添成正确的值
           * Did our symbol just get installed?
           * If so, mark the module as "used".
           */
         if (sym->secidx == idx)
           used = 1;
       }
     }
     return used;
   }
   ...

7.create_this_module(f, m_name) 生成module结构,加入module_list中。
   ...
   static int create_this_module(struct obj_file *f, const char *m_name)
   {
     struct obj_section *sec;
     // 创建一个.this节,节大小为struct module的大小
     sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long, sizeof(struct module));
     memset(sec->contents, 0, sizeof(struct module));
     // 向hash表中添加一个__this_module的符号
   obj_add_symbol(f, "__this_module",-1,
   ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT),sec->idx, 0, sizeof(struct module));
     // 创建.kstrtab节
     obj_string_patch(f, sec->idx, offsetof(struct module, name), m_name);
     return 1;
   }
   ...

8.obj_check_undefineds 检查是否还有un_def的symbol
   ...
   obj_check_undefineds(struct obj_file *f, int quiet)
   {
     unsigned long i;
     int ret = 1;
     for (i = 0; i < HASH_BUCKETS; ++i)
     {
       struct obj_symbol *sym;
       for (sym = f->symtab; sym ; sym = sym->next)
       {
         if (sym->secidx == SHN_UNDEF)
         {
           if (ELFW(ST_BIND)(sym->info) == STB_WEAK)
           {
             sym->secidx = SHN_ABS;
             sym->value = 0;
           }
           else if (sym->r_type) /* assumes R_arch_NONE is 0 on all arch */
           {
             if (!quiet)
               error("unresolved symbol %s", sym->name);
             ret = 0;
           }
         }
       }
     }
     return ret;
   }
   ...
  
9.add_archdata 添加结构相关的section,不过i386没什么用。

10.add_kallsyms 如果symbol使用的都是kernel提供的,就添加一个.kallsyms section

11.obj_load_size 计算module的大小
   ...
   /* Module has now finished growing; find its size and install it. */
   m_size = obj_load_size(f); /* DEPMOD */
   ...
   obj_load_size (struct obj_file *f)
   {
     unsigned long dot = 0;
     struct obj_section *sec;
    
     /* Finalize the positions of the sections relative to one another. */
     for (sec = f->load_order; sec ; sec = sec->load_next)
     {  // 按照装载的顺序,计算module的大小。
       ElfW(Addr) align;
       align = sec->header.sh_addralign;
       if (align && (dot & (align - 1)))
         dot = (dot | (align - 1)) + 1;
       sec->header.sh_addr = dot;
       dot += sec->header.sh_size;
     }
     return dot;
   }
   ...

12.create_module 调用sys_create_module系统调用创建模块,分配module的空间

13.obj_relocate
   ...
   int obj_relocate (struct obj_file *f, ElfW(Addr) base)
   {  // base就是create_module时分配的地址m_addr
     int i, n = f->header.e_shnum;
     int ret = 1;
    
     /* Finalize the addresses of the sections. */
     // 将各个section的sh_addr(原来是相对地址)加上此基地址
     arch_finalize_section_address(f, base);
    
     /* And iterate over all of the relocations. */
     for (i = 0; i < n; ++i)
     {
       struct obj_section *relsec, *symsec, *targsec, *strsec;
       ElfW(RelM) *rel, *relend;
       ElfW(Sym) *symtab;
       const char *strtab;
      
       relsec = f->sections;//找到重定位section
       if (relsec->header.sh_type != SHT_RELM)
         continue;
       // relse的sh_link指向.symtab节,sh_info指向.text节
       symsec = f->sections[relsec->header.sh_link];
       targsec = f->sections[relsec->header.sh_info];
       // symtab节的sh_link指向符号字符串那节。
       strsec = f->sections[symsec->header.sh_link];
      
       //获得相应section的内容
       rel = (ElfW(RelM) *)relsec->contents;
       relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
       symtab = (ElfW(Sym) *)symsec->contents;
       strtab = (const char *)strsec->contents;
      
       for (; rel < relend; ++rel)
       {
         ElfW(Addr) value = 0;
         struct obj_symbol *intsym = NULL;
         unsigned long symndx;
         ElfW(Sym) *extsym = 0;
         const char *errmsg;
        
         /* Attempt to find a value to use for this relocation. */
         // 根据rel的描述找到需要重定位的符号索引值
         symndx = ELFW(R_SYM)(rel->r_info);
        
         // 同上,找到符号的描述,存入intsym变量中。
         if (symndx)
         {
           /* Note we've already checked for undefined symbols. */
           extsym = &symtab[symndx];
           if (ELFW(ST_BIND)(extsym->st_info) == STB_LOCAL)
           {
             /* Local symbols we look up in the local table to be sure
               we get the one that is really intended. */
             intsym = f->local_symtab[symndx];
           }
           else
           {
             /* Others we look up in the hash table. */
             const char *name;
             if (extsym->st_name)
               name = strtab + extsym->st_name;
             else
               name = f->sections[extsym->st_shndx]->name;
             intsym = obj_find_symbol(f, name);
           }
           // 虽然有些函数的symbol的value已经被填写过了,
   // 但是rel中还有象.rodata节这样的需要relocate的符号,
   // 因此他的value是0+section[.rodata]->header->sh_addr的值
           value = obj_symbol_final_value(f, intsym);
         }
        
   #if SHT_RELM == SHT_RELA
   #if defined(__alpha__) && defined(AXP_BROKEN_GAS)
         /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */
         if (!extsym ||
   !extsym->st_name ||
   ELFW(ST_BIND)(extsym->st_info) != STB_LOCAL
   )
   #endif
           value += rel->r_addend;
   #endif
         /* Do it! */  // 此函数将.text中需要relocate的地方都填写正确value
         // 非常重要的一个函数
         switch (arch_apply_relocation(f,targsec,symsec,intsym,rel,value))
         {
     // fbjfile结构,targsec:.text节,symsec:.symtab节,rel:.rel结构,value:绝对地址
         case obj_reloc_ok:
           break;
         case obj_reloc_overflow:
           errmsg = "Relocation overflow";
           goto bad_reloc;
         case obj_reloc_dangerous:
           errmsg = "Dangerous relocation";
           goto bad_reloc;
         case obj_reloc_unhandled:
           errmsg = "Unhandled relocation";
           goto bad_reloc;
         case obj_reloc_constant_gp:
           errmsg = "Modules compiled with -mconstant-gp cannot be loaded";
           goto bad_reloc;
   bad_reloc:
           if (extsym)
           {
             error("%s of type %ld for %s", errmsg,
               (long)ELFW(R_TYPE)(rel->r_info),
               strtab + extsym->st_name);
           }
           else
           {
             error("%s of type %ld", errmsg,(long)ELFW(R_TYPE)(rel->r_info));
           }
           ret = 0;
           break;
         }
       }
     }
  
     /* Finally, take care of the patches. */
     if (f->string_patches)
     {
       struct obj_string_patch_struct *p;
       struct obj_section *strsec;
       ElfW(Addr) strsec_base;
       strsec = obj_find_section(f, ".kstrtab");
       strsec_base = strsec->header.sh_addr;
      
       for (p = f->string_patches; p ; p = p->next)
       {
         struct obj_section *targsec = f->sections[p->reloc_secidx];
         *(ElfW(Addr) *)(targsec->contents + p->reloc_offset)
           = strsec_base + p->string_offset;
       }
     }
     if (f->symbol_patches)
     {
       struct obj_symbol_patch_struct *p;
       for (p = f->symbol_patches; p; p = p->next)
       {
         struct obj_section *targsec = f->sections[p->reloc_secidx];
         *(ElfW(Addr) *)(targsec->contents + p->reloc_offset)
           = obj_symbol_final_value(f, p->sym);
       }
     }
     return ret;
   }
   ...

14.init_module 初始化有create_module生成的module,是由sys_init_module系统调用实现的。
   ...
   static int init_module(const char *m_name, struct obj_file *f,
              unsigned long m_size, const char *blob_name,
              unsigned int noload, unsigned int flag_load_map)
   {  // 一般情况下后三个参数都是0
     struct module *module;
     struct obj_section *sec;
     void *image;
     int ret = 0;
     tgt_long m_addr;
     // 找到原来用create_this_module生成的.this节
     sec = obj_find_section(f, ".this");
     module = (struct module *) sec->contents;
     m_addr = sec->header.sh_addr;//base基地址
    
     module->size_of_struct = sizeof(*module);  // module结构大小
     module->size = m_size;         // module总共的大小
     module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
     sec = obj_find_section(f, "__ksymtab");
     if (sec && sec->header.sh_size)     // 模块自己export的symbol
     {
       module->syms = sec->header.sh_addr;
       module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
     }
     if (n_ext_modules_used)         // 填写此module依靠的模块
     {
       sec = obj_find_section(f, ".kmodtab");
       module->deps = sec->header.sh_addr;
       module->ndeps = n_ext_modules_used;
     }
     // 填写init_module,cleanup_module的地址
     module->init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
     module->cleanup = obj_symbol_final_value(f,obj_find_symbol(f, "cleanup_module"));
    
     // exception_table_entry的地址,一般没有
     sec = obj_find_section(f, "__ex_table");
     if(sec)
     {
       module->ex_table_start = sec->header.sh_addr;
       module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
     }
     sec = obj_find_section(f, ".text.init");
     if (sec)  // 这个runsize不知道是什么东西,一般没有
     {
       module->runsize = sec->header.sh_addr - m_addr;
     }
     sec = obj_find_section(f, ".data.init");
     if (sec)
     {
       if (!module->runsize ||
         module->runsize > sec->header.sh_addr - m_addr)
         module->runsize = sec->header.sh_addr - m_addr;
     }
     sec = obj_find_section(f, ARCHDATA_SEC_NAME);
     if (sec && sec->header.sh_size)
     {
       module->archdata_start = sec->header.sh_addr;
       module->archdata_end = module->archdata_start + sec->header.sh_size;
     }
     sec = obj_find_section(f, KALLSYMS_SEC_NAME);
     if (sec && sec->header.sh_size)
     {
       module->kallsyms_start = sec->header.sh_addr;
       module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
     }
     if (!arch_init_module(f, module))  // i386此函数直接返回
       return 0;
     /*
     * Whew! All of the initialization is complete.
     * Collect the final module image and give it to the kernel.
     */
     image = xmalloc(m_size);
     obj_create_image(f, image);
     // 现在用户空间分配module的image的内存,然后将
     // 各个section的内容拷入image中,包括原文件中的section和后来构造的section
    
     if (flag_load_map)
       print_load_map(f);
     if (blob_name)
     {
       int fd, l;
       fd = open(blob_name, O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
       if (fd < 0)
       {
         error("open %s failed %m", blob_name);
         ret = -1;
       }
       else
       {
         if ((l = write(fd, image, m_size)) != m_size)
         {
           error("write %s failed %m", blob_name);
           ret = -1;
         }
         close(fd);
       }
     }
     if (ret == 0 && !noload)    // 调用系统调用完成最后的insmod工作。
     {
       fflush(stdout); /* Flush any debugging output */
       ret = sys_init_module(m_name, (struct module *) image);
       if (ret)
       {
         error("init_module: %m");
         lprintf("Hint: insmod errors can be caused by incorrect module parameters, "
           "including invalid IO or IRQ parameters");
       }
     }
     free(image);
     return ret == 0;
   }
   ...

最后清理一下思路。
   1.get_kernel_info函数负责取得kernel中先以注册的modules,放入module_stat中.
   2.set_ncv_prefix(NULL);判断symbolname中是否有前缀,象_smp之类,一般没有。
   3.检查是否已有同名的module。
   4.obj_load,将.o文件读入到struct obj_file结构f中。
   5.比较kernel版本和module的版本,在版本的判断中,不是想象中的那样简单,还有是否有checksum的逻辑关系。
   6.add_kernel_symbols替换.o中的symbol为ksyms中的符号值
   7.create_this_module(f,m_name)生成module结构,加入module_list中。
   8.obj_check_undefineds检查是否还有un_def的symbol
   9.add_archdata添加结构相关的section,不过i386没什么用。
   10.add_kallsyms如果symbol使用的都是kernel提供的,就添加一个.kallsyms section
   11.obj_load_size计算module的大小
   12.create_module调用sys_create_module系统调用创建模块,分配module的空间
   13.obj_relocate重定位module文件中.text中的地址
   14.init_module先在用户空间创建module结构的image影响,是由sys_init_module系统调用实现向kernel的copy。

   经过对insmod主要流程的分析,发现原来的理解没有偏差,那问什么会被成功加载呢?后来仔细用gdb跟了一把,发现所有对symtab的操作都被调过,因为它一直为NULL嘛,可是发现在没有symtab的情况下,2.4.6竟然一个判断都没有,一路平稳的跑了下来,然后在module结构init和cleanup指针都为NULL的情况下被加载成功。
   而2.4.10的modutils中的insmod对strip后的module加载后有除零的异常,在obj_load函数实现中有
      nsyms = symtab->header.sh_size / symtab->header.sh_entsize;
   由于module中根本没有symtab所以这个结构中的所有值都为0。


原文出处:
前 一 篇:
下 一 篇:
Copyright© 2004-2006 青龙山庄 Email:dragon_hn@sohu.com