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

处理完命令行参数,INSMOD_MAIN接下来调用arch_create_got。这个函数在./modutils-2.4.0/obj/obj_i386.c中。

Insmod——arch_create_got函数

158  int

159  arch_create_got (struct obj_file *f)

160  {

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

162    int i, n, offset = 0, gotneeded = 0;

163

164    n = ifile->root.header.e_shnum;

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

166      {

167        struct obj_section *relsec, *symsec, *strsec;

168        Elf32_Rel *rel, *relend;

169        Elf32_Sym *symtab;

170        const char *strtab;

171

172        relsec = ifile->root.sections[i];

173       if (relsec->header.sh_type != SHT_REL)

174                continue;

175

176        symsec = ifile->root.sections[relsec->header.sh_link];

177       strsec = ifile->root.sections[symsec->header.sh_link];

178

179        rel = (Elf32_Rel *)relsec->contents;

180       relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rel));

181       symtab = (Elf32_Sym *)symsec->contents;

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

183

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

185       {

186           Elf32_Sym *extsym;

187           struct i386_symbol *intsym;

188           const char *name;

189

190           switch (ELF32_R_TYPE(rel->r_info))

191               {

192             case R_386_GOTPC:

193             case R_386_GOTOFF:

194               gotneeded = 1;

195             default:

196               continue;

197

198             case R_386_GOT32:

199               break;

200             }

201

202           extsym = &symtab[ELF32_R_SYM(rel->r_info)];

203           if (extsym->st_name)

204             name = strtab + extsym->st_name;

205           else

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

207           intsym = (struct i386_symbol *)obj_find_symbol(&ifile->root, name);

208

209           if (!intsym->gotent.offset_done)

210             {

211               intsym->gotent.offset_done = 1;

212               intsym->gotent.offset = offset;

213               offset += 4;

214             }

215         }

216      }

217

218    if (offset > 0 || gotneeded)

219      ifile->got = obj_create_alloced_section(&ifile->root, ".got", 4, offset);

220

221    return 1;

222          }

 

这个函数的作用是创建文件的.GOT段。这是怎么回事呢?.GOT的全称是global offset table。在这个段里保存的是绝对地址,这些地址不受重定位的影响。如果程序需要直接引用符号的绝对地址,这些符号就必须在.GOT段中出现(原文:that symbol will have a global offset table entry)。什么情况下程序需要引用符号的绝对地址呢?这跟重定位操作的类型有关。重定位操作为类型R_386_GOTOFFR_386_GOTPCR_386_GOT32的符号,会使用.GOT段。

函数里使用了i386_filei386_symbol结构,这2个结构定义在同一文件里。

 

34    struct i386_got_entry

35    {

36      int offset;

37      unsigned offset_done : 1;

38      unsigned reloc_done : 1;

39    };

40

41    struct i386_file

42    {

43      struct obj_file root;

44      struct obj_section *got;

45    };

46

47    struct i386_symbol

48    {

49      struct obj_symbol root;

50      struct i386_got_entry gotent;

51             };

 

这些结构都很简单,只是添加了些辅助控制位。

接下来,INSMOD_MAIN通过hide_special_symbols将符号“cleanup_module”,“init_module”,“kernel_version”的属性改为local,从而使其外部不可见。这个函数也在insmod.c里。

Insmod——hide_special_symbols函数

283  static void hide_special_symbols(struct obj_file *f)

284  {

285         struct obj_symbol *sym;

286         const char *const *p;

287         static const char *const specials[] =

288         {

289                "cleanup_module",

290                "init_module",

291                "kernel_version",

292                NULL

293         };

294

295         for (p = specials; *p; ++p)

296                if ((sym = obj_find_symbol(f, *p)) != NULL)

297                       sym->info = ELFW(ST_INFO) (STB_LOCAL, ELFW(ST_TYPE) (sym->info));

298          }

 

接下来,如果命令行参数来自文件,将文件名保存好,下面要从那里读出参数值。

 

1700    if (persist_parms && persist_name && *persist_name) {

1701              f->persist = persist_name;

1702              persist_name = NULL;

1703       }

1704

1705       if (persist_parms &&

1706           persist_name && !*persist_name) {

1707              /* -e "".  This is ugly.  Take the filename, compare it against

1708              * each of the module paths until we find a match on the start

1709              * of the filename, assume the rest is the relative path.  Have

1710              * to do it this way because modprobe uses absolute filenames

1711              * for module names in modules.dep and the format of modules.dep

1712              * does not allow for any backwards compatible changes, so there

1713              * is nowhere to store the relative filename.  The only way this

1714              * should fail to calculate a relative path is "insmod ./xxx", for

1715              * that case the user has to specify -e filename.

1716              */

1717              int j, l = strlen(filename);

1718              char *relative = NULL;

1719              char *p;

1720              for (i = 0; i < nmodpath; ++i) {

1721                     p = modpath[i].path;

1722                     j = strlen(p);

1723                     while (j && p[j] == '/')

1724                            --j;

1725                     if (j < l && strncmp(filename, p, j) == 0 && filename[j] == '/') {

1726                            while (filename[j] == '/')

1727                                   ++j;

1728                            relative = xstrdup(filename+j);

1729                            break;

1730                     }

1731              }

1732              if (relative) {

1733                     i = strlen(relative);

1734                     if (i > 3 && strcmp(relative+i-3, ".gz") == 0)

1735                            relative[i -= 3] = '/0';

1736                     if (i > 2 && strcmp(relative+i-2, ".o") == 0)

1737                            relative[i -= 2] = '/0';

1738                     else if (i > 4 && strcmp(relative+i-4, ".mod") == 0)

1739                            relative[i -= 4] = '/0';

1740                     f->persist = xmalloc(strlen(persistdir) + 1 + i + 1);

1741                     strcpy(f->persist, persistdir);  /* safe, xmalloc */

1742                     strcat(f->persist, "/");     /* safe, xmalloc */

1743                     strcat(f->persist, relative);     /* safe, xmalloc */

1744                     free(relative);

1745              }

1746              else

1747                     error("Cannot calculate persistent filename");

1748      }

 

这段代码没什么可说的。1705~1748行是防止-e “”这样的恶作剧。接下来是一些健康检查,并且处理从文件里读出的参数值。

 

1750       if (f->persist && *(f->persist) != '/') {

1751              error("Persistent filenames must be absolute, ignoring '%s'",

1752                     f->persist);

1753              free(f->persist);

1754              f->persist = NULL;

1755       }

1756

1757       if (f->persist && !flag_ksymoops) {

1758              error("has persistent data but ksymoops symbols are not available");

1759              free(f->persist);

1760              f->persist = NULL;

1761       }

1762

1763       if (f->persist && !k_new_syscalls) {

1764              error("has persistent data but the kernel is too old to support it");

1765              free(f->persist);

1766              f->persist = NULL;

1767       }

1768

1769       if (persist_parms && flag_verbose) {

1770              if (f->persist)

1771                     lprintf("Persist filename '%s'", f->persist);

1772              else

1773                     lprintf("No persistent filename available");

1774      }

1775      

1776       if (f->persist) {

1777              FILE *fp = fopen(f->persist, "r");

1778              if (!fp) {

1779                     if (flag_verbose)

1780                            lprintf("Cannot open persist file '%s' %m", f->persist);

1781              }

1782              else {

1783                     int pargc = 0;

1784                     char *pargv[1000]; /* hard coded but big enough */

1785                     char line[3000];     /* hard coded but big enough */

1786                     char *p;

1787                     while (fgets(line, sizeof(line), fp)) {

1788                            p = strchr(line, '/n');

1789                            if (!p) {

1790                                   error("Persistent data line is too long/n%s", line);

1791                                   break;

1792                            }

1793                            *p = '/0';

1794                            p = line;

1795                            while (isspace(*p))

1796                                   ++p;

1797                            if (!*p || *p == '#')

1798                                   continue;

1799                            if (pargc == sizeof(pargv)/sizeof(pargv[0])) {

1800                                   error("More than %d persistent parameters", pargc);

1801                                   break;

1802                            }

1803                            pargv[pargc++] = xstrdup(p);

1804                     }

1805                     fclose(fp);

1806                     if (!process_module_arguments(f, pargc, pargv, 0))

1807                            goto out;

1808                     while (pargc--)

1809                            free(pargv[pargc]);

1810              }

1811      }

 

这里处理的主体是process_module_arguments,这个函数我们已经看过了。从这里可以看到文件里的内容类似,A=a B=b

 

1813        if (flag_ksymoops)

1814                     add_ksymoops_symbols(f, filename, m_name);

 

如果定义了ksymoops符号,调用add_ksymoops_symbols函数。ksymoops是一个调试辅助工具,它将试图将代码转换为指令并将堆栈值映射到内核符号。在很多情况下,这些信息就足够您确定错误的可能原因是什么了。这个函数也在insmod.c中。

Insmod——add_ksymoops_symbols函数

619  /* add module source, timestamp, kernel version and a symbol for the

620  * start of some sections.  this info is used by ksymoops to do better

621  * debugging.

622  */

623  static void add_ksymoops_symbols(struct obj_file *f, const char *filename,

624                              const char *m_name)

625  {

626         struct obj_section *sec;

627         struct obj_symbol *sym;

628         char *name, *absolute_filename;

629         char str[STRVERSIONLEN], real[PATH_MAX];

630         int i, l, lm_name, lfilename, use_ksymtab, version;

631         struct stat statbuf;

632

633         static const char *section_names[] = {

634                ".text",

635                ".rodata",

636                ".data",

637                ".bss"

638         };

639

640         if (realpath(filename, real)) {

641                absolute_filename = xstrdup(real);

642         }

643         else {

644                int save_errno = errno;

645                error("cannot get realpath for %s", filename);

646                errno = save_errno;

647                perror("");

648                absolute_filename = xstrdup(filename);

649         }

650

651         lm_name = strlen(m_name);

652         lfilename = strlen(absolute_filename);

653

654         /* add to ksymtab if it already exists or there is no ksymtab and other symbols

655         * are not to be exported.  otherwise leave ksymtab alone for now, the

656         * "export all symbols" compatibility code will export these symbols later.

657         */

658

659         use_ksymtab =  obj_find_section(f, "__ksymtab") || !flag_export;

660

661         if ((sec = obj_find_section(f, ".this"))) {

662                /* tag the module header with the object name, last modified

663                * timestamp and module version.  worst case for module version

664                * is 0xffffff, decimal 16777215.  putting all three fields in

665                * one symbol is less readable but saves kernel space.

666                */

667                l = sizeof(symprefix)+                 /* "__insmod_" */

668                    lm_name+                           /* module name */

669                    2+                               /* "_O" */

670                    lfilename+                           /* object filename */

671                    2+                               /* "_M" */

672                    2*sizeof(statbuf.st_mtime)+         /* mtime in hex */

673                    2+                               /* "_V" */

674                    8+                               /* version in dec */

675                    1;                                /* nul */

676                name = xmalloc(l);

677                if (stat(absolute_filename, &statbuf) != 0)

678                       statbuf.st_mtime = 0;

679                version = get_module_version(f, str);   /* -1 if not found */

680                snprintf(name, l, "%s%s_O%s_M%0*lX_V%d",

681                       symprefix, m_name, absolute_filename,

682                       2*sizeof(statbuf.st_mtime), statbuf.st_mtime,

683                       version);

684                sym = obj_add_symbol(f, name, -1,

685                                   ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),

686                                   sec->idx, sec->header.sh_addr, 0);

687                if (use_ksymtab)

688                       add_ksymtab(f, sym);

689         }

690         free(absolute_filename);

691

692         /* record where the persistent data is going, same address as previous symbol */

693

694         if (f->persist) {

695                l = sizeof(symprefix)+          /* "__insmod_" */

696                       lm_name+             /* module name */

697                       2+                 /* "_P" */

698                       strlen(f->persist)+  /* data store */

699                       1;                  /* nul */

700                name = xmalloc(l);

701                snprintf(name, l, "%s%s_P%s",

702                       symprefix, m_name, f->persist);

703                sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),

704                            sec->idx, sec->header.sh_addr, 0);

705                if (use_ksymtab)

706                       add_ksymtab(f, sym);

707         }

708

709         /* tag the desired sections if size is non-zero */

710

711         for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) {

712                if ((sec = obj_find_section(f, section_names[i])) &&

713                    sec->header.sh_size) {

714                       l = sizeof(symprefix)+          /* "__insmod_" */

715                              lm_name+             /* module name */

716                              2+                 /* "_S" */

717                              strlen(sec->name)+ /* section name */

718                              2+                 /* "_L" */

719                              8+                 /* length in dec */

720                              1;                  /* nul */

721                       name = xmalloc(l);

722                       snprintf(name, l, "%s%s_S%s_L%ld",

723                              symprefix, m_name, sec->name,

724                              (long)sec->header.sh_size);

725                       sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),

726                                   sec->idx, sec->header.sh_addr, 0);

727                       if (use_ksymtab)

728                              add_ksymtab(f, sym);

729                }

730         }

731          }

 

这个函数做的事情不复杂,就是记录经过修饰的模块名和经过修饰的长度不为0的段名。另外,如果模块运行时参数是通过文件传入的,也要修饰、记录这个文件名。在这里修饰的目的,应该是为了唯一确定所使用的模块。

在这里flag_export是全局变量,初始化为1,当运行insmod时,如果使用了-x,就置为0,如果使用了-X,就置为1。默认为1

在存在__ksymtab段或者不存在这个段,而且其他符号都不导出的情况下,还要将这些符号添加入__symtab段。这个由函数add_ksymtab完成。这个函数在同一文件里。

Insmod——add_ksymtab函数

476  /* add an entry to the __ksymtab section, creating it if necessary */

477  static void add_ksymtab(struct obj_file *f, struct obj_symbol *sym)

478  {

479         struct obj_section *sec;

480         ElfW(Addr) ofs;

481

482         /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.

483         * If __ksymtab is defined but not marked alloc, x out the first character

484         * (no obj_delete routine) and create a new __ksymtab with the correct

485         * characteristics.

486         */

487         sec = obj_find_section(f, "__ksymtab");

488         if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {

489                *((char *)(sec->name)) = 'x'; /* override const */

490                sec = NULL;

491         }

492         if (!sec)

493                sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);

494         if (!sec)

495                return;

496         sec->header.sh_flags |= SHF_ALLOC;

497

498         ofs = sec->header.sh_size;

499         obj_symbol_patch(f, sec->idx, ofs, sym);

500         obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);

501         obj_extend_section(sec, 2 * tgt_sizeof_char_p);

502          }

 

在这个函数里,如果存在__symtab段,但是没有SHF_ALLOC标志(表明段在程序执行时不占据内存资源),那么直接在段的开头写入“x”,标识它已删除。为了使段容易扩展,在段里面保存的都是“指针”。所以,函数使用obj_symbol_patch来保存这些“额外”的symbol。这个函数在obj_reloc.c中。

Insmod——obj_symbol_patch函数

65    int

66    obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,

67                  struct obj_symbol *sym)

68    {

69      struct obj_symbol_patch_struct *p;

70

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

72      p->next = f->symbol_patches;

73      p->reloc_secidx = secidx;

74      p->reloc_offset = offset;

75      p->sym = sym;

76      f->symbol_patches = p;

77

78      return 1;

79             }

 

如果内核版本在v2.1.x以上,调用create_module­_symtab。这个函数也在insmod.c里。

 

1816              if (k_new_syscalls)

1817                     create_module_ksymtab(f);

Insmod——create_module_ksymtab函数

504  static int create_module_ksymtab(struct obj_file *f)

505  {

506         struct obj_section *sec;

507         int i;

508

509         /* We must always add the module references.  */

510

511         if (n_ext_modules_used) {

512                struct module_ref *dep;

513                struct obj_symbol *tm;

514

515                sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,

516                                        (sizeof(struct module_ref)

517                                         * n_ext_modules_used));

518                if (!sec)

519                       return 0;

520

521                tm = obj_find_symbol(f, "__this_module");

522                dep = (struct module_ref *) sec->contents;

523                for (i = 0; i < n_module_stat; ++i)

524                       if (module_stat[i].status /* used */) {

525                              dep->dep = module_stat[i].addr;

526                              obj_symbol_patch(f, sec->idx, (char *) &dep->ref - sec->contents, tm);

527                              dep->next_ref = 0;

528                              ++dep;

529                       }

530         }

531         if (flag_export && !obj_find_section(f, "__ksymtab")) {

532                int *loaded;

533

534                /* We don't want to export symbols residing in sections that

535                   aren't loaded.  There are a number of these created so that

536                   we make sure certain module options don't appear twice.  */

537

538                loaded = alloca(sizeof(int) * (i = f->header.e_shnum));

539                while (--i >= 0)

540                       loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;

541

542                for (i = 0; i < HASH_BUCKETS; ++i) {

543                       struct obj_symbol *sym;

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

545                              if (ELFW(ST_BIND) (sym->info) != STB_LOCAL

546                                  && sym->secidx <= SHN_HIRESERVE

547                                  && (sym->secidx >= SHN_LORESERVE

548                                     || loaded[sym->secidx])) {

549                                     add_ksymtab(f, sym);

550                              }

551                       }

552                }

553         }

554         return 1;

555          }

 

n_ext_modules_used是该模块引用模块的数目。内核所有加载的模块的信息都保存在module_stat数组里,每个被引用的模块的module_stat里的status1(见add_kernel_symbols)。函数在511~530行创建一个.kmodtab段保存该模块所引用的模块信息(包括这些模块对该模块的引用信息)。

如果需要导出外部(extern)符号,而__ksymtab段不存在(这种情况下,add_ksymoops_symbols里是不会创建__ksymtab段的。而实际上,模块一般是不会不包含__ksymtab段的,因为模块的导出符号都位于这个段里。),那么通过add_ksymtab创建__ksymtab段,并加入模块要导出的符号。在这里可以发现,如果模块需要导出符号,那么经过修饰的模块名、模块文件名,甚至参数文件名会在这里加入__ksymtab段(因为在前面的add_ksymoops_symbols函数里,这些符号被设为STB_GLOBOL了,段序号指向.this段)。

注意,在这里545行的if!在不存在__ksymtab段的情况下(这时模块没有定义任何需要导出的参数),直接导出序号在SHN_LORESERVE~SHN_HIRESERVE的符号,以及其他已分配资源段的非local符号。(根据elf规范里,位于SHN_LORESERVE~SHN_HIRESERVE区的已定义序号有SHN_LOPROCSHN_HIPROCSHN_ABSSHN_COMMON。经过前面的处理,到这里SHN_COMMON对应的符号已经不存在了。这些序号的段实际上是不存在的,存在的是持有这些序号的符号。其中SHN_ABS是不受重定位影响的符号,其他2个则是与处理器有关的。至于为什么模块不需要导出符号时,要导出这些区的符号,我也不太清楚,可以肯定的是,这和GCC有关,谁知道它放了什么进来?!)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值