From 1d2ad084800edad81cdc955304272742b10721c7 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Sun, 6 Mar 2022 20:56:07 +0100 Subject: s390/nospec: add an option to use thunk-extern Currently with -mindirect-branch=thunk and -mfunction-return=thunk compiler options expoline thunks are put into individual COMDAT group sections. s390 is the only architecture which has group sections and it has implications for kpatch and objtool tools support. Using -mindirect-branch=thunk-extern and -mfunction-return=thunk-extern is an alternative, which comes with a need to generate all required expoline thunks manually. Unfortunately modules area is too far away from the kernel image, and expolines from the kernel image cannon be used. But since all new distributions (except Debian) build kernels for machine generations newer than z10, where "exrl" instruction is available, that leaves only 16 expolines thunks possible. Provide an option to build the kernel with -mindirect-branch=thunk-extern and -mfunction-return=thunk-extern for z10 or newer. This also requires to postlink expoline thunks into all modules explicitly. Currently modules already contain most expolines anyhow. Unfortunately -mindirect-branch=thunk-extern and -mfunction-return=thunk-extern options support is broken in gcc <= 11.2. Additional compile test is required to verify proper gcc support. Acked-by: Ilya Leoshkevich Co-developed-by: Sumanth Korikkar Signed-off-by: Sumanth Korikkar Signed-off-by: Vasily Gorbik --- scripts/mod/modpost.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 6bfa33217914..dbc0aaf69e43 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -658,6 +658,11 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname) strstarts(symname, "_savevr_") || strcmp(symname, ".TOC.") == 0) return 1; + + if (info->hdr->e_machine == EM_S390) + /* Expoline thunks are linked on all kernel modules during final link of .ko */ + if (strstarts(symname, "__s390_indirect_jump_r")) + return 1; /* Do not ignore this symbol */ return 0; } -- cgit From 4d94f910e79a349b00a4f8aab6f3ae87129d8c5a Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 8 Mar 2022 22:56:13 +0100 Subject: Kbuild: use -Wdeclaration-after-statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel is moving from using `-std=gnu89` to `-std=gnu11`, permitting the use of additional C11 features such as for-loop initial declarations. One contentious aspect of C99 is that it permits mixed declarations and code, and for now at least, it seems preferable to enforce that declarations must come first. These warnings were already enabled in the kernel itself, but not for KBUILD_USERCFLAGS or the compat VDSO on arch/arm64, which uses a separate set of CFLAGS. This patch fixes an existing violation in modpost.c, which is not reported because of the missing flag in KBUILD_USERCFLAGS: | scripts/mod/modpost.c: In function ‘match’: | scripts/mod/modpost.c:837:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] | 837 | const char *endp = p + strlen(p) - 1; | | ^~~~~ Signed-off-by: Mark Rutland [arnd: don't add a duplicate flag to the default set, update changelog] Signed-off-by: Arnd Bergmann Reviewed-by: Nathan Chancellor Reviewed-by: Nick Desaulniers Tested-by: Sedat Dilek # LLVM/Clang v13.0.0 (x86-64) Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 6bfa33217914..fe693304b120 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -833,8 +833,10 @@ static int match(const char *sym, const char * const pat[]) { const char *p; while (*pat) { + const char *endp; + p = *pat++; - const char *endp = p + strlen(p) - 1; + endp = p + strlen(p) - 1; /* "*foo*" */ if (*p == '*' && *endp == '*') { -- cgit From d31ed5d767c0452b4f49846d80a0bfeafa3a4ded Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 18 Mar 2022 12:19:27 +0100 Subject: kbuild: Fixup the IBT kbuild changes Masahiro-san deemed my kbuild changes to support whole module objtool runs too terrible to live and gracefully provided an alternative. Suggested-by: Masahiro Yamada Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/CAK7LNAQ2mYMnOKMQheVi+6byUFE3KEkjm1zcndNUfe0tORGvug@mail.gmail.com --- scripts/mod/modpost.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 6bfa33217914..09c3ab0a9b37 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1989,9 +1989,9 @@ static char *remove_dot(char *s) if (m && (s[n + m] == '.' || s[n + m] == 0)) s[n] = 0; - /* strip trailing .lto */ - if (strends(s, ".lto")) - s[strlen(s) - 4] = '\0'; + /* strip trailing .prelink */ + if (strends(s, ".prelink")) + s[strlen(s) - 8] = '\0'; } return s; } @@ -2015,9 +2015,9 @@ static void read_symbols(const char *modname) /* strip trailing .o */ tmp = NOFAIL(strdup(modname)); tmp[strlen(tmp) - 2] = '\0'; - /* strip trailing .lto */ - if (strends(tmp, ".lto")) - tmp[strlen(tmp) - 4] = '\0'; + /* strip trailing .prelink */ + if (strends(tmp, ".prelink")) + tmp[strlen(tmp) - 8] = '\0'; mod = new_module(tmp); free(tmp); } -- cgit From bf5c0c2231bcab677e5cdfb7f73e6c79f6d8c2d4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 2 Apr 2022 00:56:10 +0900 Subject: modpost: restore the warning message for missing symbol versions This log message was accidentally chopped off. I was wondering why this happened, but checking the ML log, Mark precisely followed my suggestion [1]. I just used "..." because I was too lazy to type the sentence fully. Sorry for the confusion. [1]: https://lore.kernel.org/all/CAK7LNAR6bXXk9-ZzZYpTqzFqdYbQsZHmiWspu27rtsFxvfRuVA@mail.gmail.com/ Fixes: 4a6795933a89 ("kbuild: modpost: Explicitly warn about unprototyped symbols") Signed-off-by: Masahiro Yamada Acked-by: Mark Brown Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d10f93aac1c8..ed9d056d2108 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -674,7 +674,7 @@ static void handle_modversion(const struct module *mod, unsigned int crc; if (sym->st_shndx == SHN_UNDEF) { - warn("EXPORT symbol \"%s\" [%s%s] version ...\n" + warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" "Is \"%s\" prototyped in ?\n", symname, mod->name, mod->is_vmlinux ? "" : ".ko", symname); -- cgit From 7ce3e410e0188ce7ca65b49c90cff2863d6e232e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 5 Apr 2022 20:33:51 +0900 Subject: modpost: remove useless export_from_sec() With commit 1743694eb235 ("modpost: stop symbol preloading for modversion CRC") applied, now export_from_sec() is useless. handle_symbol() is called for every symbol in the ELF. When 'symname' does not start with "__ksymtab", export_from_sec() is called, and the returned value is stored in 'export'. It is used in the last part of handle_symbol(): if (strstarts(symname, "__ksymtab_")) { name = symname + strlen("__ksymtab_"); sym_add_exported(name, mod, export); } 'export' is used only when 'symname' starts with "__ksymtab_". So, the value returned by export_from_sec() is never used. Remove useless export_from_sec(). This makes further cleanups possible. I put the temporary code: export = export_unknown; Otherwise, I would get the compiler warning: warning: 'export' may be used uninitialized in this function [-Wmaybe-uninitialized] This is apparently false positive because if (strstarts(symname, "__ksymtab_") ... is a stronger condition than: if (strstarts(symname, "__ksymtab") Anyway, this part will be cleaned up by the next commit. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index ed9d056d2108..eebb32689816 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -369,16 +369,6 @@ static enum export export_from_secname(struct elf_info *elf, unsigned int sec) return export_unknown; } -static enum export export_from_sec(struct elf_info *elf, unsigned int sec) -{ - if (sec == elf->export_sec) - return export_plain; - else if (sec == elf->export_gpl_sec) - return export_gpl; - else - return export_unknown; -} - static const char *namespace_from_kstrtabns(const struct elf_info *info, const Elf_Sym *sym) { @@ -576,10 +566,7 @@ static int parse_elf(struct elf_info *info, const char *filename) fatal("%s has NOBITS .modinfo\n", filename); info->modinfo = (void *)hdr + sechdrs[i].sh_offset; info->modinfo_len = sechdrs[i].sh_size; - } else if (strcmp(secname, "__ksymtab") == 0) - info->export_sec = i; - else if (strcmp(secname, "__ksymtab_gpl") == 0) - info->export_gpl_sec = i; + } if (sechdrs[i].sh_type == SHT_SYMTAB) { unsigned int sh_link_idx; @@ -703,7 +690,7 @@ static void handle_symbol(struct module *mod, struct elf_info *info, if (strstarts(symname, "__ksymtab")) export = export_from_secname(info, get_secindex(info, sym)); else - export = export_from_sec(info, get_secindex(info, sym)); + export = export_unknown; switch (sym->st_shndx) { case SHN_COMMON: -- cgit From 535b3e05f435698f8f661d9e6449beb5791fff59 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 5 Apr 2022 20:33:52 +0900 Subject: modpost: move export_from_secname() call to more relevant place The assigned 'export' is only used when if (strstarts(symname, "__ksymtab_")) is met. The else-part of the assignment is the dead code. Move the export_from_secname() call to where it is used. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index eebb32689816..f9e54247ae1d 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -684,14 +684,8 @@ static void handle_modversion(const struct module *mod, static void handle_symbol(struct module *mod, struct elf_info *info, const Elf_Sym *sym, const char *symname) { - enum export export; const char *name; - if (strstarts(symname, "__ksymtab")) - export = export_from_secname(info, get_secindex(info, sym)); - else - export = export_unknown; - switch (sym->st_shndx) { case SHN_COMMON: if (strstarts(symname, "__gnu_lto_")) { @@ -726,7 +720,11 @@ static void handle_symbol(struct module *mod, struct elf_info *info, default: /* All exported symbols */ if (strstarts(symname, "__ksymtab_")) { + enum export export; + name = symname + strlen("__ksymtab_"); + export = export_from_secname(info, + get_secindex(info, sym)); sym_add_exported(name, mod, export); } if (strcmp(symname, "init_module") == 0) -- cgit From b5f1a52a59eb810f68c96d1cea7cf1256c39956c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 5 Apr 2022 20:33:53 +0900 Subject: modpost: remove redundant initializes for static variables These are initialized with zeros without explicit initializers. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f9e54247ae1d..2a202764ff48 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -23,15 +23,15 @@ #include "../../include/linux/license.h" /* Are we using CONFIG_MODVERSIONS? */ -static int modversions = 0; +static int modversions; /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ -static int all_versions = 0; +static int all_versions; /* If we are modposting external module set to 1 */ -static int external_module = 0; +static int external_module; /* Only warn about unresolved symbols */ -static int warn_unresolved = 0; +static int warn_unresolved; /* How a symbol is exported */ -static int sec_mismatch_count = 0; +static int sec_mismatch_count; static int sec_mismatch_warn_only = true; /* ignore missing files */ static int ignore_missing_files; -- cgit From 79f646e8654b6b8e4f7dda456ec3eabd51052041 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 5 Apr 2022 20:33:54 +0900 Subject: modpost: remove annoying namespace_from_kstrtabns() There are two call sites for sym_update_namespace(). When the symbol has no namespace, s->namespace is set to NULL, but the conversion from "" to NULL is done in two different places. [1] read_symbols() This gets the namespace from __kstrtabns_. If the symbol has no namespace, sym_get_data(info, sym) returns the empty string "". namespace_from_kstrtabns() converts it to NULL before it is passed to sym_update_namespace(). [2] read_dump() This gets the namespace from the dump file, *.symvers. If the symbol has no namespace, the 'namespace' is the empty string "", which is directly passed into sym_update_namespace(). The conversion from "" to NULL is done in sym_update_namespace(). namespace_from_kstrtabns() exists only for creating this inconsistency. Remove namespace_from_kstrtabns() so that sym_update_namespace() is consistently passed with "" instead of NULL. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 2a202764ff48..522d5249d196 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -369,13 +369,6 @@ static enum export export_from_secname(struct elf_info *elf, unsigned int sec) return export_unknown; } -static const char *namespace_from_kstrtabns(const struct elf_info *info, - const Elf_Sym *sym) -{ - const char *value = sym_get_data(info, sym); - return value[0] ? value : NULL; -} - static void sym_update_namespace(const char *symname, const char *namespace) { struct symbol *s = find_symbol(symname); @@ -391,8 +384,7 @@ static void sym_update_namespace(const char *symname, const char *namespace) } free(s->namespace); - s->namespace = - namespace && namespace[0] ? NOFAIL(strdup(namespace)) : NULL; + s->namespace = namespace[0] ? NOFAIL(strdup(namespace)) : NULL; } /** @@ -2049,9 +2041,7 @@ static void read_symbols(const char *modname) /* Apply symbol namespaces from __kstrtabns_ entries. */ if (strstarts(symname, "__kstrtabns_")) sym_update_namespace(symname + strlen("__kstrtabns_"), - namespace_from_kstrtabns(&info, - sym)); - + sym_get_data(&info, sym)); if (strstarts(symname, "__crc_")) handle_modversion(mod, &info, sym, symname + strlen("__crc_")); -- cgit From 15a28c7c72917f96820e9e9ccd113606363ba3ac Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 Apr 2022 04:07:45 +0900 Subject: modpost: use snprintf() instead of sprintf() for safety Use snprintf() to avoid the potential buffer overflow, and also check the return value to detect the too long path. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 522d5249d196..141370ebbfd3 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2560,6 +2560,7 @@ int main(int argc, char **argv) for (mod = modules; mod; mod = mod->next) { char fname[PATH_MAX]; + int ret; if (mod->is_vmlinux || mod->from_dump) continue; @@ -2578,7 +2579,12 @@ int main(int argc, char **argv) add_moddevtable(&buf, mod); add_srcversion(&buf, mod); - sprintf(fname, "%s.mod.c", mod->name); + ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); + if (ret >= sizeof(fname)) { + error("%s: too long path was truncated\n", fname); + continue; + } + write_if_changed(&buf, fname); } -- cgit From c155a47d83ab0b5ee96ebe1721057cba1936d0d5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 Apr 2022 04:07:46 +0900 Subject: modpost: do not write out any file when error occurred If an error occurs, modpost will fail anyway. Do not write out any content (, which might be invalid). Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 141370ebbfd3..f0d48f65fb33 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2333,6 +2333,9 @@ static void write_buf(struct buffer *b, const char *fname) { FILE *file; + if (error_occurred) + return; + file = fopen(fname, "w"); if (!file) { perror(fname); -- cgit From 594ade3eef3f2d458902ced2cb2614dfae8558de Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 Apr 2022 04:07:47 +0900 Subject: modpost: remove stale comment about sym_add_exported() The description, it may have already been added without a CRC, in this case just update the CRC ... is no longer valid. In the old days, this function was used to update the CRC as well. Commit 040fcc819a2e ("kbuild: improved modversioning support for external modules") started to use a separate function (sym_update_crc) for updating the CRC. The first part, "Add an exported symbol" is correct, but it is too obvious from the function name. Drop this comment entirely. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f0d48f65fb33..c7cfeeb088f7 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -387,10 +387,6 @@ static void sym_update_namespace(const char *symname, const char *namespace) s->namespace = namespace[0] ? NOFAIL(strdup(namespace)) : NULL; } -/** - * Add an exported symbol - it may have already been added without a - * CRC, in this case just update the CRC - **/ static struct symbol *sym_add_exported(const char *name, struct module *mod, enum export export) { -- cgit From 23beb44a0effaad1bd627fd134f0301c622deba7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 Apr 2022 04:07:48 +0900 Subject: modpost: add a separate error for exported symbols without definition It took me a while to understand the intent of "exp->module == mod". This code goes back to 2003. [1] The commit is not in this git repository, and might be worth a little explanation. You can add EXPORT_SYMBOL() without having its definition in the same file (but you need to put a declaration). This is typical when EXPORT_SYMBOL() is added in a C file, but the actual implementation is in a separate assembly file. One example is arch/arm/kernel/armksyms.c In the old days, EXPORT_SYMBOL() was only available in C files (but this limitation does not exist any more). If you forget to add the definition, this error occurs. Add a separate, clearer message for this case. It should be an error even if KBUILD_MODPOST_WARN is given. [1]: https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=2763b6bcb96e6a38a2fe31108fe5759ec5bcc80a Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index c7cfeeb088f7..969a081dba62 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2147,13 +2147,18 @@ static void check_exports(struct module *mod) for (s = mod->unres; s; s = s->next) { const char *basename; exp = find_symbol(s->name); - if (!exp || exp->module == mod) { + if (!exp) { if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS) modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR, "\"%s\" [%s.ko] undefined!\n", s->name, mod->name); continue; } + if (exp->module == mod) { + error("\"%s\" [%s.ko] was exported without definition\n", + s->name, mod->name); + continue; + } basename = strrchr(mod->name, '/'); if (basename) basename++; -- cgit From 4cae77ac582b430d6ad6fbf0e1b23248997ceac8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 Apr 2022 04:07:49 +0900 Subject: modpost: retrieve the module dependency and CRCs in check_exports() Do not repeat the similar code. It is simpler to do this in check_exports() instead of add_versions(). Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 969a081dba62..f9cbb6b6b7a5 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2159,6 +2159,11 @@ static void check_exports(struct module *mod) s->name, mod->name); continue; } + + s->module = exp->module; + s->crc_valid = exp->crc_valid; + s->crc = exp->crc; + basename = strrchr(mod->name, '/'); if (basename) basename++; @@ -2251,16 +2256,7 @@ static void add_staging_flag(struct buffer *b, const char *name) **/ static void add_versions(struct buffer *b, struct module *mod) { - struct symbol *s, *exp; - - for (s = mod->unres; s; s = s->next) { - exp = find_symbol(s->name); - if (!exp || exp->module == mod) - continue; - s->module = exp->module; - s->crc_valid = exp->crc_valid; - s->crc = exp->crc; - } + struct symbol *s; if (!modversions) return; -- cgit From 70ddb48db4aaddd3c2a7d8802463e15b21ce8525 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 25 Apr 2022 04:07:56 +0900 Subject: modpost: move struct namespace_list to modpost.c There is no good reason to define struct namespace_list in modpost.h struct module has pointers to struct namespace_list, but that does not require the definition of struct namespace_list. Move it to modpost.c. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f9cbb6b6b7a5..689a34229809 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -270,6 +270,11 @@ static struct symbol *find_symbol(const char *name) return NULL; } +struct namespace_list { + struct namespace_list *next; + char namespace[]; +}; + static bool contains_namespace(struct namespace_list *list, const char *namespace) { -- cgit From 58e01fcae18c9d01be701bec9e9a8ee58269c7c1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:07 +0900 Subject: modpost: use bool type where appropriate Use 'bool' to clarify that the valid value is true or false. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 689a34229809..a6035ab78cd8 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -23,20 +23,20 @@ #include "../../include/linux/license.h" /* Are we using CONFIG_MODVERSIONS? */ -static int modversions; +static bool modversions; /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ -static int all_versions; +static bool all_versions; /* If we are modposting external module set to 1 */ -static int external_module; +static bool external_module; /* Only warn about unresolved symbols */ -static int warn_unresolved; +static bool warn_unresolved; /* How a symbol is exported */ static int sec_mismatch_count; -static int sec_mismatch_warn_only = true; +static bool sec_mismatch_warn_only = true; /* ignore missing files */ -static int ignore_missing_files; +static bool ignore_missing_files; /* If set to 1, only warn (instead of error) about missing ns imports */ -static int allow_missing_ns_imports; +static bool allow_missing_ns_imports; static bool error_occurred; @@ -202,11 +202,11 @@ static struct module *new_module(const char *modname) struct symbol { struct symbol *next; struct module *module; - unsigned int crc; - int crc_valid; char *namespace; - unsigned int weak:1; - unsigned int is_static:1; /* 1 if symbol is not global */ + unsigned int crc; + bool crc_valid; + bool weak; + bool is_static; /* true if symbol is not global */ enum export export; /* Type of export */ char name[]; }; @@ -230,7 +230,7 @@ static inline unsigned int tdb_hash(const char *name) * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module **/ -static struct symbol *alloc_symbol(const char *name, unsigned int weak, +static struct symbol *alloc_symbol(const char *name, bool weak, struct symbol *next) { struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); @@ -239,7 +239,7 @@ static struct symbol *alloc_symbol(const char *name, unsigned int weak, strcpy(s->name, name); s->weak = weak; s->next = next; - s->is_static = 1; + s->is_static = true; return s; } @@ -250,7 +250,7 @@ static struct symbol *new_symbol(const char *name, struct module *module, unsigned int hash; hash = tdb_hash(name) % SYMBOL_HASH_SIZE; - symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); + symbolhash[hash] = alloc_symbol(name, false, symbolhash[hash]); return symbolhash[hash]; } @@ -424,7 +424,7 @@ static void sym_set_crc(const char *name, unsigned int crc) return; s->crc = crc; - s->crc_valid = 1; + s->crc_valid = true; } static void *grab_file(const char *filename, size_t *size) @@ -721,9 +721,9 @@ static void handle_symbol(struct module *mod, struct elf_info *info, sym_add_exported(name, mod, export); } if (strcmp(symname, "init_module") == 0) - mod->has_init = 1; + mod->has_init = true; if (strcmp(symname, "cleanup_module") == 0) - mod->has_cleanup = 1; + mod->has_cleanup = true; break; } } @@ -2058,7 +2058,7 @@ static void read_symbols(const char *modname) sym->st_name)); if (s) - s->is_static = 0; + s->is_static = false; } } @@ -2078,7 +2078,7 @@ static void read_symbols(const char *modname) * the automatic versioning doesn't pick it up, but it's really * important anyhow */ if (modversions) - mod->unres = alloc_symbol("module_layout", 0, mod->unres); + mod->unres = alloc_symbol("module_layout", false, mod->unres); } static void read_symbols_from_files(const char *filename) @@ -2310,7 +2310,7 @@ static void add_depends(struct buffer *b, struct module *mod) if (s->module->seen) continue; - s->module->seen = 1; + s->module->seen = true; p = strrchr(s->module->name, '/'); if (p) p++; @@ -2427,10 +2427,10 @@ static void read_dump(const char *fname) mod = find_module(modname); if (!mod) { mod = new_module(modname); - mod->from_dump = 1; + mod->from_dump = true; } s = sym_add_exported(symname, mod, export_no(export)); - s->is_static = 0; + s->is_static = false; sym_set_crc(symname, crc); sym_update_namespace(symname, namespace); } @@ -2508,7 +2508,7 @@ int main(int argc, char **argv) while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) { switch (opt) { case 'e': - external_module = 1; + external_module = true; break; case 'i': *dump_read_iter = @@ -2517,28 +2517,28 @@ int main(int argc, char **argv) dump_read_iter = &(*dump_read_iter)->next; break; case 'm': - modversions = 1; + modversions = true; break; case 'n': - ignore_missing_files = 1; + ignore_missing_files = true; break; case 'o': dump_write = optarg; break; case 'a': - all_versions = 1; + all_versions = true; break; case 'T': files_source = optarg; break; case 'w': - warn_unresolved = 1; + warn_unresolved = true; break; case 'E': sec_mismatch_warn_only = false; break; case 'N': - allow_missing_ns_imports = 1; + allow_missing_ns_imports = true; break; case 'd': missing_namespace_deps = optarg; -- cgit From 5066743e4c2f702c1da8ba00a1dc217527a0ab7c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:08 +0900 Subject: modpost: change mod->gpl_compatible to bool type Currently, mod->gpl_compatible is tristate; it is set to -1 by default, then to 1 or 0 when MODULE_LICENSE() is found. Maybe, -1 was chosen to represent the 'unknown' license, but it is not useful. The current code: if (!mod->gpl_compatible) check_for_gpl_usage(exp->export, basename, exp->name); ... only cares whether gpl_compatible is zero or not. Change it to a bool type with the initial value 'true', which has no functional change. The default value should be 'true' instead of 'false'. Since commit 1d6cd3929360 ("modpost: turn missing MODULE_LICENSE() into error"), unknown module license is an error. The error message, "missing MODULE_LICENSE()" is enough to explain the issue. It is not sensible to show another message, "GPL-incompatible module ... uses GPL-only symbol". Add comments to explain this. While I was here, I renamed gpl_compatible to is_gpl_compatible for clarification, and also slightly refactored the code. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a6035ab78cd8..25066dc25790 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -187,7 +187,14 @@ static struct module *new_module(const char *modname) /* add to list */ strcpy(mod->name, modname); mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); - mod->gpl_compatible = -1; + + /* + * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() + * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue + * modpost will exit wiht error anyway. + */ + mod->is_gpl_compatible = true; + mod->next = modules; modules = mod; @@ -2012,10 +2019,8 @@ static void read_symbols(const char *modname) if (!license) error("missing MODULE_LICENSE() in %s\n", modname); while (license) { - if (license_is_gpl_compatible(license)) - mod->gpl_compatible = 1; - else { - mod->gpl_compatible = 0; + if (!license_is_gpl_compatible(license)) { + mod->is_gpl_compatible = false; break; } license = get_next_modinfo(&info, "license", license); @@ -2183,7 +2188,7 @@ static void check_exports(struct module *mod) add_namespace(&mod->missing_namespaces, exp->namespace); } - if (!mod->gpl_compatible) + if (!mod->is_gpl_compatible) check_for_gpl_usage(exp->export, basename, exp->name); } } -- cgit From 325eba05e8ab53a9182a2734f0986c15e5f87349 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:10 +0900 Subject: modpost: traverse modules in order Currently, modpost manages modules in a singly linked list; it adds a new node to the head, and traverses the list from new to old. It works, but the error messages are shown in the reverse order. If you have a Makefile like this: obj-m += foo.o bar.o then, modpost shows error messages in bar.o, foo.o, in this order. Use a doubly linked list to keep the order in modules.order; use list_add_tail() for the node addition and list_for_each_entry() for the list traverse. Now that the kernel's list macros have been imported to modpost, I will use them actively going forward. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 25066dc25790..1b9dcb44621c 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -165,16 +165,17 @@ char *get_line(char **stringp) } /* A list of all modules we processed */ -static struct module *modules; +LIST_HEAD(modules); static struct module *find_module(const char *modname) { struct module *mod; - for (mod = modules; mod; mod = mod->next) + list_for_each_entry(mod, &modules, list) { if (strcmp(mod->name, modname) == 0) - break; - return mod; + return mod; + } + return NULL; } static struct module *new_module(const char *modname) @@ -184,7 +185,6 @@ static struct module *new_module(const char *modname) mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); memset(mod, 0, sizeof(*mod)); - /* add to list */ strcpy(mod->name, modname); mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); @@ -195,8 +195,7 @@ static struct module *new_module(const char *modname) */ mod->is_gpl_compatible = true; - mod->next = modules; - modules = mod; + list_add_tail(&mod->list, &modules); return mod; } @@ -2477,7 +2476,7 @@ static void write_namespace_deps_files(const char *fname) struct namespace_list *ns; struct buffer ns_deps_buf = {}; - for (mod = modules; mod; mod = mod->next) { + list_for_each_entry(mod, &modules, list) { if (mod->from_dump || !mod->missing_namespaces) continue; @@ -2568,7 +2567,7 @@ int main(int argc, char **argv) if (files_source) read_symbols_from_files(files_source); - for (mod = modules; mod; mod = mod->next) { + list_for_each_entry(mod, &modules, list) { char fname[PATH_MAX]; int ret; -- cgit From e882e89bcf1d54da2e4388570325774c3e3078a9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:11 +0900 Subject: modpost: add sym_add_unresolved() helper Add a small helper, sym_add_unresolved() to ease the further refactoring. Remove the 'weak' argument from alloc_symbol() because it is sensible only for unresolved symbols. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 1b9dcb44621c..59d817692893 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -236,14 +236,12 @@ static inline unsigned int tdb_hash(const char *name) * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module **/ -static struct symbol *alloc_symbol(const char *name, bool weak, - struct symbol *next) +static struct symbol *alloc_symbol(const char *name, struct symbol *next) { struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); memset(s, 0, sizeof(*s)); strcpy(s->name, name); - s->weak = weak; s->next = next; s->is_static = true; return s; @@ -256,11 +254,17 @@ static struct symbol *new_symbol(const char *name, struct module *module, unsigned int hash; hash = tdb_hash(name) % SYMBOL_HASH_SIZE; - symbolhash[hash] = alloc_symbol(name, false, symbolhash[hash]); + symbolhash[hash] = alloc_symbol(name, symbolhash[hash]); return symbolhash[hash]; } +static void sym_add_unresolved(const char *name, struct module *mod, bool weak) +{ + mod->unres = alloc_symbol(name, mod->unres); + mod->unres->weak = weak; +} + static struct symbol *find_symbol(const char *name) { struct symbol *s; @@ -712,9 +716,8 @@ static void handle_symbol(struct module *mod, struct elf_info *info, } } - mod->unres = alloc_symbol(symname, - ELF_ST_BIND(sym->st_info) == STB_WEAK, - mod->unres); + sym_add_unresolved(symname, mod, + ELF_ST_BIND(sym->st_info) == STB_WEAK); break; default: /* All exported symbols */ @@ -2082,7 +2085,7 @@ static void read_symbols(const char *modname) * the automatic versioning doesn't pick it up, but it's really * important anyhow */ if (modversions) - mod->unres = alloc_symbol("module_layout", false, mod->unres); + sym_add_unresolved("module_layout", mod, false); } static void read_symbols_from_files(const char *filename) -- cgit From 8a69152be9a8c1f7a02c6b8410b35c68cb200f6d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:12 +0900 Subject: modpost: traverse unresolved symbols in order Currently, modpost manages unresolved in a singly linked list; it adds a new node to the head, and traverses the list from new to old. Use a doubly linked list to keep the order in the symbol table in the ELF file. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 59d817692893..70a66ce9ffd9 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -185,6 +185,8 @@ static struct module *new_module(const char *modname) mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); memset(mod, 0, sizeof(*mod)); + INIT_LIST_HEAD(&mod->unresolved_symbols); + strcpy(mod->name, modname); mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); @@ -207,6 +209,7 @@ static struct module *new_module(const char *modname) struct symbol { struct symbol *next; + struct list_head list; /* link to module::unresolved_symbols */ struct module *module; char *namespace; unsigned int crc; @@ -261,8 +264,12 @@ static struct symbol *new_symbol(const char *name, struct module *module, static void sym_add_unresolved(const char *name, struct module *mod, bool weak) { - mod->unres = alloc_symbol(name, mod->unres); - mod->unres->weak = weak; + struct symbol *sym; + + sym = alloc_symbol(name, NULL); + sym->weak = weak; + + list_add_tail(&sym->list, &mod->unresolved_symbols); } static struct symbol *find_symbol(const char *name) @@ -2156,7 +2163,7 @@ static void check_exports(struct module *mod) { struct symbol *s, *exp; - for (s = mod->unres; s; s = s->next) { + list_for_each_entry(s, &mod->unresolved_symbols, list) { const char *basename; exp = find_symbol(s->name); if (!exp) { @@ -2277,7 +2284,7 @@ static void add_versions(struct buffer *b, struct module *mod) buf_printf(b, "static const struct modversion_info ____versions[]\n"); buf_printf(b, "__used __section(\"__versions\") = {\n"); - for (s = mod->unres; s; s = s->next) { + list_for_each_entry(s, &mod->unresolved_symbols, list) { if (!s->module) continue; if (!s->crc_valid) { @@ -2303,13 +2310,14 @@ static void add_depends(struct buffer *b, struct module *mod) int first = 1; /* Clear ->seen flag of modules that own symbols needed by this. */ - for (s = mod->unres; s; s = s->next) + list_for_each_entry(s, &mod->unresolved_symbols, list) { if (s->module) s->module->seen = s->module->is_vmlinux; + } buf_printf(b, "\n"); buf_printf(b, "MODULE_INFO(depends, \""); - for (s = mod->unres; s; s = s->next) { + list_for_each_entry(s, &mod->unresolved_symbols, list) { const char *p; if (!s->module) continue; -- cgit From 4484054816cab940fc2fde23fa989174fec889d0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:13 +0900 Subject: modpost: use doubly linked list for dump_lists This looks easier to understand (just because this is a pattern in the kernel code). No functional change is intended. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 70a66ce9ffd9..a6a55f359396 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2505,7 +2505,7 @@ static void write_namespace_deps_files(const char *fname) } struct dump_list { - struct dump_list *next; + struct list_head list; const char *file; }; @@ -2517,8 +2517,8 @@ int main(int argc, char **argv) char *dump_write = NULL, *files_source = NULL; int opt; int n; - struct dump_list *dump_read_start = NULL; - struct dump_list **dump_read_iter = &dump_read_start; + LIST_HEAD(dump_lists); + struct dump_list *dl, *dl2; while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) { switch (opt) { @@ -2526,10 +2526,9 @@ int main(int argc, char **argv) external_module = true; 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; + dl = NOFAIL(malloc(sizeof(*dl))); + dl->file = optarg; + list_add_tail(&dl->list, &dump_lists); break; case 'm': modversions = true; @@ -2563,13 +2562,10 @@ int main(int argc, char **argv) } } - 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; + list_for_each_entry_safe(dl, dl2, &dump_lists, list) { + read_dump(dl->file); + list_del(&dl->list); + free(dl); } while (optind < argc) -- cgit From ab489d6002fc27dc5db6d66f121da6fc0bda13ad Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:14 +0900 Subject: modpost: traverse the namespace_list in order Use the doubly linked list to traverse the list in the added order. This makes the code more consistent. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a6a55f359396..8bdde738803f 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -186,6 +186,8 @@ static struct module *new_module(const char *modname) memset(mod, 0, sizeof(*mod)); INIT_LIST_HEAD(&mod->unresolved_symbols); + INIT_LIST_HEAD(&mod->missing_namespaces); + INIT_LIST_HEAD(&mod->imported_namespaces); strcpy(mod->name, modname); mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); @@ -288,39 +290,34 @@ static struct symbol *find_symbol(const char *name) } struct namespace_list { - struct namespace_list *next; + struct list_head list; char namespace[]; }; -static bool contains_namespace(struct namespace_list *list, - const char *namespace) +static bool contains_namespace(struct list_head *head, const char *namespace) { - for (; list; list = list->next) + struct namespace_list *list; + + list_for_each_entry(list, head, list) { if (!strcmp(list->namespace, namespace)) return true; + } return false; } -static void add_namespace(struct namespace_list **list, const char *namespace) +static void add_namespace(struct list_head *head, const char *namespace) { struct namespace_list *ns_entry; - if (!contains_namespace(*list, namespace)) { - ns_entry = NOFAIL(malloc(sizeof(struct namespace_list) + + if (!contains_namespace(head, namespace)) { + ns_entry = NOFAIL(malloc(sizeof(*ns_entry) + strlen(namespace) + 1)); strcpy(ns_entry->namespace, namespace); - ns_entry->next = *list; - *list = ns_entry; + list_add_tail(&ns_entry->list, head); } } -static bool module_imports_namespace(struct module *module, - const char *namespace) -{ - return contains_namespace(module->imported_namespaces, namespace); -} - static const struct { const char *str; enum export export; @@ -2190,7 +2187,7 @@ static void check_exports(struct module *mod) basename = mod->name; if (exp->namespace && - !module_imports_namespace(mod, exp->namespace)) { + !contains_namespace(&mod->imported_namespaces, exp->namespace)) { modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR, "module %s uses symbol %s from namespace %s, but does not import it.\n", basename, exp->name, exp->namespace); @@ -2489,12 +2486,12 @@ static void write_namespace_deps_files(const char *fname) list_for_each_entry(mod, &modules, list) { - if (mod->from_dump || !mod->missing_namespaces) + if (mod->from_dump || list_empty(&mod->missing_namespaces)) continue; buf_printf(&ns_deps_buf, "%s.ko:", mod->name); - for (ns = mod->missing_namespaces; ns; ns = ns->next) + list_for_each_entry(ns, &mod->missing_namespaces, list) buf_printf(&ns_deps_buf, " %s", ns->namespace); buf_printf(&ns_deps_buf, "\n"); -- cgit From f841536e8c5b28e1fbf8743911ae1dc78993abd4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:15 +0900 Subject: modpost: dump Module.symvers in the same order of modules.order modpost dumps the exported symbols into Module.symvers, but currently in random order because it iterates in the hash table. Add a linked list of exported symbols in struct module, so we can iterate on symbols per module. This commit makes Module.symvers much more readable; the outer loop in write_dump() iterates over the modules in the order of modules.order, and the inner loop dumps symbols in each module. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 8bdde738803f..cdd9098b6035 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -185,6 +185,7 @@ static struct module *new_module(const char *modname) mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); memset(mod, 0, sizeof(*mod)); + INIT_LIST_HEAD(&mod->exported_symbols); INIT_LIST_HEAD(&mod->unresolved_symbols); INIT_LIST_HEAD(&mod->missing_namespaces); INIT_LIST_HEAD(&mod->imported_namespaces); @@ -211,7 +212,7 @@ static struct module *new_module(const char *modname) struct symbol { struct symbol *next; - struct list_head list; /* link to module::unresolved_symbols */ + struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ struct module *module; char *namespace; unsigned int crc; @@ -413,6 +414,7 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, if (!s) { s = new_symbol(name, mod, export); + list_add_tail(&s->list, &mod->exported_symbols); } else if (!external_module || s->module->is_vmlinux || s->module == mod) { warn("%s: '%s' exported twice. Previous export was in %s%s\n", @@ -2456,22 +2458,17 @@ fail: static void write_dump(const char *fname) { struct buffer buf = { }; - struct symbol *symbol; - const char *namespace; - int n; + struct module *mod; + struct symbol *sym; - for (n = 0; n < SYMBOL_HASH_SIZE ; n++) { - symbol = symbolhash[n]; - while (symbol) { - if (!symbol->module->from_dump) { - namespace = symbol->namespace; - buf_printf(&buf, "0x%08x\t%s\t%s\t%s\t%s\n", - symbol->crc, symbol->name, - symbol->module->name, - export_str(symbol->export), - namespace ? namespace : ""); - } - symbol = symbol->next; + list_for_each_entry(mod, &modules, list) { + if (mod->from_dump) + continue; + list_for_each_entry(sym, &mod->exported_symbols, list) { + buf_printf(&buf, "0x%08x\t%s\t%s\t%s\t%s\n", + sym->crc, sym->name, mod->name, + export_str(sym->export), + sym->namespace ?: ""); } } write_buf(&buf, fname); -- cgit From b8422711080f57cdf9fb1c0cb8683a2112bed27c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:17 +0900 Subject: modpost: make multiple export error This is currently a warning, but I think modpost should stop building in this case. If the same symbol is exported multiple times and we let it keep going, the sanity check becomes difficult. Only the legitimate case is that an external module overrides the corresponding in-tree module to provide a different implementation with the same interface. Also, there exists an upstream example that exploits this feature. $ make M=tools/testing/nvdimm ... builds tools/testing/nvdimm/libnvdimm.ko. This is a mocked module that overrides the symbols from drivers/nvdimm/libnvdimm.ko. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index cdd9098b6035..841f69475247 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -417,9 +417,9 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, list_add_tail(&s->list, &mod->exported_symbols); } 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, - s->module->is_vmlinux ? "" : ".ko"); + error("%s: '%s' exported twice. Previous export was in %s%s\n", + mod->name, name, s->module->name, + s->module->is_vmlinux ? "" : ".ko"); return s; } -- cgit From e76cc48d8e6df5d949284132981db73d2dd8c6b5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:18 +0900 Subject: modpost: make sym_add_exported() always allocate a new symbol Currently, sym_add_exported() does not allocate a symbol if the same name symbol already exists in the hash table. This does not reflect the real use cases. You can let an external module override the in-tree one. In this case, the external module will export the same name symbols as the in-tree one. However, modpost simply ignores those symbols, then Module.symvers for the external module loses its symbols. sym_add_exported() should allocate a new symbol. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 841f69475247..6024c668a07c 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -412,19 +412,17 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, { struct symbol *s = find_symbol(name); - if (!s) { - s = new_symbol(name, mod, export); - list_add_tail(&s->list, &mod->exported_symbols); - } else if (!external_module || s->module->is_vmlinux || - s->module == mod) { + if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { error("%s: '%s' exported twice. Previous export was in %s%s\n", mod->name, name, s->module->name, s->module->is_vmlinux ? "" : ".ko"); - return s; } + s = new_symbol(name, mod, export); s->module = mod; s->export = export; + list_add_tail(&s->list, &mod->exported_symbols); + return s; } -- cgit From f18379a30271c0289c2d0e1074e1ed633bfd708c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2022 17:40:19 +0900 Subject: modpost: split new_symbol() to symbol allocation and hash table addition new_symbol() does two things; allocate a new symbol and register it to the hash table. Using a separate function for each is easier to understand. Replace new_symbol() with hash_add_symbol(). Remove the second parameter of alloc_symbol(). Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 6024c668a07c..085abe541280 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -242,34 +242,31 @@ static inline unsigned int tdb_hash(const char *name) * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module **/ -static struct symbol *alloc_symbol(const char *name, struct symbol *next) +static struct symbol *alloc_symbol(const char *name) { struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); memset(s, 0, sizeof(*s)); strcpy(s->name, name); - s->next = next; s->is_static = true; return s; } /* For the hash of exported symbols */ -static struct symbol *new_symbol(const char *name, struct module *module, - enum export export) +static void hash_add_symbol(struct symbol *sym) { unsigned int hash; - hash = tdb_hash(name) % SYMBOL_HASH_SIZE; - symbolhash[hash] = alloc_symbol(name, symbolhash[hash]); - - return symbolhash[hash]; + hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; + sym->next = symbolhash[hash]; + symbolhash[hash] = sym; } static void sym_add_unresolved(const char *name, struct module *mod, bool weak) { struct symbol *sym; - sym = alloc_symbol(name, NULL); + sym = alloc_symbol(name); sym->weak = weak; list_add_tail(&sym->list, &mod->unresolved_symbols); @@ -418,10 +415,11 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, s->module->is_vmlinux ? "" : ".ko"); } - s = new_symbol(name, mod, export); + s = alloc_symbol(name); s->module = mod; s->export = export; list_add_tail(&s->list, &mod->exported_symbols); + hash_add_symbol(s); return s; } -- cgit From 7fedac9698b3a56571064eb3b23063f09c93eb94 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 5 May 2022 16:22:32 +0900 Subject: modpost: merge add_{intree_flag,retpoline,staging_flag} to add_header add_intree_flag(), add_retpoline(), and add_staging_flag() are small enough to be merged into add_header(). Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Tested-by: Nathan Chancellor --- scripts/mod/modpost.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 085abe541280..8cc386346298 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2243,25 +2243,17 @@ static void add_header(struct buffer *b, struct module *mod) "#endif\n"); buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); buf_printf(b, "};\n"); -} -static void add_intree_flag(struct buffer *b, int is_intree) -{ - if (is_intree) + if (!external_module) buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); -} -/* Cannot check for assembler */ -static void add_retpoline(struct buffer *b) -{ - buf_printf(b, "\n#ifdef CONFIG_RETPOLINE\n"); - buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n"); - buf_printf(b, "#endif\n"); -} + buf_printf(b, + "\n" + "#ifdef CONFIG_RETPOLINE\n" + "MODULE_INFO(retpoline, \"Y\");\n" + "#endif\n"); -static void add_staging_flag(struct buffer *b, const char *name) -{ - if (strstarts(name, "drivers/staging")) + if (strstarts(mod->name, "drivers/staging")) buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); } @@ -2577,9 +2569,6 @@ int main(int argc, char **argv) check_exports(mod); add_header(&buf, mod); - add_intree_flag(&buf, !external_module); - add_retpoline(&buf); - add_staging_flag(&buf, mod->name); add_versions(&buf, mod); add_depends(&buf, mod); add_moddevtable(&buf, mod); -- cgit From a44abaca0e196cfeef2374ed663b97daa1ad112a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 5 May 2022 16:22:33 +0900 Subject: modpost: move *.mod.c generation to write_mod_c_files() A later commit will add more code to this list_for_each_entry loop. Before that, move the loop body into a separate helper function. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Tested-by: Nathan Chancellor --- scripts/mod/modpost.c | 56 ++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 25 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 8cc386346298..d9efbd5b31a6 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2390,6 +2390,34 @@ static void write_if_changed(struct buffer *b, const char *fname) write_buf(b, fname); } +/* do sanity checks, and generate *.mod.c file */ +static void write_mod_c_file(struct module *mod) +{ + struct buffer buf = { }; + char fname[PATH_MAX]; + int ret; + + check_modname_len(mod); + check_exports(mod); + + add_header(&buf, mod); + add_versions(&buf, mod); + add_depends(&buf, mod); + add_moddevtable(&buf, mod); + add_srcversion(&buf, mod); + + ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); + if (ret >= sizeof(fname)) { + error("%s: too long path was truncated\n", fname); + goto free; + } + + write_if_changed(&buf, fname); + +free: + free(buf.p); +} + /* parse Module.symvers file. line format: * 0x12345678symbolmoduleexportnamespace **/ @@ -2494,7 +2522,6 @@ struct dump_list { int main(int argc, char **argv) { struct module *mod; - struct buffer buf = { }; char *missing_namespace_deps = NULL; char *dump_write = NULL, *files_source = NULL; int opt; @@ -2557,30 +2584,11 @@ int main(int argc, char **argv) read_symbols_from_files(files_source); list_for_each_entry(mod, &modules, list) { - char fname[PATH_MAX]; - int ret; - - if (mod->is_vmlinux || mod->from_dump) - continue; - - buf.pos = 0; - - check_modname_len(mod); - check_exports(mod); - - add_header(&buf, mod); - add_versions(&buf, mod); - add_depends(&buf, mod); - add_moddevtable(&buf, mod); - add_srcversion(&buf, mod); - - ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); - if (ret >= sizeof(fname)) { - error("%s: too long path was truncated\n", fname); + if (mod->from_dump) continue; - } - write_if_changed(&buf, fname); + if (!mod->is_vmlinux) + write_mod_c_file(mod); } if (missing_namespace_deps) @@ -2606,7 +2614,5 @@ int main(int argc, char **argv) warn("suppressed %u unresolved symbol warnings because there were too many)\n", nr_unresolved - MAX_UNRESOLVED_REPORTS); - free(buf.p); - return error_occurred ? 1 : 0; } -- cgit From 2a66c3124afd2782015d160f8bad693488ce68de Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 9 May 2022 04:06:19 +0900 Subject: modpost: change the license of EXPORT_SYMBOL to bool type There were more EXPORT_SYMBOL types in the past. The following commits removed unused ones. - f1c3d73e973c ("module: remove EXPORT_SYMBOL_GPL_FUTURE") - 367948220fce ("module: remove EXPORT_UNUSED_SYMBOL*") There are 3 remaining in enum export, but export_unknown does not make any sense because we never expect such a situation like "we do not know how it was exported". If the symbol name starts with "__ksymtab_", but the section name does not start with "___ksymtab+" or "___ksymtab_gpl+", it is not an exported symbol. It occurs when a variable starting with "__ksymtab_" is directly defined: int __ksymtab_foo; Presumably, there is no practical issue for using such a weird variable name (but there is no good reason for doing so, either). Anyway, that is not an exported symbol. Setting export_unknown is not the right thing to do. Do not call sym_add_exported() in this case. With pointless export_unknown removed, the export type finally becomes boolean (either EXPORT_SYMBOL or EXPORT_SYMBOL_GPL). I renamed the field name to is_gpl_only. EXPORT_SYMBOL_GPL sets it true. Only GPL-compatible modules can use it. I removed the orphan comment, "How a symbol is exported", which is unrelated to sec_mismatch_count. It is about enum export. See commit bd5cbcedf446 ("kbuild: export-type enhancement to modpost.c") Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Tested-by: Nathan Chancellor --- scripts/mod/modpost.c | 108 ++++++++++++++------------------------------------ 1 file changed, 30 insertions(+), 78 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d9efbd5b31a6..a78b75f0eeb0 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -30,7 +30,7 @@ static bool all_versions; static bool external_module; /* Only warn about unresolved symbols */ static bool warn_unresolved; -/* How a symbol is exported */ + static int sec_mismatch_count; static bool sec_mismatch_warn_only = true; /* ignore missing files */ @@ -47,12 +47,6 @@ static bool error_occurred; #define MAX_UNRESOLVED_REPORTS 10 static unsigned int nr_unresolved; -enum export { - export_plain, - export_gpl, - export_unknown -}; - /* In kernel, this size is defined in linux/module.h; * here we use Elf_Addr instead of long for covering cross-compile */ @@ -219,7 +213,7 @@ struct symbol { bool crc_valid; bool weak; bool is_static; /* true if symbol is not global */ - enum export export; /* Type of export */ + bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ char name[]; }; @@ -316,34 +310,6 @@ static void add_namespace(struct list_head *head, const char *namespace) } } -static const struct { - const char *str; - enum export export; -} export_list[] = { - { .str = "EXPORT_SYMBOL", .export = export_plain }, - { .str = "EXPORT_SYMBOL_GPL", .export = export_gpl }, - { .str = "(unknown)", .export = export_unknown }, -}; - - -static const char *export_str(enum export ex) -{ - return export_list[ex].str; -} - -static enum export export_no(const char *s) -{ - int i; - - if (!s) - return export_unknown; - for (i = 0; export_list[i].export != export_unknown; i++) { - if (strcmp(export_list[i].str, s) == 0) - return export_list[i].export; - } - return export_unknown; -} - static void *sym_get_data_by_offset(const struct elf_info *info, unsigned int secindex, unsigned long offset) { @@ -374,18 +340,6 @@ static const char *sec_name(const struct elf_info *info, int secindex) #define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) -static enum export export_from_secname(struct elf_info *elf, unsigned int sec) -{ - const char *secname = sec_name(elf, sec); - - if (strstarts(secname, "___ksymtab+")) - return export_plain; - else if (strstarts(secname, "___ksymtab_gpl+")) - return export_gpl; - else - return export_unknown; -} - static void sym_update_namespace(const char *symname, const char *namespace) { struct symbol *s = find_symbol(symname); @@ -405,7 +359,7 @@ static void sym_update_namespace(const char *symname, const char *namespace) } static struct symbol *sym_add_exported(const char *name, struct module *mod, - enum export export) + bool gpl_only) { struct symbol *s = find_symbol(name); @@ -417,7 +371,7 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, s = alloc_symbol(name); s->module = mod; - s->export = export; + s->is_gpl_only = gpl_only; list_add_tail(&s->list, &mod->exported_symbols); hash_add_symbol(s); @@ -689,8 +643,6 @@ static void handle_modversion(const struct module *mod, static void handle_symbol(struct module *mod, struct elf_info *info, const Elf_Sym *sym, const char *symname) { - const char *name; - switch (sym->st_shndx) { case SHN_COMMON: if (strstarts(symname, "__gnu_lto_")) { @@ -724,12 +676,15 @@ static void handle_symbol(struct module *mod, struct elf_info *info, default: /* All exported symbols */ if (strstarts(symname, "__ksymtab_")) { - enum export export; + const char *name, *secname; name = symname + strlen("__ksymtab_"); - export = export_from_secname(info, - get_secindex(info, sym)); - sym_add_exported(name, mod, export); + secname = sec_name(info, get_secindex(info, sym)); + + if (strstarts(secname, "___ksymtab_gpl+")) + sym_add_exported(name, mod, true); + else if (strstarts(secname, "___ksymtab+")) + sym_add_exported(name, mod, false); } if (strcmp(symname, "init_module") == 0) mod->has_init = true; @@ -2140,20 +2095,6 @@ void buf_write(struct buffer *buf, const char *s, int len) buf->pos += len; } -static void check_for_gpl_usage(enum export exp, const char *m, const char *s) -{ - switch (exp) { - case export_gpl: - error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", - m, s); - break; - case export_plain: - case export_unknown: - /* ignore */ - break; - } -} - static void check_exports(struct module *mod) { struct symbol *s, *exp; @@ -2192,8 +2133,9 @@ static void check_exports(struct module *mod) add_namespace(&mod->missing_namespaces, exp->namespace); } - if (!mod->is_gpl_compatible) - check_for_gpl_usage(exp->export, basename, exp->name); + if (!mod->is_gpl_compatible && exp->is_gpl_only) + error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", + basename, exp->name); } } @@ -2437,6 +2379,7 @@ static void read_dump(const char *fname) unsigned int crc; struct module *mod; struct symbol *s; + bool gpl_only; if (!(symname = strchr(line, '\t'))) goto fail; @@ -2454,12 +2397,22 @@ static void read_dump(const char *fname) crc = strtoul(line, &d, 16); if (*symname == '\0' || *modname == '\0' || *d != '\0') goto fail; + + if (!strcmp(export, "EXPORT_SYMBOL_GPL")) { + gpl_only = true; + } else if (!strcmp(export, "EXPORT_SYMBOL")) { + gpl_only = false; + } else { + error("%s: unknown license %s. skip", symname, export); + continue; + } + mod = find_module(modname); if (!mod) { mod = new_module(modname); mod->from_dump = true; } - s = sym_add_exported(symname, mod, export_no(export)); + s = sym_add_exported(symname, mod, gpl_only); s->is_static = false; sym_set_crc(symname, crc); sym_update_namespace(symname, namespace); @@ -2481,9 +2434,9 @@ static void write_dump(const char *fname) if (mod->from_dump) continue; list_for_each_entry(sym, &mod->exported_symbols, list) { - buf_printf(&buf, "0x%08x\t%s\t%s\t%s\t%s\n", + buf_printf(&buf, "0x%08x\t%s\t%s\tEXPORT_SYMBOL%s\t%s\n", sym->crc, sym->name, mod->name, - export_str(sym->export), + sym->is_gpl_only ? "_GPL" : "", sym->namespace ?: ""); } } @@ -2604,9 +2557,8 @@ int main(int argc, char **argv) for (s = symbolhash[n]; s; s = s->next) { if (s->is_static) - error("\"%s\" [%s] is a static %s\n", - s->name, s->module->name, - export_str(s->export)); + error("\"%s\" [%s] is a static EXPORT_SYMBOL\n", + s->name, s->module->name); } } -- cgit From 69c4cc99bbcbf3ef2e1901b569954e9226180840 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 12 May 2022 01:45:04 +0900 Subject: modpost: add sym_find_with_module() helper find_symbol() returns the first symbol found in the hash table. This table is global, so it may return a symbol from an unexpected module. There is a case where we want to search for a symbol with a given name in a specified module. Add sym_find_with_module(), which receives the module pointer as the second argument. It is equivalent to find_module() if NULL is passed as the module pointer. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Tested-by: Nathan Chancellor Tested-by: Sedat Dilek # LLVM-14 (x86-64) --- scripts/mod/modpost.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a78b75f0eeb0..f36f02d4b79b 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -266,7 +266,7 @@ static void sym_add_unresolved(const char *name, struct module *mod, bool weak) list_add_tail(&sym->list, &mod->unresolved_symbols); } -static struct symbol *find_symbol(const char *name) +static struct symbol *sym_find_with_module(const char *name, struct module *mod) { struct symbol *s; @@ -275,12 +275,17 @@ static struct symbol *find_symbol(const char *name) name++; for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { - if (strcmp(s->name, name) == 0) + if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } return NULL; } +static struct symbol *find_symbol(const char *name) +{ + return sym_find_with_module(name, NULL); +} + struct namespace_list { struct list_head list; char namespace[]; -- cgit From f292d875d0dc700b3af0bef04c5abc1dc7b3b62c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 13 May 2022 20:39:21 +0900 Subject: modpost: extract symbol versions from *.cmd files Currently, CONFIG_MODVERSIONS needs extra link to embed the symbol versions into ELF objects. Then, modpost extracts the version CRCs from them. The following figures show how it currently works, and how I am trying to change it. Current implementation ====================== |----------| embed CRC -------------------------->| final | $(CC) $(LD) / |---------| | link for | -----> *.o -------> *.o -->| modpost | | vmlinux | / / | |-- *.mod.c -->| or | / genksyms / |---------| | module | *.c ------> *.symversions |----------| Genksyms outputs the calculated CRCs in the form of linker script (*.symversions), which is used by $(LD) to update the object. If CONFIG_LTO_CLANG=y, the build process is much more complex. Embedding the CRCs is postponed until the LLVM bitcode is converted into ELF, creating another intermediate *.prelink.o. However, this complexity is unneeded. There is no reason why we must embed version CRCs in objects so early. There is final link stage for vmlinux (scripts/link-vmlinux.sh) and modules (scripts/Makefile.modfinal). We can link CRCs at the very last moment. New implementation ================== |----------| --------------------------------------->| final | $(CC) / |---------| | link for | -----> *.o ---->| | | vmlinux | / | modpost |--- .vmlinux.export.c -->| or | / genksyms | |--- *.mod.c ------------>| module | *.c ------> *.cmd -->|---------| |----------| Pass the symbol versions to modpost as separate text data, which are available in *.cmd files. This commit changes modpost to extract CRCs from *.cmd files instead of from ELF objects. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Tested-by: Nathan Chancellor Reviewed-by: Sami Tolvanen Tested-by: Sedat Dilek # LLVM-14 (x86-64) --- scripts/mod/modpost.c | 179 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 48 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f36f02d4b79b..1e2949775e1b 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -383,19 +383,10 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod, return s; } -static void sym_set_crc(const char *name, unsigned int crc) +static void sym_set_crc(struct symbol *sym, unsigned int crc) { - struct symbol *s = find_symbol(name); - - /* - * Ignore stand-alone __crc_*, which might be auto-generated symbols - * such as __*_veneer in ARM ELF. - */ - if (!s) - return; - - s->crc = crc; - s->crc_valid = true; + sym->crc = crc; + sym->crc_valid = true; } static void *grab_file(const char *filename, size_t *size) @@ -618,33 +609,6 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname) return 0; } -static void handle_modversion(const struct module *mod, - const struct elf_info *info, - const Elf_Sym *sym, const char *symname) -{ - unsigned int crc; - - if (sym->st_shndx == SHN_UNDEF) { - warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" - "Is \"%s\" prototyped in ?\n", - symname, mod->name, mod->is_vmlinux ? "" : ".ko", - symname); - - return; - } - - if (sym->st_shndx == SHN_ABS) { - crc = sym->st_value; - } else { - unsigned int *crcp; - - /* symbol points to the CRC in the ELF object */ - crcp = sym_get_data(info, sym); - crc = TO_NATIVE(*crcp); - } - sym_set_crc(symname, crc); -} - static void handle_symbol(struct module *mod, struct elf_info *info, const Elf_Sym *sym, const char *symname) { @@ -1952,6 +1916,104 @@ static char *remove_dot(char *s) return s; } +/* + * The CRCs are recorded in .*.cmd files in the form of: + * #SYMVER + */ +static void extract_crcs_for_object(const char *object, struct module *mod) +{ + char cmd_file[PATH_MAX]; + char *buf, *p; + const char *base; + int dirlen, ret; + + base = strrchr(object, '/'); + if (base) { + base++; + dirlen = base - object; + } else { + dirlen = 0; + base = object; + } + + ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd", + dirlen, object, base); + if (ret >= sizeof(cmd_file)) { + error("%s: too long path was truncated\n", cmd_file); + return; + } + + buf = read_text_file(cmd_file); + p = buf; + + while ((p = strstr(p, "\n#SYMVER "))) { + char *name; + size_t namelen; + unsigned int crc; + struct symbol *sym; + + name = p + strlen("\n#SYMVER "); + + p = strchr(name, ' '); + if (!p) + break; + + namelen = p - name; + p++; + + if (!isdigit(*p)) + continue; /* skip this line */ + + crc = strtol(p, &p, 0); + if (*p != '\n') + continue; /* skip this line */ + + name[namelen] = '\0'; + + /* + * sym_find_with_module() may return NULL here. + * It typically occurs when CONFIG_TRIM_UNUSED_KSYMS=y. + * Since commit e1327a127703, genksyms calculates CRCs of all + * symbols, including trimmed ones. Ignore orphan CRCs. + */ + sym = sym_find_with_module(name, mod); + if (sym) + sym_set_crc(sym, crc); + } + + free(buf); +} + +/* + * The symbol versions (CRC) are recorded in the .*.cmd files. + * Parse them to retrieve CRCs for the current module. + */ +static void mod_set_crcs(struct module *mod) +{ + char objlist[PATH_MAX]; + char *buf, *p, *obj; + int ret; + + if (mod->is_vmlinux) { + strcpy(objlist, ".vmlinux.objs"); + } else { + /* objects for a module are listed in the *.mod file. */ + ret = snprintf(objlist, sizeof(objlist), "%s.mod", mod->name); + if (ret >= sizeof(objlist)) { + error("%s: too long path was truncated\n", objlist); + return; + } + } + + buf = read_text_file(objlist); + p = buf; + + while ((obj = strsep(&p, "\n")) && obj[0]) + extract_crcs_for_object(obj, mod); + + free(buf); +} + static void read_symbols(const char *modname) { const char *symname; @@ -2012,9 +2074,6 @@ static void read_symbols(const char *modname) if (strstarts(symname, "__kstrtabns_")) sym_update_namespace(symname + strlen("__kstrtabns_"), sym_get_data(&info, sym)); - if (strstarts(symname, "__crc_")) - handle_modversion(mod, &info, sym, - symname + strlen("__crc_")); } // check for static EXPORT_SYMBOL_* functions && global vars @@ -2042,12 +2101,17 @@ static void read_symbols(const char *modname) parse_elf_finish(&info); - /* Our trick to get versioning for module struct etc. - 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) + if (modversions) { + /* + * Our trick to get versioning for module struct etc. - 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. + */ sym_add_unresolved("module_layout", mod, false); + + mod_set_crcs(mod); + } } static void read_symbols_from_files(const char *filename) @@ -2204,6 +2268,23 @@ static void add_header(struct buffer *b, struct module *mod) buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); } +static void check_symversions(struct module *mod) +{ + struct symbol *sym; + + if (!modversions) + return; + + list_for_each_entry(sym, &mod->exported_symbols, list) { + if (!sym->crc_valid) { + warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" + "Is \"%s\" prototyped in ?\n", + sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", + sym->name); + } + } +} + /** * Record CRCs for unresolved symbols **/ @@ -2419,7 +2500,7 @@ static void read_dump(const char *fname) } s = sym_add_exported(symname, mod, gpl_only); s->is_static = false; - sym_set_crc(symname, crc); + sym_set_crc(s, crc); sym_update_namespace(symname, namespace); } free(buf); @@ -2545,6 +2626,8 @@ int main(int argc, char **argv) if (mod->from_dump) continue; + check_symversions(mod); + if (!mod->is_vmlinux) write_mod_c_file(mod); } -- cgit From 7b4537199a4a8480b8c3ba37a2d44765ce76cd9b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 13 May 2022 20:39:22 +0900 Subject: kbuild: link symbol CRCs at final link, removing CONFIG_MODULE_REL_CRCS include/{linux,asm-generic}/export.h defines a weak symbol, __crc_* as a placeholder. Genksyms writes the version CRCs into the linker script, which will be used for filling the __crc_* symbols. The linker script format depends on CONFIG_MODULE_REL_CRCS. If it is enabled, __crc_* holds the offset to the reference of CRC. It is time to get rid of this complexity. Now that modpost parses text files (.*.cmd) to collect all the CRCs, it can generate C code that will be linked to the vmlinux or modules. Generate a new C file, .vmlinux.export.c, which contains the CRCs of symbols exported by vmlinux. It is compiled and linked to vmlinux in scripts/link-vmlinux.sh. Put the CRCs of symbols exported by modules into the existing *.mod.c files. No additional build step is needed for modules. As before, *.mod.c are compiled and linked to *.ko in scripts/Makefile.modfinal. No linker magic is used here. The new C implementation works in the same way, whether CONFIG_RELOCATABLE is enabled or not. CONFIG_MODULE_REL_CRCS is no longer needed. Previously, Kbuild invoked additional $(LD) to update the CRCs in objects, but this step is unneeded too. Signed-off-by: Masahiro Yamada Tested-by: Nathan Chancellor Tested-by: Nicolas Schier Reviewed-by: Nicolas Schier Tested-by: Sedat Dilek # LLVM-14 (x86-64) --- scripts/mod/modpost.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 1e2949775e1b..42e949cbc255 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2234,6 +2234,7 @@ static void add_header(struct buffer *b, struct module *mod) buf_printf(b, "#define INCLUDE_VERMAGIC\n"); buf_printf(b, "#include \n"); buf_printf(b, "#include \n"); + buf_printf(b, "#include \n"); buf_printf(b, "#include \n"); buf_printf(b, "#include \n"); buf_printf(b, "\n"); @@ -2268,20 +2269,26 @@ static void add_header(struct buffer *b, struct module *mod) buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); } -static void check_symversions(struct module *mod) +static void add_exported_symbols(struct buffer *buf, struct module *mod) { struct symbol *sym; if (!modversions) return; + /* record CRCs for exported symbols */ + buf_printf(buf, "\n"); list_for_each_entry(sym, &mod->exported_symbols, list) { if (!sym->crc_valid) { warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" "Is \"%s\" prototyped in ?\n", sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", sym->name); + continue; } + + buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n", + sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : ""); } } @@ -2418,6 +2425,18 @@ static void write_if_changed(struct buffer *b, const char *fname) write_buf(b, fname); } +static void write_vmlinux_export_c_file(struct module *mod) +{ + struct buffer buf = { }; + + buf_printf(&buf, + "#include \n"); + + add_exported_symbols(&buf, mod); + write_if_changed(&buf, ".vmlinux.export.c"); + free(buf.p); +} + /* do sanity checks, and generate *.mod.c file */ static void write_mod_c_file(struct module *mod) { @@ -2429,6 +2448,7 @@ static void write_mod_c_file(struct module *mod) check_exports(mod); add_header(&buf, mod); + add_exported_symbols(&buf, mod); add_versions(&buf, mod); add_depends(&buf, mod); add_moddevtable(&buf, mod); @@ -2626,9 +2646,9 @@ int main(int argc, char **argv) if (mod->from_dump) continue; - check_symversions(mod); - - if (!mod->is_vmlinux) + if (mod->is_vmlinux) + write_vmlinux_export_c_file(mod); + else write_mod_c_file(mod); } -- cgit From b5beffa20d83c4e15306c991ffd00de0d8628338 Mon Sep 17 00:00:00 2001 From: Alexander Lobakin Date: Tue, 24 May 2022 17:27:18 +0200 Subject: modpost: fix removing numeric suffixes With the `-z unique-symbol` linker flag or any similar mechanism, it is possible to trigger the following: ERROR: modpost: "param_set_uint.0" [vmlinux] is a static EXPORT_SYMBOL The reason is that for now the condition from remove_dot(): if (m && (s[n + m] == '.' || s[n + m] == 0)) which was designed to test if it's a dot or a '\0' after the suffix is never satisfied. This is due to that `s[n + m]` always points to the last digit of a numeric suffix, not on the symbol next to it (from a custom debug print added to modpost): param_set_uint.0, s[n + m] is '0', s[n + m + 1] is '\0' So it's off-by-one and was like that since 2014. Fix this for the sake of any potential upcoming features, but don't bother stable-backporting, as it's well hidden -- apart from that LD flag, it can be triggered only with GCC LTO which never landed upstream. Fixes: fcd38ed0ff26 ("scripts: modpost: fix compilation warning") Signed-off-by: Alexander Lobakin Reviewed-by: Petr Mladek Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 42e949cbc255..08f6989437bd 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1906,7 +1906,7 @@ static char *remove_dot(char *s) if (n && s[n]) { size_t m = strspn(s + n + 1, "0123456789"); - if (m && (s[n + m] == '.' || s[n + m] == 0)) + if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0)) s[n] = 0; /* strip trailing .prelink */ -- cgit From d6b732666a1bae0df3c3ae06925043bba34502b1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 24 May 2022 01:46:22 +0900 Subject: modpost: fix undefined behavior of is_arm_mapping_symbol() The return value of is_arm_mapping_symbol() is unpredictable when "$" is passed in. strchr(3) says: The strchr() and strrchr() functions return a pointer to the matched character or NULL if the character is not found. The terminating null byte is considered part of the string, so that if c is specified as '\0', these functions return a pointer to the terminator. When str[1] is '\0', strchr("axtd", str[1]) is not NULL, and str[2] is referenced (i.e. buffer overrun). Test code --------- char str1[] = "abc"; char str2[] = "ab"; strcpy(str1, "$"); strcpy(str2, "$"); printf("test1: %d\n", is_arm_mapping_symbol(str1)); printf("test2: %d\n", is_arm_mapping_symbol(str2)); Result ------ test1: 0 test2: 1 Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 08f6989437bd..1092906867f0 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1180,7 +1180,8 @@ static int secref_whitelist(const struct sectioncheck *mismatch, static inline int is_arm_mapping_symbol(const char *str) { - return str[0] == '$' && strchr("axtd", str[1]) + return str[0] == '$' && + (str[1] == 'a' || str[1] == 'd' || str[1] == 't' || str[1] == 'x') && (str[2] == '\0' || str[2] == '.'); } -- cgit From 76954527fe05354add150737aa1b9a6baa4a6ee5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 24 May 2022 01:46:23 +0900 Subject: modpost: remove the unused argument of check_sec_ref() check_sec_ref() does not use the first parameter 'mod'. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 1092906867f0..2806a5c528c8 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1884,8 +1884,7 @@ static void section_rel(const char *modname, struct elf_info *elf, * to find all references to a section that reference a section that will * be discarded and warns about it. **/ -static void check_sec_ref(struct module *mod, const char *modname, - struct elf_info *elf) +static void check_sec_ref(const char *modname, struct elf_info *elf) { int i; Elf_Shdr *sechdrs = elf->sechdrs; @@ -2091,7 +2090,7 @@ static void read_symbols(const char *modname) } } - check_sec_ref(mod, modname, &info); + check_sec_ref(modname, &info); if (!mod->is_vmlinux) { version = get_modinfo(&info, "version"); -- cgit From c5c468dcc25efc0095361bb63b6255622e22f695 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 24 May 2022 01:46:25 +0900 Subject: modpost: reuse ARRAY_SIZE() macro for section_mismatch() Move ARRAY_SIZE() from file2alias.c to modpost.h to reuse it in section_mismatch(). Also, move the variable 'check' inside the for-loop. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 2806a5c528c8..fc8ba8585d23 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1049,8 +1049,6 @@ static const struct sectioncheck *section_mismatch( const char *fromsec, const char *tosec) { int i; - int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck); - const struct sectioncheck *check = §ioncheck[0]; /* * The target section could be the SHT_NUL section when we're @@ -1061,14 +1059,15 @@ static const struct sectioncheck *section_mismatch( if (*tosec == '\0') return NULL; - for (i = 0; i < elems; i++) { + for (i = 0; i < ARRAY_SIZE(sectioncheck); i++) { + const struct sectioncheck *check = §ioncheck[i]; + if (match(fromsec, check->fromsec)) { if (check->bad_tosec[0] && match(tosec, check->bad_tosec)) return check; if (check->good_tosec[0] && !match(tosec, check->good_tosec)) return check; } - check++; } return NULL; } -- cgit From 68fef6704e38581f7462cb7aac349978fd4ca5cc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 24 May 2022 01:46:26 +0900 Subject: modpost: squash if...else-if in find_elf_symbol2() if ((addr - sym->st_value) < distance) { distance = addr - sym->st_value; near = sym; } else if ((addr - sym->st_value) == distance) { near = sym; } is equivalent to: if (addr - sym->st_value <= distance) { distance = addr - sym->st_value; near = sym; } (The else-if block can overwrite 'distance' with the same value). Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index fc8ba8585d23..b70a31b4245a 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1270,13 +1270,9 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr, continue; if (!is_valid_name(elf, sym)) continue; - if (sym->st_value <= addr) { - if ((addr - sym->st_value) < distance) { - distance = addr - sym->st_value; - near = sym; - } else if ((addr - sym->st_value) == distance) { - near = sym; - } + if (sym->st_value <= addr && addr - sym->st_value <= distance) { + distance = addr - sym->st_value; + near = sym; } } return near; -- cgit From c25e1c55822f9b3b53ccbf88b85644317a525752 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 27 May 2022 19:01:49 +0900 Subject: kbuild: do not create *.prelink.o for Clang LTO or IBT When CONFIG_LTO_CLANG=y, additional intermediate *.prelink.o is created for each module. Also, objtool is postponed until LLVM IR is converted to ELF. CONFIG_X86_KERNEL_IBT works in a similar way to postpone objtool until objects are merged together. This commit stops generating *.prelink.o, so the build flow will look similar with/without LTO. The following figures show how the LTO build currently works, and how this commit is changing it. Current build flow ================== [1] single-object module $(LD) $(CC) +objtool $(LD) foo.c --------------------> foo.o -----> foo.prelink.o -----> foo.ko (LLVM IR) (ELF) | (ELF) | foo.mod.o --/ (LLVM IR) [2] multi-object module $(LD) $(CC) $(AR) +objtool $(LD) foo1.c -----> foo1.o -----> foo.o -----> foo.prelink.o -----> foo.ko | (archive) (ELF) | (ELF) foo2.c -----> foo2.o --/ | (LLVM IR) foo.mod.o --/ (LLVM IR) One confusion is that foo.o in multi-object module is an archive despite of its suffix. New build flow ============== [1] single-object module Since there is only one object, there is no need to keep the LLVM IR. Use $(CC)+$(LD) to generate an ELF object in one build rule. When LTO is disabled, $(LD) is unneeded because $(CC) produces an ELF object. $(CC)+$(LD)+objtool $(LD) foo.c ----------------------------> foo.o ---------> foo.ko (ELF) | (ELF) | foo.mod.o --/ (LLVM IR) [2] multi-object module Previously, $(AR) was used to combine LLVM IR files into an archive, but there was no technical reason to do so. Use $(LD) to merge them into a single ELF object. $(LD) $(CC) +objtool $(LD) foo1.c ---------> foo1.o ---------> foo.o ---------> foo.ko | (ELF) | (ELF) foo2.c ---------> foo2.o ----/ | (LLVM IR) foo.mod.o --/ (LLVM IR) Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Tested-by: Nathan Chancellor Reviewed-by: Sami Tolvanen Tested-by: Sedat Dilek # LLVM-14 (x86-64) Acked-by: Josh Poimboeuf --- scripts/mod/modpost.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index b70a31b4245a..e74224a6a8e8 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1903,10 +1903,6 @@ static char *remove_dot(char *s) size_t m = strspn(s + n + 1, "0123456789"); if (m && (s[n + m + 1] == '.' || s[n + m + 1] == 0)) s[n] = 0; - - /* strip trailing .prelink */ - if (strends(s, ".prelink")) - s[strlen(s) - 8] = '\0'; } return s; } @@ -2028,9 +2024,6 @@ static void read_symbols(const char *modname) /* strip trailing .o */ tmp = NOFAIL(strdup(modname)); tmp[strlen(tmp) - 2] = '\0'; - /* strip trailing .prelink */ - if (strends(tmp, ".prelink")) - tmp[strlen(tmp) - 8] = '\0'; mod = new_module(tmp); free(tmp); } -- cgit From 31cb50b5590fe911077b8463ad01144fac8fa4f3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 27 May 2022 19:01:51 +0900 Subject: kbuild: check static EXPORT_SYMBOL* by script instead of modpost The 'static' specifier and EXPORT_SYMBOL() are an odd combination. Commit 15bfc2348d54 ("modpost: check for static EXPORT_SYMBOL* functions") tried to detect it, but this check has false negatives. Here is the sample code. Makefile: obj-y += foo1.o foo2.o foo1.c: #include static void foo(void) {} EXPORT_SYMBOL(foo); foo2.c: void foo(void) {} foo1.c exports the static symbol 'foo', but modpost cannot catch it because it is fooled by foo2.c, which has a global symbol with the same name. s->is_static is cleared if a global symbol with the same name is found somewhere, but EXPORT_SYMBOL() and the global symbol do not necessarily belong to the same compilation unit. This check should be done per compilation unit, but I do not know how to do it in modpost. modpost runs against vmlinux.o or modules, which merges multiple objects, then forgets their origin. modpost cannot parse individual objects because they may not be ELF but LLVM IR when CONFIG_LTO_CLANG=y. Add a simple bash script to parse the output from ${NM}. This works for CONFIG_LTO_CLANG=y because llvm-nm can dump symbols of LLVM IR files. Revert 15bfc2348d54. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers Tested-by: Nathan Chancellor Tested-by: Sedat Dilek # LLVM-14 (x86-64) --- scripts/mod/modpost.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index e74224a6a8e8..9269735f85c5 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -212,7 +212,6 @@ struct symbol { unsigned int crc; bool crc_valid; bool weak; - bool is_static; /* true if symbol is not global */ bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ char name[]; }; @@ -242,7 +241,7 @@ static struct symbol *alloc_symbol(const char *name) memset(s, 0, sizeof(*s)); strcpy(s->name, name); - s->is_static = true; + return s; } @@ -2064,20 +2063,6 @@ static void read_symbols(const char *modname) sym_get_data(&info, sym)); } - // check for static EXPORT_SYMBOL_* functions && global vars - for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { - unsigned char bind = ELF_ST_BIND(sym->st_info); - - if (bind == STB_GLOBAL || bind == STB_WEAK) { - struct symbol *s = - find_symbol(remove_dot(info.strtab + - sym->st_name)); - - if (s) - s->is_static = false; - } - } - check_sec_ref(modname, &info); if (!mod->is_vmlinux) { @@ -2507,7 +2492,6 @@ static void read_dump(const char *fname) mod->from_dump = true; } s = sym_add_exported(symname, mod, gpl_only); - s->is_static = false; sym_set_crc(s, crc); sym_update_namespace(symname, namespace); } @@ -2572,7 +2556,6 @@ int main(int argc, char **argv) char *missing_namespace_deps = NULL; char *dump_write = NULL, *files_source = NULL; int opt; - int n; LIST_HEAD(dump_lists); struct dump_list *dl, *dl2; @@ -2648,15 +2631,6 @@ int main(int argc, char **argv) if (sec_mismatch_count && !sec_mismatch_warn_only) error("Section mismatches detected.\n" "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); - for (n = 0; n < SYMBOL_HASH_SIZE; n++) { - struct symbol *s; - - for (s = symbolhash[n]; s; s = s->next) { - if (s->is_static) - error("\"%s\" [%s] is a static EXPORT_SYMBOL\n", - s->name, s->module->name); - } - } if (nr_unresolved > MAX_UNRESOLVED_REPORTS) warn("suppressed %u unresolved symbol warnings because there were too many)\n", -- cgit From 8c9ce89c5b63028dd3be43807f10b009cd2c6e51 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 May 2022 18:01:38 +0900 Subject: modpost: simplify mod->name allocation mod->name is set to the ELF filename with the suffix ".o" stripped. The current code calls strdup() and free() to manipulate the string, but a simpler approach is to pass new_module() with the name length subtracted by 2. Also, check if the passed filename ends with ".o" before stripping it. The current code blindly chops the suffix: tmp[strlen(tmp) - 2] = '\0' It will cause buffer under-run if strlen(tmp) < 2; Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 9269735f85c5..c1558bacf717 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -172,11 +172,11 @@ static struct module *find_module(const char *modname) return NULL; } -static struct module *new_module(const char *modname) +static struct module *new_module(const char *name, size_t namelen) { struct module *mod; - mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); + mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1)); memset(mod, 0, sizeof(*mod)); INIT_LIST_HEAD(&mod->exported_symbols); @@ -184,8 +184,9 @@ static struct module *new_module(const char *modname) INIT_LIST_HEAD(&mod->missing_namespaces); INIT_LIST_HEAD(&mod->imported_namespaces); - strcpy(mod->name, modname); - mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); + memcpy(mod->name, name, namelen); + mod->name[namelen] = '\0'; + mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0); /* * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() @@ -2017,16 +2018,14 @@ static void read_symbols(const char *modname) if (!parse_elf(&info, modname)) return; - { - char *tmp; - - /* strip trailing .o */ - tmp = NOFAIL(strdup(modname)); - tmp[strlen(tmp) - 2] = '\0'; - mod = new_module(tmp); - free(tmp); + if (!strends(modname, ".o")) { + error("%s: filename must be suffixed with .o\n", modname); + return; } + /* strip trailing .o */ + mod = new_module(modname, strlen(modname) - strlen(".o")); + if (!mod->is_vmlinux) { license = get_modinfo(&info, "license"); if (!license) @@ -2488,7 +2487,7 @@ static void read_dump(const char *fname) mod = find_module(modname); if (!mod) { - mod = new_module(modname); + mod = new_module(modname, strlen(modname)); mod->from_dump = true; } s = sym_add_exported(symname, mod, gpl_only); -- cgit From a89227d769845eb9e9ab113f9f83df34d3c91db5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 May 2022 18:01:39 +0900 Subject: modpost: use fnmatch() to simplify match() Replace the own implementation for wildcard (glob) matching with a function call to fnmatch(). Also, change the return type to 'bool'. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 74 +++++++++------------------------------------------ 1 file changed, 13 insertions(+), 61 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index c1558bacf717..29d5a841e215 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -13,6 +13,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -710,29 +711,6 @@ static char *get_modinfo(struct elf_info *info, const char *tag) return get_next_modinfo(info, tag, NULL); } -/** - * Test if string s ends in string sub - * return 0 if match - **/ -static int strrcmp(const char *s, const char *sub) -{ - int slen, sublen; - - if (!s || !sub) - return 1; - - slen = strlen(s); - sublen = strlen(sub); - - if ((slen == 0) || (sublen == 0)) - return 1; - - if (sublen > slen) - return 1; - - return memcmp(s + slen - sublen, sub, sublen); -} - static const char *sym_name(struct elf_info *elf, Elf_Sym *sym) { if (sym) @@ -741,48 +719,22 @@ static const char *sym_name(struct elf_info *elf, Elf_Sym *sym) return "(unknown)"; } -/* The pattern is an array of simple patterns. - * "foo" will match an exact string equal to "foo" - * "*foo" will match a string that ends with "foo" - * "foo*" will match a string that begins with "foo" - * "*foo*" will match a string that contains "foo" +/* + * Check whether the 'string' argument matches one of the 'patterns', + * an array of shell wildcard patterns (glob). + * + * Return true is there is a match. */ -static int match(const char *sym, const char * const pat[]) +static bool match(const char *string, const char *const patterns[]) { - const char *p; - while (*pat) { - const char *endp; - - p = *pat++; - endp = p + strlen(p) - 1; + const char *pattern; - /* "*foo*" */ - if (*p == '*' && *endp == '*') { - char *bare = NOFAIL(strndup(p + 1, strlen(p) - 2)); - char *here = strstr(sym, bare); - - free(bare); - if (here != NULL) - return 1; - } - /* "*foo" */ - else if (*p == '*') { - if (strrcmp(sym, p + 1) == 0) - return 1; - } - /* "foo*" */ - else if (*endp == '*') { - if (strncmp(sym, p, strlen(p) - 1) == 0) - return 1; - } - /* no wildcards */ - else { - if (strcmp(p, sym) == 0) - return 1; - } + while ((pattern = *patterns++)) { + if (!fnmatch(pattern, string, 0)) + return true; } - /* no match */ - return 0; + + return false; } /* sections that we do not want to do full section mismatch check on */ -- cgit From 28438794aba47a27e922857d27b31b74e8559143 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 11 Jun 2022 03:32:30 +0900 Subject: modpost: fix section mismatch check for exported init/exit sections Since commit f02e8a6596b7 ("module: Sort exported symbols"), EXPORT_SYMBOL* is placed in the individual section ___ksymtab(_gpl)+ (3 leading underscores instead of 2). Since then, modpost cannot detect the bad combination of EXPORT_SYMBOL and __init/__exit. Fix the .fromsec field. Fixes: f02e8a6596b7 ("module: Sort exported symbols") Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 29d5a841e215..620dc8c4c814 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -980,7 +980,7 @@ static const struct sectioncheck sectioncheck[] = { }, /* Do not export init/exit functions or data */ { - .fromsec = { "__ksymtab*", NULL }, + .fromsec = { "___ksymtab*", NULL }, .bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }, .mismatch = EXPORT_TO_INIT_EXIT, .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, -- cgit From 74829ddf5977567d77440150d72d4c0c5c427446 Mon Sep 17 00:00:00 2001 From: David Gow Date: Fri, 8 Jul 2022 12:48:45 +0800 Subject: module: panic: Taint the kernel when selftest modules load Taint the kernel with TAINT_TEST whenever a test module loads, by adding a new "TEST" module property, and setting it for all modules in the tools/testing directory. This property can also be set manually, for tests which live outside the tools/testing directory with: MODULE_INFO(test, "Y"); Reviewed-by: Luis Chamberlain Reviewed-by: Aaron Tomlin Acked-by: Brendan Higgins Signed-off-by: David Gow Signed-off-by: Shuah Khan --- scripts/mod/modpost.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 29d5a841e215..5937212b4433 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2191,6 +2191,9 @@ static void add_header(struct buffer *b, struct module *mod) if (strstarts(mod->name, "drivers/staging")) buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); + + if (strstarts(mod->name, "tools/testing")) + buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n"); } static void add_exported_symbols(struct buffer *buf, struct module *mod) -- cgit From 028062ec222d6ed0235bbf9612ba8a05efc9e633 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 11 Jul 2022 13:49:41 +0900 Subject: Revert "scripts/mod/modpost.c: permit '.cranges' secton for sh64 architecture." This reverts commit 4d10c223baab8be8f717df3625cfece5be26dead. Commit 37744feebc08 ("sh: remove sh5 support") removed the sh64 support entirely. Note: .cranges was only used for sh64 ever. Commit 211dc24b8744 ("Remove sh5 and sh64 support") in binutils-gdb already removed the relevant code. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 620dc8c4c814..e15227ee58fc 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -742,7 +742,6 @@ static const char *const section_white_list[] = { ".comment*", ".debug*", - ".cranges", /* sh64 */ ".zdebug*", /* Compressed debug sections. */ ".GCC.command.line", /* record-gcc-switches */ ".mdebug*", /* alpha, score, mips etc. */ -- cgit From 5764f6626f5f334b27e168a33735b3899d08bcd2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 20 Jul 2022 01:52:59 +0900 Subject: modpost: drop executable ELF support Since commit 269a535ca931 ("modpost: generate vmlinux.symvers and reuse it for the second modpost"), modpost only parses relocatable files (ET_REL). Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index e15227ee58fc..75aa10413ad4 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -321,9 +321,6 @@ static void *sym_get_data_by_offset(const struct elf_info *info, { Elf_Shdr *sechdr = &info->sechdrs[secindex]; - if (info->hdr->e_type != ET_REL) - offset -= sechdr->sh_addr; - return (void *)info->hdr + sechdr->sh_offset + offset; } @@ -466,6 +463,10 @@ static int parse_elf(struct elf_info *info, const char *filename) sechdrs = (void *)hdr + hdr->e_shoff; info->sechdrs = sechdrs; + /* modpost only works for relocatable objects */ + if (hdr->e_type != ET_REL) + fatal("%s: not relocatable object.", 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=%zu\n", @@ -1622,9 +1623,6 @@ static int addend_386_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) break; case R_386_PC32: r->r_addend = TO_NATIVE(*location) + 4; - /* For CONFIG_RELOCATABLE=y */ - if (elf->hdr->e_type == ET_EXEC) - r->r_addend += r->r_offset; break; } return 0; -- cgit From abe864b8e19adf33b48997de8bc1a8f095390ade Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 20 Jul 2022 01:53:00 +0900 Subject: modpost: use sym_get_data() to get module device_table data Use sym_get_data() to replace the long code. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 75aa10413ad4..08411fff3e17 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -324,7 +324,7 @@ static void *sym_get_data_by_offset(const struct elf_info *info, return (void *)info->hdr + sechdr->sh_offset + offset; } -static void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) +void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) { return sym_get_data_by_offset(info, get_secindex(info, sym), sym->st_value); -- cgit From 125ed24a4ab0d704bab5dee5ccb2c3b05f627c78 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 31 Jul 2022 02:36:34 +0900 Subject: modpost: add array range check to sec_name() The section index is always positive, so the argument, secindex, should be unsigned. Also, inserted the array range check. If sym->st_shndx is a special section index (between SHN_LORESERVE and SHN_HIRESERVE), there is no corresponding section header. For example, if a symbol specifies an absolute value, sym->st_shndx is SHN_ABS (=0xfff1). The current users do not cause the out-of-range access of info->sechddrs[], but it is better to avoid such a pitfall. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 08411fff3e17..148b38699889 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -336,8 +336,16 @@ static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr) sechdr->sh_name); } -static const char *sec_name(const struct elf_info *info, int secindex) +static const char *sec_name(const struct elf_info *info, unsigned int secindex) { + /* + * If sym->st_shndx is a special section index, there is no + * corresponding section header. + * Return "" if the index is out of range of info->sechdrs[] array. + */ + if (secindex >= info->num_sections) + return ""; + return sech_name(info, &info->sechdrs[secindex]); } -- cgit From 5419aa2a8deea06b796222d3215dac6adc270c78 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 31 Jul 2022 02:36:35 +0900 Subject: modpost: use more reliable way to get fromsec in section_rel(a)() The section name of Rel and Rela starts with ".rel" and ".rela" respectively (but, I do not know whether this is specification or convention). For example, ".rela.text" holds relocation entries applied to the ".text" section. So, the code chops the ".rel" or ".rela" prefix to get the name of the section to which the relocation applies. However, I do not like to skip 4 or 5 bytes blindly because it is potential memory overrun. The ELF specification provides a more reliable way to do this. - The sh_info field holds extra information, whose interpretation depends on the section type - If the section type is SHT_REL or SHT_RELA, the sh_info field holds the section header index of the section to which the relocation applies. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 148b38699889..c6a055c0291e 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1723,8 +1723,7 @@ static void section_rela(const char *modname, struct elf_info *elf, Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset; Elf_Rela *stop = (void *)start + sechdr->sh_size; - fromsec = sech_name(elf, sechdr); - fromsec += strlen(".rela"); + fromsec = sec_name(elf, sechdr->sh_info); /* if from section (name) is know good then skip it */ if (match(fromsec, section_white_list)) return; @@ -1776,8 +1775,7 @@ static void section_rel(const char *modname, struct elf_info *elf, Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset; Elf_Rel *stop = (void *)start + sechdr->sh_size; - fromsec = sech_name(elf, sechdr); - fromsec += strlen(".rel"); + fromsec = sec_name(elf, sechdr->sh_info); /* if from section (name) is know good then skip it */ if (match(fromsec, section_white_list)) return; -- cgit From a25efd6ef1ef4c32991a1d5a013dd41e3b8f7eff Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 31 Jul 2022 02:36:36 +0900 Subject: Revert "Kbuild, lto, workaround: Don't warn for initcall_reference in modpost" This reverts commit 77ab21adae509c5540956729e2d03bc1a59bc82a. Even after 8 years later, GCC LTO has not been upstreamed. Also, it said "This is a workaround". If this is needed in the future, it should be added in a proper way. Signed-off-by: Masahiro Yamada Reviewed-by: Nick Desaulniers Acked-by: Jiri Slaby --- scripts/mod/modpost.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index c6a055c0291e..a8ee27496da7 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1462,9 +1462,6 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf, from = find_elf_symbol2(elf, r->r_offset, fromsec); fromsym = sym_name(elf, from); - if (strstarts(fromsym, "reference___initcall")) - return; - tosec = sec_name(elf, get_secindex(elf, sym)); to = find_elf_symbol(elf, r->r_addend, sym); tosym = sym_name(elf, to); -- cgit From 072dd2c8928f2ecdc52cdf5acf30479b327386c9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 1 Aug 2022 18:38:59 +0900 Subject: modpost: shorten warning messages in report_sec_mismatch() Each section mismatch results in long warning messages. Too much. Make each warning fit in one line, and remove a lot of messy code. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 179 +++----------------------------------------------- 1 file changed, 9 insertions(+), 170 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a8ee27496da7..9e8ae2636ec1 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1238,42 +1238,6 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr, return near; } -/* - * Convert a section name to the function/data attribute - * .init.text => __init - * .memexitconst => __memconst - * etc. - * - * The memory of returned value has been allocated on a heap. The user of this - * method should free it after usage. -*/ -static char *sec2annotation(const char *s) -{ - if (match(s, init_exit_sections)) { - char *p = NOFAIL(malloc(20)); - char *r = p; - - *p++ = '_'; - *p++ = '_'; - if (*s == '.') - s++; - while (*s && *s != '.') - *p++ = *s++; - *p = '\0'; - if (*s == '.') - s++; - if (strstr(s, "rodata") != NULL) - strcat(p, "const "); - else if (strstr(s, "data") != NULL) - strcat(p, "data "); - else - strcat(p, " "); - return r; - } else { - return NOFAIL(strdup("")); - } -} - static int is_function(Elf_Sym *sym) { if (sym) @@ -1282,19 +1246,6 @@ static int is_function(Elf_Sym *sym) return -1; } -static void print_section_list(const char * const list[20]) -{ - const char *const *s = list; - - while (*s) { - fprintf(stderr, "%s", *s); - s++; - if (*s) - fprintf(stderr, ", "); - } - fprintf(stderr, "\n"); -} - static inline void get_pretty_name(int is_func, const char** name, const char** name_p) { switch (is_func) { @@ -1312,141 +1263,31 @@ static inline void get_pretty_name(int is_func, const char** name, const char** static void report_sec_mismatch(const char *modname, const struct sectioncheck *mismatch, const char *fromsec, - unsigned long long fromaddr, const char *fromsym, - int from_is_func, - const char *tosec, const char *tosym, - int to_is_func) + const char *tosec, const char *tosym) { - const char *from, *from_p; - const char *to, *to_p; - char *prl_from; - char *prl_to; - sec_mismatch_count++; - get_pretty_name(from_is_func, &from, &from_p); - get_pretty_name(to_is_func, &to, &to_p); - - warn("%s(%s+0x%llx): Section mismatch in reference from the %s %s%s " - "to the %s %s:%s%s\n", - modname, fromsec, fromaddr, from, fromsym, from_p, to, tosec, - tosym, to_p); - switch (mismatch->mismatch) { case TEXT_TO_ANY_INIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The function %s%s() references\n" - "the %s %s%s%s.\n" - "This is often because %s lacks a %s\n" - "annotation or the annotation of %s is wrong.\n", - prl_from, fromsym, - to, prl_to, tosym, to_p, - fromsym, prl_to, tosym); - free(prl_from); - free(prl_to); - break; - case DATA_TO_ANY_INIT: { - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The variable %s references\n" - "the %s %s%s%s\n" - "If the reference is valid then annotate the\n" - "variable with __init* or __refdata (see linux/init.h) " - "or name the variable:\n", - fromsym, to, prl_to, tosym, to_p); - print_section_list(mismatch->symbol_white_list); - free(prl_to); - break; - } + case DATA_TO_ANY_INIT: case TEXT_TO_ANY_EXIT: - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The function %s() references a %s in an exit section.\n" - "Often the %s %s%s has valid usage outside the exit section\n" - "and the fix is to remove the %sannotation of %s.\n", - fromsym, to, to, tosym, to_p, prl_to, tosym); - free(prl_to); - break; - case DATA_TO_ANY_EXIT: { - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The variable %s references\n" - "the %s %s%s%s\n" - "If the reference is valid then annotate the\n" - "variable with __exit* (see linux/init.h) or " - "name the variable:\n", - fromsym, to, prl_to, tosym, to_p); - print_section_list(mismatch->symbol_white_list); - free(prl_to); - break; - } + case DATA_TO_ANY_EXIT: case XXXINIT_TO_SOME_INIT: case XXXEXIT_TO_SOME_EXIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The %s %s%s%s references\n" - "a %s %s%s%s.\n" - "If %s is only used by %s then\n" - "annotate %s with a matching annotation.\n", - from, prl_from, fromsym, from_p, - to, prl_to, tosym, to_p, - tosym, fromsym, tosym); - free(prl_from); - free(prl_to); - break; case ANY_INIT_TO_ANY_EXIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The %s %s%s%s references\n" - "a %s %s%s%s.\n" - "This is often seen when error handling " - "in the init function\n" - "uses functionality in the exit path.\n" - "The fix is often to remove the %sannotation of\n" - "%s%s so it may be used outside an exit section.\n", - from, prl_from, fromsym, from_p, - to, prl_to, tosym, to_p, - prl_to, tosym, to_p); - free(prl_from); - free(prl_to); - break; case ANY_EXIT_TO_ANY_INIT: - prl_from = sec2annotation(fromsec); - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The %s %s%s%s references\n" - "a %s %s%s%s.\n" - "This is often seen when error handling " - "in the exit function\n" - "uses functionality in the init path.\n" - "The fix is often to remove the %sannotation of\n" - "%s%s so it may be used outside an init section.\n", - from, prl_from, fromsym, from_p, - to, prl_to, tosym, to_p, - prl_to, tosym, to_p); - free(prl_from); - free(prl_to); + warn("%s: section mismatch in reference: %s (section: %s) -> %s (section: %s)\n", + modname, fromsym, fromsec, tosym, tosec); break; case EXPORT_TO_INIT_EXIT: - prl_to = sec2annotation(tosec); - fprintf(stderr, - "The symbol %s is exported and annotated %s\n" - "Fix this by removing the %sannotation of %s " - "or drop the export.\n", - tosym, prl_to, prl_to, tosym); - free(prl_to); + warn("%s: EXPORT_SYMBOL used for init/exit symbol: %s (section: %s)\n", + modname, tosym, tosec); break; case EXTABLE_TO_NON_TEXT: - fatal("There's a special handler for this mismatch type, " - "we should never get here."); + fatal("There's a special handler for this mismatch type, we should never get here.\n"); break; } - fprintf(stderr, "\n"); } static void default_mismatch_handler(const char *modname, struct elf_info *elf, @@ -1470,9 +1311,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf, if (secref_whitelist(mismatch, fromsec, fromsym, tosec, tosym)) { report_sec_mismatch(modname, mismatch, - fromsec, r->r_offset, fromsym, - is_function(from), tosec, tosym, - is_function(to)); + fromsec, fromsym, tosec, tosym); } } -- cgit From 7452dd26a59a9dfcde3f179594f3be6c4752a9a9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 1 Aug 2022 18:39:00 +0900 Subject: modpost: add PATTERNS() helper macro This will be useful to define a NULL-terminated array inside a function call. Currently, string arrays passed to match() are defined in separate places: static const char *const init_sections[] = { ALL_INIT_SECTIONS, NULL }; static const char *const text_sections[] = { ALL_TEXT_SECTIONS, NULL }; static const char *const optim_symbols[] = { "*.constprop.*", NULL }; ... /* Check for pattern 5 */ if (match(fromsec, text_sections) && match(tosec, init_sections) && match(fromsym, optim_symbols)) return 0; With the new helper macro, you can list the patterns directly in the function call, like this: /* Check for pattern 5 */ if (match(fromsec, PATTERNS(ALL_TEXT_SECTIONS)) && match(tosec, PATTERNS(ALL_INIT_SECTIONS)) && match(fromsym, PATTERNS("*.contprop.*"))) return 0; Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 9e8ae2636ec1..c2949a1a0d5e 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -746,6 +746,13 @@ static bool match(const char *string, const char *const patterns[]) return false; } +/* useful to pass patterns to match() directly */ +#define PATTERNS(...) \ + ({ \ + static const char *const patterns[] = {__VA_ARGS__, NULL}; \ + patterns; \ + }) + /* sections that we do not want to do full section mismatch check on */ static const char *const section_white_list[] = { -- cgit From 1560cb0e186e83f0572a84d22e139c100060905c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 1 Aug 2022 18:39:01 +0900 Subject: modpost: remove unneeded .symbol_white_list initializers The ->symbol_white_list field is referenced in secref_whitelist(), only when 'fromsec' is data_sections. /* Check for pattern 2 */ if (match(tosec, init_exit_sections) && match(fromsec, data_sections) && match(fromsym, mismatch->symbol_white_list)) return 0; If .fromsec is not data sections, the .symbol_white_list member is not used by anyone. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index c2949a1a0d5e..bcd1319f3097 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -930,7 +930,6 @@ static const struct sectioncheck sectioncheck[] = { .fromsec = { TEXT_SECTIONS, NULL }, .bad_tosec = { ALL_INIT_SECTIONS, NULL }, .mismatch = TEXT_TO_ANY_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { DATA_SECTIONS, NULL }, @@ -951,7 +950,6 @@ static const struct sectioncheck sectioncheck[] = { .fromsec = { TEXT_SECTIONS, NULL }, .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, .mismatch = TEXT_TO_ANY_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { DATA_SECTIONS, NULL }, @@ -964,41 +962,35 @@ static const struct sectioncheck sectioncheck[] = { .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = XXXINIT_TO_SOME_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not reference exit code/data from memexit code/data */ { .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, .bad_tosec = { EXIT_SECTIONS, NULL }, .mismatch = XXXEXIT_TO_SOME_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not use exit code/data from init code */ { .fromsec = { ALL_INIT_SECTIONS, NULL }, .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, .mismatch = ANY_INIT_TO_ANY_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not use init code/data from exit code */ { .fromsec = { ALL_EXIT_SECTIONS, NULL }, .bad_tosec = { ALL_INIT_SECTIONS, NULL }, .mismatch = ANY_EXIT_TO_ANY_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { ALL_PCI_INIT_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = ANY_INIT_TO_ANY_EXIT, - .symbol_white_list = { NULL }, }, /* Do not export init/exit functions or data */ { .fromsec = { "___ksymtab*", NULL }, .bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }, .mismatch = EXPORT_TO_INIT_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { "__ex_table", NULL }, -- cgit From 672fb6740cbfde34f4d367ffa3c939b608a927e1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 1 Aug 2022 18:39:02 +0900 Subject: modpost: remove .symbol_white_list field entirely It is not so useful to have symbol whitelists in arrays. With this over-engineering, the code is difficult to follow. Let's do it more directly, and collect the relevant code to one place. Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 55 +++++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 39 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index bcd1319f3097..8484c0798f28 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -845,28 +845,12 @@ static const char *const init_data_sections[] = /* all init sections */ static const char *const init_sections[] = { ALL_INIT_SECTIONS, NULL }; -/* All init and exit sections (code + data) */ -static const char *const init_exit_sections[] = - {ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }; - /* all text sections */ static const char *const text_sections[] = { ALL_TEXT_SECTIONS, NULL }; /* data section */ static const char *const data_sections[] = { DATA_SECTIONS, NULL }; - -/* symbols in .data that may refer to init/exit sections */ -#define DEFAULT_SYMBOL_WHITE_LIST \ - "*driver", \ - "*_template", /* scsi uses *_template a lot */ \ - "*_timer", /* arm uses ops structures named _timer a lot */ \ - "*_sht", /* scsi also used *_sht to some extent */ \ - "*_ops", \ - "*_probe", \ - "*_probe_one", \ - "*_console" - static const char *const head_sections[] = { ".head.text*", NULL }; static const char *const linker_symbols[] = { "__init_begin", "_sinittext", "_einittext", NULL }; @@ -898,9 +882,6 @@ enum mismatch { * * @mismatch: Type of mismatch. * - * @symbol_white_list: Do not match a relocation to a symbol in this list - * even if it is targeting a section in @bad_to_sec. - * * @handler: Specific handler to call when a match is found. If NULL, * default_mismatch_handler() will be called. * @@ -910,7 +891,6 @@ struct sectioncheck { const char *bad_tosec[20]; const char *good_tosec[20]; enum mismatch mismatch; - const char *symbol_white_list[20]; void (*handler)(const char *modname, struct elf_info *elf, const struct sectioncheck* const mismatch, Elf_Rela *r, Elf_Sym *sym, const char *fromsec); @@ -935,16 +915,11 @@ static const struct sectioncheck sectioncheck[] = { .fromsec = { DATA_SECTIONS, NULL }, .bad_tosec = { ALL_XXXINIT_SECTIONS, NULL }, .mismatch = DATA_TO_ANY_INIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, { .fromsec = { DATA_SECTIONS, NULL }, .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = DATA_TO_ANY_INIT, - .symbol_white_list = { - "*_template", "*_timer", "*_sht", "*_ops", - "*_probe", "*_probe_one", "*_console", NULL - }, }, { .fromsec = { TEXT_SECTIONS, NULL }, @@ -955,7 +930,6 @@ static const struct sectioncheck sectioncheck[] = { .fromsec = { DATA_SECTIONS, NULL }, .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, .mismatch = DATA_TO_ANY_EXIT, - .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, /* Do not reference init code/data from meminit code/data */ { @@ -1051,15 +1025,6 @@ static const struct sectioncheck *section_mismatch( * fromsec = .data* * atsym = __param_ops_* * - * Pattern 2: - * Many drivers utilise a *driver container with references to - * add, remove, probe functions etc. - * the pattern is identified by: - * tosec = init or exit section - * fromsec = data section - * atsym = *driver, *_template, *_sht, *_ops, *_probe, - * *probe_one, *_console, *_timer - * * Pattern 3: * Whitelist all references from .head.text to any init section * @@ -1108,10 +1073,22 @@ static int secref_whitelist(const struct sectioncheck *mismatch, strstarts(fromsym, "__param_ops_")) return 0; - /* Check for pattern 2 */ - if (match(tosec, init_exit_sections) && - match(fromsec, data_sections) && - match(fromsym, mismatch->symbol_white_list)) + /* symbols in data sections that may refer to any init/exit sections */ + if (match(fromsec, PATTERNS(DATA_SECTIONS)) && + match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) && + match(fromsym, PATTERNS("*_template", // scsi uses *_template a lot + "*_timer", // arm uses ops structures named _timer a lot + "*_sht", // scsi also used *_sht to some extent + "*_ops", + "*_probe", + "*_probe_one", + "*_console"))) + return 0; + + /* symbols in data sections that may refer to meminit/exit sections */ + if (match(fromsec, PATTERNS(DATA_SECTIONS)) && + match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_EXIT_SECTIONS)) && + match(fromsym, PATTERNS("*driver"))) return 0; /* Check for pattern 3 */ -- cgit From 5b8a9a8fd1f0c3d55d407cf759d54ca68798d9ad Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 9 Aug 2022 23:11:17 +0900 Subject: modpost: fix module versioning when a symbol lacks valid CRC Since commit 7b4537199a4a ("kbuild: link symbol CRCs at final link, removing CONFIG_MODULE_REL_CRCS"), module versioning is broken on some architectures. Loading a module fails with "disagrees about version of symbol module_layout". On such architectures (e.g. ARCH=sparc build with sparc64_defconfig), modpost shows a warning, like follows: WARNING: modpost: EXPORT symbol "_mcount" [vmlinux] version generation failed, symbol will not be versioned. Is "_mcount" prototyped in ? Previously, it was a harmless warning (CRC check was just skipped), but now wrong CRCs are used for comparison because invalid CRCs are just skipped. $ sparc64-linux-gnu-nm -n vmlinux [snip] 0000000000c2cea0 r __ksymtab__kstrtol 0000000000c2ceb8 r __ksymtab__kstrtoul 0000000000c2ced0 r __ksymtab__local_bh_enable 0000000000c2cee8 r __ksymtab__mcount 0000000000c2cf00 r __ksymtab__printk 0000000000c2cf18 r __ksymtab__raw_read_lock 0000000000c2cf30 r __ksymtab__raw_read_lock_bh [snip] 0000000000c53b34 D __crc__kstrtol 0000000000c53b38 D __crc__kstrtoul 0000000000c53b3c D __crc__local_bh_enable 0000000000c53b40 D __crc__printk 0000000000c53b44 D __crc__raw_read_lock 0000000000c53b48 D __crc__raw_read_lock_bh Please notice __crc__mcount is missing here. When the module subsystem looks up a CRC that comes after, it results in reading out a wrong address. For example, when __crc__printk is needed, the module subsystem reads 0xc53b44 instead of 0xc53b40. All CRC entries must be output for correct index accessing. Invalid CRCs will be unused, but are needed to keep the one-to-one mapping between __ksymtab_* and __crc_*. The best is to fix all modpost warnings, but several warnings are still remaining on less popular architectures. Fixes: 7b4537199a4a ("kbuild: link symbol CRCs at final link, removing CONFIG_MODULE_REL_CRCS") Reported-by: matoro Signed-off-by: Masahiro Yamada Tested-by: matoro --- scripts/mod/modpost.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts/mod/modpost.c') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 55e32af2e53f..2c80da0220c3 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2021,13 +2021,11 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod) /* record CRCs for exported symbols */ buf_printf(buf, "\n"); list_for_each_entry(sym, &mod->exported_symbols, list) { - if (!sym->crc_valid) { + if (!sym->crc_valid) warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" "Is \"%s\" prototyped in ?\n", sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", sym->name); - continue; - } buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n", sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : ""); -- cgit