xref: /linux/scripts/gendwarfksyms/dwarf.c (revision 220a0857f3a89e0dce3fc7c38d981df41c4537a7)
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 
1006b8b036SSami Tolvanen static bool do_linebreak;
1106b8b036SSami Tolvanen static int indentation_level;
1206b8b036SSami Tolvanen 
1306b8b036SSami Tolvanen /* Line breaks and indentation for pretty-printing */
1406b8b036SSami Tolvanen static void process_linebreak(struct die *cache, int n)
1506b8b036SSami Tolvanen {
1606b8b036SSami Tolvanen 	indentation_level += n;
1706b8b036SSami Tolvanen 	do_linebreak = true;
1806b8b036SSami Tolvanen 	die_map_add_linebreak(cache, n);
1906b8b036SSami Tolvanen }
2006b8b036SSami Tolvanen 
215b7780e8SSami Tolvanen #define DEFINE_GET_ATTR(attr, type)                                    \
225b7780e8SSami Tolvanen 	static bool get_##attr##_attr(Dwarf_Die *die, unsigned int id, \
235b7780e8SSami Tolvanen 				      type *value)                     \
245b7780e8SSami Tolvanen 	{                                                              \
255b7780e8SSami Tolvanen 		Dwarf_Attribute da;                                    \
265b7780e8SSami Tolvanen 		return dwarf_attr(die, id, &da) &&                     \
275b7780e8SSami Tolvanen 		       !dwarf_form##attr(&da, value);                  \
285b7780e8SSami Tolvanen 	}
295b7780e8SSami Tolvanen 
305b7780e8SSami Tolvanen DEFINE_GET_ATTR(udata, Dwarf_Word)
315b7780e8SSami Tolvanen 
32f2856884SSami Tolvanen static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value)
33f2856884SSami Tolvanen {
34f2856884SSami Tolvanen 	Dwarf_Attribute da;
35f2856884SSami Tolvanen 
36f2856884SSami Tolvanen 	/* dwarf_formref_die returns a pointer instead of an error value. */
37f2856884SSami Tolvanen 	return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value);
38f2856884SSami Tolvanen }
39f2856884SSami Tolvanen 
40f2856884SSami Tolvanen #define DEFINE_GET_STRING_ATTR(attr)                         \
41f2856884SSami Tolvanen 	static const char *get_##attr##_attr(Dwarf_Die *die) \
42f2856884SSami Tolvanen 	{                                                    \
43f2856884SSami Tolvanen 		Dwarf_Attribute da;                          \
44f2856884SSami Tolvanen 		if (dwarf_attr(die, DW_AT_##attr, &da))      \
45f2856884SSami Tolvanen 			return dwarf_formstring(&da);        \
46f2856884SSami Tolvanen 		return NULL;                                 \
47f2856884SSami Tolvanen 	}
48f2856884SSami Tolvanen 
49f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(name)
50f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(linkage_name)
51f2856884SSami Tolvanen 
52f2856884SSami Tolvanen static const char *get_symbol_name(Dwarf_Die *die)
53f2856884SSami Tolvanen {
54f2856884SSami Tolvanen 	const char *name;
55f2856884SSami Tolvanen 
56f2856884SSami Tolvanen 	/* rustc uses DW_AT_linkage_name for exported symbols */
57f2856884SSami Tolvanen 	name = get_linkage_name_attr(die);
58f2856884SSami Tolvanen 	if (!name)
59f2856884SSami Tolvanen 		name = get_name_attr(die);
60f2856884SSami Tolvanen 
61f2856884SSami Tolvanen 	return name;
62f2856884SSami Tolvanen }
63f2856884SSami Tolvanen 
64f2856884SSami Tolvanen static bool match_export_symbol(struct state *state, Dwarf_Die *die)
65f2856884SSami Tolvanen {
66f2856884SSami Tolvanen 	Dwarf_Die *source = die;
67f2856884SSami Tolvanen 	Dwarf_Die origin;
68f2856884SSami Tolvanen 
69f2856884SSami Tolvanen 	/* If the DIE has an abstract origin, use it for type information. */
70f2856884SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin))
71f2856884SSami Tolvanen 		source = &origin;
72f2856884SSami Tolvanen 
73f2856884SSami Tolvanen 	state->sym = symbol_get(get_symbol_name(die));
74f2856884SSami Tolvanen 
75f2856884SSami Tolvanen 	/* Look up using the origin name if there are no matches. */
76f2856884SSami Tolvanen 	if (!state->sym && source != die)
77f2856884SSami Tolvanen 		state->sym = symbol_get(get_symbol_name(source));
78f2856884SSami Tolvanen 
79f2856884SSami Tolvanen 	state->die = *source;
80f2856884SSami Tolvanen 	return !!state->sym;
81f2856884SSami Tolvanen }
82f2856884SSami Tolvanen 
83f2856884SSami Tolvanen /*
84f2856884SSami Tolvanen  * Type string processing
85f2856884SSami Tolvanen  */
860c1c7627SSami Tolvanen static void process(struct die *cache, const char *s)
87f2856884SSami Tolvanen {
88f2856884SSami Tolvanen 	s = s ?: "<null>";
89f2856884SSami Tolvanen 
9006b8b036SSami Tolvanen 	if (dump_dies && do_linebreak) {
9106b8b036SSami Tolvanen 		fputs("\n", stderr);
9206b8b036SSami Tolvanen 		for (int i = 0; i < indentation_level; i++)
9306b8b036SSami Tolvanen 			fputs("  ", stderr);
9406b8b036SSami Tolvanen 		do_linebreak = false;
9506b8b036SSami Tolvanen 	}
96f2856884SSami Tolvanen 	if (dump_dies)
97f2856884SSami Tolvanen 		fputs(s, stderr);
980c1c7627SSami Tolvanen 
990c1c7627SSami Tolvanen 	die_map_add_string(cache, s);
100f2856884SSami Tolvanen }
101f2856884SSami Tolvanen 
1025b7780e8SSami Tolvanen #define MAX_FMT_BUFFER_SIZE 128
1035b7780e8SSami Tolvanen 
1040c1c7627SSami Tolvanen static void process_fmt(struct die *cache, const char *fmt, ...)
1055b7780e8SSami Tolvanen {
1065b7780e8SSami Tolvanen 	char buf[MAX_FMT_BUFFER_SIZE];
1075b7780e8SSami Tolvanen 	va_list args;
1085b7780e8SSami Tolvanen 
1095b7780e8SSami Tolvanen 	va_start(args, fmt);
1105b7780e8SSami Tolvanen 
1115b7780e8SSami Tolvanen 	if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf))
1125b7780e8SSami Tolvanen 		error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE");
1135b7780e8SSami Tolvanen 
1140c1c7627SSami Tolvanen 	process(cache, buf);
1155b7780e8SSami Tolvanen 	va_end(args);
1165b7780e8SSami Tolvanen }
1175b7780e8SSami Tolvanen 
1185b7780e8SSami Tolvanen #define MAX_FQN_SIZE 64
1195b7780e8SSami Tolvanen 
1205b7780e8SSami Tolvanen /* Get a fully qualified name from DWARF scopes */
1215b7780e8SSami Tolvanen static char *get_fqn(Dwarf_Die *die)
1225b7780e8SSami Tolvanen {
1235b7780e8SSami Tolvanen 	const char *list[MAX_FQN_SIZE];
1245b7780e8SSami Tolvanen 	Dwarf_Die *scopes = NULL;
1255b7780e8SSami Tolvanen 	bool has_name = false;
1265b7780e8SSami Tolvanen 	char *fqn = NULL;
1275b7780e8SSami Tolvanen 	char *p;
1285b7780e8SSami Tolvanen 	int count = 0;
1295b7780e8SSami Tolvanen 	int len = 0;
1305b7780e8SSami Tolvanen 	int res;
1315b7780e8SSami Tolvanen 	int i;
1325b7780e8SSami Tolvanen 
1335b7780e8SSami Tolvanen 	res = checkp(dwarf_getscopes_die(die, &scopes));
1345b7780e8SSami Tolvanen 	if (!res) {
1355b7780e8SSami Tolvanen 		list[count] = get_name_attr(die);
1365b7780e8SSami Tolvanen 
1375b7780e8SSami Tolvanen 		if (!list[count])
1385b7780e8SSami Tolvanen 			return NULL;
1395b7780e8SSami Tolvanen 
1405b7780e8SSami Tolvanen 		len += strlen(list[count]);
1415b7780e8SSami Tolvanen 		count++;
1425b7780e8SSami Tolvanen 
1435b7780e8SSami Tolvanen 		goto done;
1445b7780e8SSami Tolvanen 	}
1455b7780e8SSami Tolvanen 
1465b7780e8SSami Tolvanen 	for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) {
1475b7780e8SSami Tolvanen 		if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit)
1485b7780e8SSami Tolvanen 			continue;
1495b7780e8SSami Tolvanen 
1505b7780e8SSami Tolvanen 		list[count] = get_name_attr(&scopes[i]);
1515b7780e8SSami Tolvanen 
1525b7780e8SSami Tolvanen 		if (list[count]) {
1535b7780e8SSami Tolvanen 			has_name = true;
1545b7780e8SSami Tolvanen 		} else {
1555b7780e8SSami Tolvanen 			list[count] = "<anonymous>";
1565b7780e8SSami Tolvanen 			has_name = false;
1575b7780e8SSami Tolvanen 		}
1585b7780e8SSami Tolvanen 
1595b7780e8SSami Tolvanen 		len += strlen(list[count]);
1605b7780e8SSami Tolvanen 		count++;
1615b7780e8SSami Tolvanen 
1625b7780e8SSami Tolvanen 		if (i > 0) {
1635b7780e8SSami Tolvanen 			list[count++] = "::";
1645b7780e8SSami Tolvanen 			len += 2;
1655b7780e8SSami Tolvanen 		}
1665b7780e8SSami Tolvanen 	}
1675b7780e8SSami Tolvanen 
1685b7780e8SSami Tolvanen 	free(scopes);
1695b7780e8SSami Tolvanen 
1705b7780e8SSami Tolvanen 	if (count == MAX_FQN_SIZE)
1715b7780e8SSami Tolvanen 		warn("increase MAX_FQN_SIZE: reached the maximum");
1725b7780e8SSami Tolvanen 
1735b7780e8SSami Tolvanen 	/* Consider the DIE unnamed if the last scope doesn't have a name */
1745b7780e8SSami Tolvanen 	if (!has_name)
1755b7780e8SSami Tolvanen 		return NULL;
1765b7780e8SSami Tolvanen done:
1775b7780e8SSami Tolvanen 	fqn = xmalloc(len + 1);
1785b7780e8SSami Tolvanen 	*fqn = '\0';
1795b7780e8SSami Tolvanen 
1805b7780e8SSami Tolvanen 	p = fqn;
1815b7780e8SSami Tolvanen 	for (i = 0; i < count; i++)
1825b7780e8SSami Tolvanen 		p = stpcpy(p, list[i]);
1835b7780e8SSami Tolvanen 
1845b7780e8SSami Tolvanen 	return fqn;
1855b7780e8SSami Tolvanen }
1865b7780e8SSami Tolvanen 
1870c1c7627SSami Tolvanen static void update_fqn(struct die *cache, Dwarf_Die *die)
1885b7780e8SSami Tolvanen {
1890c1c7627SSami Tolvanen 	if (!cache->fqn)
1900c1c7627SSami Tolvanen 		cache->fqn = get_fqn(die) ?: "";
1910c1c7627SSami Tolvanen }
1920c1c7627SSami Tolvanen 
1930c1c7627SSami Tolvanen static void process_fqn(struct die *cache, Dwarf_Die *die)
1940c1c7627SSami Tolvanen {
1950c1c7627SSami Tolvanen 	update_fqn(cache, die);
1960c1c7627SSami Tolvanen 	if (*cache->fqn)
1970c1c7627SSami Tolvanen 		process(cache, " ");
1980c1c7627SSami Tolvanen 	process(cache, cache->fqn);
1995b7780e8SSami Tolvanen }
2005b7780e8SSami Tolvanen 
2015b7780e8SSami Tolvanen #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute)                          \
2020c1c7627SSami Tolvanen 	static void process_##attribute##_attr(struct die *cache,          \
2030c1c7627SSami Tolvanen 					       Dwarf_Die *die)             \
2045b7780e8SSami Tolvanen 	{                                                                  \
2055b7780e8SSami Tolvanen 		Dwarf_Word value;                                          \
2065b7780e8SSami Tolvanen 		if (get_udata_attr(die, DW_AT_##attribute, &value))        \
2070c1c7627SSami Tolvanen 			process_fmt(cache, " " #attribute "(%" PRIu64 ")", \
2080c1c7627SSami Tolvanen 				    value);                                \
2095b7780e8SSami Tolvanen 	}
2105b7780e8SSami Tolvanen 
2115b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
2125b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
2135b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
2145b7780e8SSami Tolvanen 
215*220a0857SSami Tolvanen /* Match functions -- die_match_callback_t */
216*220a0857SSami Tolvanen #define DEFINE_MATCH(type)                                     \
217*220a0857SSami Tolvanen 	static bool match_##type##_type(Dwarf_Die *die)        \
218*220a0857SSami Tolvanen 	{                                                      \
219*220a0857SSami Tolvanen 		return dwarf_tag(die) == DW_TAG_##type##_type; \
220*220a0857SSami Tolvanen 	}
221*220a0857SSami Tolvanen 
222*220a0857SSami Tolvanen DEFINE_MATCH(formal_parameter)
223*220a0857SSami Tolvanen 
224f2856884SSami Tolvanen bool match_all(Dwarf_Die *die)
225f2856884SSami Tolvanen {
226f2856884SSami Tolvanen 	return true;
227f2856884SSami Tolvanen }
228f2856884SSami Tolvanen 
2290c1c7627SSami Tolvanen int process_die_container(struct state *state, struct die *cache,
2300c1c7627SSami Tolvanen 			  Dwarf_Die *die, die_callback_t func,
2310c1c7627SSami Tolvanen 			  die_match_callback_t match)
232f2856884SSami Tolvanen {
233f2856884SSami Tolvanen 	Dwarf_Die current;
234f2856884SSami Tolvanen 	int res;
235f2856884SSami Tolvanen 
236*220a0857SSami Tolvanen 	/* Track the first item in lists. */
237*220a0857SSami Tolvanen 	if (state)
238*220a0857SSami Tolvanen 		state->first_list_item = true;
239*220a0857SSami Tolvanen 
240f2856884SSami Tolvanen 	res = checkp(dwarf_child(die, &current));
241f2856884SSami Tolvanen 	while (!res) {
242f2856884SSami Tolvanen 		if (match(&current)) {
243f2856884SSami Tolvanen 			/* <0 = error, 0 = continue, >0 = stop */
2440c1c7627SSami Tolvanen 			res = checkp(func(state, cache, &current));
245f2856884SSami Tolvanen 			if (res)
246*220a0857SSami Tolvanen 				goto out;
247f2856884SSami Tolvanen 		}
248f2856884SSami Tolvanen 
249f2856884SSami Tolvanen 		res = checkp(dwarf_siblingof(&current, &current));
250f2856884SSami Tolvanen 	}
251f2856884SSami Tolvanen 
252*220a0857SSami Tolvanen 	res = 0;
253*220a0857SSami Tolvanen out:
254*220a0857SSami Tolvanen 	if (state)
255*220a0857SSami Tolvanen 		state->first_list_item = false;
256*220a0857SSami Tolvanen 
257*220a0857SSami Tolvanen 	return res;
258f2856884SSami Tolvanen }
259f2856884SSami Tolvanen 
2600c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent,
2610c1c7627SSami Tolvanen 			Dwarf_Die *die);
2625b7780e8SSami Tolvanen 
2630c1c7627SSami Tolvanen static void process_type_attr(struct state *state, struct die *cache,
2640c1c7627SSami Tolvanen 			      Dwarf_Die *die)
2655b7780e8SSami Tolvanen {
2665b7780e8SSami Tolvanen 	Dwarf_Die type;
2675b7780e8SSami Tolvanen 
2685b7780e8SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_type, &type)) {
2690c1c7627SSami Tolvanen 		check(process_type(state, cache, &type));
2705b7780e8SSami Tolvanen 		return;
2715b7780e8SSami Tolvanen 	}
2725b7780e8SSami Tolvanen 
2735b7780e8SSami Tolvanen 	/* Compilers can omit DW_AT_type -- print out 'void' to clarify */
2740c1c7627SSami Tolvanen 	process(cache, "base_type void");
2755b7780e8SSami Tolvanen }
2765b7780e8SSami Tolvanen 
277*220a0857SSami Tolvanen static void process_list_comma(struct state *state, struct die *cache)
278*220a0857SSami Tolvanen {
279*220a0857SSami Tolvanen 	if (state->first_list_item) {
280*220a0857SSami Tolvanen 		state->first_list_item = false;
281*220a0857SSami Tolvanen 	} else {
282*220a0857SSami Tolvanen 		process(cache, " ,");
283*220a0857SSami Tolvanen 		process_linebreak(cache, 0);
284*220a0857SSami Tolvanen 	}
285*220a0857SSami Tolvanen }
286*220a0857SSami Tolvanen 
287*220a0857SSami Tolvanen /* Comma-separated with DW_AT_type */
288*220a0857SSami Tolvanen static void __process_list_type(struct state *state, struct die *cache,
289*220a0857SSami Tolvanen 				Dwarf_Die *die, const char *type)
290*220a0857SSami Tolvanen {
291*220a0857SSami Tolvanen 	const char *name = get_name_attr(die);
292*220a0857SSami Tolvanen 
293*220a0857SSami Tolvanen 	process_list_comma(state, cache);
294*220a0857SSami Tolvanen 	process(cache, type);
295*220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
296*220a0857SSami Tolvanen 	if (name) {
297*220a0857SSami Tolvanen 		process(cache, " ");
298*220a0857SSami Tolvanen 		process(cache, name);
299*220a0857SSami Tolvanen 	}
300*220a0857SSami Tolvanen }
301*220a0857SSami Tolvanen 
302*220a0857SSami Tolvanen #define DEFINE_PROCESS_LIST_TYPE(type)                                       \
303*220a0857SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
304*220a0857SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
305*220a0857SSami Tolvanen 	{                                                                    \
306*220a0857SSami Tolvanen 		__process_list_type(state, cache, die, #type " ");           \
307*220a0857SSami Tolvanen 	}
308*220a0857SSami Tolvanen 
309*220a0857SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(formal_parameter)
310*220a0857SSami Tolvanen 
31106b8b036SSami Tolvanen /* Container types with DW_AT_type */
31206b8b036SSami Tolvanen static void __process_type(struct state *state, struct die *cache,
31306b8b036SSami Tolvanen 			   Dwarf_Die *die, const char *type)
31406b8b036SSami Tolvanen {
31506b8b036SSami Tolvanen 	process(cache, type);
31606b8b036SSami Tolvanen 	process_fqn(cache, die);
31706b8b036SSami Tolvanen 	process(cache, " {");
31806b8b036SSami Tolvanen 	process_linebreak(cache, 1);
31906b8b036SSami Tolvanen 	process_type_attr(state, cache, die);
32006b8b036SSami Tolvanen 	process_linebreak(cache, -1);
32106b8b036SSami Tolvanen 	process(cache, "}");
32206b8b036SSami Tolvanen 	process_byte_size_attr(cache, die);
32306b8b036SSami Tolvanen 	process_alignment_attr(cache, die);
32406b8b036SSami Tolvanen }
32506b8b036SSami Tolvanen 
32606b8b036SSami Tolvanen #define DEFINE_PROCESS_TYPE(type)                                            \
32706b8b036SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
32806b8b036SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
32906b8b036SSami Tolvanen 	{                                                                    \
33006b8b036SSami Tolvanen 		__process_type(state, cache, die, #type "_type");            \
33106b8b036SSami Tolvanen 	}
33206b8b036SSami Tolvanen 
33306b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(atomic)
33406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(const)
33506b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(immutable)
33606b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(packed)
33706b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(pointer)
33806b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(reference)
33906b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(restrict)
34006b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(rvalue_reference)
34106b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(shared)
34206b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(volatile)
34306b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(typedef)
34406b8b036SSami Tolvanen 
345*220a0857SSami Tolvanen static void __process_subroutine_type(struct state *state, struct die *cache,
346*220a0857SSami Tolvanen 				      Dwarf_Die *die, const char *type)
347*220a0857SSami Tolvanen {
348*220a0857SSami Tolvanen 	process(cache, type);
349*220a0857SSami Tolvanen 	process(cache, " (");
350*220a0857SSami Tolvanen 	process_linebreak(cache, 1);
351*220a0857SSami Tolvanen 	/* Parameters */
352*220a0857SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
353*220a0857SSami Tolvanen 				    match_formal_parameter_type));
354*220a0857SSami Tolvanen 	process_linebreak(cache, -1);
355*220a0857SSami Tolvanen 	process(cache, ")");
356*220a0857SSami Tolvanen 	process_linebreak(cache, 0);
357*220a0857SSami Tolvanen 	/* Return type */
358*220a0857SSami Tolvanen 	process(cache, "-> ");
359*220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
360*220a0857SSami Tolvanen }
361*220a0857SSami Tolvanen 
362*220a0857SSami Tolvanen static void process_subroutine_type(struct state *state, struct die *cache,
363*220a0857SSami Tolvanen 				    Dwarf_Die *die)
364*220a0857SSami Tolvanen {
365*220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subroutine_type");
366*220a0857SSami Tolvanen }
367*220a0857SSami Tolvanen 
3680c1c7627SSami Tolvanen static void process_base_type(struct state *state, struct die *cache,
3690c1c7627SSami Tolvanen 			      Dwarf_Die *die)
3705b7780e8SSami Tolvanen {
3710c1c7627SSami Tolvanen 	process(cache, "base_type");
3720c1c7627SSami Tolvanen 	process_fqn(cache, die);
3730c1c7627SSami Tolvanen 	process_byte_size_attr(cache, die);
3740c1c7627SSami Tolvanen 	process_encoding_attr(cache, die);
3750c1c7627SSami Tolvanen 	process_alignment_attr(cache, die);
3760c1c7627SSami Tolvanen }
3770c1c7627SSami Tolvanen 
3780c1c7627SSami Tolvanen static void process_cached(struct state *state, struct die *cache,
3790c1c7627SSami Tolvanen 			   Dwarf_Die *die)
3800c1c7627SSami Tolvanen {
3810c1c7627SSami Tolvanen 	struct die_fragment *df;
3820c1c7627SSami Tolvanen 	Dwarf_Die child;
3830c1c7627SSami Tolvanen 
3840c1c7627SSami Tolvanen 	list_for_each_entry(df, &cache->fragments, list) {
3850c1c7627SSami Tolvanen 		switch (df->type) {
3860c1c7627SSami Tolvanen 		case FRAGMENT_STRING:
3870c1c7627SSami Tolvanen 			process(NULL, df->data.str);
3880c1c7627SSami Tolvanen 			break;
38906b8b036SSami Tolvanen 		case FRAGMENT_LINEBREAK:
39006b8b036SSami Tolvanen 			process_linebreak(NULL, df->data.linebreak);
39106b8b036SSami Tolvanen 			break;
3920c1c7627SSami Tolvanen 		case FRAGMENT_DIE:
3930c1c7627SSami Tolvanen 			if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu),
3940c1c7627SSami Tolvanen 						(void *)df->data.addr, &child))
3950c1c7627SSami Tolvanen 				error("dwarf_die_addr_die failed");
3960c1c7627SSami Tolvanen 			check(process_type(state, NULL, &child));
3970c1c7627SSami Tolvanen 			break;
3980c1c7627SSami Tolvanen 		default:
3990c1c7627SSami Tolvanen 			error("empty die_fragment");
4000c1c7627SSami Tolvanen 		}
4010c1c7627SSami Tolvanen 	}
4025b7780e8SSami Tolvanen }
4035b7780e8SSami Tolvanen 
4045b7780e8SSami Tolvanen #define PROCESS_TYPE(type)                                \
4055b7780e8SSami Tolvanen 	case DW_TAG_##type##_type:                        \
4060c1c7627SSami Tolvanen 		process_##type##_type(state, cache, die); \
4075b7780e8SSami Tolvanen 		break;
4085b7780e8SSami Tolvanen 
4090c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
4105b7780e8SSami Tolvanen {
4110c1c7627SSami Tolvanen 	struct die *cache;
4125b7780e8SSami Tolvanen 	int tag = dwarf_tag(die);
4135b7780e8SSami Tolvanen 
4140c1c7627SSami Tolvanen 	/*
4150c1c7627SSami Tolvanen 	 * If we have the DIE already cached, use it instead of walking
4160c1c7627SSami Tolvanen 	 * through DWARF.
4170c1c7627SSami Tolvanen 	 */
4180c1c7627SSami Tolvanen 	cache = die_map_get(die, DIE_COMPLETE);
4190c1c7627SSami Tolvanen 
4200c1c7627SSami Tolvanen 	if (cache->state == DIE_COMPLETE) {
4210c1c7627SSami Tolvanen 		process_cached(state, cache, die);
4220c1c7627SSami Tolvanen 		die_map_add_die(parent, cache);
4230c1c7627SSami Tolvanen 		return 0;
4240c1c7627SSami Tolvanen 	}
4250c1c7627SSami Tolvanen 
4265b7780e8SSami Tolvanen 	switch (tag) {
42706b8b036SSami Tolvanen 	/* Type modifiers */
42806b8b036SSami Tolvanen 	PROCESS_TYPE(atomic)
42906b8b036SSami Tolvanen 	PROCESS_TYPE(const)
43006b8b036SSami Tolvanen 	PROCESS_TYPE(immutable)
43106b8b036SSami Tolvanen 	PROCESS_TYPE(packed)
43206b8b036SSami Tolvanen 	PROCESS_TYPE(pointer)
43306b8b036SSami Tolvanen 	PROCESS_TYPE(reference)
43406b8b036SSami Tolvanen 	PROCESS_TYPE(restrict)
43506b8b036SSami Tolvanen 	PROCESS_TYPE(rvalue_reference)
43606b8b036SSami Tolvanen 	PROCESS_TYPE(shared)
43706b8b036SSami Tolvanen 	PROCESS_TYPE(volatile)
438*220a0857SSami Tolvanen 	/* Subtypes */
439*220a0857SSami Tolvanen 	PROCESS_TYPE(formal_parameter)
44006b8b036SSami Tolvanen 	/* Other types */
4415b7780e8SSami Tolvanen 	PROCESS_TYPE(base)
442*220a0857SSami Tolvanen 	PROCESS_TYPE(subroutine)
44306b8b036SSami Tolvanen 	PROCESS_TYPE(typedef)
4445b7780e8SSami Tolvanen 	default:
4455b7780e8SSami Tolvanen 		debug("unimplemented type: %x", tag);
4465b7780e8SSami Tolvanen 		break;
4475b7780e8SSami Tolvanen 	}
4485b7780e8SSami Tolvanen 
4490c1c7627SSami Tolvanen 	/* Update cache state and append to the parent (if any) */
4500c1c7627SSami Tolvanen 	cache->tag = tag;
4510c1c7627SSami Tolvanen 	cache->state = DIE_COMPLETE;
4520c1c7627SSami Tolvanen 	die_map_add_die(parent, cache);
4530c1c7627SSami Tolvanen 
4545b7780e8SSami Tolvanen 	return 0;
4555b7780e8SSami Tolvanen }
4565b7780e8SSami Tolvanen 
457f2856884SSami Tolvanen /*
458f2856884SSami Tolvanen  * Exported symbol processing
459f2856884SSami Tolvanen  */
460f2856884SSami Tolvanen static void process_symbol(struct state *state, Dwarf_Die *die,
461f2856884SSami Tolvanen 			   die_callback_t process_func)
462f2856884SSami Tolvanen {
463f2856884SSami Tolvanen 	debug("%s", state->sym->name);
4640c1c7627SSami Tolvanen 	check(process_func(state, NULL, die));
465f2856884SSami Tolvanen 	if (dump_dies)
466f2856884SSami Tolvanen 		fputs("\n", stderr);
467f2856884SSami Tolvanen }
468f2856884SSami Tolvanen 
4690c1c7627SSami Tolvanen static int __process_subprogram(struct state *state, struct die *cache,
4700c1c7627SSami Tolvanen 				Dwarf_Die *die)
471f2856884SSami Tolvanen {
472*220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subprogram");
473f2856884SSami Tolvanen 	return 0;
474f2856884SSami Tolvanen }
475f2856884SSami Tolvanen 
476f2856884SSami Tolvanen static void process_subprogram(struct state *state, Dwarf_Die *die)
477f2856884SSami Tolvanen {
478f2856884SSami Tolvanen 	process_symbol(state, die, __process_subprogram);
479f2856884SSami Tolvanen }
480f2856884SSami Tolvanen 
4810c1c7627SSami Tolvanen static int __process_variable(struct state *state, struct die *cache,
4820c1c7627SSami Tolvanen 			      Dwarf_Die *die)
483f2856884SSami Tolvanen {
4840c1c7627SSami Tolvanen 	process(cache, "variable ");
4850c1c7627SSami Tolvanen 	process_type_attr(state, cache, die);
486f2856884SSami Tolvanen 	return 0;
487f2856884SSami Tolvanen }
488f2856884SSami Tolvanen 
489f2856884SSami Tolvanen static void process_variable(struct state *state, Dwarf_Die *die)
490f2856884SSami Tolvanen {
491f2856884SSami Tolvanen 	process_symbol(state, die, __process_variable);
492f2856884SSami Tolvanen }
493f2856884SSami Tolvanen 
4940c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache,
4950c1c7627SSami Tolvanen 				    Dwarf_Die *die)
496f2856884SSami Tolvanen {
497f2856884SSami Tolvanen 	int tag = dwarf_tag(die);
498f2856884SSami Tolvanen 
499f2856884SSami Tolvanen 	switch (tag) {
500f2856884SSami Tolvanen 	/* Possible containers of exported symbols */
501f2856884SSami Tolvanen 	case DW_TAG_namespace:
502f2856884SSami Tolvanen 	case DW_TAG_class_type:
503f2856884SSami Tolvanen 	case DW_TAG_structure_type:
504f2856884SSami Tolvanen 		return check(process_die_container(
5050c1c7627SSami Tolvanen 			NULL, cache, die, process_exported_symbols, match_all));
506f2856884SSami Tolvanen 
507f2856884SSami Tolvanen 	/* Possible exported symbols */
508f2856884SSami Tolvanen 	case DW_TAG_subprogram:
509f2856884SSami Tolvanen 	case DW_TAG_variable: {
510f2856884SSami Tolvanen 		struct state state;
511f2856884SSami Tolvanen 
512f2856884SSami Tolvanen 		if (!match_export_symbol(&state, die))
513f2856884SSami Tolvanen 			return 0;
514f2856884SSami Tolvanen 
515f2856884SSami Tolvanen 		if (tag == DW_TAG_subprogram)
516f2856884SSami Tolvanen 			process_subprogram(&state, &state.die);
517f2856884SSami Tolvanen 		else
518f2856884SSami Tolvanen 			process_variable(&state, &state.die);
519f2856884SSami Tolvanen 
520f2856884SSami Tolvanen 		return 0;
521f2856884SSami Tolvanen 	}
522f2856884SSami Tolvanen 	default:
523f2856884SSami Tolvanen 		return 0;
524f2856884SSami Tolvanen 	}
525f2856884SSami Tolvanen }
526f2856884SSami Tolvanen 
527f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie)
528f2856884SSami Tolvanen {
5290c1c7627SSami Tolvanen 	check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
530f2856884SSami Tolvanen 				    match_all));
531f2856884SSami Tolvanen }
532