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 symtypes output

Add support for producing genksyms-style symtypes files. Process
die_map to find the longest expansions for each type, and use symtypes
references in type definitions. The basic file format is similar to
genksyms, with two notable exceptions:

1. Type names with spaces (common with Rust) in references are
wrapped in single quotes. E.g.:

s#'core::result::Result<u8, core::num::error::ParseIntError>'

2. The actual type definition is the simple parsed DWARF format we
output with --dump-dies, not the preprocessed C-style format
genksyms produces.

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
ab443998 d2ffdc1c

+429 -3
+1
scripts/gendwarfksyms/Makefile
··· 6 6 gendwarfksyms-objs += die.o 7 7 gendwarfksyms-objs += dwarf.o 8 8 gendwarfksyms-objs += symbols.o 9 + gendwarfksyms-objs += types.o 9 10 10 11 HOSTLDLIBS_gendwarfksyms := -ldw -lelf
+11
scripts/gendwarfksyms/die.c
··· 22 22 static void init_die(struct die *cd) 23 23 { 24 24 cd->state = DIE_INCOMPLETE; 25 + cd->mapped = false; 25 26 cd->fqn = NULL; 26 27 cd->tag = -1; 27 28 cd->addr = 0; ··· 82 81 if (cd->fqn && *cd->fqn) 83 82 free(cd->fqn); 84 83 init_die(cd); 84 + } 85 + 86 + void die_map_for_each(die_map_callback_t func, void *arg) 87 + { 88 + struct hlist_node *tmp; 89 + struct die *cd; 90 + 91 + hash_for_each_safe(die_map, cd, tmp, hash) { 92 + func(cd, arg); 93 + } 85 94 } 86 95 87 96 void die_map_free(void)
+1
scripts/gendwarfksyms/dwarf.c
··· 745 745 { 746 746 debug("%s", state->sym->name); 747 747 check(process_func(state, NULL, die)); 748 + state->sym->state = SYMBOL_MAPPED; 748 749 if (dump_dies) 749 750 fputs("\n", stderr); 750 751 }
+31 -2
scripts/gendwarfksyms/gendwarfksyms.c
··· 21 21 int dump_dies; 22 22 /* Print debugging information about die_map changes */ 23 23 int dump_die_map; 24 + /* Print out type strings (i.e. type_map) */ 25 + int dump_types; 26 + /* Write a symtypes file */ 27 + int symtypes; 28 + static const char *symtypes_file; 24 29 25 30 static void usage(void) 26 31 { ··· 34 29 " -d, --debug Print debugging information\n" 35 30 " --dump-dies Dump DWARF DIE contents\n" 36 31 " --dump-die-map Print debugging information about die_map changes\n" 32 + " --dump-types Dump type strings\n" 33 + " -T, --symtypes file Write a symtypes file\n" 37 34 " -h, --help Print this message\n" 38 35 "\n", 39 36 stderr); ··· 48 41 Dwarf_Die cudie; 49 42 Dwarf_CU *cu = NULL; 50 43 Dwarf *dbg; 44 + FILE *symfile = arg; 51 45 int res; 52 46 53 47 debug("%s", name); ··· 68 60 process_cu(&cudie); 69 61 } while (cu); 70 62 63 + /* 64 + * Use die_map to expand type strings and write them to `symfile`. 65 + */ 66 + generate_symtypes(symfile); 71 67 die_map_free(); 72 68 73 69 return DWARF_CB_OK; ··· 84 72 85 73 int main(int argc, char **argv) 86 74 { 75 + FILE *symfile = NULL; 87 76 unsigned int n; 88 77 int opt; 89 78 ··· 92 79 { "debug", 0, NULL, 'd' }, 93 80 { "dump-dies", 0, &dump_dies, 1 }, 94 81 { "dump-die-map", 0, &dump_die_map, 1 }, 82 + { "dump-types", 0, &dump_types, 1 }, 83 + { "symtypes", 1, NULL, 'T' }, 95 84 { "help", 0, NULL, 'h' }, 96 85 { 0, 0, NULL, 0 } 97 86 }; 98 87 99 - while ((opt = getopt_long(argc, argv, "dh", opts, NULL)) != EOF) { 88 + while ((opt = getopt_long(argc, argv, "dT:h", opts, NULL)) != EOF) { 100 89 switch (opt) { 101 90 case 0: 102 91 break; 103 92 case 'd': 104 93 debug = 1; 94 + break; 95 + case 'T': 96 + symtypes = 1; 97 + symtypes_file = optarg; 105 98 break; 106 99 case 'h': 107 100 usage(); ··· 127 108 } 128 109 129 110 symbol_read_exports(stdin); 111 + 112 + if (symtypes_file) { 113 + symfile = fopen(symtypes_file, "w"); 114 + if (!symfile) 115 + error("fopen failed for '%s': %s", symtypes_file, 116 + strerror(errno)); 117 + } 130 118 131 119 for (n = optind; n < argc; n++) { 132 120 Dwfl *dwfl; ··· 157 131 158 132 dwfl_report_end(dwfl, NULL, NULL); 159 133 160 - if (dwfl_getmodules(dwfl, &process_module, NULL, 0)) 134 + if (dwfl_getmodules(dwfl, &process_module, symfile, 0)) 161 135 error("dwfl_getmodules failed for '%s'", argv[n]); 162 136 163 137 dwfl_end(dwfl); 164 138 } 139 + 140 + if (symfile) 141 + check(fclose(symfile)); 165 142 166 143 symbol_free(); 167 144
+19
scripts/gendwarfksyms/gendwarfksyms.h
··· 22 22 extern int debug; 23 23 extern int dump_dies; 24 24 extern int dump_die_map; 25 + extern int dump_types; 26 + extern int symtypes; 25 27 26 28 /* 27 29 * Output helpers ··· 92 90 return hash_ptr((const void *)addr); 93 91 } 94 92 93 + enum symbol_state { 94 + SYMBOL_UNPROCESSED, 95 + SYMBOL_MAPPED, 96 + }; 97 + 95 98 struct symbol_addr { 96 99 uint32_t section; 97 100 Elf64_Addr address; ··· 107 100 struct symbol_addr addr; 108 101 struct hlist_node addr_hash; 109 102 struct hlist_node name_hash; 103 + enum symbol_state state; 104 + uintptr_t die_addr; 110 105 }; 111 106 112 107 typedef void (*symbol_callback_t)(struct symbol *, void *arg); ··· 163 154 164 155 struct die { 165 156 enum die_state state; 157 + bool mapped; 166 158 char *fqn; 167 159 int tag; 168 160 uintptr_t addr; ··· 171 161 struct hlist_node hash; 172 162 }; 173 163 164 + typedef void (*die_map_callback_t)(struct die *, void *arg); 165 + 174 166 int __die_map_get(uintptr_t addr, enum die_state state, struct die **res); 175 167 struct die *die_map_get(Dwarf_Die *die, enum die_state state); 176 168 void die_map_add_string(struct die *pd, const char *str); 177 169 void die_map_add_linebreak(struct die *pd, int linebreak); 170 + void die_map_for_each(die_map_callback_t func, void *arg); 178 171 void die_map_add_die(struct die *pd, struct die *child); 179 172 void die_map_free(void); 180 173 ··· 247 234 die_match_callback_t match); 248 235 249 236 void process_cu(Dwarf_Die *cudie); 237 + 238 + /* 239 + * types.c 240 + */ 241 + 242 + void generate_symtypes(FILE *file); 250 243 251 244 #endif /* __GENDWARFKSYMS_H */
+3 -1
scripts/gendwarfksyms/symbols.c
··· 92 92 sym = xcalloc(1, sizeof(struct symbol)); 93 93 sym->name = name; 94 94 sym->addr.section = SHN_UNDEF; 95 + sym->state = SYMBOL_UNPROCESSED; 95 96 96 97 hash_add(symbol_names, &sym->name_hash, hash_str(sym->name)); 97 98 ++nsym; ··· 108 107 { 109 108 struct symbol **res = arg; 110 109 111 - *res = sym; 110 + if (sym->state == SYMBOL_UNPROCESSED) 111 + *res = sym; 112 112 } 113 113 114 114 struct symbol *symbol_get(const char *name)
+363
scripts/gendwarfksyms/types.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #define _GNU_SOURCE 7 + #include <inttypes.h> 8 + #include <stdio.h> 9 + 10 + #include "gendwarfksyms.h" 11 + 12 + static struct cache expansion_cache; 13 + 14 + /* 15 + * A simple linked list of shared or owned strings to avoid copying strings 16 + * around when not necessary. 17 + */ 18 + struct type_list_entry { 19 + const char *str; 20 + void *owned; 21 + struct list_head list; 22 + }; 23 + 24 + static void type_list_free(struct list_head *list) 25 + { 26 + struct type_list_entry *entry; 27 + struct type_list_entry *tmp; 28 + 29 + list_for_each_entry_safe(entry, tmp, list, list) { 30 + if (entry->owned) 31 + free(entry->owned); 32 + free(entry); 33 + } 34 + 35 + INIT_LIST_HEAD(list); 36 + } 37 + 38 + static int type_list_append(struct list_head *list, const char *s, void *owned) 39 + { 40 + struct type_list_entry *entry; 41 + 42 + if (!s) 43 + return 0; 44 + 45 + entry = xmalloc(sizeof(struct type_list_entry)); 46 + entry->str = s; 47 + entry->owned = owned; 48 + list_add_tail(&entry->list, list); 49 + 50 + return strlen(entry->str); 51 + } 52 + 53 + static void type_list_write(struct list_head *list, FILE *file) 54 + { 55 + struct type_list_entry *entry; 56 + 57 + list_for_each_entry(entry, list, list) { 58 + if (entry->str) 59 + checkp(fputs(entry->str, file)); 60 + } 61 + } 62 + 63 + /* 64 + * An expanded type string in symtypes format. 65 + */ 66 + struct type_expansion { 67 + char *name; 68 + size_t len; 69 + struct list_head expanded; 70 + struct hlist_node hash; 71 + }; 72 + 73 + static void type_expansion_init(struct type_expansion *type) 74 + { 75 + type->name = NULL; 76 + type->len = 0; 77 + INIT_LIST_HEAD(&type->expanded); 78 + } 79 + 80 + static inline void type_expansion_free(struct type_expansion *type) 81 + { 82 + free(type->name); 83 + type->name = NULL; 84 + type->len = 0; 85 + type_list_free(&type->expanded); 86 + } 87 + 88 + static void type_expansion_append(struct type_expansion *type, const char *s, 89 + void *owned) 90 + { 91 + type->len += type_list_append(&type->expanded, s, owned); 92 + } 93 + 94 + /* 95 + * type_map -- the longest expansions for each type. 96 + * 97 + * const char *name -> struct type_expansion * 98 + */ 99 + #define TYPE_HASH_BITS 12 100 + static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS); 101 + 102 + static int type_map_get(const char *name, struct type_expansion **res) 103 + { 104 + struct type_expansion *e; 105 + 106 + hash_for_each_possible(type_map, e, hash, hash_str(name)) { 107 + if (!strcmp(name, e->name)) { 108 + *res = e; 109 + return 0; 110 + } 111 + } 112 + 113 + return -1; 114 + } 115 + 116 + static void type_map_add(const char *name, struct type_expansion *type) 117 + { 118 + struct type_expansion *e; 119 + 120 + if (type_map_get(name, &e)) { 121 + e = xmalloc(sizeof(struct type_expansion)); 122 + type_expansion_init(e); 123 + e->name = xstrdup(name); 124 + 125 + hash_add(type_map, &e->hash, hash_str(e->name)); 126 + 127 + if (dump_types) 128 + debug("adding %s", e->name); 129 + } else { 130 + /* Use the longest available expansion */ 131 + if (type->len <= e->len) 132 + return; 133 + 134 + type_list_free(&e->expanded); 135 + 136 + if (dump_types) 137 + debug("replacing %s", e->name); 138 + } 139 + 140 + /* Take ownership of type->expanded */ 141 + list_replace_init(&type->expanded, &e->expanded); 142 + e->len = type->len; 143 + 144 + if (dump_types) { 145 + checkp(fputs(e->name, stderr)); 146 + checkp(fputs(" ", stderr)); 147 + type_list_write(&e->expanded, stderr); 148 + checkp(fputs("\n", stderr)); 149 + } 150 + } 151 + 152 + static void type_map_write(FILE *file) 153 + { 154 + struct type_expansion *e; 155 + struct hlist_node *tmp; 156 + 157 + if (!file) 158 + return; 159 + 160 + hash_for_each_safe(type_map, e, tmp, hash) { 161 + checkp(fputs(e->name, file)); 162 + checkp(fputs(" ", file)); 163 + type_list_write(&e->expanded, file); 164 + checkp(fputs("\n", file)); 165 + } 166 + } 167 + 168 + static void type_map_free(void) 169 + { 170 + struct type_expansion *e; 171 + struct hlist_node *tmp; 172 + 173 + hash_for_each_safe(type_map, e, tmp, hash) { 174 + type_expansion_free(e); 175 + free(e); 176 + } 177 + 178 + hash_init(type_map); 179 + } 180 + 181 + /* 182 + * Type reference format: <prefix>#<name>, where prefix: 183 + * s -> structure 184 + * u -> union 185 + * e -> enum 186 + * t -> typedef 187 + * 188 + * Names with spaces are additionally wrapped in single quotes. 189 + */ 190 + static char get_type_prefix(int tag) 191 + { 192 + switch (tag) { 193 + case DW_TAG_class_type: 194 + case DW_TAG_structure_type: 195 + return 's'; 196 + case DW_TAG_union_type: 197 + return 'u'; 198 + case DW_TAG_enumeration_type: 199 + return 'e'; 200 + case DW_TAG_typedef_type: 201 + return 't'; 202 + default: 203 + return 0; 204 + } 205 + } 206 + 207 + static char *get_type_name(struct die *cache) 208 + { 209 + const char *quote; 210 + char prefix; 211 + char *name; 212 + 213 + if (cache->state == DIE_INCOMPLETE) { 214 + warn("found incomplete cache entry: %p", cache); 215 + return NULL; 216 + } 217 + if (!cache->fqn || !*cache->fqn) 218 + return NULL; 219 + 220 + prefix = get_type_prefix(cache->tag); 221 + if (!prefix) 222 + return NULL; 223 + 224 + /* Wrap names with spaces in single quotes */ 225 + quote = strstr(cache->fqn, " ") ? "'" : ""; 226 + 227 + /* <prefix>#<type_name>\0 */ 228 + if (asprintf(&name, "%c#%s%s%s", prefix, quote, cache->fqn, quote) < 0) 229 + error("asprintf failed for '%s'", cache->fqn); 230 + 231 + return name; 232 + } 233 + 234 + static void __type_expand(struct die *cache, struct type_expansion *type, 235 + bool recursive); 236 + 237 + static void type_expand_child(struct die *cache, struct type_expansion *type, 238 + bool recursive) 239 + { 240 + struct type_expansion child; 241 + char *name; 242 + 243 + name = get_type_name(cache); 244 + if (!name) { 245 + __type_expand(cache, type, recursive); 246 + return; 247 + } 248 + 249 + if (recursive && !__cache_was_expanded(&expansion_cache, cache->addr)) { 250 + __cache_mark_expanded(&expansion_cache, cache->addr); 251 + type_expansion_init(&child); 252 + __type_expand(cache, &child, true); 253 + type_map_add(name, &child); 254 + type_expansion_free(&child); 255 + } 256 + 257 + type_expansion_append(type, name, name); 258 + } 259 + 260 + static void __type_expand(struct die *cache, struct type_expansion *type, 261 + bool recursive) 262 + { 263 + struct die_fragment *df; 264 + struct die *child; 265 + 266 + list_for_each_entry(df, &cache->fragments, list) { 267 + switch (df->type) { 268 + case FRAGMENT_STRING: 269 + type_expansion_append(type, df->data.str, NULL); 270 + break; 271 + case FRAGMENT_DIE: 272 + /* Use a complete die_map expansion if available */ 273 + if (__die_map_get(df->data.addr, DIE_COMPLETE, 274 + &child) && 275 + __die_map_get(df->data.addr, DIE_UNEXPANDED, 276 + &child)) 277 + error("unknown child: %" PRIxPTR, 278 + df->data.addr); 279 + 280 + type_expand_child(child, type, recursive); 281 + break; 282 + case FRAGMENT_LINEBREAK: 283 + /* 284 + * Keep whitespace in the symtypes format, but avoid 285 + * repeated spaces. 286 + */ 287 + if (list_is_last(&df->list, &cache->fragments) || 288 + list_next_entry(df, list)->type != 289 + FRAGMENT_LINEBREAK) 290 + type_expansion_append(type, " ", NULL); 291 + break; 292 + default: 293 + error("empty die_fragment in %p", cache); 294 + } 295 + } 296 + } 297 + 298 + static void type_expand(struct die *cache, struct type_expansion *type, 299 + bool recursive) 300 + { 301 + type_expansion_init(type); 302 + __type_expand(cache, type, recursive); 303 + cache_free(&expansion_cache); 304 + } 305 + 306 + static void expand_type(struct die *cache, void *arg) 307 + { 308 + struct type_expansion type; 309 + char *name; 310 + 311 + if (cache->mapped) 312 + return; 313 + 314 + cache->mapped = true; 315 + 316 + /* 317 + * Skip unexpanded die_map entries if there's a complete 318 + * expansion available for this DIE. 319 + */ 320 + if (cache->state == DIE_UNEXPANDED && 321 + !__die_map_get(cache->addr, DIE_COMPLETE, &cache)) { 322 + if (cache->mapped) 323 + return; 324 + 325 + cache->mapped = true; 326 + } 327 + 328 + name = get_type_name(cache); 329 + if (!name) 330 + return; 331 + 332 + debug("%s", name); 333 + type_expand(cache, &type, true); 334 + type_map_add(name, &type); 335 + 336 + type_expansion_free(&type); 337 + free(name); 338 + } 339 + 340 + void generate_symtypes(FILE *file) 341 + { 342 + cache_init(&expansion_cache); 343 + 344 + /* 345 + * die_map processing: 346 + * 347 + * 1. die_map contains all types referenced in exported symbol 348 + * signatures, but can contain duplicates just like the original 349 + * DWARF, and some references may not be fully expanded depending 350 + * on how far we processed the DIE tree for that specific symbol. 351 + * 352 + * For each die_map entry, find the longest available expansion, 353 + * and add it to type_map. 354 + */ 355 + die_map_for_each(expand_type, NULL); 356 + 357 + /* 358 + * 2. If a symtypes file is requested, write type_map contents to 359 + * the file. 360 + */ 361 + type_map_write(file); 362 + type_map_free(); 363 + }