1f2856884SSami Tolvanen // SPDX-License-Identifier: GPL-2.0 2f2856884SSami Tolvanen /* 3f2856884SSami Tolvanen * Copyright (C) 2024 Google LLC 4f2856884SSami Tolvanen */ 5f2856884SSami Tolvanen 6a9369418SSami Tolvanen #include <assert.h> 75b7780e8SSami Tolvanen #include <inttypes.h> 85b7780e8SSami Tolvanen #include <stdarg.h> 9f2856884SSami Tolvanen #include "gendwarfksyms.h" 10f2856884SSami Tolvanen 11a9369418SSami Tolvanen /* See get_union_kabi_status */ 12a9369418SSami Tolvanen #define KABI_PREFIX "__kabi_" 13a9369418SSami Tolvanen #define KABI_PREFIX_LEN (sizeof(KABI_PREFIX) - 1) 14a9369418SSami Tolvanen #define KABI_RESERVED_PREFIX "reserved" 15a9369418SSami Tolvanen #define KABI_RESERVED_PREFIX_LEN (sizeof(KABI_RESERVED_PREFIX) - 1) 16a9369418SSami Tolvanen #define KABI_RENAMED_PREFIX "renamed" 17a9369418SSami Tolvanen #define KABI_RENAMED_PREFIX_LEN (sizeof(KABI_RENAMED_PREFIX) - 1) 18a9369418SSami Tolvanen #define KABI_IGNORED_PREFIX "ignored" 19a9369418SSami Tolvanen #define KABI_IGNORED_PREFIX_LEN (sizeof(KABI_IGNORED_PREFIX) - 1) 20a9369418SSami Tolvanen 21a9369418SSami Tolvanen static inline bool is_kabi_prefix(const char *name) 22a9369418SSami Tolvanen { 23a9369418SSami Tolvanen return name && !strncmp(name, KABI_PREFIX, KABI_PREFIX_LEN); 24a9369418SSami Tolvanen } 25a9369418SSami Tolvanen 26a9369418SSami Tolvanen enum kabi_status { 27a9369418SSami Tolvanen /* >0 to stop DIE processing */ 28a9369418SSami Tolvanen KABI_NORMAL = 1, 29a9369418SSami Tolvanen KABI_RESERVED, 30a9369418SSami Tolvanen KABI_IGNORED, 31a9369418SSami Tolvanen }; 32a9369418SSami Tolvanen 3306b8b036SSami Tolvanen static bool do_linebreak; 3406b8b036SSami Tolvanen static int indentation_level; 3506b8b036SSami Tolvanen 3606b8b036SSami Tolvanen /* Line breaks and indentation for pretty-printing */ 3706b8b036SSami Tolvanen static void process_linebreak(struct die *cache, int n) 3806b8b036SSami Tolvanen { 3906b8b036SSami Tolvanen indentation_level += n; 4006b8b036SSami Tolvanen do_linebreak = true; 4106b8b036SSami Tolvanen die_map_add_linebreak(cache, n); 4206b8b036SSami Tolvanen } 4306b8b036SSami Tolvanen 445b7780e8SSami Tolvanen #define DEFINE_GET_ATTR(attr, type) \ 455b7780e8SSami Tolvanen static bool get_##attr##_attr(Dwarf_Die *die, unsigned int id, \ 465b7780e8SSami Tolvanen type *value) \ 475b7780e8SSami Tolvanen { \ 485b7780e8SSami Tolvanen Dwarf_Attribute da; \ 495b7780e8SSami Tolvanen return dwarf_attr(die, id, &da) && \ 505b7780e8SSami Tolvanen !dwarf_form##attr(&da, value); \ 515b7780e8SSami Tolvanen } 525b7780e8SSami Tolvanen 53f936c129SSami Tolvanen DEFINE_GET_ATTR(flag, bool) 545b7780e8SSami Tolvanen DEFINE_GET_ATTR(udata, Dwarf_Word) 555b7780e8SSami Tolvanen 56f2856884SSami Tolvanen static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value) 57f2856884SSami Tolvanen { 58f2856884SSami Tolvanen Dwarf_Attribute da; 59f2856884SSami Tolvanen 60f2856884SSami Tolvanen /* dwarf_formref_die returns a pointer instead of an error value. */ 61f2856884SSami Tolvanen return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value); 62f2856884SSami Tolvanen } 63f2856884SSami Tolvanen 64f2856884SSami Tolvanen #define DEFINE_GET_STRING_ATTR(attr) \ 65f2856884SSami Tolvanen static const char *get_##attr##_attr(Dwarf_Die *die) \ 66f2856884SSami Tolvanen { \ 67f2856884SSami Tolvanen Dwarf_Attribute da; \ 68f2856884SSami Tolvanen if (dwarf_attr(die, DW_AT_##attr, &da)) \ 69f2856884SSami Tolvanen return dwarf_formstring(&da); \ 70f2856884SSami Tolvanen return NULL; \ 71f2856884SSami Tolvanen } 72f2856884SSami Tolvanen 73f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(name) 74f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(linkage_name) 75f2856884SSami Tolvanen 76f2856884SSami Tolvanen static const char *get_symbol_name(Dwarf_Die *die) 77f2856884SSami Tolvanen { 78f2856884SSami Tolvanen const char *name; 79f2856884SSami Tolvanen 80f2856884SSami Tolvanen /* rustc uses DW_AT_linkage_name for exported symbols */ 81f2856884SSami Tolvanen name = get_linkage_name_attr(die); 82f2856884SSami Tolvanen if (!name) 83f2856884SSami Tolvanen name = get_name_attr(die); 84f2856884SSami Tolvanen 85f2856884SSami Tolvanen return name; 86f2856884SSami Tolvanen } 87f2856884SSami Tolvanen 88f2856884SSami Tolvanen static bool match_export_symbol(struct state *state, Dwarf_Die *die) 89f2856884SSami Tolvanen { 90f2856884SSami Tolvanen Dwarf_Die *source = die; 91f2856884SSami Tolvanen Dwarf_Die origin; 92f2856884SSami Tolvanen 93f2856884SSami Tolvanen /* If the DIE has an abstract origin, use it for type information. */ 94f2856884SSami Tolvanen if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin)) 95f2856884SSami Tolvanen source = &origin; 96f2856884SSami Tolvanen 97f2856884SSami Tolvanen state->sym = symbol_get(get_symbol_name(die)); 98f2856884SSami Tolvanen 99f2856884SSami Tolvanen /* Look up using the origin name if there are no matches. */ 100f2856884SSami Tolvanen if (!state->sym && source != die) 101f2856884SSami Tolvanen state->sym = symbol_get(get_symbol_name(source)); 102f2856884SSami Tolvanen 103f2856884SSami Tolvanen state->die = *source; 104f2856884SSami Tolvanen return !!state->sym; 105f2856884SSami Tolvanen } 106f2856884SSami Tolvanen 107f936c129SSami Tolvanen /* DW_AT_decl_file -> struct srcfile */ 108f936c129SSami Tolvanen static struct cache srcfile_cache; 109f936c129SSami Tolvanen 110f936c129SSami Tolvanen static bool is_definition_private(Dwarf_Die *die) 111f936c129SSami Tolvanen { 112f936c129SSami Tolvanen Dwarf_Word filenum; 113f936c129SSami Tolvanen Dwarf_Files *files; 114f936c129SSami Tolvanen Dwarf_Die cudie; 115f936c129SSami Tolvanen const char *s; 116f936c129SSami Tolvanen int res; 117f936c129SSami Tolvanen 118f936c129SSami Tolvanen /* 119f936c129SSami Tolvanen * Definitions in .c files cannot change the public ABI, 120f936c129SSami Tolvanen * so consider them private. 121f936c129SSami Tolvanen */ 122f936c129SSami Tolvanen if (!get_udata_attr(die, DW_AT_decl_file, &filenum)) 123f936c129SSami Tolvanen return false; 124f936c129SSami Tolvanen 125f936c129SSami Tolvanen res = cache_get(&srcfile_cache, filenum); 126f936c129SSami Tolvanen if (res >= 0) 127f936c129SSami Tolvanen return !!res; 128f936c129SSami Tolvanen 129f936c129SSami Tolvanen if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL)) 130f936c129SSami Tolvanen error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1)); 131f936c129SSami Tolvanen 132f936c129SSami Tolvanen if (dwarf_getsrcfiles(&cudie, &files, NULL)) 133f936c129SSami Tolvanen error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1)); 134f936c129SSami Tolvanen 135f936c129SSami Tolvanen s = dwarf_filesrc(files, filenum, NULL, NULL); 136f936c129SSami Tolvanen if (!s) 137f936c129SSami Tolvanen error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1)); 138f936c129SSami Tolvanen 139f936c129SSami Tolvanen s = strrchr(s, '.'); 140f936c129SSami Tolvanen res = s && !strcmp(s, ".c"); 141f936c129SSami Tolvanen cache_set(&srcfile_cache, filenum, res); 142f936c129SSami Tolvanen 143f936c129SSami Tolvanen return !!res; 144f936c129SSami Tolvanen } 145f936c129SSami Tolvanen 146936cf61cSSami Tolvanen static bool is_kabi_definition(struct die *cache, Dwarf_Die *die) 147f936c129SSami Tolvanen { 148f936c129SSami Tolvanen bool value; 149f936c129SSami Tolvanen 150f936c129SSami Tolvanen if (get_flag_attr(die, DW_AT_declaration, &value) && value) 151f936c129SSami Tolvanen return false; 152f936c129SSami Tolvanen 153936cf61cSSami Tolvanen if (kabi_is_declonly(cache->fqn)) 154936cf61cSSami Tolvanen return false; 155936cf61cSSami Tolvanen 156f936c129SSami Tolvanen return !is_definition_private(die); 157f936c129SSami Tolvanen } 158f936c129SSami Tolvanen 159f2856884SSami Tolvanen /* 160f2856884SSami Tolvanen * Type string processing 161f2856884SSami Tolvanen */ 1620c1c7627SSami Tolvanen static void process(struct die *cache, const char *s) 163f2856884SSami Tolvanen { 164f2856884SSami Tolvanen s = s ?: "<null>"; 165f2856884SSami Tolvanen 16606b8b036SSami Tolvanen if (dump_dies && do_linebreak) { 16706b8b036SSami Tolvanen fputs("\n", stderr); 16806b8b036SSami Tolvanen for (int i = 0; i < indentation_level; i++) 16906b8b036SSami Tolvanen fputs(" ", stderr); 17006b8b036SSami Tolvanen do_linebreak = false; 17106b8b036SSami Tolvanen } 172f2856884SSami Tolvanen if (dump_dies) 173f2856884SSami Tolvanen fputs(s, stderr); 1740c1c7627SSami Tolvanen 175d2ffdc1cSSami Tolvanen if (cache) 176d2ffdc1cSSami Tolvanen die_debug_r("cache %p string '%s'", cache, s); 1770c1c7627SSami Tolvanen die_map_add_string(cache, s); 178f2856884SSami Tolvanen } 179f2856884SSami Tolvanen 1805b7780e8SSami Tolvanen #define MAX_FMT_BUFFER_SIZE 128 1815b7780e8SSami Tolvanen 1820c1c7627SSami Tolvanen static void process_fmt(struct die *cache, const char *fmt, ...) 1835b7780e8SSami Tolvanen { 1845b7780e8SSami Tolvanen char buf[MAX_FMT_BUFFER_SIZE]; 1855b7780e8SSami Tolvanen va_list args; 1865b7780e8SSami Tolvanen 1875b7780e8SSami Tolvanen va_start(args, fmt); 1885b7780e8SSami Tolvanen 1895b7780e8SSami Tolvanen if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf)) 1905b7780e8SSami Tolvanen error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE"); 1915b7780e8SSami Tolvanen 1920c1c7627SSami Tolvanen process(cache, buf); 1935b7780e8SSami Tolvanen va_end(args); 1945b7780e8SSami Tolvanen } 1955b7780e8SSami Tolvanen 1965b7780e8SSami Tolvanen #define MAX_FQN_SIZE 64 1975b7780e8SSami Tolvanen 1985b7780e8SSami Tolvanen /* Get a fully qualified name from DWARF scopes */ 1995b7780e8SSami Tolvanen static char *get_fqn(Dwarf_Die *die) 2005b7780e8SSami Tolvanen { 2015b7780e8SSami Tolvanen const char *list[MAX_FQN_SIZE]; 2025b7780e8SSami Tolvanen Dwarf_Die *scopes = NULL; 2035b7780e8SSami Tolvanen bool has_name = false; 2045b7780e8SSami Tolvanen char *fqn = NULL; 2055b7780e8SSami Tolvanen char *p; 2065b7780e8SSami Tolvanen int count = 0; 2075b7780e8SSami Tolvanen int len = 0; 2085b7780e8SSami Tolvanen int res; 2095b7780e8SSami Tolvanen int i; 2105b7780e8SSami Tolvanen 2115b7780e8SSami Tolvanen res = checkp(dwarf_getscopes_die(die, &scopes)); 2125b7780e8SSami Tolvanen if (!res) { 2135b7780e8SSami Tolvanen list[count] = get_name_attr(die); 2145b7780e8SSami Tolvanen 2155b7780e8SSami Tolvanen if (!list[count]) 2165b7780e8SSami Tolvanen return NULL; 2175b7780e8SSami Tolvanen 2185b7780e8SSami Tolvanen len += strlen(list[count]); 2195b7780e8SSami Tolvanen count++; 2205b7780e8SSami Tolvanen 2215b7780e8SSami Tolvanen goto done; 2225b7780e8SSami Tolvanen } 2235b7780e8SSami Tolvanen 2245b7780e8SSami Tolvanen for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) { 2255b7780e8SSami Tolvanen if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit) 2265b7780e8SSami Tolvanen continue; 2275b7780e8SSami Tolvanen 2285b7780e8SSami Tolvanen list[count] = get_name_attr(&scopes[i]); 2295b7780e8SSami Tolvanen 2305b7780e8SSami Tolvanen if (list[count]) { 2315b7780e8SSami Tolvanen has_name = true; 2325b7780e8SSami Tolvanen } else { 2335b7780e8SSami Tolvanen list[count] = "<anonymous>"; 2345b7780e8SSami Tolvanen has_name = false; 2355b7780e8SSami Tolvanen } 2365b7780e8SSami Tolvanen 2375b7780e8SSami Tolvanen len += strlen(list[count]); 2385b7780e8SSami Tolvanen count++; 2395b7780e8SSami Tolvanen 2405b7780e8SSami Tolvanen if (i > 0) { 2415b7780e8SSami Tolvanen list[count++] = "::"; 2425b7780e8SSami Tolvanen len += 2; 2435b7780e8SSami Tolvanen } 2445b7780e8SSami Tolvanen } 2455b7780e8SSami Tolvanen 2465b7780e8SSami Tolvanen free(scopes); 2475b7780e8SSami Tolvanen 2485b7780e8SSami Tolvanen if (count == MAX_FQN_SIZE) 2495b7780e8SSami Tolvanen warn("increase MAX_FQN_SIZE: reached the maximum"); 2505b7780e8SSami Tolvanen 2515b7780e8SSami Tolvanen /* Consider the DIE unnamed if the last scope doesn't have a name */ 2525b7780e8SSami Tolvanen if (!has_name) 2535b7780e8SSami Tolvanen return NULL; 2545b7780e8SSami Tolvanen done: 2555b7780e8SSami Tolvanen fqn = xmalloc(len + 1); 2565b7780e8SSami Tolvanen *fqn = '\0'; 2575b7780e8SSami Tolvanen 2585b7780e8SSami Tolvanen p = fqn; 2595b7780e8SSami Tolvanen for (i = 0; i < count; i++) 2605b7780e8SSami Tolvanen p = stpcpy(p, list[i]); 2615b7780e8SSami Tolvanen 2625b7780e8SSami Tolvanen return fqn; 2635b7780e8SSami Tolvanen } 2645b7780e8SSami Tolvanen 2650c1c7627SSami Tolvanen static void update_fqn(struct die *cache, Dwarf_Die *die) 2665b7780e8SSami Tolvanen { 2670c1c7627SSami Tolvanen if (!cache->fqn) 2680c1c7627SSami Tolvanen cache->fqn = get_fqn(die) ?: ""; 2690c1c7627SSami Tolvanen } 2700c1c7627SSami Tolvanen 2710c1c7627SSami Tolvanen static void process_fqn(struct die *cache, Dwarf_Die *die) 2720c1c7627SSami Tolvanen { 2730c1c7627SSami Tolvanen update_fqn(cache, die); 2740c1c7627SSami Tolvanen if (*cache->fqn) 2750c1c7627SSami Tolvanen process(cache, " "); 2760c1c7627SSami Tolvanen process(cache, cache->fqn); 2775b7780e8SSami Tolvanen } 2785b7780e8SSami Tolvanen 2795b7780e8SSami Tolvanen #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute) \ 2800c1c7627SSami Tolvanen static void process_##attribute##_attr(struct die *cache, \ 2810c1c7627SSami Tolvanen Dwarf_Die *die) \ 2825b7780e8SSami Tolvanen { \ 2835b7780e8SSami Tolvanen Dwarf_Word value; \ 2845b7780e8SSami Tolvanen if (get_udata_attr(die, DW_AT_##attribute, &value)) \ 2850c1c7627SSami Tolvanen process_fmt(cache, " " #attribute "(%" PRIu64 ")", \ 2860c1c7627SSami Tolvanen value); \ 2875b7780e8SSami Tolvanen } 2885b7780e8SSami Tolvanen 289f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility) 2905b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment) 291f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size) 2925b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size) 2935b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding) 294f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset) 295f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location) 296f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value) 2975b7780e8SSami Tolvanen 298220a0857SSami Tolvanen /* Match functions -- die_match_callback_t */ 299220a0857SSami Tolvanen #define DEFINE_MATCH(type) \ 300220a0857SSami Tolvanen static bool match_##type##_type(Dwarf_Die *die) \ 301220a0857SSami Tolvanen { \ 302220a0857SSami Tolvanen return dwarf_tag(die) == DW_TAG_##type##_type; \ 303220a0857SSami Tolvanen } 304220a0857SSami Tolvanen 305f6bb9245SSami Tolvanen DEFINE_MATCH(enumerator) 306220a0857SSami Tolvanen DEFINE_MATCH(formal_parameter) 307f6bb9245SSami Tolvanen DEFINE_MATCH(member) 308c772f1d1SSami Tolvanen DEFINE_MATCH(subrange) 309220a0857SSami Tolvanen 310f2856884SSami Tolvanen bool match_all(Dwarf_Die *die) 311f2856884SSami Tolvanen { 312f2856884SSami Tolvanen return true; 313f2856884SSami Tolvanen } 314f2856884SSami Tolvanen 3150c1c7627SSami Tolvanen int process_die_container(struct state *state, struct die *cache, 3160c1c7627SSami Tolvanen Dwarf_Die *die, die_callback_t func, 3170c1c7627SSami Tolvanen die_match_callback_t match) 318f2856884SSami Tolvanen { 319f2856884SSami Tolvanen Dwarf_Die current; 320f2856884SSami Tolvanen int res; 321f2856884SSami Tolvanen 322220a0857SSami Tolvanen /* Track the first item in lists. */ 323220a0857SSami Tolvanen if (state) 324220a0857SSami Tolvanen state->first_list_item = true; 325220a0857SSami Tolvanen 326f2856884SSami Tolvanen res = checkp(dwarf_child(die, ¤t)); 327f2856884SSami Tolvanen while (!res) { 328f2856884SSami Tolvanen if (match(¤t)) { 329f2856884SSami Tolvanen /* <0 = error, 0 = continue, >0 = stop */ 3300c1c7627SSami Tolvanen res = checkp(func(state, cache, ¤t)); 331f2856884SSami Tolvanen if (res) 332220a0857SSami Tolvanen goto out; 333f2856884SSami Tolvanen } 334f2856884SSami Tolvanen 335f2856884SSami Tolvanen res = checkp(dwarf_siblingof(¤t, ¤t)); 336f2856884SSami Tolvanen } 337f2856884SSami Tolvanen 338220a0857SSami Tolvanen res = 0; 339220a0857SSami Tolvanen out: 340220a0857SSami Tolvanen if (state) 341220a0857SSami Tolvanen state->first_list_item = false; 342220a0857SSami Tolvanen 343220a0857SSami Tolvanen return res; 344f2856884SSami Tolvanen } 345f2856884SSami Tolvanen 3460c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, 3470c1c7627SSami Tolvanen Dwarf_Die *die); 3485b7780e8SSami Tolvanen 3490c1c7627SSami Tolvanen static void process_type_attr(struct state *state, struct die *cache, 3500c1c7627SSami Tolvanen Dwarf_Die *die) 3515b7780e8SSami Tolvanen { 3525b7780e8SSami Tolvanen Dwarf_Die type; 3535b7780e8SSami Tolvanen 3545b7780e8SSami Tolvanen if (get_ref_die_attr(die, DW_AT_type, &type)) { 3550c1c7627SSami Tolvanen check(process_type(state, cache, &type)); 3565b7780e8SSami Tolvanen return; 3575b7780e8SSami Tolvanen } 3585b7780e8SSami Tolvanen 3595b7780e8SSami Tolvanen /* Compilers can omit DW_AT_type -- print out 'void' to clarify */ 3600c1c7627SSami Tolvanen process(cache, "base_type void"); 3615b7780e8SSami Tolvanen } 3625b7780e8SSami Tolvanen 363220a0857SSami Tolvanen static void process_list_comma(struct state *state, struct die *cache) 364220a0857SSami Tolvanen { 365220a0857SSami Tolvanen if (state->first_list_item) { 366220a0857SSami Tolvanen state->first_list_item = false; 367220a0857SSami Tolvanen } else { 368220a0857SSami Tolvanen process(cache, " ,"); 369220a0857SSami Tolvanen process_linebreak(cache, 0); 370220a0857SSami Tolvanen } 371220a0857SSami Tolvanen } 372220a0857SSami Tolvanen 373220a0857SSami Tolvanen /* Comma-separated with DW_AT_type */ 374220a0857SSami Tolvanen static void __process_list_type(struct state *state, struct die *cache, 375220a0857SSami Tolvanen Dwarf_Die *die, const char *type) 376220a0857SSami Tolvanen { 377220a0857SSami Tolvanen const char *name = get_name_attr(die); 378220a0857SSami Tolvanen 379a9369418SSami Tolvanen if (stable) { 380a9369418SSami Tolvanen if (is_kabi_prefix(name)) 381a9369418SSami Tolvanen name = NULL; 382a9369418SSami Tolvanen state->kabi.orig_name = NULL; 383a9369418SSami Tolvanen } 384a9369418SSami Tolvanen 385220a0857SSami Tolvanen process_list_comma(state, cache); 386220a0857SSami Tolvanen process(cache, type); 387220a0857SSami Tolvanen process_type_attr(state, cache, die); 388a9369418SSami Tolvanen 389a9369418SSami Tolvanen if (stable && state->kabi.orig_name) 390a9369418SSami Tolvanen name = state->kabi.orig_name; 391220a0857SSami Tolvanen if (name) { 392220a0857SSami Tolvanen process(cache, " "); 393220a0857SSami Tolvanen process(cache, name); 394220a0857SSami Tolvanen } 395a9369418SSami Tolvanen 396f6bb9245SSami Tolvanen process_accessibility_attr(cache, die); 397f6bb9245SSami Tolvanen process_bit_size_attr(cache, die); 398f6bb9245SSami Tolvanen process_data_bit_offset_attr(cache, die); 399f6bb9245SSami Tolvanen process_data_member_location_attr(cache, die); 400220a0857SSami Tolvanen } 401220a0857SSami Tolvanen 402220a0857SSami Tolvanen #define DEFINE_PROCESS_LIST_TYPE(type) \ 403220a0857SSami Tolvanen static void process_##type##_type(struct state *state, \ 404220a0857SSami Tolvanen struct die *cache, Dwarf_Die *die) \ 405220a0857SSami Tolvanen { \ 406220a0857SSami Tolvanen __process_list_type(state, cache, die, #type " "); \ 407220a0857SSami Tolvanen } 408220a0857SSami Tolvanen 409220a0857SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(formal_parameter) 410f6bb9245SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(member) 411220a0857SSami Tolvanen 41206b8b036SSami Tolvanen /* Container types with DW_AT_type */ 41306b8b036SSami Tolvanen static void __process_type(struct state *state, struct die *cache, 41406b8b036SSami Tolvanen Dwarf_Die *die, const char *type) 41506b8b036SSami Tolvanen { 41606b8b036SSami Tolvanen process(cache, type); 41706b8b036SSami Tolvanen process_fqn(cache, die); 41806b8b036SSami Tolvanen process(cache, " {"); 41906b8b036SSami Tolvanen process_linebreak(cache, 1); 42006b8b036SSami Tolvanen process_type_attr(state, cache, die); 42106b8b036SSami Tolvanen process_linebreak(cache, -1); 42206b8b036SSami Tolvanen process(cache, "}"); 42306b8b036SSami Tolvanen process_byte_size_attr(cache, die); 42406b8b036SSami Tolvanen process_alignment_attr(cache, die); 42506b8b036SSami Tolvanen } 42606b8b036SSami Tolvanen 42706b8b036SSami Tolvanen #define DEFINE_PROCESS_TYPE(type) \ 42806b8b036SSami Tolvanen static void process_##type##_type(struct state *state, \ 42906b8b036SSami Tolvanen struct die *cache, Dwarf_Die *die) \ 43006b8b036SSami Tolvanen { \ 43106b8b036SSami Tolvanen __process_type(state, cache, die, #type "_type"); \ 43206b8b036SSami Tolvanen } 43306b8b036SSami Tolvanen 43406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(atomic) 43506b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(const) 43606b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(immutable) 43706b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(packed) 43806b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(pointer) 43906b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(reference) 44006b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(restrict) 44106b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(rvalue_reference) 44206b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(shared) 443f6bb9245SSami Tolvanen DEFINE_PROCESS_TYPE(template_type_parameter) 44406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(volatile) 44506b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(typedef) 44606b8b036SSami Tolvanen 447c772f1d1SSami Tolvanen static void process_subrange_type(struct state *state, struct die *cache, 448c772f1d1SSami Tolvanen Dwarf_Die *die) 449c772f1d1SSami Tolvanen { 450c772f1d1SSami Tolvanen Dwarf_Word count = 0; 451c772f1d1SSami Tolvanen 452c772f1d1SSami Tolvanen if (get_udata_attr(die, DW_AT_count, &count)) 453c772f1d1SSami Tolvanen process_fmt(cache, "[%" PRIu64 "]", count); 454c772f1d1SSami Tolvanen else if (get_udata_attr(die, DW_AT_upper_bound, &count)) 455c772f1d1SSami Tolvanen process_fmt(cache, "[%" PRIu64 "]", count + 1); 456c772f1d1SSami Tolvanen else 457c772f1d1SSami Tolvanen process(cache, "[]"); 458c772f1d1SSami Tolvanen } 459c772f1d1SSami Tolvanen 460c772f1d1SSami Tolvanen static void process_array_type(struct state *state, struct die *cache, 461c772f1d1SSami Tolvanen Dwarf_Die *die) 462c772f1d1SSami Tolvanen { 463c772f1d1SSami Tolvanen process(cache, "array_type"); 464c772f1d1SSami Tolvanen /* Array size */ 465c772f1d1SSami Tolvanen check(process_die_container(state, cache, die, process_type, 466c772f1d1SSami Tolvanen match_subrange_type)); 467c772f1d1SSami Tolvanen process(cache, " {"); 468c772f1d1SSami Tolvanen process_linebreak(cache, 1); 469c772f1d1SSami Tolvanen process_type_attr(state, cache, die); 470c772f1d1SSami Tolvanen process_linebreak(cache, -1); 471c772f1d1SSami Tolvanen process(cache, "}"); 472c772f1d1SSami Tolvanen } 473c772f1d1SSami Tolvanen 474220a0857SSami Tolvanen static void __process_subroutine_type(struct state *state, struct die *cache, 475220a0857SSami Tolvanen Dwarf_Die *die, const char *type) 476220a0857SSami Tolvanen { 477220a0857SSami Tolvanen process(cache, type); 478220a0857SSami Tolvanen process(cache, " ("); 479220a0857SSami Tolvanen process_linebreak(cache, 1); 480220a0857SSami Tolvanen /* Parameters */ 481220a0857SSami Tolvanen check(process_die_container(state, cache, die, process_type, 482220a0857SSami Tolvanen match_formal_parameter_type)); 483220a0857SSami Tolvanen process_linebreak(cache, -1); 484220a0857SSami Tolvanen process(cache, ")"); 485220a0857SSami Tolvanen process_linebreak(cache, 0); 486220a0857SSami Tolvanen /* Return type */ 487220a0857SSami Tolvanen process(cache, "-> "); 488220a0857SSami Tolvanen process_type_attr(state, cache, die); 489220a0857SSami Tolvanen } 490220a0857SSami Tolvanen 491220a0857SSami Tolvanen static void process_subroutine_type(struct state *state, struct die *cache, 492220a0857SSami Tolvanen Dwarf_Die *die) 493220a0857SSami Tolvanen { 494220a0857SSami Tolvanen __process_subroutine_type(state, cache, die, "subroutine_type"); 495220a0857SSami Tolvanen } 496220a0857SSami Tolvanen 497f6bb9245SSami Tolvanen static void process_variant_type(struct state *state, struct die *cache, 498f6bb9245SSami Tolvanen Dwarf_Die *die) 499f6bb9245SSami Tolvanen { 500f6bb9245SSami Tolvanen process_list_comma(state, cache); 501f6bb9245SSami Tolvanen process(cache, "variant {"); 502f6bb9245SSami Tolvanen process_linebreak(cache, 1); 503f6bb9245SSami Tolvanen check(process_die_container(state, cache, die, process_type, 504f6bb9245SSami Tolvanen match_member_type)); 505f6bb9245SSami Tolvanen process_linebreak(cache, -1); 506f6bb9245SSami Tolvanen process(cache, "}"); 507f6bb9245SSami Tolvanen process_discr_value_attr(cache, die); 508f6bb9245SSami Tolvanen } 509f6bb9245SSami Tolvanen 510f6bb9245SSami Tolvanen static void process_variant_part_type(struct state *state, struct die *cache, 511f6bb9245SSami Tolvanen Dwarf_Die *die) 512f6bb9245SSami Tolvanen { 513f6bb9245SSami Tolvanen process_list_comma(state, cache); 514f6bb9245SSami Tolvanen process(cache, "variant_part {"); 515f6bb9245SSami Tolvanen process_linebreak(cache, 1); 516f6bb9245SSami Tolvanen check(process_die_container(state, cache, die, process_type, 517f6bb9245SSami Tolvanen match_all)); 518f6bb9245SSami Tolvanen process_linebreak(cache, -1); 519f6bb9245SSami Tolvanen process(cache, "}"); 520f6bb9245SSami Tolvanen } 521f6bb9245SSami Tolvanen 522a9369418SSami Tolvanen static int get_kabi_status(Dwarf_Die *die, const char **suffix) 523a9369418SSami Tolvanen { 524a9369418SSami Tolvanen const char *name = get_name_attr(die); 525a9369418SSami Tolvanen 526a9369418SSami Tolvanen if (suffix) 527a9369418SSami Tolvanen *suffix = NULL; 528a9369418SSami Tolvanen 529a9369418SSami Tolvanen if (is_kabi_prefix(name)) { 530a9369418SSami Tolvanen name += KABI_PREFIX_LEN; 531a9369418SSami Tolvanen 532a9369418SSami Tolvanen if (!strncmp(name, KABI_RESERVED_PREFIX, 533a9369418SSami Tolvanen KABI_RESERVED_PREFIX_LEN)) 534a9369418SSami Tolvanen return KABI_RESERVED; 535a9369418SSami Tolvanen if (!strncmp(name, KABI_IGNORED_PREFIX, 536a9369418SSami Tolvanen KABI_IGNORED_PREFIX_LEN)) 537a9369418SSami Tolvanen return KABI_IGNORED; 538a9369418SSami Tolvanen 539a9369418SSami Tolvanen if (!strncmp(name, KABI_RENAMED_PREFIX, 540a9369418SSami Tolvanen KABI_RENAMED_PREFIX_LEN)) { 541a9369418SSami Tolvanen if (suffix) { 542a9369418SSami Tolvanen name += KABI_RENAMED_PREFIX_LEN; 543a9369418SSami Tolvanen *suffix = name; 544a9369418SSami Tolvanen } 545a9369418SSami Tolvanen return KABI_RESERVED; 546a9369418SSami Tolvanen } 547a9369418SSami Tolvanen } 548a9369418SSami Tolvanen 549a9369418SSami Tolvanen return KABI_NORMAL; 550a9369418SSami Tolvanen } 551a9369418SSami Tolvanen 552a9369418SSami Tolvanen static int check_struct_member_kabi_status(struct state *state, 553a9369418SSami Tolvanen struct die *__unused, Dwarf_Die *die) 554a9369418SSami Tolvanen { 555a9369418SSami Tolvanen int res; 556a9369418SSami Tolvanen 557a9369418SSami Tolvanen assert(dwarf_tag(die) == DW_TAG_member_type); 558a9369418SSami Tolvanen 559a9369418SSami Tolvanen /* 560a9369418SSami Tolvanen * If the union member is a struct, expect the __kabi field to 561a9369418SSami Tolvanen * be the first member of the structure, i.e..: 562a9369418SSami Tolvanen * 563a9369418SSami Tolvanen * union { 564a9369418SSami Tolvanen * type new_member; 565a9369418SSami Tolvanen * struct { 566a9369418SSami Tolvanen * type __kabi_field; 567a9369418SSami Tolvanen * } 568a9369418SSami Tolvanen * }; 569a9369418SSami Tolvanen */ 570a9369418SSami Tolvanen res = get_kabi_status(die, &state->kabi.orig_name); 571a9369418SSami Tolvanen 572a9369418SSami Tolvanen if (res == KABI_RESERVED && 573a9369418SSami Tolvanen !get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder)) 574a9369418SSami Tolvanen error("structure member missing a type?"); 575a9369418SSami Tolvanen 576a9369418SSami Tolvanen return res; 577a9369418SSami Tolvanen } 578a9369418SSami Tolvanen 579a9369418SSami Tolvanen static int check_union_member_kabi_status(struct state *state, 580a9369418SSami Tolvanen struct die *__unused, Dwarf_Die *die) 581a9369418SSami Tolvanen { 582a9369418SSami Tolvanen Dwarf_Die type; 583a9369418SSami Tolvanen int res; 584a9369418SSami Tolvanen 585a9369418SSami Tolvanen assert(dwarf_tag(die) == DW_TAG_member_type); 586a9369418SSami Tolvanen 587a9369418SSami Tolvanen if (!get_ref_die_attr(die, DW_AT_type, &type)) 588a9369418SSami Tolvanen error("union member missing a type?"); 589a9369418SSami Tolvanen 590a9369418SSami Tolvanen /* 591a9369418SSami Tolvanen * We expect a union with two members. Check if either of them 592a9369418SSami Tolvanen * has a __kabi name prefix, i.e.: 593a9369418SSami Tolvanen * 594a9369418SSami Tolvanen * union { 595a9369418SSami Tolvanen * ... 596a9369418SSami Tolvanen * type memberN; // <- type, N = {0,1} 597a9369418SSami Tolvanen * ... 598a9369418SSami Tolvanen * }; 599a9369418SSami Tolvanen * 600a9369418SSami Tolvanen * The member can also be a structure type, in which case we'll 601a9369418SSami Tolvanen * check the first structure member. 602a9369418SSami Tolvanen * 603a9369418SSami Tolvanen * In any case, stop processing after we've seen two members. 604a9369418SSami Tolvanen */ 605a9369418SSami Tolvanen res = get_kabi_status(die, &state->kabi.orig_name); 606a9369418SSami Tolvanen 607a9369418SSami Tolvanen if (res == KABI_RESERVED) 608a9369418SSami Tolvanen state->kabi.placeholder = type; 609a9369418SSami Tolvanen if (res != KABI_NORMAL) 610a9369418SSami Tolvanen return res; 611a9369418SSami Tolvanen 612a9369418SSami Tolvanen if (dwarf_tag(&type) == DW_TAG_structure_type) 613a9369418SSami Tolvanen res = checkp(process_die_container( 614a9369418SSami Tolvanen state, NULL, &type, check_struct_member_kabi_status, 615a9369418SSami Tolvanen match_member_type)); 616a9369418SSami Tolvanen 617a9369418SSami Tolvanen if (res <= KABI_NORMAL && ++state->kabi.members < 2) 618a9369418SSami Tolvanen return 0; /* Continue */ 619a9369418SSami Tolvanen 620a9369418SSami Tolvanen return res; 621a9369418SSami Tolvanen } 622a9369418SSami Tolvanen 623a9369418SSami Tolvanen static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, 624a9369418SSami Tolvanen const char **orig_name) 625a9369418SSami Tolvanen { 626a9369418SSami Tolvanen struct state state; 627a9369418SSami Tolvanen int res; 628a9369418SSami Tolvanen 629a9369418SSami Tolvanen if (!stable) 630a9369418SSami Tolvanen return KABI_NORMAL; 631a9369418SSami Tolvanen 632a9369418SSami Tolvanen /* 633a9369418SSami Tolvanen * To maintain a stable kABI, distributions may choose to reserve 634a9369418SSami Tolvanen * space in structs for later use by adding placeholder members, 635a9369418SSami Tolvanen * for example: 636a9369418SSami Tolvanen * 637a9369418SSami Tolvanen * struct s { 638a9369418SSami Tolvanen * u32 a; 639a9369418SSami Tolvanen * // an 8-byte placeholder for future use 640a9369418SSami Tolvanen * u64 __kabi_reserved_0; 641a9369418SSami Tolvanen * }; 642a9369418SSami Tolvanen * 643a9369418SSami Tolvanen * When the reserved member is taken into use, the type change 644a9369418SSami Tolvanen * would normally cause the symbol version to change as well, but 645a9369418SSami Tolvanen * if the replacement uses the following convention, gendwarfksyms 646a9369418SSami Tolvanen * continues to use the placeholder type for versioning instead, 647a9369418SSami Tolvanen * thus maintaining the same symbol version: 648a9369418SSami Tolvanen * 649a9369418SSami Tolvanen * struct s { 650a9369418SSami Tolvanen * u32 a; 651a9369418SSami Tolvanen * union { 652a9369418SSami Tolvanen * // placeholder replaced with a new member `b` 653a9369418SSami Tolvanen * struct t b; 654a9369418SSami Tolvanen * struct { 655a9369418SSami Tolvanen * // the placeholder type that is still 656a9369418SSami Tolvanen * // used for versioning 657a9369418SSami Tolvanen * u64 __kabi_reserved_0; 658a9369418SSami Tolvanen * }; 659a9369418SSami Tolvanen * }; 660a9369418SSami Tolvanen * }; 661a9369418SSami Tolvanen * 662a9369418SSami Tolvanen * I.e., as long as the replaced member is in a union, and the 663a9369418SSami Tolvanen * placeholder has a __kabi_reserved name prefix, we'll continue 664a9369418SSami Tolvanen * to use the placeholder type (here u64) for version calculation 665a9369418SSami Tolvanen * instead of the union type. 666a9369418SSami Tolvanen * 667a9369418SSami Tolvanen * It's also possible to ignore new members from versioning if 668a9369418SSami Tolvanen * they've been added to alignment holes, for example, by 669a9369418SSami Tolvanen * including them in a union with another member that uses the 670a9369418SSami Tolvanen * __kabi_ignored name prefix: 671a9369418SSami Tolvanen * 672a9369418SSami Tolvanen * struct s { 673a9369418SSami Tolvanen * u32 a; 674a9369418SSami Tolvanen * // an alignment hole is used to add `n` 675a9369418SSami Tolvanen * union { 676a9369418SSami Tolvanen * u32 n; 677a9369418SSami Tolvanen * // hide the entire union member from versioning 678a9369418SSami Tolvanen * u8 __kabi_ignored_0; 679a9369418SSami Tolvanen * }; 680a9369418SSami Tolvanen * u64 b; 681a9369418SSami Tolvanen * }; 682a9369418SSami Tolvanen * 683a9369418SSami Tolvanen * Note that the user of this feature is responsible for ensuring 684a9369418SSami Tolvanen * that the structure actually remains ABI compatible. 685a9369418SSami Tolvanen */ 686a9369418SSami Tolvanen memset(&state.kabi, 0, sizeof(struct kabi_state)); 687a9369418SSami Tolvanen 688a9369418SSami Tolvanen res = checkp(process_die_container(&state, NULL, die, 689a9369418SSami Tolvanen check_union_member_kabi_status, 690a9369418SSami Tolvanen match_member_type)); 691a9369418SSami Tolvanen 692a9369418SSami Tolvanen if (res == KABI_RESERVED) { 693a9369418SSami Tolvanen if (placeholder) 694a9369418SSami Tolvanen *placeholder = state.kabi.placeholder; 695a9369418SSami Tolvanen if (orig_name) 696a9369418SSami Tolvanen *orig_name = state.kabi.orig_name; 697a9369418SSami Tolvanen } 698a9369418SSami Tolvanen 699a9369418SSami Tolvanen return res; 700a9369418SSami Tolvanen } 701a9369418SSami Tolvanen 702a9369418SSami Tolvanen static bool is_kabi_ignored(Dwarf_Die *die) 703a9369418SSami Tolvanen { 704a9369418SSami Tolvanen Dwarf_Die type; 705a9369418SSami Tolvanen 706a9369418SSami Tolvanen if (!stable) 707a9369418SSami Tolvanen return false; 708a9369418SSami Tolvanen 709a9369418SSami Tolvanen if (!get_ref_die_attr(die, DW_AT_type, &type)) 710a9369418SSami Tolvanen error("member missing a type?"); 711a9369418SSami Tolvanen 712a9369418SSami Tolvanen return dwarf_tag(&type) == DW_TAG_union_type && 713a9369418SSami Tolvanen checkp(get_union_kabi_status(&type, NULL, NULL)) == KABI_IGNORED; 714a9369418SSami Tolvanen } 715a9369418SSami Tolvanen 716f6bb9245SSami Tolvanen static int ___process_structure_type(struct state *state, struct die *cache, 717f6bb9245SSami Tolvanen Dwarf_Die *die) 718f6bb9245SSami Tolvanen { 719f6bb9245SSami Tolvanen switch (dwarf_tag(die)) { 720f6bb9245SSami Tolvanen case DW_TAG_member: 721a9369418SSami Tolvanen if (is_kabi_ignored(die)) 722a9369418SSami Tolvanen return 0; 723a9369418SSami Tolvanen return check(process_type(state, cache, die)); 724f6bb9245SSami Tolvanen case DW_TAG_variant_part: 725f6bb9245SSami Tolvanen return check(process_type(state, cache, die)); 726f6bb9245SSami Tolvanen case DW_TAG_class_type: 727f6bb9245SSami Tolvanen case DW_TAG_enumeration_type: 728f6bb9245SSami Tolvanen case DW_TAG_structure_type: 729f6bb9245SSami Tolvanen case DW_TAG_template_type_parameter: 730f6bb9245SSami Tolvanen case DW_TAG_union_type: 731f6bb9245SSami Tolvanen case DW_TAG_subprogram: 732f6bb9245SSami Tolvanen /* Skip non-member types, including member functions */ 733f6bb9245SSami Tolvanen return 0; 734f6bb9245SSami Tolvanen default: 735f6bb9245SSami Tolvanen error("unexpected structure_type child: %x", dwarf_tag(die)); 736f6bb9245SSami Tolvanen } 737f6bb9245SSami Tolvanen } 738f6bb9245SSami Tolvanen 739f6bb9245SSami Tolvanen static void __process_structure_type(struct state *state, struct die *cache, 740f6bb9245SSami Tolvanen Dwarf_Die *die, const char *type, 741f6bb9245SSami Tolvanen die_callback_t process_func, 742f6bb9245SSami Tolvanen die_match_callback_t match_func) 743f6bb9245SSami Tolvanen { 744f936c129SSami Tolvanen bool expand; 745f936c129SSami Tolvanen 746f6bb9245SSami Tolvanen process(cache, type); 747f6bb9245SSami Tolvanen process_fqn(cache, die); 748f6bb9245SSami Tolvanen process(cache, " {"); 749f6bb9245SSami Tolvanen process_linebreak(cache, 1); 750f6bb9245SSami Tolvanen 751936cf61cSSami Tolvanen expand = state->expand.expand && is_kabi_definition(cache, die); 752f936c129SSami Tolvanen 753f936c129SSami Tolvanen if (expand) { 754936cf61cSSami Tolvanen state->expand.current_fqn = cache->fqn; 755f6bb9245SSami Tolvanen check(process_die_container(state, cache, die, process_func, 756f6bb9245SSami Tolvanen match_func)); 757f936c129SSami Tolvanen } 758f6bb9245SSami Tolvanen 759f6bb9245SSami Tolvanen process_linebreak(cache, -1); 760f6bb9245SSami Tolvanen process(cache, "}"); 761f6bb9245SSami Tolvanen 762f936c129SSami Tolvanen if (expand) { 763f6bb9245SSami Tolvanen process_byte_size_attr(cache, die); 764f6bb9245SSami Tolvanen process_alignment_attr(cache, die); 765f6bb9245SSami Tolvanen } 766f936c129SSami Tolvanen } 767f6bb9245SSami Tolvanen 768f6bb9245SSami Tolvanen #define DEFINE_PROCESS_STRUCTURE_TYPE(structure) \ 769f6bb9245SSami Tolvanen static void process_##structure##_type( \ 770f6bb9245SSami Tolvanen struct state *state, struct die *cache, Dwarf_Die *die) \ 771f6bb9245SSami Tolvanen { \ 772f6bb9245SSami Tolvanen __process_structure_type(state, cache, die, \ 773f6bb9245SSami Tolvanen #structure "_type", \ 774f6bb9245SSami Tolvanen ___process_structure_type, \ 775f6bb9245SSami Tolvanen match_all); \ 776f6bb9245SSami Tolvanen } 777f6bb9245SSami Tolvanen 778f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(class) 779f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(structure) 780a9369418SSami Tolvanen 781a9369418SSami Tolvanen static void process_union_type(struct state *state, struct die *cache, 782a9369418SSami Tolvanen Dwarf_Die *die) 783a9369418SSami Tolvanen { 784a9369418SSami Tolvanen Dwarf_Die placeholder; 785a9369418SSami Tolvanen 786a9369418SSami Tolvanen int res = checkp(get_union_kabi_status(die, &placeholder, 787a9369418SSami Tolvanen &state->kabi.orig_name)); 788a9369418SSami Tolvanen 789a9369418SSami Tolvanen if (res == KABI_RESERVED) 790a9369418SSami Tolvanen check(process_type(state, cache, &placeholder)); 791a9369418SSami Tolvanen if (res > KABI_NORMAL) 792a9369418SSami Tolvanen return; 793a9369418SSami Tolvanen 794a9369418SSami Tolvanen __process_structure_type(state, cache, die, "union_type", 795a9369418SSami Tolvanen ___process_structure_type, match_all); 796a9369418SSami Tolvanen } 797f6bb9245SSami Tolvanen 798f6bb9245SSami Tolvanen static void process_enumerator_type(struct state *state, struct die *cache, 799f6bb9245SSami Tolvanen Dwarf_Die *die) 800f6bb9245SSami Tolvanen { 801936cf61cSSami Tolvanen bool overridden = false; 802f6bb9245SSami Tolvanen Dwarf_Word value; 803f6bb9245SSami Tolvanen 804936cf61cSSami Tolvanen if (stable) { 805936cf61cSSami Tolvanen /* Get the fqn before we process anything */ 806936cf61cSSami Tolvanen update_fqn(cache, die); 807936cf61cSSami Tolvanen 808936cf61cSSami Tolvanen if (kabi_is_enumerator_ignored(state->expand.current_fqn, 809936cf61cSSami Tolvanen cache->fqn)) 810936cf61cSSami Tolvanen return; 811936cf61cSSami Tolvanen 812936cf61cSSami Tolvanen overridden = kabi_get_enumerator_value( 813936cf61cSSami Tolvanen state->expand.current_fqn, cache->fqn, &value); 814936cf61cSSami Tolvanen } 815936cf61cSSami Tolvanen 816f6bb9245SSami Tolvanen process_list_comma(state, cache); 817f6bb9245SSami Tolvanen process(cache, "enumerator"); 818f6bb9245SSami Tolvanen process_fqn(cache, die); 819f6bb9245SSami Tolvanen 820936cf61cSSami Tolvanen if (overridden || get_udata_attr(die, DW_AT_const_value, &value)) { 821f6bb9245SSami Tolvanen process(cache, " = "); 822f6bb9245SSami Tolvanen process_fmt(cache, "%" PRIu64, value); 823f6bb9245SSami Tolvanen } 824f6bb9245SSami Tolvanen } 825f6bb9245SSami Tolvanen 826f6bb9245SSami Tolvanen static void process_enumeration_type(struct state *state, struct die *cache, 827f6bb9245SSami Tolvanen Dwarf_Die *die) 828f6bb9245SSami Tolvanen { 829f6bb9245SSami Tolvanen __process_structure_type(state, cache, die, "enumeration_type", 830f6bb9245SSami Tolvanen process_type, match_enumerator_type); 831f6bb9245SSami Tolvanen } 832f6bb9245SSami Tolvanen 8330c1c7627SSami Tolvanen static void process_base_type(struct state *state, struct die *cache, 8340c1c7627SSami Tolvanen Dwarf_Die *die) 8355b7780e8SSami Tolvanen { 8360c1c7627SSami Tolvanen process(cache, "base_type"); 8370c1c7627SSami Tolvanen process_fqn(cache, die); 8380c1c7627SSami Tolvanen process_byte_size_attr(cache, die); 8390c1c7627SSami Tolvanen process_encoding_attr(cache, die); 8400c1c7627SSami Tolvanen process_alignment_attr(cache, die); 8410c1c7627SSami Tolvanen } 8420c1c7627SSami Tolvanen 843f6bb9245SSami Tolvanen static void process_unspecified_type(struct state *state, struct die *cache, 844f6bb9245SSami Tolvanen Dwarf_Die *die) 845f6bb9245SSami Tolvanen { 846f6bb9245SSami Tolvanen /* 847f6bb9245SSami Tolvanen * These can be emitted for stand-alone assembly code, which means we 848f6bb9245SSami Tolvanen * might run into them in vmlinux.o. 849f6bb9245SSami Tolvanen */ 850f6bb9245SSami Tolvanen process(cache, "unspecified_type"); 851f6bb9245SSami Tolvanen } 852f6bb9245SSami Tolvanen 8530c1c7627SSami Tolvanen static void process_cached(struct state *state, struct die *cache, 8540c1c7627SSami Tolvanen Dwarf_Die *die) 8550c1c7627SSami Tolvanen { 8560c1c7627SSami Tolvanen struct die_fragment *df; 8570c1c7627SSami Tolvanen Dwarf_Die child; 8580c1c7627SSami Tolvanen 8590c1c7627SSami Tolvanen list_for_each_entry(df, &cache->fragments, list) { 8600c1c7627SSami Tolvanen switch (df->type) { 8610c1c7627SSami Tolvanen case FRAGMENT_STRING: 862d2ffdc1cSSami Tolvanen die_debug_b("cache %p STRING '%s'", cache, 863d2ffdc1cSSami Tolvanen df->data.str); 8640c1c7627SSami Tolvanen process(NULL, df->data.str); 8650c1c7627SSami Tolvanen break; 86606b8b036SSami Tolvanen case FRAGMENT_LINEBREAK: 86706b8b036SSami Tolvanen process_linebreak(NULL, df->data.linebreak); 86806b8b036SSami Tolvanen break; 8690c1c7627SSami Tolvanen case FRAGMENT_DIE: 8700c1c7627SSami Tolvanen if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu), 8710c1c7627SSami Tolvanen (void *)df->data.addr, &child)) 8720c1c7627SSami Tolvanen error("dwarf_die_addr_die failed"); 873d2ffdc1cSSami Tolvanen die_debug_b("cache %p DIE addr %" PRIxPTR " tag %x", 874d2ffdc1cSSami Tolvanen cache, df->data.addr, dwarf_tag(&child)); 8750c1c7627SSami Tolvanen check(process_type(state, NULL, &child)); 8760c1c7627SSami Tolvanen break; 8770c1c7627SSami Tolvanen default: 8780c1c7627SSami Tolvanen error("empty die_fragment"); 8790c1c7627SSami Tolvanen } 8800c1c7627SSami Tolvanen } 8815b7780e8SSami Tolvanen } 8825b7780e8SSami Tolvanen 883f936c129SSami Tolvanen static void state_init(struct state *state) 884f936c129SSami Tolvanen { 885f936c129SSami Tolvanen state->expand.expand = true; 886936cf61cSSami Tolvanen state->expand.current_fqn = NULL; 887f936c129SSami Tolvanen cache_init(&state->expansion_cache); 888f936c129SSami Tolvanen } 889f936c129SSami Tolvanen 890f936c129SSami Tolvanen static void expansion_state_restore(struct expansion_state *state, 891f936c129SSami Tolvanen struct expansion_state *saved) 892f936c129SSami Tolvanen { 893f936c129SSami Tolvanen state->expand = saved->expand; 894936cf61cSSami Tolvanen state->current_fqn = saved->current_fqn; 895f936c129SSami Tolvanen } 896f936c129SSami Tolvanen 897f936c129SSami Tolvanen static void expansion_state_save(struct expansion_state *state, 898f936c129SSami Tolvanen struct expansion_state *saved) 899f936c129SSami Tolvanen { 900f936c129SSami Tolvanen expansion_state_restore(saved, state); 901f936c129SSami Tolvanen } 902f936c129SSami Tolvanen 903f936c129SSami Tolvanen static bool is_expanded_type(int tag) 904f936c129SSami Tolvanen { 905f936c129SSami Tolvanen return tag == DW_TAG_class_type || tag == DW_TAG_structure_type || 906f936c129SSami Tolvanen tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type; 907f936c129SSami Tolvanen } 908f936c129SSami Tolvanen 9095b7780e8SSami Tolvanen #define PROCESS_TYPE(type) \ 9105b7780e8SSami Tolvanen case DW_TAG_##type##_type: \ 9110c1c7627SSami Tolvanen process_##type##_type(state, cache, die); \ 9125b7780e8SSami Tolvanen break; 9135b7780e8SSami Tolvanen 9140c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, Dwarf_Die *die) 9155b7780e8SSami Tolvanen { 916f936c129SSami Tolvanen enum die_state want_state = DIE_COMPLETE; 9170c1c7627SSami Tolvanen struct die *cache; 918f936c129SSami Tolvanen struct expansion_state saved; 9195b7780e8SSami Tolvanen int tag = dwarf_tag(die); 9205b7780e8SSami Tolvanen 921f936c129SSami Tolvanen expansion_state_save(&state->expand, &saved); 922f936c129SSami Tolvanen 9230c1c7627SSami Tolvanen /* 924f936c129SSami Tolvanen * Structures and enumeration types are expanded only once per 925f936c129SSami Tolvanen * exported symbol. This is sufficient for detecting ABI changes 926f936c129SSami Tolvanen * within the structure. 927f936c129SSami Tolvanen */ 928f936c129SSami Tolvanen if (is_expanded_type(tag)) { 929f936c129SSami Tolvanen if (cache_was_expanded(&state->expansion_cache, die->addr)) 930f936c129SSami Tolvanen state->expand.expand = false; 931f936c129SSami Tolvanen 932f936c129SSami Tolvanen if (state->expand.expand) 933f936c129SSami Tolvanen cache_mark_expanded(&state->expansion_cache, die->addr); 934f936c129SSami Tolvanen else 935f936c129SSami Tolvanen want_state = DIE_UNEXPANDED; 936f936c129SSami Tolvanen } 937f936c129SSami Tolvanen 938f936c129SSami Tolvanen /* 939f936c129SSami Tolvanen * If we have want_state already cached, use it instead of walking 9400c1c7627SSami Tolvanen * through DWARF. 9410c1c7627SSami Tolvanen */ 942f936c129SSami Tolvanen cache = die_map_get(die, want_state); 9430c1c7627SSami Tolvanen 944f936c129SSami Tolvanen if (cache->state == want_state) { 945d2ffdc1cSSami Tolvanen die_debug_g("cached addr %p tag %x -- %s", die->addr, tag, 946d2ffdc1cSSami Tolvanen die_state_name(cache->state)); 947d2ffdc1cSSami Tolvanen 9480c1c7627SSami Tolvanen process_cached(state, cache, die); 9490c1c7627SSami Tolvanen die_map_add_die(parent, cache); 950f936c129SSami Tolvanen 951f936c129SSami Tolvanen expansion_state_restore(&state->expand, &saved); 9520c1c7627SSami Tolvanen return 0; 9530c1c7627SSami Tolvanen } 9540c1c7627SSami Tolvanen 955d2ffdc1cSSami Tolvanen die_debug_g("addr %p tag %x -- %s -> %s", die->addr, tag, 956d2ffdc1cSSami Tolvanen die_state_name(cache->state), die_state_name(want_state)); 957d2ffdc1cSSami Tolvanen 9585b7780e8SSami Tolvanen switch (tag) { 95906b8b036SSami Tolvanen /* Type modifiers */ 96006b8b036SSami Tolvanen PROCESS_TYPE(atomic) 96106b8b036SSami Tolvanen PROCESS_TYPE(const) 96206b8b036SSami Tolvanen PROCESS_TYPE(immutable) 96306b8b036SSami Tolvanen PROCESS_TYPE(packed) 96406b8b036SSami Tolvanen PROCESS_TYPE(pointer) 96506b8b036SSami Tolvanen PROCESS_TYPE(reference) 96606b8b036SSami Tolvanen PROCESS_TYPE(restrict) 96706b8b036SSami Tolvanen PROCESS_TYPE(rvalue_reference) 96806b8b036SSami Tolvanen PROCESS_TYPE(shared) 96906b8b036SSami Tolvanen PROCESS_TYPE(volatile) 970f6bb9245SSami Tolvanen /* Container types */ 971f6bb9245SSami Tolvanen PROCESS_TYPE(class) 972f6bb9245SSami Tolvanen PROCESS_TYPE(structure) 973f6bb9245SSami Tolvanen PROCESS_TYPE(union) 974f6bb9245SSami Tolvanen PROCESS_TYPE(enumeration) 975220a0857SSami Tolvanen /* Subtypes */ 976f6bb9245SSami Tolvanen PROCESS_TYPE(enumerator) 977220a0857SSami Tolvanen PROCESS_TYPE(formal_parameter) 978f6bb9245SSami Tolvanen PROCESS_TYPE(member) 979c772f1d1SSami Tolvanen PROCESS_TYPE(subrange) 980f6bb9245SSami Tolvanen PROCESS_TYPE(template_type_parameter) 981f6bb9245SSami Tolvanen PROCESS_TYPE(variant) 982f6bb9245SSami Tolvanen PROCESS_TYPE(variant_part) 98306b8b036SSami Tolvanen /* Other types */ 984c772f1d1SSami Tolvanen PROCESS_TYPE(array) 9855b7780e8SSami Tolvanen PROCESS_TYPE(base) 986220a0857SSami Tolvanen PROCESS_TYPE(subroutine) 98706b8b036SSami Tolvanen PROCESS_TYPE(typedef) 988f6bb9245SSami Tolvanen PROCESS_TYPE(unspecified) 9895b7780e8SSami Tolvanen default: 990f6bb9245SSami Tolvanen error("unexpected type: %x", tag); 9915b7780e8SSami Tolvanen } 9925b7780e8SSami Tolvanen 993d2ffdc1cSSami Tolvanen die_debug_r("parent %p cache %p die addr %p tag %x", parent, cache, 994d2ffdc1cSSami Tolvanen die->addr, tag); 995d2ffdc1cSSami Tolvanen 9960c1c7627SSami Tolvanen /* Update cache state and append to the parent (if any) */ 9970c1c7627SSami Tolvanen cache->tag = tag; 998f936c129SSami Tolvanen cache->state = want_state; 9990c1c7627SSami Tolvanen die_map_add_die(parent, cache); 10000c1c7627SSami Tolvanen 1001f936c129SSami Tolvanen expansion_state_restore(&state->expand, &saved); 10025b7780e8SSami Tolvanen return 0; 10035b7780e8SSami Tolvanen } 10045b7780e8SSami Tolvanen 1005f2856884SSami Tolvanen /* 1006f2856884SSami Tolvanen * Exported symbol processing 1007f2856884SSami Tolvanen */ 100871378888SSami Tolvanen static struct die *get_symbol_cache(struct state *state, Dwarf_Die *die) 100971378888SSami Tolvanen { 101071378888SSami Tolvanen struct die *cache; 101171378888SSami Tolvanen 101271378888SSami Tolvanen cache = die_map_get(die, DIE_SYMBOL); 101371378888SSami Tolvanen 101471378888SSami Tolvanen if (cache->state != DIE_INCOMPLETE) 101571378888SSami Tolvanen return NULL; /* We already processed a symbol for this DIE */ 101671378888SSami Tolvanen 101771378888SSami Tolvanen cache->tag = dwarf_tag(die); 101871378888SSami Tolvanen return cache; 101971378888SSami Tolvanen } 102071378888SSami Tolvanen 1021f2856884SSami Tolvanen static void process_symbol(struct state *state, Dwarf_Die *die, 1022f2856884SSami Tolvanen die_callback_t process_func) 1023f2856884SSami Tolvanen { 102471378888SSami Tolvanen struct die *cache; 102571378888SSami Tolvanen 102671378888SSami Tolvanen symbol_set_die(state->sym, die); 102771378888SSami Tolvanen 102871378888SSami Tolvanen cache = get_symbol_cache(state, die); 102971378888SSami Tolvanen if (!cache) 103071378888SSami Tolvanen return; 103171378888SSami Tolvanen 1032f2856884SSami Tolvanen debug("%s", state->sym->name); 103371378888SSami Tolvanen check(process_func(state, cache, die)); 103471378888SSami Tolvanen cache->state = DIE_SYMBOL; 1035f2856884SSami Tolvanen if (dump_dies) 1036f2856884SSami Tolvanen fputs("\n", stderr); 1037f2856884SSami Tolvanen } 1038f2856884SSami Tolvanen 10390c1c7627SSami Tolvanen static int __process_subprogram(struct state *state, struct die *cache, 10400c1c7627SSami Tolvanen Dwarf_Die *die) 1041f2856884SSami Tolvanen { 1042220a0857SSami Tolvanen __process_subroutine_type(state, cache, die, "subprogram"); 1043f2856884SSami Tolvanen return 0; 1044f2856884SSami Tolvanen } 1045f2856884SSami Tolvanen 1046f2856884SSami Tolvanen static void process_subprogram(struct state *state, Dwarf_Die *die) 1047f2856884SSami Tolvanen { 1048f2856884SSami Tolvanen process_symbol(state, die, __process_subprogram); 1049f2856884SSami Tolvanen } 1050f2856884SSami Tolvanen 10510c1c7627SSami Tolvanen static int __process_variable(struct state *state, struct die *cache, 10520c1c7627SSami Tolvanen Dwarf_Die *die) 1053f2856884SSami Tolvanen { 10540c1c7627SSami Tolvanen process(cache, "variable "); 10550c1c7627SSami Tolvanen process_type_attr(state, cache, die); 1056f2856884SSami Tolvanen return 0; 1057f2856884SSami Tolvanen } 1058f2856884SSami Tolvanen 1059f2856884SSami Tolvanen static void process_variable(struct state *state, Dwarf_Die *die) 1060f2856884SSami Tolvanen { 1061f2856884SSami Tolvanen process_symbol(state, die, __process_variable); 1062f2856884SSami Tolvanen } 1063f2856884SSami Tolvanen 1064*fa624569SSami Tolvanen static void save_symbol_ptr(struct state *state) 1065*fa624569SSami Tolvanen { 1066*fa624569SSami Tolvanen Dwarf_Die ptr_type; 1067*fa624569SSami Tolvanen Dwarf_Die type; 1068*fa624569SSami Tolvanen 1069*fa624569SSami Tolvanen if (!get_ref_die_attr(&state->die, DW_AT_type, &ptr_type) || 1070*fa624569SSami Tolvanen dwarf_tag(&ptr_type) != DW_TAG_pointer_type) 1071*fa624569SSami Tolvanen error("%s must be a pointer type!", 1072*fa624569SSami Tolvanen get_symbol_name(&state->die)); 1073*fa624569SSami Tolvanen 1074*fa624569SSami Tolvanen if (!get_ref_die_attr(&ptr_type, DW_AT_type, &type)) 1075*fa624569SSami Tolvanen error("%s pointer missing a type attribute?", 1076*fa624569SSami Tolvanen get_symbol_name(&state->die)); 1077*fa624569SSami Tolvanen 1078*fa624569SSami Tolvanen /* 1079*fa624569SSami Tolvanen * Save the symbol pointer DIE in case the actual symbol is 1080*fa624569SSami Tolvanen * missing from the DWARF. Clang, for example, intentionally 1081*fa624569SSami Tolvanen * omits external symbols from the debugging information. 1082*fa624569SSami Tolvanen */ 1083*fa624569SSami Tolvanen if (dwarf_tag(&type) == DW_TAG_subroutine_type) 1084*fa624569SSami Tolvanen symbol_set_ptr(state->sym, &type); 1085*fa624569SSami Tolvanen else 1086*fa624569SSami Tolvanen symbol_set_ptr(state->sym, &ptr_type); 1087*fa624569SSami Tolvanen } 1088*fa624569SSami Tolvanen 10890c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache, 10900c1c7627SSami Tolvanen Dwarf_Die *die) 1091f2856884SSami Tolvanen { 1092f2856884SSami Tolvanen int tag = dwarf_tag(die); 1093f2856884SSami Tolvanen 1094f2856884SSami Tolvanen switch (tag) { 1095f2856884SSami Tolvanen /* Possible containers of exported symbols */ 1096f2856884SSami Tolvanen case DW_TAG_namespace: 1097f2856884SSami Tolvanen case DW_TAG_class_type: 1098f2856884SSami Tolvanen case DW_TAG_structure_type: 1099f2856884SSami Tolvanen return check(process_die_container( 11000c1c7627SSami Tolvanen NULL, cache, die, process_exported_symbols, match_all)); 1101f2856884SSami Tolvanen 1102f2856884SSami Tolvanen /* Possible exported symbols */ 1103f2856884SSami Tolvanen case DW_TAG_subprogram: 1104f2856884SSami Tolvanen case DW_TAG_variable: { 1105f2856884SSami Tolvanen struct state state; 1106f2856884SSami Tolvanen 1107f2856884SSami Tolvanen if (!match_export_symbol(&state, die)) 1108f2856884SSami Tolvanen return 0; 1109f2856884SSami Tolvanen 1110f936c129SSami Tolvanen state_init(&state); 1111f936c129SSami Tolvanen 1112*fa624569SSami Tolvanen if (is_symbol_ptr(get_symbol_name(&state.die))) 1113*fa624569SSami Tolvanen save_symbol_ptr(&state); 1114*fa624569SSami Tolvanen else if (tag == DW_TAG_subprogram) 1115f2856884SSami Tolvanen process_subprogram(&state, &state.die); 1116f2856884SSami Tolvanen else 1117f2856884SSami Tolvanen process_variable(&state, &state.die); 1118f2856884SSami Tolvanen 1119f936c129SSami Tolvanen cache_free(&state.expansion_cache); 1120f2856884SSami Tolvanen return 0; 1121f2856884SSami Tolvanen } 1122f2856884SSami Tolvanen default: 1123f2856884SSami Tolvanen return 0; 1124f2856884SSami Tolvanen } 1125f2856884SSami Tolvanen } 1126f2856884SSami Tolvanen 1127*fa624569SSami Tolvanen static void process_symbol_ptr(struct symbol *sym, void *arg) 1128*fa624569SSami Tolvanen { 1129*fa624569SSami Tolvanen struct state state; 1130*fa624569SSami Tolvanen Dwarf *dwarf = arg; 1131*fa624569SSami Tolvanen 1132*fa624569SSami Tolvanen if (sym->state != SYMBOL_UNPROCESSED || !sym->ptr_die_addr) 1133*fa624569SSami Tolvanen return; 1134*fa624569SSami Tolvanen 1135*fa624569SSami Tolvanen debug("%s", sym->name); 1136*fa624569SSami Tolvanen state_init(&state); 1137*fa624569SSami Tolvanen state.sym = sym; 1138*fa624569SSami Tolvanen 1139*fa624569SSami Tolvanen if (!dwarf_die_addr_die(dwarf, (void *)sym->ptr_die_addr, &state.die)) 1140*fa624569SSami Tolvanen error("dwarf_die_addr_die failed for symbol ptr: '%s'", 1141*fa624569SSami Tolvanen sym->name); 1142*fa624569SSami Tolvanen 1143*fa624569SSami Tolvanen if (dwarf_tag(&state.die) == DW_TAG_subroutine_type) 1144*fa624569SSami Tolvanen process_subprogram(&state, &state.die); 1145*fa624569SSami Tolvanen else 1146*fa624569SSami Tolvanen process_variable(&state, &state.die); 1147*fa624569SSami Tolvanen 1148*fa624569SSami Tolvanen cache_free(&state.expansion_cache); 1149*fa624569SSami Tolvanen } 1150*fa624569SSami Tolvanen 1151f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie) 1152f2856884SSami Tolvanen { 11530c1c7627SSami Tolvanen check(process_die_container(NULL, NULL, cudie, process_exported_symbols, 1154f2856884SSami Tolvanen match_all)); 1155f936c129SSami Tolvanen 1156*fa624569SSami Tolvanen symbol_for_each(process_symbol_ptr, dwarf_cu_getdwarf(cudie->cu)); 1157*fa624569SSami Tolvanen 1158f936c129SSami Tolvanen cache_free(&srcfile_cache); 1159f2856884SSami Tolvanen } 1160