Skip to content

Commit f936c12

Browse files
samitolvanenmasahir0y
authored andcommitted
gendwarfksyms: Limit structure expansion
Expand each structure type only once per exported symbol. This is necessary to support self-referential structures, which would otherwise result in infinite recursion, and it's sufficient for catching ABI changes. Types defined in .c files are opaque to external users and thus cannot affect the ABI. Consider type definitions in .c files to be declarations to prevent opaque types from changing symbol versions. 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 f6bb924 commit f936c12

4 files changed

Lines changed: 215 additions & 8 deletions

File tree

scripts/gendwarfksyms/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
hostprogs-always-y += gendwarfksyms
33

44
gendwarfksyms-objs += gendwarfksyms.o
5+
gendwarfksyms-objs += cache.o
56
gendwarfksyms-objs += die.o
67
gendwarfksyms-objs += dwarf.o
78
gendwarfksyms-objs += symbols.o

scripts/gendwarfksyms/cache.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2024 Google LLC
4+
*/
5+
6+
#include "gendwarfksyms.h"
7+
8+
struct cache_item {
9+
unsigned long key;
10+
int value;
11+
struct hlist_node hash;
12+
};
13+
14+
void cache_set(struct cache *cache, unsigned long key, int value)
15+
{
16+
struct cache_item *ci;
17+
18+
ci = xmalloc(sizeof(struct cache_item));
19+
ci->key = key;
20+
ci->value = value;
21+
hash_add(cache->cache, &ci->hash, hash_32(key));
22+
}
23+
24+
int cache_get(struct cache *cache, unsigned long key)
25+
{
26+
struct cache_item *ci;
27+
28+
hash_for_each_possible(cache->cache, ci, hash, hash_32(key)) {
29+
if (ci->key == key)
30+
return ci->value;
31+
}
32+
33+
return -1;
34+
}
35+
36+
void cache_init(struct cache *cache)
37+
{
38+
hash_init(cache->cache);
39+
}
40+
41+
void cache_free(struct cache *cache)
42+
{
43+
struct hlist_node *tmp;
44+
struct cache_item *ci;
45+
46+
hash_for_each_safe(cache->cache, ci, tmp, hash) {
47+
free(ci);
48+
}
49+
50+
hash_init(cache->cache);
51+
}

scripts/gendwarfksyms/dwarf.c

Lines changed: 117 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ static void process_linebreak(struct die *cache, int n)
2727
!dwarf_form##attr(&da, value); \
2828
}
2929

30+
DEFINE_GET_ATTR(flag, bool)
3031
DEFINE_GET_ATTR(udata, Dwarf_Word)
3132

3233
static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value)
@@ -80,6 +81,55 @@ static bool match_export_symbol(struct state *state, Dwarf_Die *die)
8081
return !!state->sym;
8182
}
8283

84+
/* DW_AT_decl_file -> struct srcfile */
85+
static struct cache srcfile_cache;
86+
87+
static bool is_definition_private(Dwarf_Die *die)
88+
{
89+
Dwarf_Word filenum;
90+
Dwarf_Files *files;
91+
Dwarf_Die cudie;
92+
const char *s;
93+
int res;
94+
95+
/*
96+
* Definitions in .c files cannot change the public ABI,
97+
* so consider them private.
98+
*/
99+
if (!get_udata_attr(die, DW_AT_decl_file, &filenum))
100+
return false;
101+
102+
res = cache_get(&srcfile_cache, filenum);
103+
if (res >= 0)
104+
return !!res;
105+
106+
if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL))
107+
error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1));
108+
109+
if (dwarf_getsrcfiles(&cudie, &files, NULL))
110+
error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1));
111+
112+
s = dwarf_filesrc(files, filenum, NULL, NULL);
113+
if (!s)
114+
error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1));
115+
116+
s = strrchr(s, '.');
117+
res = s && !strcmp(s, ".c");
118+
cache_set(&srcfile_cache, filenum, res);
119+
120+
return !!res;
121+
}
122+
123+
static bool is_kabi_definition(Dwarf_Die *die)
124+
{
125+
bool value;
126+
127+
if (get_flag_attr(die, DW_AT_declaration, &value) && value)
128+
return false;
129+
130+
return !is_definition_private(die);
131+
}
132+
83133
/*
84134
* Type string processing
85135
*/
@@ -456,19 +506,27 @@ static void __process_structure_type(struct state *state, struct die *cache,
456506
die_callback_t process_func,
457507
die_match_callback_t match_func)
458508
{
509+
bool expand;
510+
459511
process(cache, type);
460512
process_fqn(cache, die);
461513
process(cache, " {");
462514
process_linebreak(cache, 1);
463515

464-
check(process_die_container(state, cache, die, process_func,
465-
match_func));
516+
expand = state->expand.expand && is_kabi_definition(die);
517+
518+
if (expand) {
519+
check(process_die_container(state, cache, die, process_func,
520+
match_func));
521+
}
466522

467523
process_linebreak(cache, -1);
468524
process(cache, "}");
469525

470-
process_byte_size_attr(cache, die);
471-
process_alignment_attr(cache, die);
526+
if (expand) {
527+
process_byte_size_attr(cache, die);
528+
process_alignment_attr(cache, die);
529+
}
472530
}
473531

474532
#define DEFINE_PROCESS_STRUCTURE_TYPE(structure) \
@@ -553,25 +611,70 @@ static void process_cached(struct state *state, struct die *cache,
553611
}
554612
}
555613

614+
static void state_init(struct state *state)
615+
{
616+
state->expand.expand = true;
617+
cache_init(&state->expansion_cache);
618+
}
619+
620+
static void expansion_state_restore(struct expansion_state *state,
621+
struct expansion_state *saved)
622+
{
623+
state->expand = saved->expand;
624+
}
625+
626+
static void expansion_state_save(struct expansion_state *state,
627+
struct expansion_state *saved)
628+
{
629+
expansion_state_restore(saved, state);
630+
}
631+
632+
static bool is_expanded_type(int tag)
633+
{
634+
return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
635+
tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type;
636+
}
637+
556638
#define PROCESS_TYPE(type) \
557639
case DW_TAG_##type##_type: \
558640
process_##type##_type(state, cache, die); \
559641
break;
560642

561643
static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
562644
{
645+
enum die_state want_state = DIE_COMPLETE;
563646
struct die *cache;
647+
struct expansion_state saved;
564648
int tag = dwarf_tag(die);
565649

650+
expansion_state_save(&state->expand, &saved);
651+
566652
/*
567-
* If we have the DIE already cached, use it instead of walking
653+
* Structures and enumeration types are expanded only once per
654+
* exported symbol. This is sufficient for detecting ABI changes
655+
* within the structure.
656+
*/
657+
if (is_expanded_type(tag)) {
658+
if (cache_was_expanded(&state->expansion_cache, die->addr))
659+
state->expand.expand = false;
660+
661+
if (state->expand.expand)
662+
cache_mark_expanded(&state->expansion_cache, die->addr);
663+
else
664+
want_state = DIE_UNEXPANDED;
665+
}
666+
667+
/*
668+
* If we have want_state already cached, use it instead of walking
568669
* through DWARF.
569670
*/
570-
cache = die_map_get(die, DIE_COMPLETE);
671+
cache = die_map_get(die, want_state);
571672

572-
if (cache->state == DIE_COMPLETE) {
673+
if (cache->state == want_state) {
573674
process_cached(state, cache, die);
574675
die_map_add_die(parent, cache);
676+
677+
expansion_state_restore(&state->expand, &saved);
575678
return 0;
576679
}
577680

@@ -612,9 +715,10 @@ static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
612715

613716
/* Update cache state and append to the parent (if any) */
614717
cache->tag = tag;
615-
cache->state = DIE_COMPLETE;
718+
cache->state = want_state;
616719
die_map_add_die(parent, cache);
617720

721+
expansion_state_restore(&state->expand, &saved);
618722
return 0;
619723
}
620724

@@ -676,11 +780,14 @@ static int process_exported_symbols(struct state *unused, struct die *cache,
676780
if (!match_export_symbol(&state, die))
677781
return 0;
678782

783+
state_init(&state);
784+
679785
if (tag == DW_TAG_subprogram)
680786
process_subprogram(&state, &state.die);
681787
else
682788
process_variable(&state, &state.die);
683789

790+
cache_free(&state.expansion_cache);
684791
return 0;
685792
}
686793
default:
@@ -692,4 +799,6 @@ void process_cu(Dwarf_Die *cudie)
692799
{
693800
check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
694801
match_all));
802+
803+
cache_free(&srcfile_cache);
695804
}

scripts/gendwarfksyms/gendwarfksyms.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ void symbol_free(void);
102102

103103
enum die_state {
104104
DIE_INCOMPLETE,
105+
DIE_UNEXPANDED,
105106
DIE_COMPLETE,
106107
DIE_LAST = DIE_COMPLETE
107108
};
@@ -131,6 +132,7 @@ static inline const char *die_state_name(enum die_state state)
131132
{
132133
switch (state) {
133134
CASE_CONST_TO_STR(DIE_INCOMPLETE)
135+
CASE_CONST_TO_STR(DIE_UNEXPANDED)
134136
CASE_CONST_TO_STR(DIE_COMPLETE)
135137
}
136138

@@ -153,16 +155,60 @@ void die_map_add_linebreak(struct die *pd, int linebreak);
153155
void die_map_add_die(struct die *pd, struct die *child);
154156
void die_map_free(void);
155157

158+
/*
159+
* cache.c
160+
*/
161+
162+
#define CACHE_HASH_BITS 10
163+
164+
/* A cache for addresses we've already seen. */
165+
struct cache {
166+
HASHTABLE_DECLARE(cache, 1 << CACHE_HASH_BITS);
167+
};
168+
169+
void cache_set(struct cache *cache, unsigned long key, int value);
170+
int cache_get(struct cache *cache, unsigned long key);
171+
void cache_init(struct cache *cache);
172+
void cache_free(struct cache *cache);
173+
174+
static inline void __cache_mark_expanded(struct cache *cache, uintptr_t addr)
175+
{
176+
cache_set(cache, addr, 1);
177+
}
178+
179+
static inline bool __cache_was_expanded(struct cache *cache, uintptr_t addr)
180+
{
181+
return cache_get(cache, addr) == 1;
182+
}
183+
184+
static inline void cache_mark_expanded(struct cache *cache, void *addr)
185+
{
186+
__cache_mark_expanded(cache, (uintptr_t)addr);
187+
}
188+
189+
static inline bool cache_was_expanded(struct cache *cache, void *addr)
190+
{
191+
return __cache_was_expanded(cache, (uintptr_t)addr);
192+
}
193+
156194
/*
157195
* dwarf.c
158196
*/
159197

198+
struct expansion_state {
199+
bool expand;
200+
};
201+
160202
struct state {
161203
struct symbol *sym;
162204
Dwarf_Die die;
163205

164206
/* List expansion */
165207
bool first_list_item;
208+
209+
/* Structure expansion */
210+
struct expansion_state expand;
211+
struct cache expansion_cache;
166212
};
167213

168214
typedef int (*die_callback_t)(struct state *state, struct die *cache,

0 commit comments

Comments
 (0)