Skip to content

Commit fa62456

Browse files
samitolvanenmasahir0y
authored andcommitted
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>
1 parent a936941 commit fa62456

4 files changed

Lines changed: 121 additions & 1 deletion

File tree

scripts/gendwarfksyms/dwarf.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,31 @@ static void process_variable(struct state *state, Dwarf_Die *die)
10611061
process_symbol(state, die, __process_variable);
10621062
}
10631063

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+
10641089
static int process_exported_symbols(struct state *unused, struct die *cache,
10651090
Dwarf_Die *die)
10661091
{
@@ -1084,7 +1109,9 @@ static int process_exported_symbols(struct state *unused, struct die *cache,
10841109

10851110
state_init(&state);
10861111

1087-
if (tag == DW_TAG_subprogram)
1112+
if (is_symbol_ptr(get_symbol_name(&state.die)))
1113+
save_symbol_ptr(&state);
1114+
else if (tag == DW_TAG_subprogram)
10881115
process_subprogram(&state, &state.die);
10891116
else
10901117
process_variable(&state, &state.die);
@@ -1097,10 +1124,36 @@ static int process_exported_symbols(struct state *unused, struct die *cache,
10971124
}
10981125
}
10991126

1127+
static void process_symbol_ptr(struct symbol *sym, void *arg)
1128+
{
1129+
struct state state;
1130+
Dwarf *dwarf = arg;
1131+
1132+
if (sym->state != SYMBOL_UNPROCESSED || !sym->ptr_die_addr)
1133+
return;
1134+
1135+
debug("%s", sym->name);
1136+
state_init(&state);
1137+
state.sym = sym;
1138+
1139+
if (!dwarf_die_addr_die(dwarf, (void *)sym->ptr_die_addr, &state.die))
1140+
error("dwarf_die_addr_die failed for symbol ptr: '%s'",
1141+
sym->name);
1142+
1143+
if (dwarf_tag(&state.die) == DW_TAG_subroutine_type)
1144+
process_subprogram(&state, &state.die);
1145+
else
1146+
process_variable(&state, &state.die);
1147+
1148+
cache_free(&state.expansion_cache);
1149+
}
1150+
11001151
void process_cu(Dwarf_Die *cudie)
11011152
{
11021153
check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
11031154
match_all));
11041155

1156+
symbol_for_each(process_symbol_ptr, dwarf_cu_getdwarf(cudie->cu));
1157+
11051158
cache_free(&srcfile_cache);
11061159
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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);

scripts/gendwarfksyms/gendwarfksyms.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ extern int symtypes;
8989
* symbols.c
9090
*/
9191

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+
9296
static inline unsigned int addr_hash(uintptr_t addr)
9397
{
9498
return hash_ptr((const void *)addr);
@@ -112,14 +116,17 @@ struct symbol {
112116
struct hlist_node name_hash;
113117
enum symbol_state state;
114118
uintptr_t die_addr;
119+
uintptr_t ptr_die_addr;
115120
unsigned long crc;
116121
};
117122

118123
typedef void (*symbol_callback_t)(struct symbol *, void *arg);
119124

125+
bool is_symbol_ptr(const char *name);
120126
void symbol_read_exports(FILE *file);
121127
void symbol_read_symtab(int fd);
122128
struct symbol *symbol_get(const char *name);
129+
void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr);
123130
void symbol_set_die(struct symbol *sym, Dwarf_Die *die);
124131
void symbol_set_crc(struct symbol *sym, unsigned long crc);
125132
void symbol_for_each(symbol_callback_t func, void *arg);

scripts/gendwarfksyms/symbols.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ static unsigned int __for_each_addr(struct symbol *sym, symbol_callback_t func,
3939
return processed;
4040
}
4141

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+
4256
static unsigned int for_each(const char *name, symbol_callback_t func,
4357
void *data)
4458
{
@@ -47,6 +61,8 @@ static unsigned int for_each(const char *name, symbol_callback_t func,
4761

4862
if (!name || !*name)
4963
return 0;
64+
if (is_symbol_ptr(name))
65+
name += SYMBOL_PTR_PREFIX_LEN;
5066

5167
hash_for_each_possible_safe(symbol_names, match, tmp, name_hash,
5268
hash_str(name)) {
@@ -84,6 +100,17 @@ void symbol_set_crc(struct symbol *sym, unsigned long crc)
84100
error("no matching symbols: '%s'", sym->name);
85101
}
86102

103+
static void set_ptr(struct symbol *sym, void *data)
104+
{
105+
sym->ptr_die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
106+
}
107+
108+
void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr)
109+
{
110+
if (for_each(sym->name, set_ptr, ptr) == 0)
111+
error("no matching symbols: '%s'", sym->name);
112+
}
113+
87114
static void set_die(struct symbol *sym, void *data)
88115
{
89116
sym->die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;

0 commit comments

Comments
 (0)