1f2856884SSami Tolvanen // SPDX-License-Identifier: GPL-2.0 2f2856884SSami Tolvanen /* 3f2856884SSami Tolvanen * Copyright (C) 2024 Google LLC 4f2856884SSami Tolvanen */ 5f2856884SSami Tolvanen 65b7780e8SSami Tolvanen #include <inttypes.h> 75b7780e8SSami Tolvanen #include <stdarg.h> 8f2856884SSami Tolvanen #include "gendwarfksyms.h" 9f2856884SSami Tolvanen 105b7780e8SSami Tolvanen #define DEFINE_GET_ATTR(attr, type) \ 115b7780e8SSami Tolvanen static bool get_##attr##_attr(Dwarf_Die *die, unsigned int id, \ 125b7780e8SSami Tolvanen type *value) \ 135b7780e8SSami Tolvanen { \ 145b7780e8SSami Tolvanen Dwarf_Attribute da; \ 155b7780e8SSami Tolvanen return dwarf_attr(die, id, &da) && \ 165b7780e8SSami Tolvanen !dwarf_form##attr(&da, value); \ 175b7780e8SSami Tolvanen } 185b7780e8SSami Tolvanen 195b7780e8SSami Tolvanen DEFINE_GET_ATTR(udata, Dwarf_Word) 205b7780e8SSami Tolvanen 21f2856884SSami Tolvanen static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value) 22f2856884SSami Tolvanen { 23f2856884SSami Tolvanen Dwarf_Attribute da; 24f2856884SSami Tolvanen 25f2856884SSami Tolvanen /* dwarf_formref_die returns a pointer instead of an error value. */ 26f2856884SSami Tolvanen return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value); 27f2856884SSami Tolvanen } 28f2856884SSami Tolvanen 29f2856884SSami Tolvanen #define DEFINE_GET_STRING_ATTR(attr) \ 30f2856884SSami Tolvanen static const char *get_##attr##_attr(Dwarf_Die *die) \ 31f2856884SSami Tolvanen { \ 32f2856884SSami Tolvanen Dwarf_Attribute da; \ 33f2856884SSami Tolvanen if (dwarf_attr(die, DW_AT_##attr, &da)) \ 34f2856884SSami Tolvanen return dwarf_formstring(&da); \ 35f2856884SSami Tolvanen return NULL; \ 36f2856884SSami Tolvanen } 37f2856884SSami Tolvanen 38f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(name) 39f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(linkage_name) 40f2856884SSami Tolvanen 41f2856884SSami Tolvanen static const char *get_symbol_name(Dwarf_Die *die) 42f2856884SSami Tolvanen { 43f2856884SSami Tolvanen const char *name; 44f2856884SSami Tolvanen 45f2856884SSami Tolvanen /* rustc uses DW_AT_linkage_name for exported symbols */ 46f2856884SSami Tolvanen name = get_linkage_name_attr(die); 47f2856884SSami Tolvanen if (!name) 48f2856884SSami Tolvanen name = get_name_attr(die); 49f2856884SSami Tolvanen 50f2856884SSami Tolvanen return name; 51f2856884SSami Tolvanen } 52f2856884SSami Tolvanen 53f2856884SSami Tolvanen static bool match_export_symbol(struct state *state, Dwarf_Die *die) 54f2856884SSami Tolvanen { 55f2856884SSami Tolvanen Dwarf_Die *source = die; 56f2856884SSami Tolvanen Dwarf_Die origin; 57f2856884SSami Tolvanen 58f2856884SSami Tolvanen /* If the DIE has an abstract origin, use it for type information. */ 59f2856884SSami Tolvanen if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin)) 60f2856884SSami Tolvanen source = &origin; 61f2856884SSami Tolvanen 62f2856884SSami Tolvanen state->sym = symbol_get(get_symbol_name(die)); 63f2856884SSami Tolvanen 64f2856884SSami Tolvanen /* Look up using the origin name if there are no matches. */ 65f2856884SSami Tolvanen if (!state->sym && source != die) 66f2856884SSami Tolvanen state->sym = symbol_get(get_symbol_name(source)); 67f2856884SSami Tolvanen 68f2856884SSami Tolvanen state->die = *source; 69f2856884SSami Tolvanen return !!state->sym; 70f2856884SSami Tolvanen } 71f2856884SSami Tolvanen 72f2856884SSami Tolvanen /* 73f2856884SSami Tolvanen * Type string processing 74f2856884SSami Tolvanen */ 75*0c1c7627SSami Tolvanen static void process(struct die *cache, const char *s) 76f2856884SSami Tolvanen { 77f2856884SSami Tolvanen s = s ?: "<null>"; 78f2856884SSami Tolvanen 79f2856884SSami Tolvanen if (dump_dies) 80f2856884SSami Tolvanen fputs(s, stderr); 81*0c1c7627SSami Tolvanen 82*0c1c7627SSami Tolvanen die_map_add_string(cache, s); 83f2856884SSami Tolvanen } 84f2856884SSami Tolvanen 855b7780e8SSami Tolvanen #define MAX_FMT_BUFFER_SIZE 128 865b7780e8SSami Tolvanen 87*0c1c7627SSami Tolvanen static void process_fmt(struct die *cache, const char *fmt, ...) 885b7780e8SSami Tolvanen { 895b7780e8SSami Tolvanen char buf[MAX_FMT_BUFFER_SIZE]; 905b7780e8SSami Tolvanen va_list args; 915b7780e8SSami Tolvanen 925b7780e8SSami Tolvanen va_start(args, fmt); 935b7780e8SSami Tolvanen 945b7780e8SSami Tolvanen if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf)) 955b7780e8SSami Tolvanen error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE"); 965b7780e8SSami Tolvanen 97*0c1c7627SSami Tolvanen process(cache, buf); 985b7780e8SSami Tolvanen va_end(args); 995b7780e8SSami Tolvanen } 1005b7780e8SSami Tolvanen 1015b7780e8SSami Tolvanen #define MAX_FQN_SIZE 64 1025b7780e8SSami Tolvanen 1035b7780e8SSami Tolvanen /* Get a fully qualified name from DWARF scopes */ 1045b7780e8SSami Tolvanen static char *get_fqn(Dwarf_Die *die) 1055b7780e8SSami Tolvanen { 1065b7780e8SSami Tolvanen const char *list[MAX_FQN_SIZE]; 1075b7780e8SSami Tolvanen Dwarf_Die *scopes = NULL; 1085b7780e8SSami Tolvanen bool has_name = false; 1095b7780e8SSami Tolvanen char *fqn = NULL; 1105b7780e8SSami Tolvanen char *p; 1115b7780e8SSami Tolvanen int count = 0; 1125b7780e8SSami Tolvanen int len = 0; 1135b7780e8SSami Tolvanen int res; 1145b7780e8SSami Tolvanen int i; 1155b7780e8SSami Tolvanen 1165b7780e8SSami Tolvanen res = checkp(dwarf_getscopes_die(die, &scopes)); 1175b7780e8SSami Tolvanen if (!res) { 1185b7780e8SSami Tolvanen list[count] = get_name_attr(die); 1195b7780e8SSami Tolvanen 1205b7780e8SSami Tolvanen if (!list[count]) 1215b7780e8SSami Tolvanen return NULL; 1225b7780e8SSami Tolvanen 1235b7780e8SSami Tolvanen len += strlen(list[count]); 1245b7780e8SSami Tolvanen count++; 1255b7780e8SSami Tolvanen 1265b7780e8SSami Tolvanen goto done; 1275b7780e8SSami Tolvanen } 1285b7780e8SSami Tolvanen 1295b7780e8SSami Tolvanen for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) { 1305b7780e8SSami Tolvanen if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit) 1315b7780e8SSami Tolvanen continue; 1325b7780e8SSami Tolvanen 1335b7780e8SSami Tolvanen list[count] = get_name_attr(&scopes[i]); 1345b7780e8SSami Tolvanen 1355b7780e8SSami Tolvanen if (list[count]) { 1365b7780e8SSami Tolvanen has_name = true; 1375b7780e8SSami Tolvanen } else { 1385b7780e8SSami Tolvanen list[count] = "<anonymous>"; 1395b7780e8SSami Tolvanen has_name = false; 1405b7780e8SSami Tolvanen } 1415b7780e8SSami Tolvanen 1425b7780e8SSami Tolvanen len += strlen(list[count]); 1435b7780e8SSami Tolvanen count++; 1445b7780e8SSami Tolvanen 1455b7780e8SSami Tolvanen if (i > 0) { 1465b7780e8SSami Tolvanen list[count++] = "::"; 1475b7780e8SSami Tolvanen len += 2; 1485b7780e8SSami Tolvanen } 1495b7780e8SSami Tolvanen } 1505b7780e8SSami Tolvanen 1515b7780e8SSami Tolvanen free(scopes); 1525b7780e8SSami Tolvanen 1535b7780e8SSami Tolvanen if (count == MAX_FQN_SIZE) 1545b7780e8SSami Tolvanen warn("increase MAX_FQN_SIZE: reached the maximum"); 1555b7780e8SSami Tolvanen 1565b7780e8SSami Tolvanen /* Consider the DIE unnamed if the last scope doesn't have a name */ 1575b7780e8SSami Tolvanen if (!has_name) 1585b7780e8SSami Tolvanen return NULL; 1595b7780e8SSami Tolvanen done: 1605b7780e8SSami Tolvanen fqn = xmalloc(len + 1); 1615b7780e8SSami Tolvanen *fqn = '\0'; 1625b7780e8SSami Tolvanen 1635b7780e8SSami Tolvanen p = fqn; 1645b7780e8SSami Tolvanen for (i = 0; i < count; i++) 1655b7780e8SSami Tolvanen p = stpcpy(p, list[i]); 1665b7780e8SSami Tolvanen 1675b7780e8SSami Tolvanen return fqn; 1685b7780e8SSami Tolvanen } 1695b7780e8SSami Tolvanen 170*0c1c7627SSami Tolvanen static void update_fqn(struct die *cache, Dwarf_Die *die) 1715b7780e8SSami Tolvanen { 172*0c1c7627SSami Tolvanen if (!cache->fqn) 173*0c1c7627SSami Tolvanen cache->fqn = get_fqn(die) ?: ""; 174*0c1c7627SSami Tolvanen } 175*0c1c7627SSami Tolvanen 176*0c1c7627SSami Tolvanen static void process_fqn(struct die *cache, Dwarf_Die *die) 177*0c1c7627SSami Tolvanen { 178*0c1c7627SSami Tolvanen update_fqn(cache, die); 179*0c1c7627SSami Tolvanen if (*cache->fqn) 180*0c1c7627SSami Tolvanen process(cache, " "); 181*0c1c7627SSami Tolvanen process(cache, cache->fqn); 1825b7780e8SSami Tolvanen } 1835b7780e8SSami Tolvanen 1845b7780e8SSami Tolvanen #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute) \ 185*0c1c7627SSami Tolvanen static void process_##attribute##_attr(struct die *cache, \ 186*0c1c7627SSami Tolvanen Dwarf_Die *die) \ 1875b7780e8SSami Tolvanen { \ 1885b7780e8SSami Tolvanen Dwarf_Word value; \ 1895b7780e8SSami Tolvanen if (get_udata_attr(die, DW_AT_##attribute, &value)) \ 190*0c1c7627SSami Tolvanen process_fmt(cache, " " #attribute "(%" PRIu64 ")", \ 191*0c1c7627SSami Tolvanen value); \ 1925b7780e8SSami Tolvanen } 1935b7780e8SSami Tolvanen 1945b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment) 1955b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size) 1965b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding) 1975b7780e8SSami Tolvanen 198f2856884SSami Tolvanen bool match_all(Dwarf_Die *die) 199f2856884SSami Tolvanen { 200f2856884SSami Tolvanen return true; 201f2856884SSami Tolvanen } 202f2856884SSami Tolvanen 203*0c1c7627SSami Tolvanen int process_die_container(struct state *state, struct die *cache, 204*0c1c7627SSami Tolvanen Dwarf_Die *die, die_callback_t func, 205*0c1c7627SSami Tolvanen die_match_callback_t match) 206f2856884SSami Tolvanen { 207f2856884SSami Tolvanen Dwarf_Die current; 208f2856884SSami Tolvanen int res; 209f2856884SSami Tolvanen 210f2856884SSami Tolvanen res = checkp(dwarf_child(die, ¤t)); 211f2856884SSami Tolvanen while (!res) { 212f2856884SSami Tolvanen if (match(¤t)) { 213f2856884SSami Tolvanen /* <0 = error, 0 = continue, >0 = stop */ 214*0c1c7627SSami Tolvanen res = checkp(func(state, cache, ¤t)); 215f2856884SSami Tolvanen if (res) 216f2856884SSami Tolvanen return res; 217f2856884SSami Tolvanen } 218f2856884SSami Tolvanen 219f2856884SSami Tolvanen res = checkp(dwarf_siblingof(¤t, ¤t)); 220f2856884SSami Tolvanen } 221f2856884SSami Tolvanen 222f2856884SSami Tolvanen return 0; 223f2856884SSami Tolvanen } 224f2856884SSami Tolvanen 225*0c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, 226*0c1c7627SSami Tolvanen Dwarf_Die *die); 2275b7780e8SSami Tolvanen 228*0c1c7627SSami Tolvanen static void process_type_attr(struct state *state, struct die *cache, 229*0c1c7627SSami Tolvanen Dwarf_Die *die) 2305b7780e8SSami Tolvanen { 2315b7780e8SSami Tolvanen Dwarf_Die type; 2325b7780e8SSami Tolvanen 2335b7780e8SSami Tolvanen if (get_ref_die_attr(die, DW_AT_type, &type)) { 234*0c1c7627SSami Tolvanen check(process_type(state, cache, &type)); 2355b7780e8SSami Tolvanen return; 2365b7780e8SSami Tolvanen } 2375b7780e8SSami Tolvanen 2385b7780e8SSami Tolvanen /* Compilers can omit DW_AT_type -- print out 'void' to clarify */ 239*0c1c7627SSami Tolvanen process(cache, "base_type void"); 2405b7780e8SSami Tolvanen } 2415b7780e8SSami Tolvanen 242*0c1c7627SSami Tolvanen static void process_base_type(struct state *state, struct die *cache, 243*0c1c7627SSami Tolvanen Dwarf_Die *die) 2445b7780e8SSami Tolvanen { 245*0c1c7627SSami Tolvanen process(cache, "base_type"); 246*0c1c7627SSami Tolvanen process_fqn(cache, die); 247*0c1c7627SSami Tolvanen process_byte_size_attr(cache, die); 248*0c1c7627SSami Tolvanen process_encoding_attr(cache, die); 249*0c1c7627SSami Tolvanen process_alignment_attr(cache, die); 250*0c1c7627SSami Tolvanen } 251*0c1c7627SSami Tolvanen 252*0c1c7627SSami Tolvanen static void process_cached(struct state *state, struct die *cache, 253*0c1c7627SSami Tolvanen Dwarf_Die *die) 254*0c1c7627SSami Tolvanen { 255*0c1c7627SSami Tolvanen struct die_fragment *df; 256*0c1c7627SSami Tolvanen Dwarf_Die child; 257*0c1c7627SSami Tolvanen 258*0c1c7627SSami Tolvanen list_for_each_entry(df, &cache->fragments, list) { 259*0c1c7627SSami Tolvanen switch (df->type) { 260*0c1c7627SSami Tolvanen case FRAGMENT_STRING: 261*0c1c7627SSami Tolvanen process(NULL, df->data.str); 262*0c1c7627SSami Tolvanen break; 263*0c1c7627SSami Tolvanen case FRAGMENT_DIE: 264*0c1c7627SSami Tolvanen if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu), 265*0c1c7627SSami Tolvanen (void *)df->data.addr, &child)) 266*0c1c7627SSami Tolvanen error("dwarf_die_addr_die failed"); 267*0c1c7627SSami Tolvanen check(process_type(state, NULL, &child)); 268*0c1c7627SSami Tolvanen break; 269*0c1c7627SSami Tolvanen default: 270*0c1c7627SSami Tolvanen error("empty die_fragment"); 271*0c1c7627SSami Tolvanen } 272*0c1c7627SSami Tolvanen } 2735b7780e8SSami Tolvanen } 2745b7780e8SSami Tolvanen 2755b7780e8SSami Tolvanen #define PROCESS_TYPE(type) \ 2765b7780e8SSami Tolvanen case DW_TAG_##type##_type: \ 277*0c1c7627SSami Tolvanen process_##type##_type(state, cache, die); \ 2785b7780e8SSami Tolvanen break; 2795b7780e8SSami Tolvanen 280*0c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, Dwarf_Die *die) 2815b7780e8SSami Tolvanen { 282*0c1c7627SSami Tolvanen struct die *cache; 2835b7780e8SSami Tolvanen int tag = dwarf_tag(die); 2845b7780e8SSami Tolvanen 285*0c1c7627SSami Tolvanen /* 286*0c1c7627SSami Tolvanen * If we have the DIE already cached, use it instead of walking 287*0c1c7627SSami Tolvanen * through DWARF. 288*0c1c7627SSami Tolvanen */ 289*0c1c7627SSami Tolvanen cache = die_map_get(die, DIE_COMPLETE); 290*0c1c7627SSami Tolvanen 291*0c1c7627SSami Tolvanen if (cache->state == DIE_COMPLETE) { 292*0c1c7627SSami Tolvanen process_cached(state, cache, die); 293*0c1c7627SSami Tolvanen die_map_add_die(parent, cache); 294*0c1c7627SSami Tolvanen return 0; 295*0c1c7627SSami Tolvanen } 296*0c1c7627SSami Tolvanen 2975b7780e8SSami Tolvanen switch (tag) { 2985b7780e8SSami Tolvanen PROCESS_TYPE(base) 2995b7780e8SSami Tolvanen default: 3005b7780e8SSami Tolvanen debug("unimplemented type: %x", tag); 3015b7780e8SSami Tolvanen break; 3025b7780e8SSami Tolvanen } 3035b7780e8SSami Tolvanen 304*0c1c7627SSami Tolvanen /* Update cache state and append to the parent (if any) */ 305*0c1c7627SSami Tolvanen cache->tag = tag; 306*0c1c7627SSami Tolvanen cache->state = DIE_COMPLETE; 307*0c1c7627SSami Tolvanen die_map_add_die(parent, cache); 308*0c1c7627SSami Tolvanen 3095b7780e8SSami Tolvanen return 0; 3105b7780e8SSami Tolvanen } 3115b7780e8SSami Tolvanen 312f2856884SSami Tolvanen /* 313f2856884SSami Tolvanen * Exported symbol processing 314f2856884SSami Tolvanen */ 315f2856884SSami Tolvanen static void process_symbol(struct state *state, Dwarf_Die *die, 316f2856884SSami Tolvanen die_callback_t process_func) 317f2856884SSami Tolvanen { 318f2856884SSami Tolvanen debug("%s", state->sym->name); 319*0c1c7627SSami Tolvanen check(process_func(state, NULL, die)); 320f2856884SSami Tolvanen if (dump_dies) 321f2856884SSami Tolvanen fputs("\n", stderr); 322f2856884SSami Tolvanen } 323f2856884SSami Tolvanen 324*0c1c7627SSami Tolvanen static int __process_subprogram(struct state *state, struct die *cache, 325*0c1c7627SSami Tolvanen Dwarf_Die *die) 326f2856884SSami Tolvanen { 327*0c1c7627SSami Tolvanen process(cache, "subprogram"); 328f2856884SSami Tolvanen return 0; 329f2856884SSami Tolvanen } 330f2856884SSami Tolvanen 331f2856884SSami Tolvanen static void process_subprogram(struct state *state, Dwarf_Die *die) 332f2856884SSami Tolvanen { 333f2856884SSami Tolvanen process_symbol(state, die, __process_subprogram); 334f2856884SSami Tolvanen } 335f2856884SSami Tolvanen 336*0c1c7627SSami Tolvanen static int __process_variable(struct state *state, struct die *cache, 337*0c1c7627SSami Tolvanen Dwarf_Die *die) 338f2856884SSami Tolvanen { 339*0c1c7627SSami Tolvanen process(cache, "variable "); 340*0c1c7627SSami Tolvanen process_type_attr(state, cache, die); 341f2856884SSami Tolvanen return 0; 342f2856884SSami Tolvanen } 343f2856884SSami Tolvanen 344f2856884SSami Tolvanen static void process_variable(struct state *state, Dwarf_Die *die) 345f2856884SSami Tolvanen { 346f2856884SSami Tolvanen process_symbol(state, die, __process_variable); 347f2856884SSami Tolvanen } 348f2856884SSami Tolvanen 349*0c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache, 350*0c1c7627SSami Tolvanen Dwarf_Die *die) 351f2856884SSami Tolvanen { 352f2856884SSami Tolvanen int tag = dwarf_tag(die); 353f2856884SSami Tolvanen 354f2856884SSami Tolvanen switch (tag) { 355f2856884SSami Tolvanen /* Possible containers of exported symbols */ 356f2856884SSami Tolvanen case DW_TAG_namespace: 357f2856884SSami Tolvanen case DW_TAG_class_type: 358f2856884SSami Tolvanen case DW_TAG_structure_type: 359f2856884SSami Tolvanen return check(process_die_container( 360*0c1c7627SSami Tolvanen NULL, cache, die, process_exported_symbols, match_all)); 361f2856884SSami Tolvanen 362f2856884SSami Tolvanen /* Possible exported symbols */ 363f2856884SSami Tolvanen case DW_TAG_subprogram: 364f2856884SSami Tolvanen case DW_TAG_variable: { 365f2856884SSami Tolvanen struct state state; 366f2856884SSami Tolvanen 367f2856884SSami Tolvanen if (!match_export_symbol(&state, die)) 368f2856884SSami Tolvanen return 0; 369f2856884SSami Tolvanen 370f2856884SSami Tolvanen if (tag == DW_TAG_subprogram) 371f2856884SSami Tolvanen process_subprogram(&state, &state.die); 372f2856884SSami Tolvanen else 373f2856884SSami Tolvanen process_variable(&state, &state.die); 374f2856884SSami Tolvanen 375f2856884SSami Tolvanen return 0; 376f2856884SSami Tolvanen } 377f2856884SSami Tolvanen default: 378f2856884SSami Tolvanen return 0; 379f2856884SSami Tolvanen } 380f2856884SSami Tolvanen } 381f2856884SSami Tolvanen 382f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie) 383f2856884SSami Tolvanen { 384*0c1c7627SSami Tolvanen check(process_die_container(NULL, NULL, cudie, process_exported_symbols, 385f2856884SSami Tolvanen match_all)); 386f2856884SSami Tolvanen } 387