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

来源:互联网 发布:数据的逻辑结构包括 编辑:程序博客网 时间:2024/05/17 22:55

回到INSMOD_MAIN里。

 

1893       if (!obj_relocate(f, m_addr)) {      /* DEPMOD */

1894              if (!noload)

1895                     delete_module(m_name);

1896              goto out;

1897      }

 

函数obj_relocate./modutils-2.4.0/obj/obj_reloc.c中。

Insmod——obj_relocate函数

269  int

270  obj_relocate (struct obj_file *f, ElfW(Addr) base)

271  {

272    int i, n = f->header.e_shnum;

273    int ret = 1;

274

275    /* Finalize the addresses of the sections.  */

276

278    arch_finalize_section_address(f, base);

279

280    /* And iterate over all of the relocations.  */

281

282    for (i = 0; i < n; ++i)

283    {

284        struct obj_section *relsec, *symsec, *targsec, *strsec;

285        ElfW(RelM) *rel, *relend;

286        ElfW(Sym) *symtab;

287        const char *strtab;

288

289        relsec = f->sections[i];

290        if (relsec->header.sh_type != SHT_RELM)

291         continue;

292

293        symsec = f->sections[relsec->header.sh_link];

294        targsec = f->sections[relsec->header.sh_info];

295        strsec = f->sections[symsec->header.sh_link];

296

297        rel = (ElfW(RelM) *)relsec->contents;

298        relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));

299        symtab = (ElfW(Sym) *)symsec->contents;

300        strtab = (const char *)strsec->contents;

301

302        for (; rel < relend; ++rel)

303        {

304           ElfW(Addr) value = 0;

305           struct obj_symbol *intsym = NULL;

306           unsigned long symndx;

307           ElfW(Sym) *extsym = 0;

308           const char *errmsg;

309

310           /* Attempt to find a value to use for this relocation.  */

311

312           symndx = ELFW(R_SYM)(rel->r_info);

313           if (symndx)

314               {

315               /* Note we've already checked for undefined symbols.  */

316

317               extsym = &symtab[symndx];

318               if (ELFW(ST_BIND)(extsym->st_info) == STB_LOCAL)

319                      {

320                  /* Local symbols we look up in the local table to be sure

321                     we get the one that is really intended.  */

322                  intsym = f->local_symtab[symndx];

323               }

324               else

325              {

326                  /* Others we look up in the hash table.  */

327                  const char *name;

328                  if (extsym->st_name)

329                    name = strtab + extsym->st_name;

330                  else

331                    name = f->sections[extsym->st_shndx]->name;

332                  intsym = obj_find_symbol(f, name);

333                }

334

335               value = obj_symbol_final_value(f, intsym);

336             }

337

338  #if SHT_RELM == SHT_RELA

339  #if defined(__alpha__) && defined(AXP_BROKEN_GAS)

340           /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9.  */

341           if (!extsym || !extsym->st_name ||

342               ELFW(ST_BIND)(extsym->st_info) != STB_LOCAL)

343  #endif

344           value += rel->r_addend;

345  #endif

346

347           /* Do it! */

348           switch (arch_apply_relocation(f,targsec,symsec,intsym,rel,value))

349               {

350             case obj_reloc_ok:

351               break;

352

353             case obj_reloc_overflow:

354               errmsg = "Relocation overflow";

355               goto bad_reloc;

356             case obj_reloc_dangerous:

357               errmsg = "Dangerous relocation";

358               goto bad_reloc;

359             case obj_reloc_unhandled:

360               errmsg = "Unhandled relocation";

361               goto bad_reloc;

362             case obj_reloc_constant_gp:

363               errmsg = "Modules compiled with -mconstant-gp cannot be loaded";

364               goto bad_reloc;

365             bad_reloc:

366               if (extsym)

367                  {

368                  error("%s of type %ld for %s", errmsg,

369                       (long)ELFW(R_TYPE)(rel->r_info),

370                       strtab + extsym->st_name);

371                   }

372               else

373                   {

374                  error("%s of type %ld", errmsg,

375                       (long)ELFW(R_TYPE)(rel->r_info));

376                    }

377               ret = 0;

378               break;

379             }

380         }

381      }

382

383    /* Finally, take care of the patches.  */

384

385   if (f->string_patches)

386   {

387        struct obj_string_patch_struct *p;

388        struct obj_section *strsec;

389        ElfW(Addr) strsec_base;

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

391        strsec_base = strsec->header.sh_addr;

392

393        for (p = f->string_patches; p ; p = p->next)

394        {

395           struct obj_section *targsec = f->sections[p->reloc_secidx];

396           *(ElfW(Addr) *)(targsec->contents + p->reloc_offset)

397                = strsec_base + p->string_offset;

398          }

399      }

400

401    if (f->symbol_patches)

402  {

403        struct obj_symbol_patch_struct *p;

404

405        for (p = f->symbol_patches; p; p = p->next)

406        {

407           struct obj_section *targsec = f->sections[p->reloc_secidx];

408           *(ElfW(Addr) *)(targsec->contents + p->reloc_offset)

409             = obj_symbol_final_value(f, p->sym);

410         }

411     }

412

413    return ret;

414          }

 

这个函数的逻辑也不复杂。首先,参数base在这里是m_addr——模块在内核的地址。而在模块elf文件里,段在内存的位置是假设文件从0地址加载而得出的,现在就要根据base值调整,函数arch_finalize_section_address./modultils-2.4-/obj/obj_i386.c中。

Insmod——arch_finalize_section_address函数

230  int

231  arch_finalize_section_address(struct obj_file *f, Elf32_Addr base)

232  {

233    int  i, n = f->header.e_shnum;

234

235    f->baseaddr = base;

236    for (i = 0; i < n; ++i)

237      f->sections[i]->header.sh_addr += base;

238    return 1;

239          }

 

接下来,开始处理重定位符号了。需要重定位的符号都可以从重定位段中找到(也只能从那里去找),需要重定位的符号一般都涉及指针、地址、外部符号这类在链接时才能确定的东西。因此,表示它们的Elf32_Sym结构中的st_value应该就是地址(实际上st_value只代表2种情况,在符号的段序号为SHN_COMMON时,这个值代表边界对齐值。而其他情况下代表被重定位符号相对保存它的段起始的偏移。还记得吗?段序号为SHN_COMMON的符号,如果对应文件内的外部符号——比如B文件引用了A文件里定义的变量,在前面已经被obj_allocate_commons函数处理了,符号的段序号都指向了.bss段的序号。而如果符号对应内核或已加载模块导出的符号,则被前面的add_kernel_symbols处理了,序号为SHN_HIRESERVE以上。)。

elf文件里,段序号大于SHN_LORESERVE的符号,是没有对应的段的。因此,符号里的值就认为是绝对地址(见209行)。内核和已加载模块导出符号就处在这个区段。

前面已经看到,local属性的符号都存放在local_symtab中,外部符号和全局符号则存放在symtabhash表里。找到存放符号的结构体后,计算符号的绝对地址,函数obj_symbol_final_value./modutils-2.4.0/obj/obj_common.c中。

Insmod——obj_symbol_final_value函数

203  ElfW(Addr)

204  obj_symbol_final_value (struct obj_file *f, struct obj_symbol *sym)

205  {

206    if (sym)

207      {

208        if (sym->secidx >= SHN_LORESERVE)

209         return sym->value;

210

211        return sym->value + f->sections[sym->secidx]->header.sh_addr;

212      }

213    else

214      {

215        /* As a special case, a NULL sym has value zero.  */

216        return 0;

217      }

218          }

 

对于类型是rela的重定位符号,因为有显式指定的偏移值r_addend,所以还要加上它。获得了绝对地址后,可已开始重定位操作了。函数arch_apply_relocationobj_i386.c中。所涉及的重定位操作,参见elf文档,这里不多说了。

Insmod——arch_apply_relocation函数

90    enum obj_reloc

91    arch_apply_relocation (struct obj_file *f,

92                         struct obj_section *targsec,

93                         struct obj_section *symsec,

94                         struct obj_symbol *sym,

95                         Elf32_Rel *rel,

96                         Elf32_Addr v)

97    {

98      struct i386_file *ifile = (struct i386_file *)f;

99      struct i386_symbol *isym  = (struct i386_symbol *)sym;

100

101    Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset);

102    Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset;

103    Elf32_Addr got = ifile->got ? ifile->got->header.sh_addr : 0;

104

105    enum obj_reloc ret = obj_reloc_ok;

106

107    switch (ELF32_R_TYPE(rel->r_info))

108      {

109      case R_386_NONE:

110        break;

111

112      case R_386_32:

113        *loc += v;

114        break;

115

116      case R_386_PLT32:

117      case R_386_PC32:

118        *loc += v - dot;

119        break;

120

121      case R_386_GLOB_DAT:

122      case R_386_JMP_SLOT:

123        *loc = v;

124        break;

125

126      case R_386_RELATIVE:

127        *loc += f->baseaddr;

128        break;

129

130      case R_386_GOTPC:

131        assert(got != 0);

132        *loc += got - dot;

133        break;

134

135      case R_386_GOT32:

136        assert(isym != NULL);

137        if (!isym->gotent.reloc_done)

138         {

139           isym->gotent.reloc_done = 1;

140           *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v;

141         }

142        *loc += isym->gotent.offset;

143        break;

144

145      case R_386_GOTOFF:

146        assert(got != 0);

147        *loc += v - got;

148        break;

149

150      default:

151        ret = obj_reloc_unhandled;

152        break;

153      }

154

155    return ret;

156          }

 

这个函数返回后,就剩下在加载文件的过程中额外生成的符号了。这些符号都通过链表保存在obj_file中。他们的重定位过程相当简单。