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

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
71378888 ab443998

+216 -9
+1 -1
scripts/gendwarfksyms/Makefile
··· 8 8 gendwarfksyms-objs += symbols.o 9 9 gendwarfksyms-objs += types.o 10 10 11 - HOSTLDLIBS_gendwarfksyms := -ldw -lelf 11 + HOSTLDLIBS_gendwarfksyms := -ldw -lelf -lz
+23 -2
scripts/gendwarfksyms/dwarf.c
··· 740 740 /* 741 741 * Exported symbol processing 742 742 */ 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 + 743 756 static void process_symbol(struct state *state, Dwarf_Die *die, 744 757 die_callback_t process_func) 745 758 { 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 + 746 767 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; 749 770 if (dump_dies) 750 771 fputs("\n", stderr); 751 772 }
+8 -2
scripts/gendwarfksyms/gendwarfksyms.c
··· 23 23 int dump_die_map; 24 24 /* Print out type strings (i.e. type_map) */ 25 25 int dump_types; 26 + /* Print out expanded type strings used for symbol versions */ 27 + int dump_versions; 26 28 /* Write a symtypes file */ 27 29 int symtypes; 28 30 static const char *symtypes_file; ··· 37 35 " --dump-dies Dump DWARF DIE contents\n" 38 36 " --dump-die-map Print debugging information about die_map changes\n" 39 37 " --dump-types Dump type strings\n" 38 + " --dump-versions Dump expanded type strings used for symbol versions\n" 40 39 " -T, --symtypes file Write a symtypes file\n" 41 40 " -h, --help Print this message\n" 42 41 "\n", ··· 72 69 } while (cu); 73 70 74 71 /* 75 - * Use die_map to expand type strings and write them to `symfile`. 72 + * Use die_map to expand type strings, write them to `symfile`, and 73 + * calculate symbol versions. 76 74 */ 77 - generate_symtypes(symfile); 75 + generate_symtypes_and_versions(symfile); 78 76 die_map_free(); 79 77 80 78 return DWARF_CB_OK; ··· 97 93 { "dump-dies", 0, &dump_dies, 1 }, 98 94 { "dump-die-map", 0, &dump_die_map, 1 }, 99 95 { "dump-types", 0, &dump_types, 1 }, 96 + { "dump-versions", 0, &dump_versions, 1 }, 100 97 { "symtypes", 1, NULL, 'T' }, 101 98 { "help", 0, NULL, 'h' }, 102 99 { 0, 0, NULL, 0 } ··· 171 166 if (symfile) 172 167 check(fclose(symfile)); 173 168 169 + symbol_print_versions(); 174 170 symbol_free(); 175 171 176 172 return 0;
+11 -2
scripts/gendwarfksyms/gendwarfksyms.h
··· 23 23 extern int dump_dies; 24 24 extern int dump_die_map; 25 25 extern int dump_types; 26 + extern int dump_versions; 26 27 extern int symtypes; 27 28 28 29 /* ··· 96 95 enum symbol_state { 97 96 SYMBOL_UNPROCESSED, 98 97 SYMBOL_MAPPED, 98 + SYMBOL_PROCESSED 99 99 }; 100 100 101 101 struct symbol_addr { ··· 111 109 struct hlist_node name_hash; 112 110 enum symbol_state state; 113 111 uintptr_t die_addr; 112 + unsigned long crc; 114 113 }; 115 114 116 115 typedef void (*symbol_callback_t)(struct symbol *, void *arg); ··· 119 116 void symbol_read_exports(FILE *file); 120 117 void symbol_read_symtab(int fd); 121 118 struct symbol *symbol_get(const char *name); 119 + void symbol_set_die(struct symbol *sym, Dwarf_Die *die); 120 + void symbol_set_crc(struct symbol *sym, unsigned long crc); 121 + void symbol_for_each(symbol_callback_t func, void *arg); 122 + void symbol_print_versions(void); 122 123 void symbol_free(void); 123 124 124 125 /* ··· 133 126 DIE_INCOMPLETE, 134 127 DIE_UNEXPANDED, 135 128 DIE_COMPLETE, 136 - DIE_LAST = DIE_COMPLETE 129 + DIE_SYMBOL, 130 + DIE_LAST = DIE_SYMBOL 137 131 }; 138 132 139 133 enum die_fragment_type { ··· 164 156 CASE_CONST_TO_STR(DIE_INCOMPLETE) 165 157 CASE_CONST_TO_STR(DIE_UNEXPANDED) 166 158 CASE_CONST_TO_STR(DIE_COMPLETE) 159 + CASE_CONST_TO_STR(DIE_SYMBOL) 167 160 } 168 161 169 162 error("unexpected die_state: %d", state); ··· 261 252 * types.c 262 253 */ 263 254 264 - void generate_symtypes(FILE *file); 255 + void generate_symtypes_and_versions(FILE *file); 265 256 266 257 #endif /* __GENDWARFKSYMS_H */
+53
scripts/gendwarfksyms/symbols.c
··· 66 66 return 0; 67 67 } 68 68 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 + 69 99 static bool is_exported(const char *name) 70 100 { 71 101 return for_each(name, NULL, NULL) > 0; ··· 148 118 149 119 for_each(name, get_symbol, &sym); 150 120 return sym; 121 + } 122 + 123 + void symbol_for_each(symbol_callback_t func, void *arg) 124 + { 125 + struct hlist_node *tmp; 126 + struct symbol *sym; 127 + 128 + hash_for_each_safe(symbol_names, sym, tmp, name_hash) { 129 + func(sym, arg); 130 + } 151 131 } 152 132 153 133 typedef void (*elf_symbol_callback_t)(const char *name, GElf_Sym *sym, ··· 284 244 void symbol_read_symtab(int fd) 285 245 { 286 246 elf_for_each_global(fd, elf_set_symbol_addr, NULL); 247 + } 248 + 249 + void symbol_print_versions(void) 250 + { 251 + struct hlist_node *tmp; 252 + struct symbol *sym; 253 + 254 + hash_for_each_safe(symbol_names, sym, tmp, name_hash) { 255 + if (sym->state != SYMBOL_PROCESSED) 256 + warn("no information for symbol %s", sym->name); 257 + 258 + printf("#SYMVER %s 0x%08lx\n", sym->name, sym->crc); 259 + } 287 260 } 288 261 289 262 void symbol_free(void)
+120 -2
scripts/gendwarfksyms/types.c
··· 6 6 #define _GNU_SOURCE 7 7 #include <inttypes.h> 8 8 #include <stdio.h> 9 + #include <zlib.h> 9 10 10 11 #include "gendwarfksyms.h" 11 12 ··· 180 179 } 181 180 182 181 /* 182 + * CRC for a type, with an optional fully expanded type string for 183 + * debugging. 184 + */ 185 + struct version { 186 + struct type_expansion type; 187 + unsigned long crc; 188 + }; 189 + 190 + static void version_init(struct version *version) 191 + { 192 + version->crc = crc32(0, NULL, 0); 193 + type_expansion_init(&version->type); 194 + } 195 + 196 + static void version_free(struct version *version) 197 + { 198 + type_expansion_free(&version->type); 199 + } 200 + 201 + static void version_add(struct version *version, const char *s) 202 + { 203 + version->crc = crc32(version->crc, (void *)s, strlen(s)); 204 + if (dump_versions) 205 + type_expansion_append(&version->type, s, NULL); 206 + } 207 + 208 + /* 183 209 * Type reference format: <prefix>#<name>, where prefix: 184 210 * s -> structure 185 211 * u -> union ··· 215 187 * 216 188 * Names with spaces are additionally wrapped in single quotes. 217 189 */ 190 + static inline bool is_type_prefix(const char *s) 191 + { 192 + return (s[0] == 's' || s[0] == 'u' || s[0] == 'e' || s[0] == 't') && 193 + s[1] == '#'; 194 + } 195 + 218 196 static char get_type_prefix(int tag) 219 197 { 220 198 switch (tag) { ··· 248 214 warn("found incomplete cache entry: %p", cache); 249 215 return NULL; 250 216 } 217 + if (cache->state == DIE_SYMBOL) 218 + return NULL; 251 219 if (!cache->fqn || !*cache->fqn) 252 220 return NULL; 253 221 ··· 265 229 error("asprintf failed for '%s'", cache->fqn); 266 230 267 231 return name; 232 + } 233 + 234 + static void __calculate_version(struct version *version, struct list_head *list) 235 + { 236 + struct type_list_entry *entry; 237 + struct type_expansion *e; 238 + 239 + /* Calculate a CRC over an expanded type string */ 240 + list_for_each_entry(entry, list, list) { 241 + if (is_type_prefix(entry->str)) { 242 + check(type_map_get(entry->str, &e)); 243 + 244 + /* 245 + * It's sufficient to expand each type reference just 246 + * once to detect changes. 247 + */ 248 + if (cache_was_expanded(&expansion_cache, e)) { 249 + version_add(version, entry->str); 250 + } else { 251 + cache_mark_expanded(&expansion_cache, e); 252 + __calculate_version(version, &e->expanded); 253 + } 254 + } else { 255 + version_add(version, entry->str); 256 + } 257 + } 258 + } 259 + 260 + static void calculate_version(struct version *version, struct list_head *list) 261 + { 262 + version_init(version); 263 + __calculate_version(version, list); 264 + cache_free(&expansion_cache); 268 265 } 269 266 270 267 static void __type_expand(struct die *cache, struct type_expansion *type, ··· 406 337 free(name); 407 338 } 408 339 409 - void generate_symtypes(FILE *file) 340 + static void expand_symbol(struct symbol *sym, void *arg) 341 + { 342 + struct type_expansion type; 343 + struct version version; 344 + struct die *cache; 345 + 346 + /* 347 + * No need to expand again unless we want a symtypes file entry 348 + * for the symbol. Note that this means `sym` has the same address 349 + * as another symbol that was already processed. 350 + */ 351 + if (!symtypes && sym->state == SYMBOL_PROCESSED) 352 + return; 353 + 354 + if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache)) 355 + return; /* We'll warn about missing CRCs later. */ 356 + 357 + type_expand(cache, &type, false); 358 + 359 + /* If the symbol already has a version, don't calculate it again. */ 360 + if (sym->state != SYMBOL_PROCESSED) { 361 + calculate_version(&version, &type.expanded); 362 + symbol_set_crc(sym, version.crc); 363 + debug("%s = %lx", sym->name, version.crc); 364 + 365 + if (dump_versions) { 366 + checkp(fputs(sym->name, stderr)); 367 + checkp(fputs(" ", stderr)); 368 + type_list_write(&version.type.expanded, stderr); 369 + checkp(fputs("\n", stderr)); 370 + } 371 + 372 + version_free(&version); 373 + } 374 + 375 + /* These aren't needed in type_map unless we want a symtypes file. */ 376 + if (symtypes) 377 + type_map_add(sym->name, &type); 378 + 379 + type_expansion_free(&type); 380 + } 381 + 382 + void generate_symtypes_and_versions(FILE *file) 410 383 { 411 384 cache_init(&expansion_cache); 412 385 ··· 466 355 die_map_for_each(expand_type, NULL); 467 356 468 357 /* 469 - * 2. If a symtypes file is requested, write type_map contents to 358 + * 2. For each exported symbol, expand the die_map type, and use 359 + * type_map expansions to calculate a symbol version from the 360 + * fully expanded type string. 361 + */ 362 + symbol_for_each(expand_symbol, NULL); 363 + 364 + /* 365 + * 3. If a symtypes file is requested, write type_map contents to 470 366 * the file. 471 367 */ 472 368 type_map_write(file);