Message ID | 20220513113930.10488-7-masahiroy@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | kbuild: yet another series of cleanups (modpost, LTO, MODULE_REL_CRCS, export.h) | expand |
On Fri, May 13, 2022 at 08:39:26PM +0900, Masahiro Yamada wrote: > 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 <linux/export.h> > 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. > > It is true modpost gets access to the lists of all the member objects > (.vmlinux.objs and *.mod), but 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. > On parisc builds, this patch results in: Building parisc:allnoconfig ... failed -------------- Error log: scripts/check-local-export: sh /opt/buildbot/slave/next-next/build/arch/parisc/nm failed Guenter
On Wed, May 25, 2022 at 5:31 PM Guenter Roeck <linux@roeck-us.net> wrote: > > On Fri, May 13, 2022 at 08:39:26PM +0900, Masahiro Yamada wrote: > > 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 <linux/export.h> > > 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. > > > > It is true modpost gets access to the lists of all the member objects > > (.vmlinux.objs and *.mod), but 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. > > > > On parisc builds, this patch results in: > > Building parisc:allnoconfig ... failed > -------------- > Error log: > scripts/check-local-export: sh /opt/buildbot/slave/next-next/build/arch/parisc/nm failed > > Guenter Thanks for the report. parisc overrides NM: NM = sh $(srctree)/arch/parisc/nm I will fix the script to return the correct exit code.
On 13/05/2022 12:39, Masahiro Yamada wrote: > 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 <linux/export.h> > 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. > > It is true modpost gets access to the lists of all the member objects > (.vmlinux.objs and *.mod), but 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 <masahiroy@kernel.org> > Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> > Tested-by: Nathan Chancellor <nathan@kernel.org> One some older build machines this is causing some builds (ARM/ARM64) to fail ... /dvs/git/dirty/git-master_l4t-upstream/kernel/scripts/check-local-export: line 54: wait: pid 48433 is not a child of this shell /dvs/git/dirty/git-master_l4t-upstream/kernel/scripts/Makefile.build:250: recipe for target 'scripts/mod/empty.o' failed make[2]: *** [scripts/mod/empty.o] Error 127 make[2]: *** Deleting file 'scripts/mod/empty.o' make[2]: *** Waiting for unfinished jobs.... /dvs/git/dirty/git-master_l4t-upstream/kernel/Makefile:1285: recipe for target 'prepare0' failed make[1]: *** [prepare0] Error 2 make[1]: Leaving directory '/dvs/git/dirty/git-master_l4t-upstream/artifacts/linux/arm64-defconfig-jetson' Makefile:228: recipe for target '__sub-make' failed make: *** [__sub-make] Error 2 Any ideas? Thanks Jon
On Tue, Jun 07, 2022 at 03:22:21PM +0100, Jon Hunter wrote: > > On 13/05/2022 12:39, Masahiro Yamada wrote: > > 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 <linux/export.h> > > 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. > > > > It is true modpost gets access to the lists of all the member objects > > (.vmlinux.objs and *.mod), but 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 <masahiroy@kernel.org> > > Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> > > Tested-by: Nathan Chancellor <nathan@kernel.org> > > > One some older build machines this is causing some builds (ARM/ARM64) > to fail ... > > /dvs/git/dirty/git-master_l4t-upstream/kernel/scripts/check-local-export: line 54: wait: pid 48433 is not a child of this shell > /dvs/git/dirty/git-master_l4t-upstream/kernel/scripts/Makefile.build:250: recipe for target 'scripts/mod/empty.o' failed > make[2]: *** [scripts/mod/empty.o] Error 127 > make[2]: *** Deleting file 'scripts/mod/empty.o' > make[2]: *** Waiting for unfinished jobs.... > /dvs/git/dirty/git-master_l4t-upstream/kernel/Makefile:1285: recipe for target 'prepare0' failed > make[1]: *** [prepare0] Error 2 > make[1]: Leaving directory '/dvs/git/dirty/git-master_l4t-upstream/artifacts/linux/arm64-defconfig-jetson' > Makefile:228: recipe for target '__sub-make' failed > make: *** [__sub-make] Error 2 > > Any ideas? https://lore.kernel.org/20220607084317.211785-1-masahiroy@kernel.org/ should resolve it if you wanted to give it a test. Cheers, Nathan
On 07/06/2022 15:25, Nathan Chancellor wrote: > On Tue, Jun 07, 2022 at 03:22:21PM +0100, Jon Hunter wrote: >> >> On 13/05/2022 12:39, Masahiro Yamada wrote: >>> 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 <linux/export.h> >>> 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. >>> >>> It is true modpost gets access to the lists of all the member objects >>> (.vmlinux.objs and *.mod), but 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 <masahiroy@kernel.org> >>> Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> >>> Tested-by: Nathan Chancellor <nathan@kernel.org> >> >> >> One some older build machines this is causing some builds (ARM/ARM64) >> to fail ... >> >> /dvs/git/dirty/git-master_l4t-upstream/kernel/scripts/check-local-export: line 54: wait: pid 48433 is not a child of this shell >> /dvs/git/dirty/git-master_l4t-upstream/kernel/scripts/Makefile.build:250: recipe for target 'scripts/mod/empty.o' failed >> make[2]: *** [scripts/mod/empty.o] Error 127 >> make[2]: *** Deleting file 'scripts/mod/empty.o' >> make[2]: *** Waiting for unfinished jobs.... >> /dvs/git/dirty/git-master_l4t-upstream/kernel/Makefile:1285: recipe for target 'prepare0' failed >> make[1]: *** [prepare0] Error 2 >> make[1]: Leaving directory '/dvs/git/dirty/git-master_l4t-upstream/artifacts/linux/arm64-defconfig-jetson' >> Makefile:228: recipe for target '__sub-make' failed >> make: *** [__sub-make] Error 2 >> >> Any ideas? > > https://lore.kernel.org/20220607084317.211785-1-masahiroy@kernel.org/ > should resolve it if you wanted to give it a test. Thanks! Works for me. Tested-by: Jon Hunter <jonathanh@nvidia.com> Cheers Jon
diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 838ea5e83174..c2a173b3fd60 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -244,9 +244,12 @@ cmd_gen_ksymdeps = \ $(CONFIG_SHELL) $(srctree)/scripts/gen_ksymdeps.sh $@ >> $(dot-target).cmd endif +cmd_check_local_export = $(srctree)/scripts/check-local-export $@ + define rule_cc_o_c $(call cmd_and_fixdep,cc_o_c) $(call cmd,gen_ksymdeps) + $(call cmd,check_local_export) $(call cmd,checksrc) $(call cmd,checkdoc) $(call cmd,gen_objtooldep) @@ -257,6 +260,7 @@ endef define rule_as_o_S $(call cmd_and_fixdep,as_o_S) $(call cmd,gen_ksymdeps) + $(call cmd,check_local_export) $(call cmd,gen_objtooldep) $(call cmd,gen_symversions_S) endef diff --git a/scripts/check-local-export b/scripts/check-local-export new file mode 100755 index 000000000000..829e0591c0be --- /dev/null +++ b/scripts/check-local-export @@ -0,0 +1,64 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2022 Masahiro Yamada <masahiroy@kernel.org> +# +# Exit with error if a local exported symbol is found. +# EXPORT_SYMBOL should be used for global symbols. + +set -e + +declare -A symbol_types +declare -a export_symbols + +exit_code=0 + +while read value type name +do + # Exceptional case for Clang LTO. + # llvm-nm outputs this: + # "---------------- t" + # Skip this line because the name is empty. + if [[ ${value} = -* && -z ${name} ]]; then + continue + fi + + # For undefined symbols, the first field (value) is empty. + # The outout looks like this: + # " U _printk" + # Shift the fields. + if [[ -z ${name} ]]; then + name=${type} + type=${value} + fi + + # save (name, type) in the associative array + symbol_types[${name}]=${type} + + # append the exported symbol to the array + if [[ ${name} == __ksymtab_* ]]; then + export_symbols+=(${name#__ksymtab_}) + fi + + # If there is no symbol in the object, ${NM} (both GNU nm and llvm-nm) + # shows 'no symbols' diagnostic (but exits with 0). It is harmless and + # hidden by '2>/dev/null'. However, it suppresses real error messages + # as well. Add a hand-crafted error message here. + # + # Use -q instead of 2>/dev/null when we upgrade the minimum version of + # binutils to 2.37, llvm to 13.0.0. +done < <(${NM} ${1} 2>/dev/null || { echo "${0}: ${NM} failed" >&2; false; } ) + +# Catch error in the process substitution +wait $! + +for name in "${export_symbols[@]}" +do + # nm(3) says "If lowercase, the symbol is usually local" + if [[ ${symbol_types[$name]} =~ [a-z] ]]; then + echo "$@: error: local symbol '${name}' was exported" >&2 + exit_code=1 + fi +done + +exit ${exit_code} diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 27c45427d989..2328522f8a78 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; } @@ -877,20 +876,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(mod, modname, &info); if (!mod->is_vmlinux) { @@ -1320,7 +1305,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); } @@ -1385,7 +1369,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; @@ -1461,15 +1444,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",