Modultils工具源码分析之insmod篇 (7)

回到INSMOD_MAIN函数。

 

1638              /* Version correspondence?  */

1639              k_version = get_kernel_version(k_strversion);

1640              m_version = get_module_version(f, m_strversion);

1641              if (m_version == -1) {

1642                     error("couldn't find the kernel version the module was compiled for");

1643                     goto out;

1644              }

1645

1646              k_crcs = is_kernel_checksummed();

1647              m_crcs = is_module_checksummed(f);

1648              if ((m_crcs == 0 || k_crcs == 0) &&

1649                  strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) {

1650                     if (flag_force_load) {

1651                            lprintf("Warning: kernel-module version mismatch/n"

1652                                "/t%s was compiled for kernel version %s/n"

1653                                   "/twhile this kernel is version %s",

1654                                   filename, m_strversion, k_strversion);

1655                     } else {

1656                            if (!quiet)

1657                                   error("kernel-module version mismatch/n"

1658                                         "/t%s was compiled for kernel version %s/n"

1659                                         "/twhile this kernel is version %s.",

1660                                         filename, m_strversion, k_strversion);

1661                            goto out;

1662                     }

1663                          }

1664

1665        if (m_crcs != k_crcs)

1666                     obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);

 

初步处理了模块文件后,在进一步处理内核已知符号前,首先要检查内核与模块的版本。查找内核与模块版本的函数也在insmod.c中,比较简单,将它列如下,不做说明了。

Insmod——get_kernel_version函数

107  /* Get the kernel version in the canonical integer form.  */

108 

109  static int get_kernel_version(char str[STRVERSIONLEN])

110  {

111         char *p, *q;

112         int a, b, c;

113

114         strncpy(str, uts_info.release, STRVERSIONLEN);

115         p = uts_info.release;

116

117         a = strtoul(p, &p, 10);

118         if (*p != '.')

119                return -1;

120         b = strtoul(p + 1, &p, 10);

121         if (*p != '.')

122                return -1;

123         c = strtoul(p + 1, &q, 10);

124         if (p + 1 == q)

125                return -1;

126

127         return a << 16 | b << 8 | c;

128          }

Insmod——get_module_version函数

557  /* Get the module's kernel version in the canonical integer form.  */

558  static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN])

559  {

560         int a, b, c;

561         char *p, *q;

562 

563         if ((p = get_modinfo_value(f, "kernel_version")) == NULL) {

564                struct obj_symbol *sym;

565

566         m_has_modinfo = 0;

567                if ((sym = obj_find_symbol(f, "kernel_version")) == NULL)

568                       sym = obj_find_symbol(f, "__module_kernel_version");

569                if (sym == NULL)

570                       return -1;

571                p = f->sections[sym->secidx]->contents + sym->value;

572         } else

573                m_has_modinfo = 1;

574

575         strncpy(str, p, STRVERSIONLEN);

576

578         a = strtoul(p, &p, 10);

579         if (*p != '.')

580                return -1;

581         b = strtoul(p + 1, &p, 10);

582         if (*p != '.')

583                return -1;

584         c = strtoul(p + 1, &q, 10);

585         if (p + 1 == q)

586                return -1;

587

588         return a << 16 | b << 8 | c;

589          }

 

接下来还要测试内核和模块是否使用了版本的附加信息。这2个函数也在同一文件里。

Insmod——is_kernel_checksummed函数

590  /* Return the kernel symbol checksum version, or zero if not used. */

591  static int is_kernel_checksummed(void)

592  {

593         struct module_symbol *s;

594         size_t i;

595

596         /*

597         * Using_Versions might not be the first symbol,

598         * but it should be in there.

599         */

600         for (i = 0, s = ksyms; i < nksyms; ++i, ++s)

601                if (strcmp((char *) s->name, "Using_Versions") == 0)

602                       return s->value;

603

604         return 0;

605  }

Insmod——is_module_checksummed函数

607  static int is_module_checksummed(struct obj_file *f)

608  {

609         if (m_has_modinfo) {

610                const char *p = get_modinfo_value(f, "using_checksums");

611                if (p)

612                       return atoi(p);

613                else

614                       return 0;

615         } else

616                return obj_find_symbol(f, "Using_Versions") != NULL;

617          }

 

如果不使用附加信息,这2个函数都返回01648~1662行,如果版本不对,而且没有强制加载,到此就要出错退出。否则,打印警告信息,继续执行。

如果内核或模块之一使用附加信息,而且附加信息不一致,就要通过obj_set_symbol_compare设置新的符号比较和hash函数,并根据新的hash函数重构symtab表。这个函数在obj_common.c中。

Insmod——obj_set_symbol_compare函数

62    void

63    obj_set_symbol_compare (struct obj_file *f,

64                  int (*cmp)(const char *, const char *),

65                  unsigned long (*hash)(const char *))

66    {

67      if (cmp)

68        f->symbol_cmp = cmp;

69      if (hash)

70        {

71          struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;

72          int i;

73

74          f->symbol_hash = hash;

75

76          memcpy(tmptab, f->symtab, sizeof(tmptab));

77          memset(f->symtab, 0, sizeof(f->symtab));

78

79          for (i = 0; i < HASH_BUCKETS; ++i)

80           for (sym = tmptab[i]; sym ; sym = next)

81             {

82               unsigned long h = hash(sym->name) % HASH_BUCKETS;

83               next = sym->next;

84               sym->next = f->symtab[h];

85               f->symtab[h] = sym;

86             }

87        }

88             }

 

完成这些处理后,又到了一个关键操作。

 

1667              /* Let the module know about the kernel symbols.  */

1668                          add_kernel_symbols(f);

 

add_kernel_symbols函数也在insmod.c里。

Insmod——add_kernel_symbols函数

266  static void add_kernel_symbols(struct obj_file *f)

267  {

268         struct module_stat *m;

269         size_t i, nused = 0;

270

271         /* Add module symbols first.  */

272         for (i = 0, m = module_stat; i < n_module_stat; ++i, ++m)

273                if (m->nsyms &&

274                    add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms))

275                       m->status = 1 /* used */, ++nused;

276         n_ext_modules_used = nused;

277

278         /* And finally the symbols from the kernel proper.  */

279         if (nksyms)

280                add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);

281          }

 

函数首先处理已加载模块。每个模块的符号通过add_symbols_from函数处理,该函数也在insmod.c中。

Insmod——add_symbol_from函数

233  static int add_symbols_from(struct obj_file *f, int idx,

234                           struct module_symbol *syms, size_t nsyms)

235  {

236         struct module_symbol *s;

237         size_t i;

238         int used = 0;

239 

240         for (i = 0, s = syms; i < nsyms; ++i, ++s) {

241                /*

242                * Only add symbols that are already marked external.

243                * If we override locals we may cause problems for

244                * argument initialization.

245                * We will also create a false dependency on the module.

246                */

247                struct obj_symbol *sym;

248

249                sym = obj_find_symbol(f, (char *) s->name);

250                if (sym && !ELFW(ST_BIND) (sym->info) == STB_LOCAL) {

251                       sym = obj_add_symbol(f, (char *) s->name, -1,

252                                ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),

253                                   idx, s->value, 0);

254                       /*

255                       * Did our symbol just get installed?

256                       * If so, mark the module as "used".

257                       */

258                       if (sym->secidx == idx)

259                              used = 1;

260                }

261         }

262

263         return used;

263          }

 

函数首先在obj_filesymtab中查找与模块特定符号同名的符号。只要这个符号不是local属性的,就要通过obj_add_symbol测试、添加符号。注意传给函数info参数为ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),也就是加入符号的属性是global,类型是STT_NOTYPE。而symidx参数则是-1 这样在obj_add_symbol函数里,只有原来的符号的属性是weak,或者原来符号的段序号为SHN_COMMON时,才会进行符号替换。如果发生了符号替换,将used置为1,表示模块已经被使用。并且该符号被新的段序号标识(注意原来段序号为SHN_COMMON的符号,现在它持有的段序号被改掉了,不再是SHN_COMMON,记住这点很重要!)。

SHN_HIRESERVE对应系统保留段的上限,因此,add_kernel_symbols使用SHN_HIRESERVE以上的段来保存已加载模块的符号。注意274280行。在这个函数结束的时候,n_ext_modules_used记载了被使用模块的数目。

回到INSMOM_MAIN

 

1670        /* Allocate common symbols, symbol tables, and string tables.

1671        *

1672        * The calls marked DEPMOD indicate the bits of code that depmod

1673        * uses to do a pseudo relocation, ignoring undefined symbols.

1674        * Any changes made to the relocation sequence here should be

1675        * checked against depmod.

1676        */

1677    #ifdef COMPAT_2_0

1678        if (k_new_syscalls

1679            ? !create_this_module(f, m_name)

1680            : !old_create_mod_use_count(f))

1681            goto out;

1682    #else

1683        if (!create_this_module(f, m_name))

1684            goto out;

1685    #endif

 

COMPAT_2_0表示支持2.0的内核(如果是2.0以前的内核,这时k_new_syscalls0),这样就要调用old_create_mod_use_count了,这里我们不看它了。2.0以后内核则使用函数create_this_module,这个函数也在同一文件里。

Insmod——create_this_module函数

429  static int create_this_module(struct obj_file *f, const char *m_name)

430  {

431         struct obj_section *sec;

432 

433         sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long,

434                                            sizeof(struct module));

435  memset(sec->contents, 0, sizeof(struct module));

436

437         obj_add_symbol(f, "__this_module", -1, ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT),

438                       sec->idx, 0, sizeof(struct module));

439

440  obj_string_patch(f, sec->idx, offsetof(struct module, name), m_name);

441

442         return 1;

443          }

 

首先通过函数obj_create_alloced_section_first分配一个段头。函数在obj_common.c中。

Insmod——obj_create_alloced_section_first函数

290  struct obj_section *

291  obj_create_alloced_section_first (struct obj_file *f, const char *name,

292                                unsigned long align, unsigned long size)

293  {

294    int newidx = f->header.e_shnum++;

295    struct obj_section *sec;

296

297    f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec));

298    f->sections[newidx] = sec = arch_new_section();

299

300    memset(sec, 0, sizeof(*sec));

301    sec->header.sh_type = SHT_PROGBITS;

302    sec->header.sh_flags = SHF_WRITE|SHF_ALLOC;

303    sec->header.sh_size = size;

304    sec->header.sh_addralign = align;

305    sec->name = name;

306    sec->idx = newidx;

307    if (size)

308      sec->contents = xmalloc(size);

309

310    sec->load_next = f->load_order;

311    f->load_order = sec;

312    if (f->load_order_search_start == &f->load_order)

313      f->load_order_search_start = &sec->load_next;

314

315    return sec;

316          }

 

这个函数是给obj_file添加一个SHT_PROGBITS段,SHT_PROGBITS表示段里保存的信息由程序定义的,它的格式和含义由程序唯一确定。这个段的名字为“.this”,与tgt_sizeof_long(实际上就是sizeof(long))对齐,长度为sizeof(struct module)。显然准备在这个段里存放module结构。

然后回到create_this_module,添加了.this段后,再通过obj_add_symbolobj_file添加符号,符号的名字是“__this_module”,属性是STB_LOCAL,类型是STT_OBJECT,注意调用obj_add_symbol时,symidx-1,所以这个符号不加入local_symtab中(因为这个符号不是文件原有的)。最后通过obj_string_patch将模块名加入字符段。这个函数在./modutils-2.4.0/obj/obj_reloc.c中。

Insmod——obj_string_patch函数

33    int

34    obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,

35                  const char *string)

36    {

37      struct obj_string_patch_struct *p;

38      struct obj_section *strsec;

39      size_t len = strlen(string)+1;

40      char *loc;

41

42      p = xmalloc(sizeof(*p));

43      p->next = f->string_patches;

44      p->reloc_secidx = secidx;

45      p->reloc_offset = offset;

46      f->string_patches = p;

47

48      strsec = obj_find_section(f, ".kstrtab");

49      if (strsec == NULL)

50        {

51          strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);

52          p->string_offset = 0;

53          loc = strsec->contents;

54        }

55      else

56        {

57          p->string_offset = strsec->header.sh_size;

58          loc = obj_extend_section(strsec, len);

59        }

60      memcpy(loc, string, len);

61

62      return 1;

63             }

 

为了能在obj_file里引用模块名(回忆一下,每个字符串要么与段名对应,要么对应于一个符号。而在这里,模块名没有对应的符号或段),因此,obj_file引入了obj_string_patch_struct结构。收留这些孤独的字符串。这个结构的定义在./modutils-2.40/include/obj.h中。

Insmod——obj_string_patch_struct函数

121  struct obj_string_patch_struct

122  {

123    struct obj_string_patch_struct *next;

124    int reloc_secidx;

125    ElfW(Addr) reloc_offset;

126    ElfW(Addr) string_offset;

127          };

 

obj_extent_section则在obj_common.c中。

Insmod——obj_extend_section函数

318  void *

319  obj_extend_section (struct obj_section *sec, unsigned long more)

320  {

321    unsigned long oldsize = sec->header.sh_size;

322    sec->contents = xrealloc(sec->contents, sec->header.sh_size += more);

323    return sec->contents + oldsize;

324  }

 

再一次辗转回到INSMOD_MAIN中。

 

1687              if (!obj_check_undefineds(f, quiet))     /* DEPMOD, obj_clear_undefineds */

1688                     goto out;

1689              obj_allocate_commons(f);     /* DEPMOD */

 

这次使用obj_check_undefineds检查是否有未解析的符号,该函数在obj_reloc.c中。

Insmod——obj_check_undefineds函数

81    int

82    obj_check_undefineds(struct obj_file *f, int quiet)

83    {

84      unsigned long i;

85      int ret = 1;

86

87      for (i = 0; i < HASH_BUCKETS; ++i)

88        {

89          struct obj_symbol *sym;

90          for (sym = f->symtab[i]; sym ; sym = sym->next)

91           if (sym->secidx == SHN_UNDEF)

92             {

93               if (ELFW(ST_BIND)(sym->info) == STB_WEAK)

94                 {

95                  sym->secidx = SHN_ABS;

96                  sym->value = 0;

97                 }

98               else if (sym->r_type) /* assumes R_arch_NONE is 0 on all arch */

99                 {

100                if (!quiet)

101                       error("unresolved symbol %s", sym->name);

102                ret = 0;

103               }

104           }

105      }

106

107    return ret;

108          }

 

elf文档里指明,对于属性为weak的符号,如果未能解析(resolve),链接器是不会多非周折的,只是将它置为0了事。段序号为SHN_ABS的段,表示其内容不受重定位影响。

如果符号的属性不是weak,而且需要重定位操作(R_arch_NONE0,表示不需要重定位操作,在symtab中的local符号,就包含这样的属性)。那就出错了,也就是我们常在cc++里看到的unresolved symbol这样的错误。

接下来需要处理的是SHN_COMMON类型的符号,这些符号是未分配资源的。结构中的st_value给出的是边界对齐要求,函数obj_allocate_commonobj_reloc.c中。

Insmod——obj_allocate_commons函数

126  void

127  obj_allocate_commons(struct obj_file *f)

128  {

129    struct common_entry

130    {

131      struct common_entry *next;

132      struct obj_symbol *sym;

133    } *common_head = NULL;

134

135    unsigned long i;

136

137    for (i = 0; i < HASH_BUCKETS; ++i)

138      {

139        struct obj_symbol *sym;

140        for (sym = f->symtab[i]; sym ; sym = sym->next)

141         if (sym->secidx == SHN_COMMON)

142         {

143                /* Collect all COMMON symbols and sort them by size so as to

144                minimize space wasted by alignment requirements.  */

145                {

146                       struct common_entry **p, *n;

147                       for (p = &common_head; *p ; p = &(*p)->next)

148                              if (sym->size <= (*p)->sym->size)

149                                     break;

150

151                       n = alloca(sizeof(*n));

152                       n->next = *p;

153                       n->sym = sym;

154                       *p = n;

155                }

156         }

157      }

158

159    for (i = 1; i < f->local_symtab_size; ++i)

160    {

161        struct obj_symbol *sym = f->local_symtab[i];

162        if (sym && sym->secidx == SHN_COMMON)

163        {

164         struct common_entry **p, *n;

165         for (p = &common_head; *p ; p = &(*p)->next)

166                if (sym == (*p)->sym)

167                       break;

168                else if (sym->size < (*p)->sym->size)

169                {

170                       n = alloca(sizeof(*n));

171                       n->next = *p;

172                       n->sym = sym;

173                       *p = n;

174                       break;

175                }

176         }

177     }

178

179    if (common_head)

180      {

181        /* Find the bss section.  */

182        for (i = 0; i < f->header.e_shnum; ++i)

183         if (f->sections[i]->header.sh_type == SHT_NOBITS)

184                  break;

185

186        /* If for some reason there hadn't been one, create one.  */

187        if (i == f->header.e_shnum)

188        {

189           struct obj_section *sec;

190

191           f->sections = xrealloc(f->sections, (i+1) * sizeof(sec));

192           f->sections[i] = sec = arch_new_section();

193           f->header.e_shnum = i+1;

194

195           memset(sec, 0, sizeof(*sec));

196           sec->header.sh_type = SHT_PROGBITS;

197           sec->header.sh_flags = SHF_WRITE|SHF_ALLOC;

198           sec->name = ".bss";

199           sec->idx = i;

200        }

201

202        /* Allocate the COMMONS.  */

203        {

204         ElfW(Addr) bss_size = f->sections[i]->header.sh_size;

205         ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;

206         struct common_entry *c;

207

208         for (c = common_head; c ; c = c->next)

209           {

210             ElfW(Addr) align = c->sym->value;

211

212             if (align > max_align)

213               max_align = align;

214             if (bss_size & (align - 1))

215               bss_size = (bss_size | (align - 1)) + 1;

216

217             c->sym->secidx = i;

218             c->sym->value = bss_size;

219

220             bss_size += c->sym->size;

221           }

222

223         f->sections[i]->header.sh_size = bss_size;

224         f->sections[i]->header.sh_addralign = max_align;

225        }

226      }

227

228    /* For the sake of patch relocation and parameter initialization,

229       allocate zeroed data for NOBITS sections now.  Note that after

230       this we cannot assume NOBITS are really empty.  */

231    for (i = 0; i < f->header.e_shnum; ++i)

232      {

233        struct obj_section *s = f->sections[i];

234        if (s->header.sh_type == SHT_NOBITS)

235         {

236           if (s->header.sh_size)

237             s->contents = memset(xmalloc(s->header.sh_size),

238                              0, s->header.sh_size);

239           else

240             s->contents = NULL;

241           s->header.sh_type = SHT_PROGBITS;

242         }

243      }

244          }

 

137行的for循环,找出symtab(包含了globalweaklocal符号)中所有类型为SHN_COMMON的符号,并按大小排序,注释里说这样可以减少因对齐要求造成的空间浪费。

159行的for循环,则找出local_symtab(只包含local符号,但可能与symtab中重复)中所有所持段序号为SHN_COMMON的符号,也按大小排序。166~167行就是为了防止符号重复。

如果所持段序号为SHN_COMMON的符号,那么在182行的循环里查找.bss段。SHT_NOBITS表明这个段不占据文件空间,但其含义与SHT_PROGBITS相同。这正是.bss段的类型。如果找不到.bss段,就通过187~200行添加一个,注意添加段的类型现在变为SHT_PROGBITS,属性是SHF_WRITESHF_ALLOC

203~225行,计算所有所持段序号为SHN_COMMON的符号占据的大小和段最终要求的对齐边界。然后在231行的循环里,为SHT_NOBITS段分配资源,并将它设为SHN_PROGBITS。注意现在这些符号的段序号也给改变了,记住这一点。从这里可以看到,文件定义的静态、全局的变量,还有引用的外部变量,最终都放在了.bss段中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值