Skip to content

Commit 7137888

Browse files
samitolvanenmasahir0y
authored andcommitted
gendwarfksyms: Add symbol versioning
Calculate symbol versions from the fully expanded type strings in type_map, and output the versions in a genksyms-compatible format. 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 ab44399 commit 7137888

6 files changed

Lines changed: 216 additions & 9 deletions

File tree

scripts/gendwarfksyms/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ gendwarfksyms-objs += dwarf.o
88
gendwarfksyms-objs += symbols.o
99
gendwarfksyms-objs += types.o
1010

11-
HOSTLDLIBS_gendwarfksyms := -ldw -lelf
11+
HOSTLDLIBS_gendwarfksyms := -ldw -lelf -lz

scripts/gendwarfksyms/dwarf.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,12 +740,33 @@ static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
740740
/*
741741
* Exported symbol processing
742742
*/
743+
static struct die *get_symbol_cache(struct state *state, Dwarf_Die *die)
744+
{
745+
struct die *cache;
746+
747+
cache = die_map_get(die, DIE_SYMBOL);
748+
749+
if (cache->state != DIE_INCOMPLETE)
750+
return NULL; /* We already processed a symbol for this DIE */
751+
752+
cache->tag = dwarf_tag(die);
753+
return cache;
754+
}
755+
743756
static void process_symbol(struct state *state, Dwarf_Die *die,
744757
die_callback_t process_func)
745758
{
759+
struct die *cache;
760+
761+
symbol_set_die(state->sym, die);
762+
763+
cache = get_symbol_cache(state, die);
764+
if (!cache)
765+
return;
766+
746767
debug("%s", state->sym->name);
747-
check(process_func(state, NULL, die));
748-
state->sym->state = SYMBOL_MAPPED;
768+
check(process_func(state, cache, die));
769+
cache->state = DIE_SYMBOL;
749770
if (dump_dies)
750771
fputs("\n", stderr);
751772
}

scripts/gendwarfksyms/gendwarfksyms.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ int dump_dies;
2323
int dump_die_map;
2424
/* Print out type strings (i.e. type_map) */
2525
int dump_types;
26+
/* Print out expanded type strings used for symbol versions */
27+
int dump_versions;
2628
/* Write a symtypes file */
2729
int symtypes;
2830
static const char *symtypes_file;
@@ -35,6 +37,7 @@ static void usage(void)
3537
" --dump-dies Dump DWARF DIE contents\n"
3638
" --dump-die-map Print debugging information about die_map changes\n"
3739
" --dump-types Dump type strings\n"
40+
" --dump-versions Dump expanded type strings used for symbol versions\n"
3841
" -T, --symtypes file Write a symtypes file\n"
3942
" -h, --help Print this message\n"
4043
"\n",
@@ -69,9 +72,10 @@ static int process_module(Dwfl_Module *mod, void **userdata, const char *name,
6972
} while (cu);
7073

7174
/*
72-
* Use die_map to expand type strings and write them to `symfile`.
75+
* Use die_map to expand type strings, write them to `symfile`, and
76+
* calculate symbol versions.
7377
*/
74-
generate_symtypes(symfile);
78+
generate_symtypes_and_versions(symfile);
7579
die_map_free();
7680

7781
return DWARF_CB_OK;
@@ -93,6 +97,7 @@ int main(int argc, char **argv)
9397
{ "dump-dies", 0, &dump_dies, 1 },
9498
{ "dump-die-map", 0, &dump_die_map, 1 },
9599
{ "dump-types", 0, &dump_types, 1 },
100+
{ "dump-versions", 0, &dump_versions, 1 },
96101
{ "symtypes", 1, NULL, 'T' },
97102
{ "help", 0, NULL, 'h' },
98103
{ 0, 0, NULL, 0 }
@@ -166,6 +171,7 @@ int main(int argc, char **argv)
166171
if (symfile)
167172
check(fclose(symfile));
168173

174+
symbol_print_versions();
169175
symbol_free();
170176

171177
return 0;

scripts/gendwarfksyms/gendwarfksyms.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extern int debug;
2323
extern int dump_dies;
2424
extern int dump_die_map;
2525
extern int dump_types;
26+
extern int dump_versions;
2627
extern int symtypes;
2728

2829
/*
@@ -95,6 +96,7 @@ static inline unsigned int addr_hash(uintptr_t addr)
9596
enum symbol_state {
9697
SYMBOL_UNPROCESSED,
9798
SYMBOL_MAPPED,
99+
SYMBOL_PROCESSED
98100
};
99101

100102
struct symbol_addr {
@@ -109,13 +111,18 @@ struct symbol {
109111
struct hlist_node name_hash;
110112
enum symbol_state state;
111113
uintptr_t die_addr;
114+
unsigned long crc;
112115
};
113116

114117
typedef void (*symbol_callback_t)(struct symbol *, void *arg);
115118

116119
void symbol_read_exports(FILE *file);
117120
void symbol_read_symtab(int fd);
118121
struct symbol *symbol_get(const char *name);
122+
void symbol_set_die(struct symbol *sym, Dwarf_Die *die);
123+
void symbol_set_crc(struct symbol *sym, unsigned long crc);
124+
void symbol_for_each(symbol_callback_t func, void *arg);
125+
void symbol_print_versions(void);
119126
void symbol_free(void);
120127

121128
/*
@@ -126,7 +133,8 @@ enum die_state {
126133
DIE_INCOMPLETE,
127134
DIE_UNEXPANDED,
128135
DIE_COMPLETE,
129-
DIE_LAST = DIE_COMPLETE
136+
DIE_SYMBOL,
137+
DIE_LAST = DIE_SYMBOL
130138
};
131139

132140
enum die_fragment_type {
@@ -156,6 +164,7 @@ static inline const char *die_state_name(enum die_state state)
156164
CASE_CONST_TO_STR(DIE_INCOMPLETE)
157165
CASE_CONST_TO_STR(DIE_UNEXPANDED)
158166
CASE_CONST_TO_STR(DIE_COMPLETE)
167+
CASE_CONST_TO_STR(DIE_SYMBOL)
159168
}
160169

161170
error("unexpected die_state: %d", state);
@@ -252,6 +261,6 @@ void process_cu(Dwarf_Die *cudie);
252261
* types.c
253262
*/
254263

255-
void generate_symtypes(FILE *file);
264+
void generate_symtypes_and_versions(FILE *file);
256265

257266
#endif /* __GENDWARFKSYMS_H */

scripts/gendwarfksyms/symbols.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,36 @@ static unsigned int for_each(const char *name, symbol_callback_t func,
6666
return 0;
6767
}
6868

69+
static void set_crc(struct symbol *sym, void *data)
70+
{
71+
unsigned long *crc = data;
72+
73+
if (sym->state == SYMBOL_PROCESSED && sym->crc != *crc)
74+
warn("overriding version for symbol %s (crc %lx vs. %lx)",
75+
sym->name, sym->crc, *crc);
76+
77+
sym->state = SYMBOL_PROCESSED;
78+
sym->crc = *crc;
79+
}
80+
81+
void symbol_set_crc(struct symbol *sym, unsigned long crc)
82+
{
83+
if (for_each(sym->name, set_crc, &crc) == 0)
84+
error("no matching symbols: '%s'", sym->name);
85+
}
86+
87+
static void set_die(struct symbol *sym, void *data)
88+
{
89+
sym->die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
90+
sym->state = SYMBOL_MAPPED;
91+
}
92+
93+
void symbol_set_die(struct symbol *sym, Dwarf_Die *die)
94+
{
95+
if (for_each(sym->name, set_die, die) == 0)
96+
error("no matching symbols: '%s'", sym->name);
97+
}
98+
6999
static bool is_exported(const char *name)
70100
{
71101
return for_each(name, NULL, NULL) > 0;
@@ -120,6 +150,16 @@ struct symbol *symbol_get(const char *name)
120150
return sym;
121151
}
122152

153+
void symbol_for_each(symbol_callback_t func, void *arg)
154+
{
155+
struct hlist_node *tmp;
156+
struct symbol *sym;
157+
158+
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
159+
func(sym, arg);
160+
}
161+
}
162+
123163
typedef void (*elf_symbol_callback_t)(const char *name, GElf_Sym *sym,
124164
Elf32_Word xndx, void *arg);
125165

@@ -246,6 +286,19 @@ void symbol_read_symtab(int fd)
246286
elf_for_each_global(fd, elf_set_symbol_addr, NULL);
247287
}
248288

289+
void symbol_print_versions(void)
290+
{
291+
struct hlist_node *tmp;
292+
struct symbol *sym;
293+
294+
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
295+
if (sym->state != SYMBOL_PROCESSED)
296+
warn("no information for symbol %s", sym->name);
297+
298+
printf("#SYMVER %s 0x%08lx\n", sym->name, sym->crc);
299+
}
300+
}
301+
249302
void symbol_free(void)
250303
{
251304
struct hlist_node *tmp;

scripts/gendwarfksyms/types.c

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define _GNU_SOURCE
77
#include <inttypes.h>
88
#include <stdio.h>
9+
#include <zlib.h>
910

1011
#include "gendwarfksyms.h"
1112

@@ -178,6 +179,33 @@ static void type_map_free(void)
178179
hash_init(type_map);
179180
}
180181

182+
/*
183+
* CRC for a type, with an optional fully expanded type string for
184+
* debugging.
185+
*/
186+
struct version {
187+
struct type_expansion type;
188+
unsigned long crc;
189+
};
190+
191+
static void version_init(struct version *version)
192+
{
193+
version->crc = crc32(0, NULL, 0);
194+
type_expansion_init(&version->type);
195+
}
196+
197+
static void version_free(struct version *version)
198+
{
199+
type_expansion_free(&version->type);
200+
}
201+
202+
static void version_add(struct version *version, const char *s)
203+
{
204+
version->crc = crc32(version->crc, (void *)s, strlen(s));
205+
if (dump_versions)
206+
type_expansion_append(&version->type, s, NULL);
207+
}
208+
181209
/*
182210
* Type reference format: <prefix>#<name>, where prefix:
183211
* s -> structure
@@ -187,6 +215,12 @@ static void type_map_free(void)
187215
*
188216
* Names with spaces are additionally wrapped in single quotes.
189217
*/
218+
static inline bool is_type_prefix(const char *s)
219+
{
220+
return (s[0] == 's' || s[0] == 'u' || s[0] == 'e' || s[0] == 't') &&
221+
s[1] == '#';
222+
}
223+
190224
static char get_type_prefix(int tag)
191225
{
192226
switch (tag) {
@@ -214,6 +248,8 @@ static char *get_type_name(struct die *cache)
214248
warn("found incomplete cache entry: %p", cache);
215249
return NULL;
216250
}
251+
if (cache->state == DIE_SYMBOL)
252+
return NULL;
217253
if (!cache->fqn || !*cache->fqn)
218254
return NULL;
219255

@@ -231,6 +267,39 @@ static char *get_type_name(struct die *cache)
231267
return name;
232268
}
233269

270+
static void __calculate_version(struct version *version, struct list_head *list)
271+
{
272+
struct type_list_entry *entry;
273+
struct type_expansion *e;
274+
275+
/* Calculate a CRC over an expanded type string */
276+
list_for_each_entry(entry, list, list) {
277+
if (is_type_prefix(entry->str)) {
278+
check(type_map_get(entry->str, &e));
279+
280+
/*
281+
* It's sufficient to expand each type reference just
282+
* once to detect changes.
283+
*/
284+
if (cache_was_expanded(&expansion_cache, e)) {
285+
version_add(version, entry->str);
286+
} else {
287+
cache_mark_expanded(&expansion_cache, e);
288+
__calculate_version(version, &e->expanded);
289+
}
290+
} else {
291+
version_add(version, entry->str);
292+
}
293+
}
294+
}
295+
296+
static void calculate_version(struct version *version, struct list_head *list)
297+
{
298+
version_init(version);
299+
__calculate_version(version, list);
300+
cache_free(&expansion_cache);
301+
}
302+
234303
static void __type_expand(struct die *cache, struct type_expansion *type,
235304
bool recursive);
236305

@@ -337,7 +406,49 @@ static void expand_type(struct die *cache, void *arg)
337406
free(name);
338407
}
339408

340-
void generate_symtypes(FILE *file)
409+
static void expand_symbol(struct symbol *sym, void *arg)
410+
{
411+
struct type_expansion type;
412+
struct version version;
413+
struct die *cache;
414+
415+
/*
416+
* No need to expand again unless we want a symtypes file entry
417+
* for the symbol. Note that this means `sym` has the same address
418+
* as another symbol that was already processed.
419+
*/
420+
if (!symtypes && sym->state == SYMBOL_PROCESSED)
421+
return;
422+
423+
if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
424+
return; /* We'll warn about missing CRCs later. */
425+
426+
type_expand(cache, &type, false);
427+
428+
/* If the symbol already has a version, don't calculate it again. */
429+
if (sym->state != SYMBOL_PROCESSED) {
430+
calculate_version(&version, &type.expanded);
431+
symbol_set_crc(sym, version.crc);
432+
debug("%s = %lx", sym->name, version.crc);
433+
434+
if (dump_versions) {
435+
checkp(fputs(sym->name, stderr));
436+
checkp(fputs(" ", stderr));
437+
type_list_write(&version.type.expanded, stderr);
438+
checkp(fputs("\n", stderr));
439+
}
440+
441+
version_free(&version);
442+
}
443+
444+
/* These aren't needed in type_map unless we want a symtypes file. */
445+
if (symtypes)
446+
type_map_add(sym->name, &type);
447+
448+
type_expansion_free(&type);
449+
}
450+
451+
void generate_symtypes_and_versions(FILE *file)
341452
{
342453
cache_init(&expansion_cache);
343454

@@ -355,7 +466,14 @@ void generate_symtypes(FILE *file)
355466
die_map_for_each(expand_type, NULL);
356467

357468
/*
358-
* 2. If a symtypes file is requested, write type_map contents to
469+
* 2. For each exported symbol, expand the die_map type, and use
470+
* type_map expansions to calculate a symbol version from the
471+
* fully expanded type string.
472+
*/
473+
symbol_for_each(expand_symbol, NULL);
474+
475+
/*
476+
* 3. If a symtypes file is requested, write type_map contents to
359477
* the file.
360478
*/
361479
type_map_write(file);

0 commit comments

Comments
 (0)