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: 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>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
f936c129 f6bb9245

+215 -8
+1
scripts/gendwarfksyms/Makefile
··· 2 2 hostprogs-always-y += gendwarfksyms 3 3 4 4 gendwarfksyms-objs += gendwarfksyms.o 5 + gendwarfksyms-objs += cache.o 5 6 gendwarfksyms-objs += die.o 6 7 gendwarfksyms-objs += dwarf.o 7 8 gendwarfksyms-objs += symbols.o
+51
scripts/gendwarfksyms/cache.c
··· 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 + }
+117 -8
scripts/gendwarfksyms/dwarf.c
··· 27 27 !dwarf_form##attr(&da, value); \ 28 28 } 29 29 30 + DEFINE_GET_ATTR(flag, bool) 30 31 DEFINE_GET_ATTR(udata, Dwarf_Word) 31 32 32 33 static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value) ··· 79 78 80 79 state->die = *source; 81 80 return !!state->sym; 81 + } 82 + 83 + /* DW_AT_decl_file -> struct srcfile */ 84 + static struct cache srcfile_cache; 85 + 86 + static bool is_definition_private(Dwarf_Die *die) 87 + { 88 + Dwarf_Word filenum; 89 + Dwarf_Files *files; 90 + Dwarf_Die cudie; 91 + const char *s; 92 + int res; 93 + 94 + /* 95 + * Definitions in .c files cannot change the public ABI, 96 + * so consider them private. 97 + */ 98 + if (!get_udata_attr(die, DW_AT_decl_file, &filenum)) 99 + return false; 100 + 101 + res = cache_get(&srcfile_cache, filenum); 102 + if (res >= 0) 103 + return !!res; 104 + 105 + if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL)) 106 + error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1)); 107 + 108 + if (dwarf_getsrcfiles(&cudie, &files, NULL)) 109 + error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1)); 110 + 111 + s = dwarf_filesrc(files, filenum, NULL, NULL); 112 + if (!s) 113 + error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1)); 114 + 115 + s = strrchr(s, '.'); 116 + res = s && !strcmp(s, ".c"); 117 + cache_set(&srcfile_cache, filenum, res); 118 + 119 + return !!res; 120 + } 121 + 122 + static bool is_kabi_definition(Dwarf_Die *die) 123 + { 124 + bool value; 125 + 126 + if (get_flag_attr(die, DW_AT_declaration, &value) && value) 127 + return false; 128 + 129 + return !is_definition_private(die); 82 130 } 83 131 84 132 /* ··· 506 456 die_callback_t process_func, 507 457 die_match_callback_t match_func) 508 458 { 459 + bool expand; 460 + 509 461 process(cache, type); 510 462 process_fqn(cache, die); 511 463 process(cache, " {"); 512 464 process_linebreak(cache, 1); 513 465 514 - check(process_die_container(state, cache, die, process_func, 515 - match_func)); 466 + expand = state->expand.expand && is_kabi_definition(die); 467 + 468 + if (expand) { 469 + check(process_die_container(state, cache, die, process_func, 470 + match_func)); 471 + } 516 472 517 473 process_linebreak(cache, -1); 518 474 process(cache, "}"); 519 475 520 - process_byte_size_attr(cache, die); 521 - process_alignment_attr(cache, die); 476 + if (expand) { 477 + process_byte_size_attr(cache, die); 478 + process_alignment_attr(cache, die); 479 + } 522 480 } 523 481 524 482 #define DEFINE_PROCESS_STRUCTURE_TYPE(structure) \ ··· 611 553 } 612 554 } 613 555 556 + static void state_init(struct state *state) 557 + { 558 + state->expand.expand = true; 559 + cache_init(&state->expansion_cache); 560 + } 561 + 562 + static void expansion_state_restore(struct expansion_state *state, 563 + struct expansion_state *saved) 564 + { 565 + state->expand = saved->expand; 566 + } 567 + 568 + static void expansion_state_save(struct expansion_state *state, 569 + struct expansion_state *saved) 570 + { 571 + expansion_state_restore(saved, state); 572 + } 573 + 574 + static bool is_expanded_type(int tag) 575 + { 576 + return tag == DW_TAG_class_type || tag == DW_TAG_structure_type || 577 + tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type; 578 + } 579 + 614 580 #define PROCESS_TYPE(type) \ 615 581 case DW_TAG_##type##_type: \ 616 582 process_##type##_type(state, cache, die); \ ··· 642 560 643 561 static int process_type(struct state *state, struct die *parent, Dwarf_Die *die) 644 562 { 563 + enum die_state want_state = DIE_COMPLETE; 645 564 struct die *cache; 565 + struct expansion_state saved; 646 566 int tag = dwarf_tag(die); 647 567 568 + expansion_state_save(&state->expand, &saved); 569 + 648 570 /* 649 - * If we have the DIE already cached, use it instead of walking 571 + * Structures and enumeration types are expanded only once per 572 + * exported symbol. This is sufficient for detecting ABI changes 573 + * within the structure. 574 + */ 575 + if (is_expanded_type(tag)) { 576 + if (cache_was_expanded(&state->expansion_cache, die->addr)) 577 + state->expand.expand = false; 578 + 579 + if (state->expand.expand) 580 + cache_mark_expanded(&state->expansion_cache, die->addr); 581 + else 582 + want_state = DIE_UNEXPANDED; 583 + } 584 + 585 + /* 586 + * If we have want_state already cached, use it instead of walking 650 587 * through DWARF. 651 588 */ 652 - cache = die_map_get(die, DIE_COMPLETE); 589 + cache = die_map_get(die, want_state); 653 590 654 - if (cache->state == DIE_COMPLETE) { 591 + if (cache->state == want_state) { 655 592 process_cached(state, cache, die); 656 593 die_map_add_die(parent, cache); 594 + 595 + expansion_state_restore(&state->expand, &saved); 657 596 return 0; 658 597 } 659 598 ··· 715 612 716 613 /* Update cache state and append to the parent (if any) */ 717 614 cache->tag = tag; 718 - cache->state = DIE_COMPLETE; 615 + cache->state = want_state; 719 616 die_map_add_die(parent, cache); 720 617 618 + expansion_state_restore(&state->expand, &saved); 721 619 return 0; 722 620 } 723 621 ··· 780 676 if (!match_export_symbol(&state, die)) 781 677 return 0; 782 678 679 + state_init(&state); 680 + 783 681 if (tag == DW_TAG_subprogram) 784 682 process_subprogram(&state, &state.die); 785 683 else 786 684 process_variable(&state, &state.die); 787 685 686 + cache_free(&state.expansion_cache); 788 687 return 0; 789 688 } 790 689 default: ··· 799 692 { 800 693 check(process_die_container(NULL, NULL, cudie, process_exported_symbols, 801 694 match_all)); 695 + 696 + cache_free(&srcfile_cache); 802 697 }
+46
scripts/gendwarfksyms/gendwarfksyms.h
··· 102 102 103 103 enum die_state { 104 104 DIE_INCOMPLETE, 105 + DIE_UNEXPANDED, 105 106 DIE_COMPLETE, 106 107 DIE_LAST = DIE_COMPLETE 107 108 }; ··· 132 131 { 133 132 switch (state) { 134 133 CASE_CONST_TO_STR(DIE_INCOMPLETE) 134 + CASE_CONST_TO_STR(DIE_UNEXPANDED) 135 135 CASE_CONST_TO_STR(DIE_COMPLETE) 136 136 } 137 137 ··· 156 154 void die_map_free(void); 157 155 158 156 /* 157 + * cache.c 158 + */ 159 + 160 + #define CACHE_HASH_BITS 10 161 + 162 + /* A cache for addresses we've already seen. */ 163 + struct cache { 164 + HASHTABLE_DECLARE(cache, 1 << CACHE_HASH_BITS); 165 + }; 166 + 167 + void cache_set(struct cache *cache, unsigned long key, int value); 168 + int cache_get(struct cache *cache, unsigned long key); 169 + void cache_init(struct cache *cache); 170 + void cache_free(struct cache *cache); 171 + 172 + static inline void __cache_mark_expanded(struct cache *cache, uintptr_t addr) 173 + { 174 + cache_set(cache, addr, 1); 175 + } 176 + 177 + static inline bool __cache_was_expanded(struct cache *cache, uintptr_t addr) 178 + { 179 + return cache_get(cache, addr) == 1; 180 + } 181 + 182 + static inline void cache_mark_expanded(struct cache *cache, void *addr) 183 + { 184 + __cache_mark_expanded(cache, (uintptr_t)addr); 185 + } 186 + 187 + static inline bool cache_was_expanded(struct cache *cache, void *addr) 188 + { 189 + return __cache_was_expanded(cache, (uintptr_t)addr); 190 + } 191 + 192 + /* 159 193 * dwarf.c 160 194 */ 195 + 196 + struct expansion_state { 197 + bool expand; 198 + }; 161 199 162 200 struct state { 163 201 struct symbol *sym; ··· 205 163 206 164 /* List expansion */ 207 165 bool first_list_item; 166 + 167 + /* Structure expansion */ 168 + struct expansion_state expand; 169 + struct cache expansion_cache; 208 170 }; 209 171 210 172 typedef int (*die_callback_t)(struct state *state, struct die *cache,