Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <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.

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>
Tested-by: Sedat Dilek <sedat.dilek@gmail.com> # LLVM-14 (x86-64)

+70 -27
+4
scripts/Makefile.build
··· 249 249 $(CONFIG_SHELL) $(srctree)/scripts/gen_ksymdeps.sh $@ >> $(dot-target).cmd 250 250 endif 251 251 252 + cmd_check_local_export = $(srctree)/scripts/check-local-export $@ 253 + 252 254 define rule_cc_o_c 253 255 $(call cmd_and_fixdep,cc_o_c) 254 256 $(call cmd,gen_ksymdeps) 257 + $(call cmd,check_local_export) 255 258 $(call cmd,checksrc) 256 259 $(call cmd,checkdoc) 257 260 $(call cmd,gen_objtooldep) ··· 265 262 define rule_as_o_S 266 263 $(call cmd_and_fixdep,as_o_S) 267 264 $(call cmd,gen_ksymdeps) 265 + $(call cmd,check_local_export) 268 266 $(call cmd,gen_objtooldep) 269 267 $(call cmd,gen_symversions_S) 270 268 endef
+65
scripts/check-local-export
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0-only 3 + # 4 + # Copyright (C) 2022 Masahiro Yamada <masahiroy@kernel.org> 5 + # 6 + # Exit with error if a local exported symbol is found. 7 + # EXPORT_SYMBOL should be used for global symbols. 8 + 9 + set -e 10 + 11 + declare -A symbol_types 12 + declare -a export_symbols 13 + 14 + exit_code=0 15 + 16 + while read value type name 17 + do 18 + # Skip the line if the number of fields is less than 3. 19 + # 20 + # case 1) 21 + # For undefined symbols, the first field (value) is empty. 22 + # The outout looks like this: 23 + # " U _printk" 24 + # It is unneeded to record undefined symbols. 25 + # 26 + # case 2) 27 + # For Clang LTO, llvm-nm outputs a line with type 't' but empty name: 28 + # "---------------- t" 29 + if [[ -z ${name} ]]; then 30 + continue 31 + fi 32 + 33 + # save (name, type) in the associative array 34 + symbol_types[${name}]=${type} 35 + 36 + # append the exported symbol to the array 37 + if [[ ${name} == __ksymtab_* ]]; then 38 + export_symbols+=(${name#__ksymtab_}) 39 + fi 40 + 41 + # If there is no symbol in the object, ${NM} (both GNU nm and llvm-nm) 42 + # shows 'no symbols' diagnostic (but exits with 0). It is harmless and 43 + # hidden by '2>/dev/null'. However, it suppresses real error messages 44 + # as well. Add a hand-crafted error message here. 45 + # 46 + # Use --quiet instead of 2>/dev/null when we upgrade the minimum version 47 + # of binutils to 2.37, llvm to 13.0.0. 48 + # 49 + # Then, the following line will be really simple: 50 + # done < <(${NM} --quiet ${1}) 51 + done < <(${NM} ${1} 2>/dev/null || { echo "${0}: ${NM} failed" >&2; false; } ) 52 + 53 + # Catch error in the process substitution 54 + wait $! 55 + 56 + for name in "${export_symbols[@]}" 57 + do 58 + # nm(3) says "If lowercase, the symbol is usually local" 59 + if [[ ${symbol_types[$name]} =~ [a-z] ]]; then 60 + echo "$@: error: local symbol '${name}' was exported" >&2 61 + exit_code=1 62 + fi 63 + done 64 + 65 + exit ${exit_code}
+1 -27
scripts/mod/modpost.c
··· 212 212 unsigned int crc; 213 213 bool crc_valid; 214 214 bool weak; 215 - bool is_static; /* true if symbol is not global */ 216 215 bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ 217 216 char name[]; 218 217 }; ··· 241 242 242 243 memset(s, 0, sizeof(*s)); 243 244 strcpy(s->name, name); 244 - s->is_static = true; 245 + 245 246 return s; 246 247 } 247 248 ··· 2063 2064 sym_get_data(&info, sym)); 2064 2065 } 2065 2066 2066 - // check for static EXPORT_SYMBOL_* functions && global vars 2067 - for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { 2068 - unsigned char bind = ELF_ST_BIND(sym->st_info); 2069 - 2070 - if (bind == STB_GLOBAL || bind == STB_WEAK) { 2071 - struct symbol *s = 2072 - find_symbol(remove_dot(info.strtab + 2073 - sym->st_name)); 2074 - 2075 - if (s) 2076 - s->is_static = false; 2077 - } 2078 - } 2079 - 2080 2067 check_sec_ref(modname, &info); 2081 2068 2082 2069 if (!mod->is_vmlinux) { ··· 2492 2507 mod->from_dump = true; 2493 2508 } 2494 2509 s = sym_add_exported(symname, mod, gpl_only); 2495 - s->is_static = false; 2496 2510 sym_set_crc(s, crc); 2497 2511 sym_update_namespace(symname, namespace); 2498 2512 } ··· 2556 2572 char *missing_namespace_deps = NULL; 2557 2573 char *dump_write = NULL, *files_source = NULL; 2558 2574 int opt; 2559 - int n; 2560 2575 LIST_HEAD(dump_lists); 2561 2576 struct dump_list *dl, *dl2; 2562 2577 ··· 2631 2648 if (sec_mismatch_count && !sec_mismatch_warn_only) 2632 2649 error("Section mismatches detected.\n" 2633 2650 "Set CONFIG_SECTION_MISMATCH_WARN_ONLY=y to allow them.\n"); 2634 - for (n = 0; n < SYMBOL_HASH_SIZE; n++) { 2635 - struct symbol *s; 2636 - 2637 - for (s = symbolhash[n]; s; s = s->next) { 2638 - if (s->is_static) 2639 - error("\"%s\" [%s] is a static EXPORT_SYMBOL\n", 2640 - s->name, s->module->name); 2641 - } 2642 - } 2643 2651 2644 2652 if (nr_unresolved > MAX_UNRESOLVED_REPORTS) 2645 2653 warn("suppressed %u unresolved symbol warnings because there were too many)\n",