回到INSMOD_MAIN函数里。
1899 /* Do archdata again, this time we have the final addresses */
1900 if (add_archdata(f, &archdata))
1901 goto out;
1902
1903 /* Do kallsyms again, this time we have the final addresses */
1904 if (add_kallsyms(f, &kallsyms, force_kallsyms))
1905 goto out;
这2函数在前面都看过了。add_archdata因为__archdata段已经存在,所以什么都没干。add_kallsyms用绝对地址重新生成kallsyms段的内容(现在的__kallsyms段才真正可用)。
1907 #ifdef COMPAT_2_0
1908 if (k_new_syscalls)
1909 init_module(m_name, f, m_size, blob_name, noload, flag_load_map);
1910 else if (!noload)
1911 old_init_module(m_name, f, m_size);
1912 #else
1913 init_module(m_name, f, m_size, blob_name, noload, flag_load_map);
1914 #endif
1915 if (errors) {
1916 if (!noload)
1917 delete_module(m_name);
1918 goto out;
1919 }
1920 exit_status = 0;
1921
1922 out:
1923 if (dolock)
1924 flock(fp, LOCK_UN);
1925 close(fp);
1926 if (!noload)
1927 snap_shot(NULL, 0);
1928
1929 return exit_status;
1930 }
假定COMPAT_2.0没有定义。这已经是整个INSMOD_MAIN的最后工作了,不过这个工作也不轻松。其主体是init_module函数,该函数也在insmod.c中。
1058 static int init_module(const char *m_name, struct obj_file *f,
1059 unsigned long m_size, const char *blob_name,
1060 unsigned int noload, unsigned int flag_load_map)
1061 {
1062 struct module *module;
1063 struct obj_section *sec;
1064 void *image;
1065 int ret = 0;
1066 tgt_long m_addr;
1067
1068 sec = obj_find_section(f, ".this");
1069 module = (struct module *) sec->contents;
1070 m_addr = sec->header.sh_addr;
1071
1072 module->size_of_struct = sizeof(*module);
1073 module->size = m_size;
1074 module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
1075
1076 sec = obj_find_section(f, "__ksymtab");
1077 if (sec && sec->header.sh_size) {
1078 module->syms = sec->header.sh_addr;
1079 module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
1080 }
1081 if (n_ext_modules_used) {
1082 sec = obj_find_section(f, ".kmodtab");
1083 module->deps = sec->header.sh_addr;
1084 module->ndeps = n_ext_modules_used;
1085 }
1086 module->init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
1087 module->cleanup = obj_symbol_final_value(f,
1088 obj_find_symbol(f, "cleanup_module"));
1089
1090 sec = obj_find_section(f, "__ex_table");
1091 if (sec) {
1092 module->ex_table_start = sec->header.sh_addr;
1093 module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
1094 }
1095 sec = obj_find_section(f, ".text.init");
1096 if (sec) {
1097 module->runsize = sec->header.sh_addr - m_addr;
1098 }
1099 sec = obj_find_section(f, ".data.init");
1100 if (sec) {
1101 if (!module->runsize ||
1102 module->runsize > sec->header.sh_addr - m_addr)
1103 module->runsize = sec->header.sh_addr - m_addr;
1104 }
1105 sec = obj_find_section(f, ARCHDATA_SEC_NAME);
1106 if (sec && sec->header.sh_size) {
1107 module->archdata_start = sec->header.sh_addr;
1108 module->archdata_end = module->archdata_start + sec->header.sh_size;
1109 }
1110 sec = obj_find_section(f, KALLSYMS_SEC_NAME);
1111 if (sec && sec->header.sh_size) {
1112 module->kallsyms_start = sec->header.sh_addr;
1113 module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
1114 }
1115 if (!arch_init_module(f, module))
1116 return 0;
1117
1118 /*
1119 * Whew! All of the initialization is complete.
1120 * Collect the final module image and give it to the kernel.
1121 */
1122 image = xmalloc(m_size);
1123 obj_create_image(f, image);
1124
1125 if (flag_load_map)
1126 print_load_map(f);
1127
1128 if (blob_name) {
1129 int fd, l;
1130 fd=open(blob_name,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
1131 if (fd < 0) {
1132 error("open %s failed %m", blob_name);
1133 ret = -1;
1134 }
1135 else {
1136 if ((l = write(fd, image, m_size)) != m_size) {
1137 error("write %s failed %m", blob_name);
1138 ret = -1;
1139 }
1140 close(fd);
1141 }
1142 }
1143
1144 if (ret == 0 && !noload) {
1145 fflush(stdout); /* Flush any debugging output */
1146 ret = sys_init_module(m_name, (struct module *) image);
1147 if (ret) {
1148 error("init_module: %m");
1149 lprintf("Hint: insmod errors can be caused by incorrect module parameters, "
1150 "including invalid IO or IRQ parameters");
1151 }
1152 }
1153
1154 free(image);
1155
1156 return ret == 0;
1157 }
__ksymtab段里保存的是内核和模块导出的符号。这些符号对应于module里的syms。在前面已经看到过module的deps对象对应于.kmodtab段。
除了runsize外,其他都很到理解。那么runsize是怎么回事呢?在内核里,所有只在系统初始化时使用一次的数据和代码都放入特殊的段里,也就是data.init和text.init段。这些段占用的空间会在系统启动后回收。
如果模块不是动态安装的,毫无疑问,它的初始化数据和代码都将放入这些段。那么如果模块是动态安装的,在模块编译的时候,它并不知道内核的这些段,必然是在自己文件里生成这2个段。这些段空间的回收就需要模块自己做。很自然,这些段一定是放在模块空间的末尾,这样才能使空间的回收最方便(其实前面的obj_load_order_prio函数已经说得很清楚了)。
因为,这2个段在模块初始化后,就不再使用也不应该使用(不管你释不释放),而且这2个段的先后顺序也不一定。所以,1095~1104行找出这2个段的起始地址到模块起始地址之差的最小值,这就是模块的runsize。不过,这个变量现在还没使用。
如果模块没有初始化的数据和代码,从代码中可以看出,runsize无法求得。
1115行的arch_init_module在x86体系下什么都不干,直接返回1。1122~1123行分配资源,构建模块影像。函数obj_create_image在obj_reloc.c中。
415 int
416 obj_create_image (struct obj_file *f, char *image)
417 {
418 struct obj_section *sec;
419 ElfW(Addr) base = f->baseaddr;
420
421 for (sec = f->load_order; sec ; sec = sec->load_next)
422 {
423 char *secimg;
424
425 if (sec->contents == 0)
426 continue;
427
428 secimg = image + (sec->header.sh_addr - base);
429
430 /* Note that we allocated data for NOBITS sections earlier. */
431 memcpy(secimg, sec->contents, sec->header.sh_size);
432 }
433
434 return 1;
435 }
如果需要打印加载位图,则调用print_load_map函数,它还是在insmod.c中。
300 static void print_load_map(struct obj_file *f)
301 {
302 struct obj_symbol *sym;
303 struct obj_symbol **all, **p;
304 struct obj_section *sec;
305 int load_map_cmp(const void *a, const void *b) {
306 struct obj_symbol **as = (struct obj_symbol **) a;
307 struct obj_symbol **bs = (struct obj_symbol **) b;
308 unsigned long aa = obj_symbol_final_value(f, *as);
309 unsigned long ba = obj_symbol_final_value(f, *bs);
310 return aa < ba ? -1 : aa > ba ? 1 : 0;
311 }
312 int i, nsyms, *loaded;
313
314 /* Report on the section layout. */
315
316 lprintf("Sections: Size %-*s Align",
317 (int) (2 * sizeof(void *)), "Address");
318
319 for (sec = f->load_order; sec; sec = sec->load_next) {
320 int a;
321 unsigned long tmp;
322
323 for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
324 tmp >>= 1;
325 if (a == -1)
326 a = 0;
327
328 lprintf("%-16s%08lx %0*lx 2**%d",
329 sec->name,
330 (long)sec->header.sh_size,
331 (int) (2 * sizeof(void *)),
332 (long)sec->header.sh_addr,
333 a);
334 }
335
336 /* Quick reference which section indicies are loaded. */
337
338 loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
339 while (--i >= 0)
340 loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
341
342 /* Collect the symbols we'll be listing. */
343
344 for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
345 for (sym = f->symtab[i]; sym; sym = sym->next)
346 if (sym->secidx <= SHN_HIRESERVE
347 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
348 ++nsyms;
349
350 all = alloca(nsyms * sizeof(struct obj_symbol *));
351
352 for (i = 0, p = all; i < HASH_BUCKETS; ++i)
353 for (sym = f->symtab[i]; sym; sym = sym->next)
354 if (sym->secidx <= SHN_HIRESERVE
355 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
356 *p++ = sym;
357
358 /* Sort them by final value. */
359 qsort(all, nsyms, sizeof(struct obj_file *), load_map_cmp);
360
361 /* And list them. */
362 lprintf("/nSymbols:");
363 for (p = all; p < all + nsyms; ++p) {
364 char type = '?';
365 unsigned long value;
366
367 sym = *p;
368 if (sym->secidx == SHN_ABS) {
369 type = 'A';
370 value = sym->value;
371 } else if (sym->secidx == SHN_UNDEF) {
372 type = 'U';
373 value = 0;
374 } else {
375 struct obj_section *sec = f->sections[sym->secidx];
376
377 if (sec->header.sh_type == SHT_NOBITS)
378 type = 'B';
379 else if (sec->header.sh_flags & SHF_ALLOC) {
380 if (sec->header.sh_flags & SHF_EXECINSTR)
381 type = 'T';
382 else if (sec->header.sh_flags & SHF_WRITE)
383 type = 'D';
384 else
385 type = 'R';
386 }
387 value = sym->value + sec->header.sh_addr;
388 }
389
390 if (ELFW(ST_BIND) (sym->info) == STB_LOCAL)
391 type = tolower(type);
392
393 lprintf("%0*lx %c %s", (int) (2 * sizeof(void *)), value,
394 type, sym->name);
395 }
395 }
这个函数不复杂,就不说那么多了。另外,如果指定了要输出生成的模块到blob_name指定的文件,那么也保存它。
最后一步,调用sys_init_module把image指向的module内容拷贝到内核的module对象中。有没有注意到,前面的重定位工作都是以模块对象在内核中的地址为起始地址,来进行的(就是前面的m_addr),就是为了这一步的拷贝。经过这一步,我们的模块终于成为内核的一员,发挥它的作用。至此,insmod_main结束了。