1f2856884SSami Tolvanen // SPDX-License-Identifier: GPL-2.0 2f2856884SSami Tolvanen /* 3f2856884SSami Tolvanen * Copyright (C) 2024 Google LLC 4f2856884SSami Tolvanen */ 5f2856884SSami Tolvanen 6*a9369418SSami Tolvanen #include <assert.h> 75b7780e8SSami Tolvanen #include <inttypes.h> 85b7780e8SSami Tolvanen #include <stdarg.h> 9f2856884SSami Tolvanen #include "gendwarfksyms.h" 10f2856884SSami Tolvanen 11*a9369418SSami Tolvanen /* See get_union_kabi_status */ 12*a9369418SSami Tolvanen #define KABI_PREFIX "__kabi_" 13*a9369418SSami Tolvanen #define KABI_PREFIX_LEN (sizeof(KABI_PREFIX) - 1) 14*a9369418SSami Tolvanen #define KABI_RESERVED_PREFIX "reserved" 15*a9369418SSami Tolvanen #define KABI_RESERVED_PREFIX_LEN (sizeof(KABI_RESERVED_PREFIX) - 1) 16*a9369418SSami Tolvanen #define KABI_RENAMED_PREFIX "renamed" 17*a9369418SSami Tolvanen #define KABI_RENAMED_PREFIX_LEN (sizeof(KABI_RENAMED_PREFIX) - 1) 18*a9369418SSami Tolvanen #define KABI_IGNORED_PREFIX "ignored" 19*a9369418SSami Tolvanen #define KABI_IGNORED_PREFIX_LEN (sizeof(KABI_IGNORED_PREFIX) - 1) 20*a9369418SSami Tolvanen 21*a9369418SSami Tolvanen static inline bool is_kabi_prefix(const char *name) 22*a9369418SSami Tolvanen { 23*a9369418SSami Tolvanen return name && !strncmp(name, KABI_PREFIX, KABI_PREFIX_LEN); 24*a9369418SSami Tolvanen } 25*a9369418SSami Tolvanen 26*a9369418SSami Tolvanen enum kabi_status { 27*a9369418SSami Tolvanen /* >0 to stop DIE processing */ 28*a9369418SSami Tolvanen KABI_NORMAL = 1, 29*a9369418SSami Tolvanen KABI_RESERVED, 30*a9369418SSami Tolvanen KABI_IGNORED, 31*a9369418SSami Tolvanen }; 32*a9369418SSami 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 379*a9369418SSami Tolvanen if (stable) { 380*a9369418SSami Tolvanen if (is_kabi_prefix(name)) 381*a9369418SSami Tolvanen name = NULL; 382*a9369418SSami Tolvanen state->kabi.orig_name = NULL; 383*a9369418SSami Tolvanen } 384*a9369418SSami Tolvanen 385220a0857SSami Tolvanen process_list_comma(state, cache); 386220a0857SSami Tolvanen process(cache, type); 387220a0857SSami Tolvanen process_type_attr(state, cache, die); 388*a9369418SSami Tolvanen 389*a9369418SSami Tolvanen if (stable && state->kabi.orig_name) 390*a9369418SSami Tolvanen name = state->kabi.orig_name; 391220a0857SSami Tolvanen if (name) { 392220a0857SSami Tolvanen process(cache, " "); 393220a0857SSami Tolvanen process(cache, name); 394220a0857SSami Tolvanen } 395*a9369418SSami 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 522*a9369418SSami Tolvanen static int get_kabi_status(Dwarf_Die *die, const char **suffix) 523*a9369418SSami Tolvanen { 524*a9369418SSami Tolvanen const char *name = get_name_attr(die); 525*a9369418SSami Tolvanen 526*a9369418SSami Tolvanen if (suffix) 527*a9369418SSami Tolvanen *suffix = NULL; 528*a9369418SSami Tolvanen 529*a9369418SSami Tolvanen if (is_kabi_prefix(name)) { 530*a9369418SSami Tolvanen name += KABI_PREFIX_LEN; 531*a9369418SSami Tolvanen 532*a9369418SSami Tolvanen if (!strncmp(name, KABI_RESERVED_PREFIX, 533*a9369418SSami Tolvanen KABI_RESERVED_PREFIX_LEN)) 534*a9369418SSami Tolvanen return KABI_RESERVED; 535*a9369418SSami Tolvanen if (!strncmp(name, KABI_IGNORED_PREFIX, 536*a9369418SSami Tolvanen KABI_IGNORED_PREFIX_LEN)) 537*a9369418SSami Tolvanen return KABI_IGNORED; 538*a9369418SSami Tolvanen 539*a9369418SSami Tolvanen if (!strncmp(name, KABI_RENAMED_PREFIX, 540*a9369418SSami Tolvanen KABI_RENAMED_PREFIX_LEN)) { 541*a9369418SSami Tolvanen if (suffix) { 542*a9369418SSami Tolvanen name += KABI_RENAMED_PREFIX_LEN; 543*a9369418SSami Tolvanen *suffix = name; 544*a9369418SSami Tolvanen } 545*a9369418SSami Tolvanen return KABI_RESERVED; 546*a9369418SSami Tolvanen } 547*a9369418SSami Tolvanen } 548*a9369418SSami Tolvanen 549*a9369418SSami Tolvanen return KABI_NORMAL; 550*a9369418SSami Tolvanen } 551*a9369418SSami Tolvanen 552*a9369418SSami Tolvanen static int check_struct_member_kabi_status(struct state *state, 553*a9369418SSami Tolvanen struct die *__unused, Dwarf_Die *die) 554*a9369418SSami Tolvanen { 555*a9369418SSami Tolvanen int res; 556*a9369418SSami Tolvanen 557*a9369418SSami Tolvanen assert(dwarf_tag(die) == DW_TAG_member_type); 558*a9369418SSami Tolvanen 559*a9369418SSami Tolvanen /* 560*a9369418SSami Tolvanen * If the union member is a struct, expect the __kabi field to 561*a9369418SSami Tolvanen * be the first member of the structure, i.e..: 562*a9369418SSami Tolvanen * 563*a9369418SSami Tolvanen * union { 564*a9369418SSami Tolvanen * type new_member; 565*a9369418SSami Tolvanen * struct { 566*a9369418SSami Tolvanen * type __kabi_field; 567*a9369418SSami Tolvanen * } 568*a9369418SSami Tolvanen * }; 569*a9369418SSami Tolvanen */ 570*a9369418SSami Tolvanen res = get_kabi_status(die, &state->kabi.orig_name); 571*a9369418SSami Tolvanen 572*a9369418SSami Tolvanen if (res == KABI_RESERVED && 573*a9369418SSami Tolvanen !get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder)) 574*a9369418SSami Tolvanen error("structure member missing a type?"); 575*a9369418SSami Tolvanen 576*a9369418SSami Tolvanen return res; 577*a9369418SSami Tolvanen } 578*a9369418SSami Tolvanen 579*a9369418SSami Tolvanen static int check_union_member_kabi_status(struct state *state, 580*a9369418SSami Tolvanen struct die *__unused, Dwarf_Die *die) 581*a9369418SSami Tolvanen { 582*a9369418SSami Tolvanen Dwarf_Die type; 583*a9369418SSami Tolvanen int res; 584*a9369418SSami Tolvanen 585*a9369418SSami Tolvanen assert(dwarf_tag(die) == DW_TAG_member_type); 586*a9369418SSami Tolvanen 587*a9369418SSami Tolvanen if (!get_ref_die_attr(die, DW_AT_type, &type)) 588*a9369418SSami Tolvanen error("union member missing a type?"); 589*a9369418SSami Tolvanen 590*a9369418SSami Tolvanen /* 591*a9369418SSami Tolvanen * We expect a union with two members. Check if either of them 592*a9369418SSami Tolvanen * has a __kabi name prefix, i.e.: 593*a9369418SSami Tolvanen * 594*a9369418SSami Tolvanen * union { 595*a9369418SSami Tolvanen * ... 596*a9369418SSami Tolvanen * type memberN; // <- type, N = {0,1} 597*a9369418SSami Tolvanen * ... 598*a9369418SSami Tolvanen * }; 599*a9369418SSami Tolvanen * 600*a9369418SSami Tolvanen * The member can also be a structure type, in which case we'll 601*a9369418SSami Tolvanen * check the first structure member. 602*a9369418SSami Tolvanen * 603*a9369418SSami Tolvanen * In any case, stop processing after we've seen two members. 604*a9369418SSami Tolvanen */ 605*a9369418SSami Tolvanen res = get_kabi_status(die, &state->kabi.orig_name); 606*a9369418SSami Tolvanen 607*a9369418SSami Tolvanen if (res == KABI_RESERVED) 608*a9369418SSami Tolvanen state->kabi.placeholder = type; 609*a9369418SSami Tolvanen if (res != KABI_NORMAL) 610*a9369418SSami Tolvanen return res; 611*a9369418SSami Tolvanen 612*a9369418SSami Tolvanen if (dwarf_tag(&type) == DW_TAG_structure_type) 613*a9369418SSami Tolvanen res = checkp(process_die_container( 614*a9369418SSami Tolvanen state, NULL, &type, check_struct_member_kabi_status, 615*a9369418SSami Tolvanen match_member_type)); 616*a9369418SSami Tolvanen 617*a9369418SSami Tolvanen if (res <= KABI_NORMAL && ++state->kabi.members < 2) 618*a9369418SSami Tolvanen return 0; /* Continue */ 619*a9369418SSami Tolvanen 620*a9369418SSami Tolvanen return res; 621*a9369418SSami Tolvanen } 622*a9369418SSami Tolvanen 623*a9369418SSami Tolvanen static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, 624*a9369418SSami Tolvanen const char **orig_name) 625*a9369418SSami Tolvanen { 626*a9369418SSami Tolvanen struct state state; 627*a9369418SSami Tolvanen int res; 628*a9369418SSami Tolvanen 629*a9369418SSami Tolvanen if (!stable) 630*a9369418SSami Tolvanen return KABI_NORMAL; 631*a9369418SSami Tolvanen 632*a9369418SSami Tolvanen /* 633*a9369418SSami Tolvanen * To maintain a stable kABI, distributions may choose to reserve 634*a9369418SSami Tolvanen * space in structs for later use by adding placeholder members, 635*a9369418SSami Tolvanen * for example: 636*a9369418SSami Tolvanen * 637*a9369418SSami Tolvanen * struct s { 638*a9369418SSami Tolvanen * u32 a; 639*a9369418SSami Tolvanen * // an 8-byte placeholder for future use 640*a9369418SSami Tolvanen * u64 __kabi_reserved_0; 641*a9369418SSami Tolvanen * }; 642*a9369418SSami Tolvanen * 643*a9369418SSami Tolvanen * When the reserved member is taken into use, the type change 644*a9369418SSami Tolvanen * would normally cause the symbol version to change as well, but 645*a9369418SSami Tolvanen * if the replacement uses the following convention, gendwarfksyms 646*a9369418SSami Tolvanen * continues to use the placeholder type for versioning instead, 647*a9369418SSami Tolvanen * thus maintaining the same symbol version: 648*a9369418SSami Tolvanen * 649*a9369418SSami Tolvanen * struct s { 650*a9369418SSami Tolvanen * u32 a; 651*a9369418SSami Tolvanen * union { 652*a9369418SSami Tolvanen * // placeholder replaced with a new member `b` 653*a9369418SSami Tolvanen * struct t b; 654*a9369418SSami Tolvanen * struct { 655*a9369418SSami Tolvanen * // the placeholder type that is still 656*a9369418SSami Tolvanen * // used for versioning 657*a9369418SSami Tolvanen * u64 __kabi_reserved_0; 658*a9369418SSami Tolvanen * }; 659*a9369418SSami Tolvanen * }; 660*a9369418SSami Tolvanen * }; 661*a9369418SSami Tolvanen * 662*a9369418SSami Tolvanen * I.e., as long as the replaced member is in a union, and the 663*a9369418SSami Tolvanen * placeholder has a __kabi_reserved name prefix, we'll continue 664*a9369418SSami Tolvanen * to use the placeholder type (here u64) for version calculation 665*a9369418SSami Tolvanen * instead of the union type. 666*a9369418SSami Tolvanen * 667*a9369418SSami Tolvanen * It's also possible to ignore new members from versioning if 668*a9369418SSami Tolvanen * they've been added to alignment holes, for example, by 669*a9369418SSami Tolvanen * including them in a union with another member that uses the 670*a9369418SSami Tolvanen * __kabi_ignored name prefix: 671*a9369418SSami Tolvanen * 672*a9369418SSami Tolvanen * struct s { 673*a9369418SSami Tolvanen * u32 a; 674*a9369418SSami Tolvanen * // an alignment hole is used to add `n` 675*a9369418SSami Tolvanen * union { 676*a9369418SSami Tolvanen * u32 n; 677*a9369418SSami Tolvanen * // hide the entire union member from versioning 678*a9369418SSami Tolvanen * u8 __kabi_ignored_0; 679*a9369418SSami Tolvanen * }; 680*a9369418SSami Tolvanen * u64 b; 681*a9369418SSami Tolvanen * }; 682*a9369418SSami Tolvanen * 683*a9369418SSami Tolvanen * Note that the user of this feature is responsible for ensuring 684*a9369418SSami Tolvanen * that the structure actually remains ABI compatible. 685*a9369418SSami Tolvanen */ 686*a9369418SSami Tolvanen memset(&state.kabi, 0, sizeof(struct kabi_state)); 687*a9369418SSami Tolvanen 688*a9369418SSami Tolvanen res = checkp(process_die_container(&state, NULL, die, 689*a9369418SSami Tolvanen check_union_member_kabi_status, 690*a9369418SSami Tolvanen match_member_type)); 691*a9369418SSami Tolvanen 692*a9369418SSami Tolvanen if (res == KABI_RESERVED) { 693*a9369418SSami Tolvanen if (placeholder) 694*a9369418SSami Tolvanen *placeholder = state.kabi.placeholder; 695*a9369418SSami Tolvanen if (orig_name) 696*a9369418SSami Tolvanen *orig_name = state.kabi.orig_name; 697*a9369418SSami Tolvanen } 698*a9369418SSami Tolvanen 699*a9369418SSami Tolvanen return res; 700*a9369418SSami Tolvanen } 701*a9369418SSami Tolvanen 702*a9369418SSami Tolvanen static bool is_kabi_ignored(Dwarf_Die *die) 703*a9369418SSami Tolvanen { 704*a9369418SSami Tolvanen Dwarf_Die type; 705*a9369418SSami Tolvanen 706*a9369418SSami Tolvanen if (!stable) 707*a9369418SSami Tolvanen return false; 708*a9369418SSami Tolvanen 709*a9369418SSami Tolvanen if (!get_ref_die_attr(die, DW_AT_type, &type)) 710*a9369418SSami Tolvanen error("member missing a type?"); 711*a9369418SSami Tolvanen 712*a9369418SSami Tolvanen return dwarf_tag(&type) == DW_TAG_union_type && 713*a9369418SSami Tolvanen checkp(get_union_kabi_status(&type, NULL, NULL)) == KABI_IGNORED; 714*a9369418SSami Tolvanen } 715*a9369418SSami 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: 721*a9369418SSami Tolvanen if (is_kabi_ignored(die)) 722*a9369418SSami Tolvanen return 0; 723*a9369418SSami 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) 780*a9369418SSami Tolvanen 781*a9369418SSami Tolvanen static void process_union_type(struct state *state, struct die *cache, 782*a9369418SSami Tolvanen Dwarf_Die *die) 783*a9369418SSami Tolvanen { 784*a9369418SSami Tolvanen Dwarf_Die placeholder; 785*a9369418SSami Tolvanen 786*a9369418SSami Tolvanen int res = checkp(get_union_kabi_status(die, &placeholder, 787*a9369418SSami Tolvanen &state->kabi.orig_name)); 788*a9369418SSami Tolvanen 789*a9369418SSami Tolvanen if (res == KABI_RESERVED) 790*a9369418SSami Tolvanen check(process_type(state, cache, &placeholder)); 791*a9369418SSami Tolvanen if (res > KABI_NORMAL) 792*a9369418SSami Tolvanen return; 793*a9369418SSami Tolvanen 794*a9369418SSami Tolvanen __process_structure_type(state, cache, die, "union_type", 795*a9369418SSami Tolvanen ___process_structure_type, match_all); 796*a9369418SSami 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 10640c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache, 10650c1c7627SSami Tolvanen Dwarf_Die *die) 1066f2856884SSami Tolvanen { 1067f2856884SSami Tolvanen int tag = dwarf_tag(die); 1068f2856884SSami Tolvanen 1069f2856884SSami Tolvanen switch (tag) { 1070f2856884SSami Tolvanen /* Possible containers of exported symbols */ 1071f2856884SSami Tolvanen case DW_TAG_namespace: 1072f2856884SSami Tolvanen case DW_TAG_class_type: 1073f2856884SSami Tolvanen case DW_TAG_structure_type: 1074f2856884SSami Tolvanen return check(process_die_container( 10750c1c7627SSami Tolvanen NULL, cache, die, process_exported_symbols, match_all)); 1076f2856884SSami Tolvanen 1077f2856884SSami Tolvanen /* Possible exported symbols */ 1078f2856884SSami Tolvanen case DW_TAG_subprogram: 1079f2856884SSami Tolvanen case DW_TAG_variable: { 1080f2856884SSami Tolvanen struct state state; 1081f2856884SSami Tolvanen 1082f2856884SSami Tolvanen if (!match_export_symbol(&state, die)) 1083f2856884SSami Tolvanen return 0; 1084f2856884SSami Tolvanen 1085f936c129SSami Tolvanen state_init(&state); 1086f936c129SSami Tolvanen 1087f2856884SSami Tolvanen if (tag == DW_TAG_subprogram) 1088f2856884SSami Tolvanen process_subprogram(&state, &state.die); 1089f2856884SSami Tolvanen else 1090f2856884SSami Tolvanen process_variable(&state, &state.die); 1091f2856884SSami Tolvanen 1092f936c129SSami Tolvanen cache_free(&state.expansion_cache); 1093f2856884SSami Tolvanen return 0; 1094f2856884SSami Tolvanen } 1095f2856884SSami Tolvanen default: 1096f2856884SSami Tolvanen return 0; 1097f2856884SSami Tolvanen } 1098f2856884SSami Tolvanen } 1099f2856884SSami Tolvanen 1100f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie) 1101f2856884SSami Tolvanen { 11020c1c7627SSami Tolvanen check(process_die_container(NULL, NULL, cudie, process_exported_symbols, 1103f2856884SSami Tolvanen match_all)); 1104f936c129SSami Tolvanen 1105f936c129SSami Tolvanen cache_free(&srcfile_cache); 1106f2856884SSami Tolvanen } 1107