回到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个函数都返回0。1648~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_file的symtab中查找与模块特定符号同名的符号。只要这个符号不是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以上的段来保存已加载模块的符号。注意274和280行。在这个函数结束的时候,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_syscalls为0),这样就要调用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_symbol向obj_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_NONE为0,表示不需要重定位操作,在symtab中的local符号,就包含这样的属性)。那就出错了,也就是我们常在c或c++里看到的unresolved symbol这样的错误。
接下来需要处理的是SHN_COMMON类型的符号,这些符号是未分配资源的。结构中的st_value给出的是边界对齐要求,函数obj_allocate_common在obj_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(包含了global,weak,local符号)中所有类型为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_WRITE和SHF_ALLOC。
203~225行,计算所有所持段序号为SHN_COMMON的符号占据的大小和段最终要求的对齐边界。然后在231行的循环里,为SHT_NOBITS段分配资源,并将它设为SHN_PROGBITS。注意现在这些符号的段序号也给改变了,记住这一点。从这里可以看到,文件定义的静态、全局的变量,还有引用的外部变量,最终都放在了.bss段中。