diff options
author | Rob Herring <robh@kernel.org> | 2020-06-12 09:57:00 -0600 |
---|---|---|
committer | Rob Herring <robh@kernel.org> | 2020-06-12 09:57:00 -0600 |
commit | 8440d4a75d90556cfb8fb3e244443f67381aafd6 (patch) | |
tree | 2e1c986942c3beb6257121e52449f827a8ce4eaf /scripts/mod/modpost.c | |
parent | f8d8b46cd20e3a262c17ba1061640d9c190ad769 (diff) | |
parent | 447615701525c910a386a491a6780dec4971f797 (diff) | |
download | lwn-8440d4a75d90556cfb8fb3e244443f67381aafd6.tar.gz lwn-8440d4a75d90556cfb8fb3e244443f67381aafd6.zip |
Merge branch 'dt/schema-cleanups' into dt/linus
Diffstat (limited to 'scripts/mod/modpost.c')
-rw-r--r-- | scripts/mod/modpost.c | 389 |
1 files changed, 187 insertions, 202 deletions
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 5c3c50c5ec52..6aea65c65745 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -30,8 +30,6 @@ static int have_vmlinux = 0; static int all_versions = 0; /* If we are modposting external module set to 1 */ static int external_module = 0; -/* Warn about section mismatch in vmlinux if set to 1 */ -static int vmlinux_section_warnings = 1; /* Only warn about unresolved symbols */ static int warn_unresolved = 0; /* How a symbol is exported */ @@ -90,26 +88,61 @@ static inline bool strends(const char *str, const char *postfix) return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; } -static int is_vmlinux(const char *modname) +void *do_nofail(void *ptr, const char *expr) { - const char *myname; + if (!ptr) + fatal("Memory allocation failure: %s.\n", expr); - myname = strrchr(modname, '/'); - if (myname) - myname++; - else - myname = modname; + return ptr; +} + +char *read_text_file(const char *filename) +{ + struct stat st; + size_t nbytes; + int fd; + char *buf; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } - return (strcmp(myname, "vmlinux") == 0) || - (strcmp(myname, "vmlinux.o") == 0); + if (fstat(fd, &st) < 0) { + perror(filename); + exit(1); + } + + buf = NOFAIL(malloc(st.st_size + 1)); + + nbytes = st.st_size; + + while (nbytes) { + ssize_t bytes_read; + + bytes_read = read(fd, buf, nbytes); + if (bytes_read < 0) { + perror(filename); + exit(1); + } + + nbytes -= bytes_read; + } + buf[st.st_size] = '\0'; + + close(fd); + + return buf; } -void *do_nofail(void *ptr, const char *expr) +char *get_line(char **stringp) { - if (!ptr) - fatal("Memory allocation failure: %s.\n", expr); + /* do not return the unwanted extra line at EOF */ + if (*stringp && **stringp == '\0') + return NULL; - return ptr; + return strsep(stringp, "\n"); } /* A list of all modules we processed */ @@ -128,24 +161,20 @@ static struct module *find_module(const char *modname) static struct module *new_module(const char *modname) { struct module *mod; - char *p; - mod = NOFAIL(malloc(sizeof(*mod))); + mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); memset(mod, 0, sizeof(*mod)); - p = NOFAIL(strdup(modname)); - - /* strip trailing .o */ - if (strends(p, ".o")) { - p[strlen(p) - 2] = '\0'; - mod->is_dot_o = 1; - } /* add to list */ - mod->name = p; + strcpy(mod->name, modname); + mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); mod->gpl_compatible = -1; mod->next = modules; modules = mod; + if (mod->is_vmlinux) + have_vmlinux = 1; + return mod; } @@ -161,12 +190,9 @@ struct symbol { int crc_valid; char *namespace; unsigned int weak:1; - unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */ - unsigned int kernel:1; /* 1 if symbol is from kernel - * (only for external modules) **/ unsigned int is_static:1; /* 1 if symbol is not global */ enum export export; /* Type of export */ - char name[0]; + char name[]; }; static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; @@ -288,29 +314,32 @@ static enum export export_no(const char *s) return export_unknown; } -static const char *sech_name(struct elf_info *elf, Elf_Shdr *sechdr) +static void *sym_get_data_by_offset(const struct elf_info *info, + unsigned int secindex, unsigned long offset) { - return (void *)elf->hdr + - elf->sechdrs[elf->secindex_strings].sh_offset + - sechdr->sh_name; -} + Elf_Shdr *sechdr = &info->sechdrs[secindex]; -static const char *sec_name(struct elf_info *elf, int secindex) -{ - return sech_name(elf, &elf->sechdrs[secindex]); + if (info->hdr->e_type != ET_REL) + offset -= sechdr->sh_addr; + + return (void *)info->hdr + sechdr->sh_offset + offset; } static void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) { - unsigned int secindex = get_secindex(info, sym); - Elf_Shdr *sechdr = &info->sechdrs[secindex]; - unsigned long offset; + return sym_get_data_by_offset(info, get_secindex(info, sym), + sym->st_value); +} - offset = sym->st_value; - if (info->hdr->e_type != ET_REL) - offset -= sechdr->sh_addr; +static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr) +{ + return sym_get_data_by_offset(info, info->secindex_strings, + sechdr->sh_name); +} - return (void *)info->hdr + sechdr->sh_offset + offset; +static const char *sec_name(const struct elf_info *info, int secindex) +{ + return sech_name(info, &info->sechdrs[secindex]); } #define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) @@ -386,17 +415,15 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, if (!s) { s = new_symbol(name, mod, export); - } else if (!external_module || is_vmlinux(s->module->name) || + } else if (!external_module || s->module->is_vmlinux || s->module == mod) { warn("%s: '%s' exported twice. Previous export was in %s%s\n", mod->name, name, s->module->name, - is_vmlinux(s->module->name) ? "" : ".ko"); + s->module->is_vmlinux ? "" : ".ko"); return s; } s->module = mod; - s->vmlinux = is_vmlinux(mod->name); - s->kernel = 0; s->export = export; return s; } @@ -416,7 +443,7 @@ static void sym_set_crc(const char *name, unsigned int crc) s->crc_valid = 1; } -void *grab_file(const char *filename, unsigned long *size) +static void *grab_file(const char *filename, size_t *size) { struct stat st; void *map = MAP_FAILED; @@ -438,41 +465,7 @@ failed: return map; } -/** - * Return a copy of the next line in a mmap'ed file. - * spaces in the beginning of the line is trimmed away. - * Return a pointer to a static buffer. - **/ -char *get_next_line(unsigned long *pos, void *file, unsigned long size) -{ - static char line[4096]; - int skip = 1; - size_t len = 0; - signed char *p = (signed char *)file + *pos; - char *s = line; - - for (; *pos < size ; (*pos)++) { - if (skip && isspace(*p)) { - p++; - continue; - } - skip = 0; - if (*p != '\n' && (*pos < size)) { - len++; - *s++ = *p++; - if (len > 4095) - break; /* Too long, stop */ - } else { - /* End of string */ - *s = '\0'; - return line; - } - } - /* End of buffer */ - return NULL; -} - -void release_file(void *file, unsigned long size) +static void release_file(void *file, size_t size) { munmap(file, size); } @@ -528,9 +521,8 @@ static int parse_elf(struct elf_info *info, const char *filename) /* Check if file offset is correct */ if (hdr->e_shoff > info->size) { - fatal("section header offset=%lu in file '%s' is bigger than " - "filesize=%lu\n", (unsigned long)hdr->e_shoff, - filename, info->size); + fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n", + (unsigned long)hdr->e_shoff, filename, info->size); return 0; } @@ -683,7 +675,7 @@ static void handle_modversion(const struct module *mod, if (sym->st_shndx == SHN_UNDEF) { warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n", - symname, mod->name, is_vmlinux(mod->name) ? "":".ko"); + symname, mod->name, mod->is_vmlinux ? "" : ".ko"); return; } @@ -705,8 +697,7 @@ static void handle_symbol(struct module *mod, struct elf_info *info, enum export export; const char *name; - if ((!is_vmlinux(mod->name) || mod->is_dot_o) && - strstarts(symname, "__ksymtab")) + if (strstarts(symname, "__ksymtab")) export = export_from_secname(info, get_secindex(info, sym)); else export = export_from_sec(info, get_secindex(info, sym)); @@ -948,7 +939,7 @@ static void check_section(const char *modname, struct elf_info *elf, #define DATA_SECTIONS ".data", ".data.rel" #define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \ - ".kprobes.text", ".cpuidle.text" + ".kprobes.text", ".cpuidle.text", ".noinstr.text" #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \ ".fixup", ".entry.text", ".exception.text", ".text.*", \ ".coldtext" @@ -1752,11 +1743,7 @@ static void check_section_mismatch(const char *modname, struct elf_info *elf, static unsigned int *reloc_location(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) { - Elf_Shdr *sechdrs = elf->sechdrs; - int section = sechdr->sh_info; - - return (void *)elf->hdr + sechdrs[section].sh_offset + - r->r_offset; + return sym_get_data_by_offset(elf, sechdr->sh_info, r->r_offset); } static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) @@ -2005,34 +1992,36 @@ static void read_symbols(const char *modname) 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, "license"); - if (!license && !is_vmlinux(modname)) - warn("missing MODULE_LICENSE() in %s\n" - "see include/linux/module.h for " - "more information\n", modname); - while (license) { - if (license_is_gpl_compatible(license)) - mod->gpl_compatible = 1; - else { - mod->gpl_compatible = 0; - break; + { + char *tmp; + + /* strip trailing .o */ + tmp = NOFAIL(strdup(modname)); + tmp[strlen(tmp) - 2] = '\0'; + mod = new_module(tmp); + free(tmp); + } + + if (!mod->is_vmlinux) { + license = get_modinfo(&info, "license"); + if (!license) + warn("missing MODULE_LICENSE() in %s\n", modname); + while (license) { + if (license_is_gpl_compatible(license)) + mod->gpl_compatible = 1; + else { + mod->gpl_compatible = 0; + break; + } + license = get_next_modinfo(&info, "license", license); } - license = get_next_modinfo(&info, "license", license); - } - namespace = get_modinfo(&info, "import_ns"); - while (namespace) { - add_namespace(&mod->imported_namespaces, namespace); - namespace = get_next_modinfo(&info, "import_ns", namespace); + namespace = get_modinfo(&info, "import_ns"); + while (namespace) { + add_namespace(&mod->imported_namespaces, namespace); + namespace = get_next_modinfo(&info, "import_ns", + namespace); + } } for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { @@ -2070,16 +2059,14 @@ static void read_symbols(const char *modname) } } - if (!is_vmlinux(modname) || vmlinux_section_warnings) - check_sec_ref(mod, modname, &info); + check_sec_ref(mod, modname, &info); - version = get_modinfo(&info, "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); + if (!mod->is_vmlinux) { + version = get_modinfo(&info, "version"); + if (version || all_versions) + get_src_version(modname, mod->srcversion, + sizeof(mod->srcversion) - 1); + } parse_elf_finish(&info); @@ -2143,20 +2130,18 @@ void buf_write(struct buffer *buf, const char *s, int 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("GPL-incompatible module %s%s " - "uses GPL-only symbol '%s'\n", m, e, s); + fatal("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", + m, s); break; case export_unused_gpl: - fatal("GPL-incompatible module %s%s " - "uses GPL-only symbol marked UNUSED '%s'\n", m, e, s); + fatal("GPL-incompatible module %s.ko uses GPL-only symbol marked UNUSED '%s'\n", + m, s); break; case export_gpl_future: - warn("GPL-incompatible module %s%s " - "uses future GPL-only symbol '%s'\n", m, e, s); + warn("GPL-incompatible module %s.ko uses future GPL-only symbol '%s'\n", + m, s); break; case export_plain: case export_unused: @@ -2168,13 +2153,11 @@ static void check_for_gpl_usage(enum export exp, const char *m, const char *s) 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("module %s%s " - "uses symbol '%s' marked UNUSED\n", m, e, s); + warn("module %s.ko uses symbol '%s' marked UNUSED\n", + m, s); break; default: /* ignore */ @@ -2251,6 +2234,7 @@ static void add_header(struct buffer *b, struct module *mod) * Include build-salt.h after module.h in order to * inherit the definitions. */ + buf_printf(b, "#define INCLUDE_VERMAGIC\n"); buf_printf(b, "#include <linux/build-salt.h>\n"); buf_printf(b, "#include <linux/vermagic.h>\n"); buf_printf(b, "#include <linux/compiler.h>\n"); @@ -2348,7 +2332,7 @@ static void add_depends(struct buffer *b, struct module *mod) /* Clear ->seen flag of modules that own symbols needed by this. */ for (s = mod->unres; s; s = s->next) if (s->module) - s->module->seen = is_vmlinux(s->module->name); + s->module->seen = s->module->is_vmlinux; buf_printf(b, "\n"); buf_printf(b, "MODULE_INFO(depends, \""); @@ -2381,6 +2365,25 @@ static void add_srcversion(struct buffer *b, struct module *mod) } } +static void write_buf(struct buffer *b, const char *fname) +{ + FILE *file; + + file = fopen(fname, "w"); + if (!file) { + perror(fname); + exit(1); + } + if (fwrite(b->p, 1, b->pos, file) != b->pos) { + perror(fname); + exit(1); + } + if (fclose(file) != 0) { + perror(fname); + exit(1); + } +} + static void write_if_changed(struct buffer *b, const char *fname) { char *tmp; @@ -2413,32 +2416,24 @@ static void write_if_changed(struct buffer *b, const char *fname) 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); + write_buf(b, fname); } /* parse Module.symvers file. line format: * 0x12345678<tab>symbol<tab>module<tab>export<tab>namespace **/ -static void read_dump(const char *fname, unsigned int kernel) +static void read_dump(const char *fname) { - unsigned long size, pos = 0; - void *file = grab_file(fname, &size); - char *line; + char *buf, *pos, *line; - if (!file) + buf = read_text_file(fname); + if (!buf) /* No symbol versions, silently ignore */ return; - while ((line = get_next_line(&pos, file, size))) { + pos = buf; + + while ((line = get_line(&pos))) { char *symname, *namespace, *modname, *d, *export; unsigned int crc; struct module *mod; @@ -2462,21 +2457,18 @@ static void read_dump(const char *fname, unsigned int kernel) goto fail; mod = find_module(modname); if (!mod) { - if (is_vmlinux(modname)) - have_vmlinux = 1; mod = new_module(modname); - mod->skip = 1; + mod->from_dump = 1; } s = sym_add_exported(symname, mod, export_no(export)); - s->kernel = kernel; s->is_static = 0; sym_set_crc(symname, crc); sym_update_namespace(symname, namespace); } - release_file(file, size); + free(buf); return; fail: - release_file(file, size); + free(buf); fatal("parse error in symbol dump file\n"); } @@ -2488,7 +2480,7 @@ static int dump_sym(struct symbol *sym) { if (!external_module) return 1; - if (sym->vmlinux || sym->kernel) + if (sym->module->from_dump) return 0; return 1; } @@ -2514,7 +2506,7 @@ static void write_dump(const char *fname) symbol = symbol->next; } } - write_if_changed(&buf, fname); + write_buf(&buf, fname); free(buf.p); } @@ -2526,7 +2518,7 @@ static void write_namespace_deps_files(const char *fname) for (mod = modules; mod; mod = mod->next) { - if (mod->skip || !mod->missing_namespaces) + if (mod->from_dump || !mod->missing_namespaces) continue; buf_printf(&ns_deps_buf, "%s.ko:", mod->name); @@ -2541,8 +2533,8 @@ static void write_namespace_deps_files(const char *fname) free(ns_deps_buf.p); } -struct ext_sym_list { - struct ext_sym_list *next; +struct dump_list { + struct dump_list *next; const char *file; }; @@ -2550,28 +2542,24 @@ int main(int argc, char **argv) { struct module *mod; struct buffer buf = { }; - char *kernel_read = NULL; char *missing_namespace_deps = NULL; char *dump_write = NULL, *files_source = NULL; int opt; int err; int n; - struct ext_sym_list *extsym_iter; - struct ext_sym_list *extsym_start = NULL; + struct dump_list *dump_read_start = NULL; + struct dump_list **dump_read_iter = &dump_read_start; - while ((opt = getopt(argc, argv, "i:e:mnsT:o:awENd:")) != -1) { + while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) { switch (opt) { - case 'i': - kernel_read = optarg; - external_module = 1; - break; case 'e': external_module = 1; - extsym_iter = - NOFAIL(malloc(sizeof(*extsym_iter))); - extsym_iter->next = extsym_start; - extsym_iter->file = optarg; - extsym_start = extsym_iter; + break; + case 'i': + *dump_read_iter = + NOFAIL(calloc(1, sizeof(**dump_read_iter))); + (*dump_read_iter)->file = optarg; + dump_read_iter = &(*dump_read_iter)->next; break; case 'm': modversions = 1; @@ -2585,9 +2573,6 @@ int main(int argc, char **argv) case 'a': all_versions = 1; break; - case 's': - vmlinux_section_warnings = 0; - break; case 'T': files_source = optarg; break; @@ -2608,13 +2593,13 @@ int main(int argc, char **argv) } } - if (kernel_read) - read_dump(kernel_read, 1); - while (extsym_start) { - read_dump(extsym_start->file, 0); - extsym_iter = extsym_start->next; - free(extsym_start); - extsym_start = extsym_iter; + while (dump_read_start) { + struct dump_list *tmp; + + read_dump(dump_read_start->file); + tmp = dump_read_start->next; + free(dump_read_start); + dump_read_start = tmp; } while (optind < argc) @@ -2623,12 +2608,19 @@ int main(int argc, char **argv) if (files_source) read_symbols_from_files(files_source); + /* + * When there's no vmlinux, don't print warnings about + * unresolved symbols (since there'll be too many ;) + */ + if (!have_vmlinux) + warn("Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped.\n"); + err = 0; for (mod = modules; mod; mod = mod->next) { char fname[PATH_MAX]; - if (mod->skip) + if (mod->is_vmlinux || mod->from_dump) continue; buf.pos = 0; @@ -2661,13 +2653,6 @@ int main(int argc, char **argv) struct symbol *s; for (s = symbolhash[n]; s; s = s->next) { - /* - * Do not check "vmlinux". This avoids the same warnings - * shown twice, and false-positives for ARCH=um. - */ - if (is_vmlinux(s->module->name) && !s->module->is_dot_o) - continue; - if (s->is_static) warn("\"%s\" [%s] is a static %s\n", s->name, s->module->name, |