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

gendwarfksyms: Add support for symbol type pointers

The compiler may choose not to emit type information in DWARF for
external symbols. Clang, for example, does this for symbols not
defined in the current TU.

To provide a way to work around this issue, add support for
__gendwarfksyms_ptr_<symbol> pointers that force the compiler to emit
the necessary type information in DWARF also for the missing symbols.

Example usage:

#define GENDWARFKSYMS_PTR(sym) \
static typeof(sym) *__gendwarfksyms_ptr_##sym __used \
__section(".discard.gendwarfksyms") = &sym;

extern int external_symbol(void);
GENDWARFKSYMS_PTR(external_symbol);

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
fa624569 a9369418

+121 -1
+54 -1
scripts/gendwarfksyms/dwarf.c
··· 1061 1061 process_symbol(state, die, __process_variable); 1062 1062 } 1063 1063 1064 + static void save_symbol_ptr(struct state *state) 1065 + { 1066 + Dwarf_Die ptr_type; 1067 + Dwarf_Die type; 1068 + 1069 + if (!get_ref_die_attr(&state->die, DW_AT_type, &ptr_type) || 1070 + dwarf_tag(&ptr_type) != DW_TAG_pointer_type) 1071 + error("%s must be a pointer type!", 1072 + get_symbol_name(&state->die)); 1073 + 1074 + if (!get_ref_die_attr(&ptr_type, DW_AT_type, &type)) 1075 + error("%s pointer missing a type attribute?", 1076 + get_symbol_name(&state->die)); 1077 + 1078 + /* 1079 + * Save the symbol pointer DIE in case the actual symbol is 1080 + * missing from the DWARF. Clang, for example, intentionally 1081 + * omits external symbols from the debugging information. 1082 + */ 1083 + if (dwarf_tag(&type) == DW_TAG_subroutine_type) 1084 + symbol_set_ptr(state->sym, &type); 1085 + else 1086 + symbol_set_ptr(state->sym, &ptr_type); 1087 + } 1088 + 1064 1089 static int process_exported_symbols(struct state *unused, struct die *cache, 1065 1090 Dwarf_Die *die) 1066 1091 { ··· 1109 1084 1110 1085 state_init(&state); 1111 1086 1112 - if (tag == DW_TAG_subprogram) 1087 + if (is_symbol_ptr(get_symbol_name(&state.die))) 1088 + save_symbol_ptr(&state); 1089 + else if (tag == DW_TAG_subprogram) 1113 1090 process_subprogram(&state, &state.die); 1114 1091 else 1115 1092 process_variable(&state, &state.die); ··· 1124 1097 } 1125 1098 } 1126 1099 1100 + static void process_symbol_ptr(struct symbol *sym, void *arg) 1101 + { 1102 + struct state state; 1103 + Dwarf *dwarf = arg; 1104 + 1105 + if (sym->state != SYMBOL_UNPROCESSED || !sym->ptr_die_addr) 1106 + return; 1107 + 1108 + debug("%s", sym->name); 1109 + state_init(&state); 1110 + state.sym = sym; 1111 + 1112 + if (!dwarf_die_addr_die(dwarf, (void *)sym->ptr_die_addr, &state.die)) 1113 + error("dwarf_die_addr_die failed for symbol ptr: '%s'", 1114 + sym->name); 1115 + 1116 + if (dwarf_tag(&state.die) == DW_TAG_subroutine_type) 1117 + process_subprogram(&state, &state.die); 1118 + else 1119 + process_variable(&state, &state.die); 1120 + 1121 + cache_free(&state.expansion_cache); 1122 + } 1123 + 1127 1124 void process_cu(Dwarf_Die *cudie) 1128 1125 { 1129 1126 check(process_die_container(NULL, NULL, cudie, process_exported_symbols, 1130 1127 match_all)); 1128 + 1129 + symbol_for_each(process_symbol_ptr, dwarf_cu_getdwarf(cudie->cu)); 1131 1130 1132 1131 cache_free(&srcfile_cache); 1133 1132 }
+33
scripts/gendwarfksyms/examples/symbolptr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + * 5 + * Example for symbol pointers. When compiled with Clang, gendwarfkyms 6 + * uses a symbol pointer for `f`. 7 + * 8 + * $ clang -g -c examples/symbolptr.c -o examples/symbolptr.o 9 + * $ echo -e "f\ng\np" | ./gendwarfksyms -d examples/symbolptr.o 10 + */ 11 + 12 + /* Kernel macros for userspace testing. */ 13 + #ifndef __used 14 + #define __used __attribute__((__used__)) 15 + #endif 16 + #ifndef __section 17 + #define __section(section) __attribute__((__section__(section))) 18 + #endif 19 + 20 + #define __GENDWARFKSYMS_EXPORT(sym) \ 21 + static typeof(sym) *__gendwarfksyms_ptr_##sym __used \ 22 + __section(".discard.gendwarfksyms") = &sym; 23 + 24 + extern void f(unsigned int arg); 25 + void g(int *arg); 26 + void g(int *arg) {} 27 + 28 + struct s; 29 + extern struct s *p; 30 + 31 + __GENDWARFKSYMS_EXPORT(f); 32 + __GENDWARFKSYMS_EXPORT(g); 33 + __GENDWARFKSYMS_EXPORT(p);
+7
scripts/gendwarfksyms/gendwarfksyms.h
··· 89 89 * symbols.c 90 90 */ 91 91 92 + /* See symbols.c:is_symbol_ptr */ 93 + #define SYMBOL_PTR_PREFIX "__gendwarfksyms_ptr_" 94 + #define SYMBOL_PTR_PREFIX_LEN (sizeof(SYMBOL_PTR_PREFIX) - 1) 95 + 92 96 static inline unsigned int addr_hash(uintptr_t addr) 93 97 { 94 98 return hash_ptr((const void *)addr); ··· 116 112 struct hlist_node name_hash; 117 113 enum symbol_state state; 118 114 uintptr_t die_addr; 115 + uintptr_t ptr_die_addr; 119 116 unsigned long crc; 120 117 }; 121 118 122 119 typedef void (*symbol_callback_t)(struct symbol *, void *arg); 123 120 121 + bool is_symbol_ptr(const char *name); 124 122 void symbol_read_exports(FILE *file); 125 123 void symbol_read_symtab(int fd); 126 124 struct symbol *symbol_get(const char *name); 125 + void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr); 127 126 void symbol_set_die(struct symbol *sym, Dwarf_Die *die); 128 127 void symbol_set_crc(struct symbol *sym, unsigned long crc); 129 128 void symbol_for_each(symbol_callback_t func, void *arg);
+27
scripts/gendwarfksyms/symbols.c
··· 39 39 return processed; 40 40 } 41 41 42 + /* 43 + * For symbols without debugging information (e.g. symbols defined in other 44 + * TUs), we also match __gendwarfksyms_ptr_<symbol_name> symbols, which the 45 + * kernel uses to ensure type information is present in the TU that exports 46 + * the symbol. A __gendwarfksyms_ptr pointer must have the same type as the 47 + * exported symbol, e.g.: 48 + * 49 + * typeof(symname) *__gendwarf_ptr_symname = &symname; 50 + */ 51 + bool is_symbol_ptr(const char *name) 52 + { 53 + return name && !strncmp(name, SYMBOL_PTR_PREFIX, SYMBOL_PTR_PREFIX_LEN); 54 + } 55 + 42 56 static unsigned int for_each(const char *name, symbol_callback_t func, 43 57 void *data) 44 58 { ··· 61 47 62 48 if (!name || !*name) 63 49 return 0; 50 + if (is_symbol_ptr(name)) 51 + name += SYMBOL_PTR_PREFIX_LEN; 64 52 65 53 hash_for_each_possible_safe(symbol_names, match, tmp, name_hash, 66 54 hash_str(name)) { ··· 97 81 void symbol_set_crc(struct symbol *sym, unsigned long crc) 98 82 { 99 83 if (for_each(sym->name, set_crc, &crc) == 0) 84 + error("no matching symbols: '%s'", sym->name); 85 + } 86 + 87 + static void set_ptr(struct symbol *sym, void *data) 88 + { 89 + sym->ptr_die_addr = (uintptr_t)((Dwarf_Die *)data)->addr; 90 + } 91 + 92 + void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr) 93 + { 94 + if (for_each(sym->name, set_ptr, ptr) == 0) 100 95 error("no matching symbols: '%s'", sym->name); 101 96 } 102 97