Skip to content

Commit 4c56eb3

Browse files
committed
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 dbacb0e ("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 <[email protected]> Suggested-by: Linus Torvalds <[email protected]> Signed-off-by: Masahiro Yamada <[email protected]>
1 parent 738fc99 commit 4c56eb3

File tree

5 files changed

+47
-1
lines changed

5 files changed

+47
-1
lines changed

include/asm-generic/vmlinux.lds.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,7 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
10381038
*(.discard) \
10391039
*(.discard.*) \
10401040
*(.export_symbol) \
1041+
*(.no_trim_symbol) \
10411042
*(.modinfo) \
10421043
/* ld.bfd warns about .gnu.version* even when not emitted */ \
10431044
*(.gnu.version*) \

include/linux/module.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,10 @@ extern int modules_disabled; /* for sysctl */
306306
/* Get/put a kernel symbol (calls must be symmetric) */
307307
void *__symbol_get(const char *symbol);
308308
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))); })
310313

311314
/* modules using other modules: kdb wants to see this. */
312315
struct module_use {

scripts/mod/modpost.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@ static int parse_elf(struct elf_info *info, const char *filename)
507507
info->modinfo_len = sechdrs[i].sh_size;
508508
} else if (!strcmp(secname, ".export_symbol")) {
509509
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;
510513
}
511514

512515
if (sechdrs[i].sh_type == SHT_SYMTAB) {
@@ -1566,6 +1569,14 @@ static void read_symbols(const char *modname)
15661569
/* strip trailing .o */
15671570
mod = new_module(modname, strlen(modname) - strlen(".o"));
15681571

1572+
/* save .no_trim_symbol section for later use */
1573+
if (info.no_trim_symbol_len) {
1574+
mod->no_trim_symbol = xmalloc(info.no_trim_symbol_len);
1575+
memcpy(mod->no_trim_symbol, info.no_trim_symbol,
1576+
info.no_trim_symbol_len);
1577+
mod->no_trim_symbol_len = info.no_trim_symbol_len;
1578+
}
1579+
15691580
if (!mod->is_vmlinux) {
15701581
license = get_modinfo(&info, "license");
15711582
if (!license)
@@ -1728,6 +1739,28 @@ static void handle_white_list_exports(const char *white_list)
17281739
free(buf);
17291740
}
17301741

1742+
/*
1743+
* Keep symbols recorded in the .no_trim_symbol section. This is necessary to
1744+
* prevent CONFIG_TRIM_UNUSED_KSYMS from dropping EXPORT_SYMBOL because
1745+
* symbol_get() relies on the symbol being present in the ksymtab for lookups.
1746+
*/
1747+
static void keep_no_trim_symbols(struct module *mod)
1748+
{
1749+
unsigned long size = mod->no_trim_symbol_len;
1750+
1751+
for (char *s = mod->no_trim_symbol; s; s = next_string(s , &size)) {
1752+
struct symbol *sym;
1753+
1754+
/*
1755+
* If find_symbol() returns NULL, this symbol is not provided
1756+
* by any module, and symbol_get() will fail.
1757+
*/
1758+
sym = find_symbol(s);
1759+
if (sym)
1760+
sym->used = true;
1761+
}
1762+
}
1763+
17311764
static void check_modname_len(struct module *mod)
17321765
{
17331766
const char *mod_name;
@@ -2254,6 +2287,8 @@ int main(int argc, char **argv)
22542287
read_symbols_from_files(files_source);
22552288

22562289
list_for_each_entry(mod, &modules, list) {
2290+
keep_no_trim_symbols(mod);
2291+
22572292
if (mod->dump_file || mod->is_vmlinux)
22582293
continue;
22592294

scripts/mod/modpost.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ struct module_alias {
111111
*
112112
* @dump_file: path to the .symvers file if loaded from a file
113113
* @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
114116
*/
115117
struct module {
116118
struct list_head list;
@@ -128,6 +130,8 @@ struct module {
128130
// Actual imported namespaces
129131
struct list_head imported_namespaces;
130132
struct list_head aliases;
133+
char *no_trim_symbol;
134+
unsigned int no_trim_symbol_len;
131135
char name[];
132136
};
133137

@@ -141,6 +145,8 @@ struct elf_info {
141145
char *strtab;
142146
char *modinfo;
143147
unsigned int modinfo_len;
148+
char *no_trim_symbol;
149+
unsigned int no_trim_symbol_len;
144150

145151
/* support for 32bit section numbers */
146152

scripts/module.lds.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ SECTIONS {
1616
*(.discard)
1717
*(.discard.*)
1818
*(.export_symbol)
19+
*(.no_trim_symbol)
1920
}
2021

2122
__ksymtab 0 : ALIGN(8) { *(SORT(___ksymtab+*)) }

0 commit comments

Comments
 (0)