📄 modpost.c
字号:
const char *namelist3 [] = { ".unwind", /* Sample: IA_64.unwind.exit.text */ NULL }; for (s = namelist1; *s; s++) if (strcmp(*s, name) == 0) return 1; for (s = namelist2; *s; s++) if (strncmp(*s, name, strlen(*s)) == 0) return 1; for (s = namelist3; *s; s++) if (strstr(name, *s) != NULL) return 1; return 0;}/* * Identify sections from which references to a .init section is OK. * * Unfortunately references to read only data that referenced .init * sections had to be excluded. Almost all of these are false * positives, they are created by gcc. The downside of excluding rodata * is that there really are some user references from rodata to * init code, e.g. drivers/video/vgacon.c: * * const struct consw vga_con = { * con_startup: vgacon_startup, * * where vgacon_startup is __init. If you want to wade through the false * positives, take out the check for rodata. */static int init_section_ref_ok(const char *name){ const char **s; /* Absolute section names */ const char *namelist1[] = { "__dbe_table", /* MIPS generate these */ "__ftr_fixup", /* powerpc cpu feature fixup */ "__fw_ftr_fixup", /* powerpc firmware feature fixup */ "__param", ".data.rel.ro", /* used by parisc64 */ ".init", ".text.lock", NULL }; /* Start of section names */ const char *namelist2[] = { ".init.", ".pci_fixup", ".rodata", NULL }; if (initexit_section_ref_ok(name)) return 1; for (s = namelist1; *s; s++) if (strcmp(*s, name) == 0) return 1; for (s = namelist2; *s; s++) if (strncmp(*s, name, strlen(*s)) == 0) return 1; /* If section name ends with ".init" we allow references * as is the case with .initcallN.init, .early_param.init, .taglist.init etc */ if (strrcmp(name, ".init") == 0) return 1; return 0;}/* * Identify sections from which references to a .exit section is OK. */static int exit_section_ref_ok(const char *name){ const char **s; /* Absolute section names */ const char *namelist1[] = { ".exit.data", ".exit.text", ".exitcall.exit", ".rodata", NULL }; if (initexit_section_ref_ok(name)) return 1; for (s = namelist1; *s; s++) if (strcmp(*s, name) == 0) return 1; return 0;}static void read_symbols(char *modname){ const char *symname; char *version; char *license; struct module *mod; struct elf_info info = { }; Elf_Sym *sym; if (!parse_elf(&info, modname)) return; mod = new_module(modname); /* When there's no vmlinux, don't print warnings about * unresolved symbols (since there'll be too many ;) */ if (is_vmlinux(modname)) { have_vmlinux = 1; mod->skip = 1; } license = get_modinfo(info.modinfo, info.modinfo_len, "license"); while (license) { if (license_is_gpl_compatible(license)) mod->gpl_compatible = 1; else { mod->gpl_compatible = 0; break; } license = get_next_modinfo(info.modinfo, info.modinfo_len, "license", license); } for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = info.strtab + sym->st_name; handle_modversions(mod, &info, sym, symname); handle_moddevtable(mod, &info, sym, symname); } if (is_vmlinux(modname) && vmlinux_section_warnings) { check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok); check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok); } version = get_modinfo(info.modinfo, info.modinfo_len, "version"); if (version) maybe_frob_rcs_version(modname, version, info.modinfo, version - (char *)info.hdr); if (version || (all_versions && !is_vmlinux(modname))) get_src_version(modname, mod->srcversion, sizeof(mod->srcversion)-1); parse_elf_finish(&info); /* Our trick to get versioning for struct_module - it's * never passed as an argument to an exported function, so * the automatic versioning doesn't pick it up, but it's really * important anyhow */ if (modversions) mod->unres = alloc_symbol("struct_module", 0, mod->unres);}#define SZ 500/* We first write the generated file into memory using the * following helper, then compare to the file on disk and * only update the later if anything changed */void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf, const char *fmt, ...){ char tmp[SZ]; int len; va_list ap; va_start(ap, fmt); len = vsnprintf(tmp, SZ, fmt, ap); buf_write(buf, tmp, len); va_end(ap);}void buf_write(struct buffer *buf, const char *s, int len){ if (buf->size - buf->pos < len) { buf->size += len + SZ; buf->p = realloc(buf->p, buf->size); } strncpy(buf->p + buf->pos, s, len); buf->pos += len;}static void check_for_gpl_usage(enum export exp, const char *m, const char *s){ const char *e = is_vmlinux(m) ?"":".ko"; switch (exp) { case export_gpl: fatal("modpost: GPL-incompatible module %s%s " "uses GPL-only symbol '%s'\n", m, e, s); break; case export_unused_gpl: fatal("modpost: GPL-incompatible module %s%s " "uses GPL-only symbol marked UNUSED '%s'\n", m, e, s); break; case export_gpl_future: warn("modpost: GPL-incompatible module %s%s " "uses future GPL-only symbol '%s'\n", m, e, s); break; case export_plain: case export_unused: case export_unknown: /* ignore */ break; }}static void check_for_unused(enum export exp, const char* m, const char* s){ const char *e = is_vmlinux(m) ?"":".ko"; switch (exp) { case export_unused: case export_unused_gpl: warn("modpost: module %s%s " "uses symbol '%s' marked UNUSED\n", m, e, s); break; default: /* ignore */ break; }}static void check_exports(struct module *mod){ struct symbol *s, *exp; for (s = mod->unres; s; s = s->next) { const char *basename; exp = find_symbol(s->name); if (!exp || exp->module == mod) continue; basename = strrchr(mod->name, '/'); if (basename) basename++; else basename = mod->name; if (!mod->gpl_compatible) check_for_gpl_usage(exp->export, basename, exp->name); check_for_unused(exp->export, basename, exp->name); }}/** * Header for the generated file **/static void add_header(struct buffer *b, struct module *mod){ buf_printf(b, "#include <linux/module.h>\n"); buf_printf(b, "#include <linux/vermagic.h>\n"); buf_printf(b, "#include <linux/compiler.h>\n"); buf_printf(b, "\n"); buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); buf_printf(b, "\n"); buf_printf(b, "struct module __this_module\n"); buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n"); buf_printf(b, " .name = KBUILD_MODNAME,\n"); if (mod->has_init) buf_printf(b, " .init = init_module,\n"); if (mod->has_cleanup) buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" " .exit = cleanup_module,\n" "#endif\n"); buf_printf(b, " .arch = MODULE_ARCH_INIT,\n"); buf_printf(b, "};\n");}/** * Record CRCs for unresolved symbols **/static int add_versions(struct buffer *b, struct module *mod){ struct symbol *s, *exp; int err = 0; for (s = mod->unres; s; s = s->next) { exp = find_symbol(s->name); if (!exp || exp->module == mod) { if (have_vmlinux && !s->weak) { if (warn_unresolved) { warn("\"%s\" [%s.ko] undefined!\n", s->name, mod->name); } else { merror("\"%s\" [%s.ko] undefined!\n", s->name, mod->name); err = 1; } } continue; } s->module = exp->module; s->crc_valid = exp->crc_valid; s->crc = exp->crc; } if (!modversions) return err; buf_printf(b, "\n"); buf_printf(b, "static const struct modversion_info ____versions[]\n"); buf_printf(b, "__attribute_used__\n"); buf_printf(b, "__attribute__((section(\"__versions\"))) = {\n"); for (s = mod->unres; s; s = s->next) { if (!s->module) { continue; } if (!s->crc_valid) { warn("\"%s\" [%s.ko] has no CRC!\n", s->name, mod->name); continue; } buf_printf(b, "\t{ %#8x, \"%s\" },\n", s->crc, s->name); } buf_printf(b, "};\n"); return err;}static void add_depends(struct buffer *b, struct module *mod, struct module *modules){ struct symbol *s; struct module *m; int first = 1; for (m = modules; m; m = m->next) { m->seen = is_vmlinux(m->name); } buf_printf(b, "\n"); buf_printf(b, "static const char __module_depends[]\n"); buf_printf(b, "__attribute_used__\n"); buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n"); buf_printf(b, "\"depends="); for (s = mod->unres; s; s = s->next) { const char *p; if (!s->module) continue; if (s->module->seen) continue; s->module->seen = 1; if ((p = strrchr(s->module->name, '/')) != NULL) p++; else p = s->module->name; buf_printf(b, "%s%s", first ? "" : ",", p); first = 0; } buf_printf(b, "\";\n");}static void add_srcversion(struct buffer *b, struct module *mod){ if (mod->srcversion[0]) { buf_printf(b, "\n"); buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", mod->srcversion); }}static void write_if_changed(struct buffer *b, const char *fname){ char *tmp; FILE *file; struct stat st; file = fopen(fname, "r"); if (!file) goto write; if (fstat(fileno(file), &st) < 0) goto close_write; if (st.st_size != b->pos) goto close_write; tmp = NOFAIL(malloc(b->pos)); if (fread(tmp, 1, b->pos, file) != b->pos) goto free_write; if (memcmp(tmp, b->p, b->pos) != 0) goto free_write; free(tmp); fclose(file); return; free_write: free(tmp); close_write: fclose(file); write: file = fopen(fname, "w"); if (!file) { perror(fname); exit(1); } if (fwrite(b->p, 1, b->pos, file) != b->pos) { perror(fname); exit(1); } fclose(file);}/* parse Module.symvers file. line format: * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something] **/static void read_dump(const char *fname, unsigned int kernel){ unsigned long size, pos = 0; void *file = grab_file(fname, &size); char *line; if (!file) /* No symbol versions, silently ignore */ return; while ((line = get_next_line(&pos, file, size))) { char *symname, *modname, *d, *export, *end; unsigned int crc; struct module *mod; struct symbol *s; if (!(symname = strchr(line, '\t'))) goto fail; *symname++ = '\0'; if (!(modname = strchr(symname, '\t'))) goto fail; *modname++ = '\0'; if ((export = strchr(modname, '\t')) != NULL) *export++ = '\0'; if (export && ((end = strchr(export, '\t')) != NULL)) *end = '\0'; crc = strtoul(line, &d, 16); if (*symname == '\0' || *modname == '\0' || *d != '\0') goto fail; if (!(mod = find_module(modname))) { if (is_vmlinux(modname)) { have_vmlinux = 1; } mod = new_module(NOFAIL(strdup(modname))); mod->skip = 1; } s = sym_add_exported(symname, mod, export_no(export)); s->kernel = kernel; s->preloaded = 1; sym_update_crc(symname, mod, crc, export_no(export)); } return;fail: fatal("parse error in symbol dump file\n");}/* For normal builds always dump all symbols. * For external modules only dump symbols * that are not read from kernel Module.symvers. **/static int dump_sym(struct symbol *sym){ if (!external_module) return 1; if (sym->vmlinux || sym->kernel) return 0; return 1;}static void write_dump(const char *fname){ struct buffer buf = { }; struct symbol *symbol; int n; for (n = 0; n < SYMBOL_HASH_SIZE ; n++) { symbol = symbolhash[n]; while (symbol) { if (dump_sym(symbol)) buf_printf(&buf, "0x%08x\t%s\t%s\t%s\n", symbol->crc, symbol->name, symbol->module->name, export_str(symbol->export)); symbol = symbol->next; } } write_if_changed(&buf, fname);}int main(int argc, char **argv){ struct module *mod; struct buffer buf = { }; char fname[SZ]; char *kernel_read = NULL, *module_read = NULL; char *dump_write = NULL; int opt; int err; while ((opt = getopt(argc, argv, "i:I:mso:aw")) != -1) { switch(opt) { case 'i': kernel_read = optarg; break; case 'I': module_read = optarg; external_module = 1; break; case 'm': modversions = 1; break; case 'o': dump_write = optarg; break; case 'a': all_versions = 1; break; case 's': vmlinux_section_warnings = 0; break; case 'w': warn_unresolved = 1; break; default: exit(1); } } if (kernel_read) read_dump(kernel_read, 1); if (module_read) read_dump(module_read, 0); while (optind < argc) { read_symbols(argv[optind++]); } for (mod = modules; mod; mod = mod->next) { if (mod->skip) continue; check_exports(mod); } err = 0; for (mod = modules; mod; mod = mod->next) { if (mod->skip) continue; buf.pos = 0; add_header(&buf, mod); err |= add_versions(&buf, mod); add_depends(&buf, mod, modules); add_moddevtable(&buf, mod); add_srcversion(&buf, mod); sprintf(fname, "%s.mod.c", mod->name); write_if_changed(&buf, fname); } if (dump_write) write_dump(dump_write); return err;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -