kbuild: keep symbols for symbol_get() even with CONFIG_TRIM_UNUSED_KSYMS

Linus observed that the symbol_request(utf8_data_table) call fails when
CONFIG_UNICODE=y and CONFIG_TRIM_UNUSED_KSYMS=y.

symbol_get() relies on the symbol data being present in the ksymtab for
symbol lookups. However, EXPORT_SYMBOL_GPL(utf8_data_table) is dropped
due to CONFIG_TRIM_UNUSED_KSYMS, as no module references it in this case.

Probably, this has been broken since commit dbacb0ef670d ("kconfig option
for TRIM_UNUSED_KSYMS").

This commit addresses the issue by leveraging modpost. Symbol names
passed to symbol_get() are recorded in the special .no_trim_symbol
section, which is then parsed by modpost to forcibly keep such symbols.
The .no_trim_symbol section is discarded by the linker scripts, so there
is no impact on the size of the final vmlinux or modules.

This commit cannot resolve the issue for direct calls to __symbol_get()
because the symbol name is not known at compile-time.

Although symbol_get() may eventually be deprecated, this workaround
should be good enough meanwhile.

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

Changed files
+47 -1
include
asm-generic
linux
scripts
+1
include/asm-generic/vmlinux.lds.h
··· 1038 1038 *(.discard) \ 1039 1039 *(.discard.*) \ 1040 1040 *(.export_symbol) \ 1041 + *(.no_trim_symbol) \ 1041 1042 *(.modinfo) \ 1042 1043 /* ld.bfd warns about .gnu.version* even when not emitted */ \ 1043 1044 *(.gnu.version*) \
+4 -1
include/linux/module.h
··· 306 306 /* Get/put a kernel symbol (calls must be symmetric) */ 307 307 void *__symbol_get(const char *symbol); 308 308 void *__symbol_get_gpl(const char *symbol); 309 - #define symbol_get(x) ((typeof(&x))(__symbol_get(__stringify(x)))) 309 + #define symbol_get(x) ({ \ 310 + static const char __notrim[] \ 311 + __used __section(".no_trim_symbol") = __stringify(x); \ 312 + (typeof(&x))(__symbol_get(__stringify(x))); }) 310 313 311 314 /* modules using other modules: kdb wants to see this. */ 312 315 struct module_use {
+35
scripts/mod/modpost.c
··· 507 507 info->modinfo_len = sechdrs[i].sh_size; 508 508 } else if (!strcmp(secname, ".export_symbol")) { 509 509 info->export_symbol_secndx = i; 510 + } else if (!strcmp(secname, ".no_trim_symbol")) { 511 + info->no_trim_symbol = (void *)hdr + sechdrs[i].sh_offset; 512 + info->no_trim_symbol_len = sechdrs[i].sh_size; 510 513 } 511 514 512 515 if (sechdrs[i].sh_type == SHT_SYMTAB) { ··· 1569 1566 /* strip trailing .o */ 1570 1567 mod = new_module(modname, strlen(modname) - strlen(".o")); 1571 1568 1569 + /* save .no_trim_symbol section for later use */ 1570 + if (info.no_trim_symbol_len) { 1571 + mod->no_trim_symbol = xmalloc(info.no_trim_symbol_len); 1572 + memcpy(mod->no_trim_symbol, info.no_trim_symbol, 1573 + info.no_trim_symbol_len); 1574 + mod->no_trim_symbol_len = info.no_trim_symbol_len; 1575 + } 1576 + 1572 1577 if (!mod->is_vmlinux) { 1573 1578 license = get_modinfo(&info, "license"); 1574 1579 if (!license) ··· 1737 1726 } 1738 1727 1739 1728 free(buf); 1729 + } 1730 + 1731 + /* 1732 + * Keep symbols recorded in the .no_trim_symbol section. This is necessary to 1733 + * prevent CONFIG_TRIM_UNUSED_KSYMS from dropping EXPORT_SYMBOL because 1734 + * symbol_get() relies on the symbol being present in the ksymtab for lookups. 1735 + */ 1736 + static void keep_no_trim_symbols(struct module *mod) 1737 + { 1738 + unsigned long size = mod->no_trim_symbol_len; 1739 + 1740 + for (char *s = mod->no_trim_symbol; s; s = next_string(s , &size)) { 1741 + struct symbol *sym; 1742 + 1743 + /* 1744 + * If find_symbol() returns NULL, this symbol is not provided 1745 + * by any module, and symbol_get() will fail. 1746 + */ 1747 + sym = find_symbol(s); 1748 + if (sym) 1749 + sym->used = true; 1750 + } 1740 1751 } 1741 1752 1742 1753 static void check_modname_len(struct module *mod) ··· 2287 2254 read_symbols_from_files(files_source); 2288 2255 2289 2256 list_for_each_entry(mod, &modules, list) { 2257 + keep_no_trim_symbols(mod); 2258 + 2290 2259 if (mod->dump_file || mod->is_vmlinux) 2291 2260 continue; 2292 2261
+6
scripts/mod/modpost.h
··· 111 111 * 112 112 * @dump_file: path to the .symvers file if loaded from a file 113 113 * @aliases: list head for module_aliases 114 + * @no_trim_symbol: .no_trim_symbol section data 115 + * @no_trim_symbol_len: length of the .no_trim_symbol section 114 116 */ 115 117 struct module { 116 118 struct list_head list; ··· 130 128 // Actual imported namespaces 131 129 struct list_head imported_namespaces; 132 130 struct list_head aliases; 131 + char *no_trim_symbol; 132 + unsigned int no_trim_symbol_len; 133 133 char name[]; 134 134 }; 135 135 ··· 145 141 char *strtab; 146 142 char *modinfo; 147 143 unsigned int modinfo_len; 144 + char *no_trim_symbol; 145 + unsigned int no_trim_symbol_len; 148 146 149 147 /* support for 32bit section numbers */ 150 148
+1
scripts/module.lds.S
··· 16 16 *(.discard) 17 17 *(.discard.*) 18 18 *(.export_symbol) 19 + *(.no_trim_symbol) 19 20 } 20 21 21 22 __ksymtab 0 : ALIGN(8) { *(SORT(___ksymtab+*)) }