xref: /linux/scripts/gendwarfksyms/dwarf.c (revision 936cf61c3ef5d6dad714d6c01a85704027dddeb9)
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 
30f936c129SSami Tolvanen DEFINE_GET_ATTR(flag, bool)
315b7780e8SSami Tolvanen DEFINE_GET_ATTR(udata, Dwarf_Word)
325b7780e8SSami Tolvanen 
33f2856884SSami Tolvanen static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value)
34f2856884SSami Tolvanen {
35f2856884SSami Tolvanen 	Dwarf_Attribute da;
36f2856884SSami Tolvanen 
37f2856884SSami Tolvanen 	/* dwarf_formref_die returns a pointer instead of an error value. */
38f2856884SSami Tolvanen 	return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value);
39f2856884SSami Tolvanen }
40f2856884SSami Tolvanen 
41f2856884SSami Tolvanen #define DEFINE_GET_STRING_ATTR(attr)                         \
42f2856884SSami Tolvanen 	static const char *get_##attr##_attr(Dwarf_Die *die) \
43f2856884SSami Tolvanen 	{                                                    \
44f2856884SSami Tolvanen 		Dwarf_Attribute da;                          \
45f2856884SSami Tolvanen 		if (dwarf_attr(die, DW_AT_##attr, &da))      \
46f2856884SSami Tolvanen 			return dwarf_formstring(&da);        \
47f2856884SSami Tolvanen 		return NULL;                                 \
48f2856884SSami Tolvanen 	}
49f2856884SSami Tolvanen 
50f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(name)
51f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(linkage_name)
52f2856884SSami Tolvanen 
53f2856884SSami Tolvanen static const char *get_symbol_name(Dwarf_Die *die)
54f2856884SSami Tolvanen {
55f2856884SSami Tolvanen 	const char *name;
56f2856884SSami Tolvanen 
57f2856884SSami Tolvanen 	/* rustc uses DW_AT_linkage_name for exported symbols */
58f2856884SSami Tolvanen 	name = get_linkage_name_attr(die);
59f2856884SSami Tolvanen 	if (!name)
60f2856884SSami Tolvanen 		name = get_name_attr(die);
61f2856884SSami Tolvanen 
62f2856884SSami Tolvanen 	return name;
63f2856884SSami Tolvanen }
64f2856884SSami Tolvanen 
65f2856884SSami Tolvanen static bool match_export_symbol(struct state *state, Dwarf_Die *die)
66f2856884SSami Tolvanen {
67f2856884SSami Tolvanen 	Dwarf_Die *source = die;
68f2856884SSami Tolvanen 	Dwarf_Die origin;
69f2856884SSami Tolvanen 
70f2856884SSami Tolvanen 	/* If the DIE has an abstract origin, use it for type information. */
71f2856884SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin))
72f2856884SSami Tolvanen 		source = &origin;
73f2856884SSami Tolvanen 
74f2856884SSami Tolvanen 	state->sym = symbol_get(get_symbol_name(die));
75f2856884SSami Tolvanen 
76f2856884SSami Tolvanen 	/* Look up using the origin name if there are no matches. */
77f2856884SSami Tolvanen 	if (!state->sym && source != die)
78f2856884SSami Tolvanen 		state->sym = symbol_get(get_symbol_name(source));
79f2856884SSami Tolvanen 
80f2856884SSami Tolvanen 	state->die = *source;
81f2856884SSami Tolvanen 	return !!state->sym;
82f2856884SSami Tolvanen }
83f2856884SSami Tolvanen 
84f936c129SSami Tolvanen /* DW_AT_decl_file -> struct srcfile */
85f936c129SSami Tolvanen static struct cache srcfile_cache;
86f936c129SSami Tolvanen 
87f936c129SSami Tolvanen static bool is_definition_private(Dwarf_Die *die)
88f936c129SSami Tolvanen {
89f936c129SSami Tolvanen 	Dwarf_Word filenum;
90f936c129SSami Tolvanen 	Dwarf_Files *files;
91f936c129SSami Tolvanen 	Dwarf_Die cudie;
92f936c129SSami Tolvanen 	const char *s;
93f936c129SSami Tolvanen 	int res;
94f936c129SSami Tolvanen 
95f936c129SSami Tolvanen 	/*
96f936c129SSami Tolvanen 	 * Definitions in .c files cannot change the public ABI,
97f936c129SSami Tolvanen 	 * so consider them private.
98f936c129SSami Tolvanen 	 */
99f936c129SSami Tolvanen 	if (!get_udata_attr(die, DW_AT_decl_file, &filenum))
100f936c129SSami Tolvanen 		return false;
101f936c129SSami Tolvanen 
102f936c129SSami Tolvanen 	res = cache_get(&srcfile_cache, filenum);
103f936c129SSami Tolvanen 	if (res >= 0)
104f936c129SSami Tolvanen 		return !!res;
105f936c129SSami Tolvanen 
106f936c129SSami Tolvanen 	if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL))
107f936c129SSami Tolvanen 		error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1));
108f936c129SSami Tolvanen 
109f936c129SSami Tolvanen 	if (dwarf_getsrcfiles(&cudie, &files, NULL))
110f936c129SSami Tolvanen 		error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1));
111f936c129SSami Tolvanen 
112f936c129SSami Tolvanen 	s = dwarf_filesrc(files, filenum, NULL, NULL);
113f936c129SSami Tolvanen 	if (!s)
114f936c129SSami Tolvanen 		error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1));
115f936c129SSami Tolvanen 
116f936c129SSami Tolvanen 	s = strrchr(s, '.');
117f936c129SSami Tolvanen 	res = s && !strcmp(s, ".c");
118f936c129SSami Tolvanen 	cache_set(&srcfile_cache, filenum, res);
119f936c129SSami Tolvanen 
120f936c129SSami Tolvanen 	return !!res;
121f936c129SSami Tolvanen }
122f936c129SSami Tolvanen 
123*936cf61cSSami Tolvanen static bool is_kabi_definition(struct die *cache, Dwarf_Die *die)
124f936c129SSami Tolvanen {
125f936c129SSami Tolvanen 	bool value;
126f936c129SSami Tolvanen 
127f936c129SSami Tolvanen 	if (get_flag_attr(die, DW_AT_declaration, &value) && value)
128f936c129SSami Tolvanen 		return false;
129f936c129SSami Tolvanen 
130*936cf61cSSami Tolvanen 	if (kabi_is_declonly(cache->fqn))
131*936cf61cSSami Tolvanen 		return false;
132*936cf61cSSami Tolvanen 
133f936c129SSami Tolvanen 	return !is_definition_private(die);
134f936c129SSami Tolvanen }
135f936c129SSami Tolvanen 
136f2856884SSami Tolvanen /*
137f2856884SSami Tolvanen  * Type string processing
138f2856884SSami Tolvanen  */
1390c1c7627SSami Tolvanen static void process(struct die *cache, const char *s)
140f2856884SSami Tolvanen {
141f2856884SSami Tolvanen 	s = s ?: "<null>";
142f2856884SSami Tolvanen 
14306b8b036SSami Tolvanen 	if (dump_dies && do_linebreak) {
14406b8b036SSami Tolvanen 		fputs("\n", stderr);
14506b8b036SSami Tolvanen 		for (int i = 0; i < indentation_level; i++)
14606b8b036SSami Tolvanen 			fputs("  ", stderr);
14706b8b036SSami Tolvanen 		do_linebreak = false;
14806b8b036SSami Tolvanen 	}
149f2856884SSami Tolvanen 	if (dump_dies)
150f2856884SSami Tolvanen 		fputs(s, stderr);
1510c1c7627SSami Tolvanen 
152d2ffdc1cSSami Tolvanen 	if (cache)
153d2ffdc1cSSami Tolvanen 		die_debug_r("cache %p string '%s'", cache, s);
1540c1c7627SSami Tolvanen 	die_map_add_string(cache, s);
155f2856884SSami Tolvanen }
156f2856884SSami Tolvanen 
1575b7780e8SSami Tolvanen #define MAX_FMT_BUFFER_SIZE 128
1585b7780e8SSami Tolvanen 
1590c1c7627SSami Tolvanen static void process_fmt(struct die *cache, const char *fmt, ...)
1605b7780e8SSami Tolvanen {
1615b7780e8SSami Tolvanen 	char buf[MAX_FMT_BUFFER_SIZE];
1625b7780e8SSami Tolvanen 	va_list args;
1635b7780e8SSami Tolvanen 
1645b7780e8SSami Tolvanen 	va_start(args, fmt);
1655b7780e8SSami Tolvanen 
1665b7780e8SSami Tolvanen 	if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf))
1675b7780e8SSami Tolvanen 		error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE");
1685b7780e8SSami Tolvanen 
1690c1c7627SSami Tolvanen 	process(cache, buf);
1705b7780e8SSami Tolvanen 	va_end(args);
1715b7780e8SSami Tolvanen }
1725b7780e8SSami Tolvanen 
1735b7780e8SSami Tolvanen #define MAX_FQN_SIZE 64
1745b7780e8SSami Tolvanen 
1755b7780e8SSami Tolvanen /* Get a fully qualified name from DWARF scopes */
1765b7780e8SSami Tolvanen static char *get_fqn(Dwarf_Die *die)
1775b7780e8SSami Tolvanen {
1785b7780e8SSami Tolvanen 	const char *list[MAX_FQN_SIZE];
1795b7780e8SSami Tolvanen 	Dwarf_Die *scopes = NULL;
1805b7780e8SSami Tolvanen 	bool has_name = false;
1815b7780e8SSami Tolvanen 	char *fqn = NULL;
1825b7780e8SSami Tolvanen 	char *p;
1835b7780e8SSami Tolvanen 	int count = 0;
1845b7780e8SSami Tolvanen 	int len = 0;
1855b7780e8SSami Tolvanen 	int res;
1865b7780e8SSami Tolvanen 	int i;
1875b7780e8SSami Tolvanen 
1885b7780e8SSami Tolvanen 	res = checkp(dwarf_getscopes_die(die, &scopes));
1895b7780e8SSami Tolvanen 	if (!res) {
1905b7780e8SSami Tolvanen 		list[count] = get_name_attr(die);
1915b7780e8SSami Tolvanen 
1925b7780e8SSami Tolvanen 		if (!list[count])
1935b7780e8SSami Tolvanen 			return NULL;
1945b7780e8SSami Tolvanen 
1955b7780e8SSami Tolvanen 		len += strlen(list[count]);
1965b7780e8SSami Tolvanen 		count++;
1975b7780e8SSami Tolvanen 
1985b7780e8SSami Tolvanen 		goto done;
1995b7780e8SSami Tolvanen 	}
2005b7780e8SSami Tolvanen 
2015b7780e8SSami Tolvanen 	for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) {
2025b7780e8SSami Tolvanen 		if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit)
2035b7780e8SSami Tolvanen 			continue;
2045b7780e8SSami Tolvanen 
2055b7780e8SSami Tolvanen 		list[count] = get_name_attr(&scopes[i]);
2065b7780e8SSami Tolvanen 
2075b7780e8SSami Tolvanen 		if (list[count]) {
2085b7780e8SSami Tolvanen 			has_name = true;
2095b7780e8SSami Tolvanen 		} else {
2105b7780e8SSami Tolvanen 			list[count] = "<anonymous>";
2115b7780e8SSami Tolvanen 			has_name = false;
2125b7780e8SSami Tolvanen 		}
2135b7780e8SSami Tolvanen 
2145b7780e8SSami Tolvanen 		len += strlen(list[count]);
2155b7780e8SSami Tolvanen 		count++;
2165b7780e8SSami Tolvanen 
2175b7780e8SSami Tolvanen 		if (i > 0) {
2185b7780e8SSami Tolvanen 			list[count++] = "::";
2195b7780e8SSami Tolvanen 			len += 2;
2205b7780e8SSami Tolvanen 		}
2215b7780e8SSami Tolvanen 	}
2225b7780e8SSami Tolvanen 
2235b7780e8SSami Tolvanen 	free(scopes);
2245b7780e8SSami Tolvanen 
2255b7780e8SSami Tolvanen 	if (count == MAX_FQN_SIZE)
2265b7780e8SSami Tolvanen 		warn("increase MAX_FQN_SIZE: reached the maximum");
2275b7780e8SSami Tolvanen 
2285b7780e8SSami Tolvanen 	/* Consider the DIE unnamed if the last scope doesn't have a name */
2295b7780e8SSami Tolvanen 	if (!has_name)
2305b7780e8SSami Tolvanen 		return NULL;
2315b7780e8SSami Tolvanen done:
2325b7780e8SSami Tolvanen 	fqn = xmalloc(len + 1);
2335b7780e8SSami Tolvanen 	*fqn = '\0';
2345b7780e8SSami Tolvanen 
2355b7780e8SSami Tolvanen 	p = fqn;
2365b7780e8SSami Tolvanen 	for (i = 0; i < count; i++)
2375b7780e8SSami Tolvanen 		p = stpcpy(p, list[i]);
2385b7780e8SSami Tolvanen 
2395b7780e8SSami Tolvanen 	return fqn;
2405b7780e8SSami Tolvanen }
2415b7780e8SSami Tolvanen 
2420c1c7627SSami Tolvanen static void update_fqn(struct die *cache, Dwarf_Die *die)
2435b7780e8SSami Tolvanen {
2440c1c7627SSami Tolvanen 	if (!cache->fqn)
2450c1c7627SSami Tolvanen 		cache->fqn = get_fqn(die) ?: "";
2460c1c7627SSami Tolvanen }
2470c1c7627SSami Tolvanen 
2480c1c7627SSami Tolvanen static void process_fqn(struct die *cache, Dwarf_Die *die)
2490c1c7627SSami Tolvanen {
2500c1c7627SSami Tolvanen 	update_fqn(cache, die);
2510c1c7627SSami Tolvanen 	if (*cache->fqn)
2520c1c7627SSami Tolvanen 		process(cache, " ");
2530c1c7627SSami Tolvanen 	process(cache, cache->fqn);
2545b7780e8SSami Tolvanen }
2555b7780e8SSami Tolvanen 
2565b7780e8SSami Tolvanen #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute)                          \
2570c1c7627SSami Tolvanen 	static void process_##attribute##_attr(struct die *cache,          \
2580c1c7627SSami Tolvanen 					       Dwarf_Die *die)             \
2595b7780e8SSami Tolvanen 	{                                                                  \
2605b7780e8SSami Tolvanen 		Dwarf_Word value;                                          \
2615b7780e8SSami Tolvanen 		if (get_udata_attr(die, DW_AT_##attribute, &value))        \
2620c1c7627SSami Tolvanen 			process_fmt(cache, " " #attribute "(%" PRIu64 ")", \
2630c1c7627SSami Tolvanen 				    value);                                \
2645b7780e8SSami Tolvanen 	}
2655b7780e8SSami Tolvanen 
266f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
2675b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
268f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
2695b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
2705b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
271f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
272f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
273f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
2745b7780e8SSami Tolvanen 
275220a0857SSami Tolvanen /* Match functions -- die_match_callback_t */
276220a0857SSami Tolvanen #define DEFINE_MATCH(type)                                     \
277220a0857SSami Tolvanen 	static bool match_##type##_type(Dwarf_Die *die)        \
278220a0857SSami Tolvanen 	{                                                      \
279220a0857SSami Tolvanen 		return dwarf_tag(die) == DW_TAG_##type##_type; \
280220a0857SSami Tolvanen 	}
281220a0857SSami Tolvanen 
282f6bb9245SSami Tolvanen DEFINE_MATCH(enumerator)
283220a0857SSami Tolvanen DEFINE_MATCH(formal_parameter)
284f6bb9245SSami Tolvanen DEFINE_MATCH(member)
285c772f1d1SSami Tolvanen DEFINE_MATCH(subrange)
286220a0857SSami Tolvanen 
287f2856884SSami Tolvanen bool match_all(Dwarf_Die *die)
288f2856884SSami Tolvanen {
289f2856884SSami Tolvanen 	return true;
290f2856884SSami Tolvanen }
291f2856884SSami Tolvanen 
2920c1c7627SSami Tolvanen int process_die_container(struct state *state, struct die *cache,
2930c1c7627SSami Tolvanen 			  Dwarf_Die *die, die_callback_t func,
2940c1c7627SSami Tolvanen 			  die_match_callback_t match)
295f2856884SSami Tolvanen {
296f2856884SSami Tolvanen 	Dwarf_Die current;
297f2856884SSami Tolvanen 	int res;
298f2856884SSami Tolvanen 
299220a0857SSami Tolvanen 	/* Track the first item in lists. */
300220a0857SSami Tolvanen 	if (state)
301220a0857SSami Tolvanen 		state->first_list_item = true;
302220a0857SSami Tolvanen 
303f2856884SSami Tolvanen 	res = checkp(dwarf_child(die, &current));
304f2856884SSami Tolvanen 	while (!res) {
305f2856884SSami Tolvanen 		if (match(&current)) {
306f2856884SSami Tolvanen 			/* <0 = error, 0 = continue, >0 = stop */
3070c1c7627SSami Tolvanen 			res = checkp(func(state, cache, &current));
308f2856884SSami Tolvanen 			if (res)
309220a0857SSami Tolvanen 				goto out;
310f2856884SSami Tolvanen 		}
311f2856884SSami Tolvanen 
312f2856884SSami Tolvanen 		res = checkp(dwarf_siblingof(&current, &current));
313f2856884SSami Tolvanen 	}
314f2856884SSami Tolvanen 
315220a0857SSami Tolvanen 	res = 0;
316220a0857SSami Tolvanen out:
317220a0857SSami Tolvanen 	if (state)
318220a0857SSami Tolvanen 		state->first_list_item = false;
319220a0857SSami Tolvanen 
320220a0857SSami Tolvanen 	return res;
321f2856884SSami Tolvanen }
322f2856884SSami Tolvanen 
3230c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent,
3240c1c7627SSami Tolvanen 			Dwarf_Die *die);
3255b7780e8SSami Tolvanen 
3260c1c7627SSami Tolvanen static void process_type_attr(struct state *state, struct die *cache,
3270c1c7627SSami Tolvanen 			      Dwarf_Die *die)
3285b7780e8SSami Tolvanen {
3295b7780e8SSami Tolvanen 	Dwarf_Die type;
3305b7780e8SSami Tolvanen 
3315b7780e8SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_type, &type)) {
3320c1c7627SSami Tolvanen 		check(process_type(state, cache, &type));
3335b7780e8SSami Tolvanen 		return;
3345b7780e8SSami Tolvanen 	}
3355b7780e8SSami Tolvanen 
3365b7780e8SSami Tolvanen 	/* Compilers can omit DW_AT_type -- print out 'void' to clarify */
3370c1c7627SSami Tolvanen 	process(cache, "base_type void");
3385b7780e8SSami Tolvanen }
3395b7780e8SSami Tolvanen 
340220a0857SSami Tolvanen static void process_list_comma(struct state *state, struct die *cache)
341220a0857SSami Tolvanen {
342220a0857SSami Tolvanen 	if (state->first_list_item) {
343220a0857SSami Tolvanen 		state->first_list_item = false;
344220a0857SSami Tolvanen 	} else {
345220a0857SSami Tolvanen 		process(cache, " ,");
346220a0857SSami Tolvanen 		process_linebreak(cache, 0);
347220a0857SSami Tolvanen 	}
348220a0857SSami Tolvanen }
349220a0857SSami Tolvanen 
350220a0857SSami Tolvanen /* Comma-separated with DW_AT_type */
351220a0857SSami Tolvanen static void __process_list_type(struct state *state, struct die *cache,
352220a0857SSami Tolvanen 				Dwarf_Die *die, const char *type)
353220a0857SSami Tolvanen {
354220a0857SSami Tolvanen 	const char *name = get_name_attr(die);
355220a0857SSami Tolvanen 
356220a0857SSami Tolvanen 	process_list_comma(state, cache);
357220a0857SSami Tolvanen 	process(cache, type);
358220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
359220a0857SSami Tolvanen 	if (name) {
360220a0857SSami Tolvanen 		process(cache, " ");
361220a0857SSami Tolvanen 		process(cache, name);
362220a0857SSami Tolvanen 	}
363f6bb9245SSami Tolvanen 	process_accessibility_attr(cache, die);
364f6bb9245SSami Tolvanen 	process_bit_size_attr(cache, die);
365f6bb9245SSami Tolvanen 	process_data_bit_offset_attr(cache, die);
366f6bb9245SSami Tolvanen 	process_data_member_location_attr(cache, die);
367220a0857SSami Tolvanen }
368220a0857SSami Tolvanen 
369220a0857SSami Tolvanen #define DEFINE_PROCESS_LIST_TYPE(type)                                       \
370220a0857SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
371220a0857SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
372220a0857SSami Tolvanen 	{                                                                    \
373220a0857SSami Tolvanen 		__process_list_type(state, cache, die, #type " ");           \
374220a0857SSami Tolvanen 	}
375220a0857SSami Tolvanen 
376220a0857SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(formal_parameter)
377f6bb9245SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(member)
378220a0857SSami Tolvanen 
37906b8b036SSami Tolvanen /* Container types with DW_AT_type */
38006b8b036SSami Tolvanen static void __process_type(struct state *state, struct die *cache,
38106b8b036SSami Tolvanen 			   Dwarf_Die *die, const char *type)
38206b8b036SSami Tolvanen {
38306b8b036SSami Tolvanen 	process(cache, type);
38406b8b036SSami Tolvanen 	process_fqn(cache, die);
38506b8b036SSami Tolvanen 	process(cache, " {");
38606b8b036SSami Tolvanen 	process_linebreak(cache, 1);
38706b8b036SSami Tolvanen 	process_type_attr(state, cache, die);
38806b8b036SSami Tolvanen 	process_linebreak(cache, -1);
38906b8b036SSami Tolvanen 	process(cache, "}");
39006b8b036SSami Tolvanen 	process_byte_size_attr(cache, die);
39106b8b036SSami Tolvanen 	process_alignment_attr(cache, die);
39206b8b036SSami Tolvanen }
39306b8b036SSami Tolvanen 
39406b8b036SSami Tolvanen #define DEFINE_PROCESS_TYPE(type)                                            \
39506b8b036SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
39606b8b036SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
39706b8b036SSami Tolvanen 	{                                                                    \
39806b8b036SSami Tolvanen 		__process_type(state, cache, die, #type "_type");            \
39906b8b036SSami Tolvanen 	}
40006b8b036SSami Tolvanen 
40106b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(atomic)
40206b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(const)
40306b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(immutable)
40406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(packed)
40506b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(pointer)
40606b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(reference)
40706b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(restrict)
40806b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(rvalue_reference)
40906b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(shared)
410f6bb9245SSami Tolvanen DEFINE_PROCESS_TYPE(template_type_parameter)
41106b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(volatile)
41206b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(typedef)
41306b8b036SSami Tolvanen 
414c772f1d1SSami Tolvanen static void process_subrange_type(struct state *state, struct die *cache,
415c772f1d1SSami Tolvanen 				  Dwarf_Die *die)
416c772f1d1SSami Tolvanen {
417c772f1d1SSami Tolvanen 	Dwarf_Word count = 0;
418c772f1d1SSami Tolvanen 
419c772f1d1SSami Tolvanen 	if (get_udata_attr(die, DW_AT_count, &count))
420c772f1d1SSami Tolvanen 		process_fmt(cache, "[%" PRIu64 "]", count);
421c772f1d1SSami Tolvanen 	else if (get_udata_attr(die, DW_AT_upper_bound, &count))
422c772f1d1SSami Tolvanen 		process_fmt(cache, "[%" PRIu64 "]", count + 1);
423c772f1d1SSami Tolvanen 	else
424c772f1d1SSami Tolvanen 		process(cache, "[]");
425c772f1d1SSami Tolvanen }
426c772f1d1SSami Tolvanen 
427c772f1d1SSami Tolvanen static void process_array_type(struct state *state, struct die *cache,
428c772f1d1SSami Tolvanen 			       Dwarf_Die *die)
429c772f1d1SSami Tolvanen {
430c772f1d1SSami Tolvanen 	process(cache, "array_type");
431c772f1d1SSami Tolvanen 	/* Array size */
432c772f1d1SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
433c772f1d1SSami Tolvanen 				    match_subrange_type));
434c772f1d1SSami Tolvanen 	process(cache, " {");
435c772f1d1SSami Tolvanen 	process_linebreak(cache, 1);
436c772f1d1SSami Tolvanen 	process_type_attr(state, cache, die);
437c772f1d1SSami Tolvanen 	process_linebreak(cache, -1);
438c772f1d1SSami Tolvanen 	process(cache, "}");
439c772f1d1SSami Tolvanen }
440c772f1d1SSami Tolvanen 
441220a0857SSami Tolvanen static void __process_subroutine_type(struct state *state, struct die *cache,
442220a0857SSami Tolvanen 				      Dwarf_Die *die, const char *type)
443220a0857SSami Tolvanen {
444220a0857SSami Tolvanen 	process(cache, type);
445220a0857SSami Tolvanen 	process(cache, " (");
446220a0857SSami Tolvanen 	process_linebreak(cache, 1);
447220a0857SSami Tolvanen 	/* Parameters */
448220a0857SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
449220a0857SSami Tolvanen 				    match_formal_parameter_type));
450220a0857SSami Tolvanen 	process_linebreak(cache, -1);
451220a0857SSami Tolvanen 	process(cache, ")");
452220a0857SSami Tolvanen 	process_linebreak(cache, 0);
453220a0857SSami Tolvanen 	/* Return type */
454220a0857SSami Tolvanen 	process(cache, "-> ");
455220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
456220a0857SSami Tolvanen }
457220a0857SSami Tolvanen 
458220a0857SSami Tolvanen static void process_subroutine_type(struct state *state, struct die *cache,
459220a0857SSami Tolvanen 				    Dwarf_Die *die)
460220a0857SSami Tolvanen {
461220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subroutine_type");
462220a0857SSami Tolvanen }
463220a0857SSami Tolvanen 
464f6bb9245SSami Tolvanen static void process_variant_type(struct state *state, struct die *cache,
465f6bb9245SSami Tolvanen 				 Dwarf_Die *die)
466f6bb9245SSami Tolvanen {
467f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
468f6bb9245SSami Tolvanen 	process(cache, "variant {");
469f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
470f6bb9245SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
471f6bb9245SSami Tolvanen 				    match_member_type));
472f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
473f6bb9245SSami Tolvanen 	process(cache, "}");
474f6bb9245SSami Tolvanen 	process_discr_value_attr(cache, die);
475f6bb9245SSami Tolvanen }
476f6bb9245SSami Tolvanen 
477f6bb9245SSami Tolvanen static void process_variant_part_type(struct state *state, struct die *cache,
478f6bb9245SSami Tolvanen 				      Dwarf_Die *die)
479f6bb9245SSami Tolvanen {
480f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
481f6bb9245SSami Tolvanen 	process(cache, "variant_part {");
482f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
483f6bb9245SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
484f6bb9245SSami Tolvanen 				    match_all));
485f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
486f6bb9245SSami Tolvanen 	process(cache, "}");
487f6bb9245SSami Tolvanen }
488f6bb9245SSami Tolvanen 
489f6bb9245SSami Tolvanen static int ___process_structure_type(struct state *state, struct die *cache,
490f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
491f6bb9245SSami Tolvanen {
492f6bb9245SSami Tolvanen 	switch (dwarf_tag(die)) {
493f6bb9245SSami Tolvanen 	case DW_TAG_member:
494f6bb9245SSami Tolvanen 	case DW_TAG_variant_part:
495f6bb9245SSami Tolvanen 		return check(process_type(state, cache, die));
496f6bb9245SSami Tolvanen 	case DW_TAG_class_type:
497f6bb9245SSami Tolvanen 	case DW_TAG_enumeration_type:
498f6bb9245SSami Tolvanen 	case DW_TAG_structure_type:
499f6bb9245SSami Tolvanen 	case DW_TAG_template_type_parameter:
500f6bb9245SSami Tolvanen 	case DW_TAG_union_type:
501f6bb9245SSami Tolvanen 	case DW_TAG_subprogram:
502f6bb9245SSami Tolvanen 		/* Skip non-member types, including member functions */
503f6bb9245SSami Tolvanen 		return 0;
504f6bb9245SSami Tolvanen 	default:
505f6bb9245SSami Tolvanen 		error("unexpected structure_type child: %x", dwarf_tag(die));
506f6bb9245SSami Tolvanen 	}
507f6bb9245SSami Tolvanen }
508f6bb9245SSami Tolvanen 
509f6bb9245SSami Tolvanen static void __process_structure_type(struct state *state, struct die *cache,
510f6bb9245SSami Tolvanen 				     Dwarf_Die *die, const char *type,
511f6bb9245SSami Tolvanen 				     die_callback_t process_func,
512f6bb9245SSami Tolvanen 				     die_match_callback_t match_func)
513f6bb9245SSami Tolvanen {
514f936c129SSami Tolvanen 	bool expand;
515f936c129SSami Tolvanen 
516f6bb9245SSami Tolvanen 	process(cache, type);
517f6bb9245SSami Tolvanen 	process_fqn(cache, die);
518f6bb9245SSami Tolvanen 	process(cache, " {");
519f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
520f6bb9245SSami Tolvanen 
521*936cf61cSSami Tolvanen 	expand = state->expand.expand && is_kabi_definition(cache, die);
522f936c129SSami Tolvanen 
523f936c129SSami Tolvanen 	if (expand) {
524*936cf61cSSami Tolvanen 		state->expand.current_fqn = cache->fqn;
525f6bb9245SSami Tolvanen 		check(process_die_container(state, cache, die, process_func,
526f6bb9245SSami Tolvanen 					    match_func));
527f936c129SSami Tolvanen 	}
528f6bb9245SSami Tolvanen 
529f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
530f6bb9245SSami Tolvanen 	process(cache, "}");
531f6bb9245SSami Tolvanen 
532f936c129SSami Tolvanen 	if (expand) {
533f6bb9245SSami Tolvanen 		process_byte_size_attr(cache, die);
534f6bb9245SSami Tolvanen 		process_alignment_attr(cache, die);
535f6bb9245SSami Tolvanen 	}
536f936c129SSami Tolvanen }
537f6bb9245SSami Tolvanen 
538f6bb9245SSami Tolvanen #define DEFINE_PROCESS_STRUCTURE_TYPE(structure)                        \
539f6bb9245SSami Tolvanen 	static void process_##structure##_type(                         \
540f6bb9245SSami Tolvanen 		struct state *state, struct die *cache, Dwarf_Die *die) \
541f6bb9245SSami Tolvanen 	{                                                               \
542f6bb9245SSami Tolvanen 		__process_structure_type(state, cache, die,             \
543f6bb9245SSami Tolvanen 					 #structure "_type",            \
544f6bb9245SSami Tolvanen 					 ___process_structure_type,     \
545f6bb9245SSami Tolvanen 					 match_all);                    \
546f6bb9245SSami Tolvanen 	}
547f6bb9245SSami Tolvanen 
548f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(class)
549f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(structure)
550f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(union)
551f6bb9245SSami Tolvanen 
552f6bb9245SSami Tolvanen static void process_enumerator_type(struct state *state, struct die *cache,
553f6bb9245SSami Tolvanen 				    Dwarf_Die *die)
554f6bb9245SSami Tolvanen {
555*936cf61cSSami Tolvanen 	bool overridden = false;
556f6bb9245SSami Tolvanen 	Dwarf_Word value;
557f6bb9245SSami Tolvanen 
558*936cf61cSSami Tolvanen 	if (stable) {
559*936cf61cSSami Tolvanen 		/* Get the fqn before we process anything */
560*936cf61cSSami Tolvanen 		update_fqn(cache, die);
561*936cf61cSSami Tolvanen 
562*936cf61cSSami Tolvanen 		if (kabi_is_enumerator_ignored(state->expand.current_fqn,
563*936cf61cSSami Tolvanen 					       cache->fqn))
564*936cf61cSSami Tolvanen 			return;
565*936cf61cSSami Tolvanen 
566*936cf61cSSami Tolvanen 		overridden = kabi_get_enumerator_value(
567*936cf61cSSami Tolvanen 			state->expand.current_fqn, cache->fqn, &value);
568*936cf61cSSami Tolvanen 	}
569*936cf61cSSami Tolvanen 
570f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
571f6bb9245SSami Tolvanen 	process(cache, "enumerator");
572f6bb9245SSami Tolvanen 	process_fqn(cache, die);
573f6bb9245SSami Tolvanen 
574*936cf61cSSami Tolvanen 	if (overridden || get_udata_attr(die, DW_AT_const_value, &value)) {
575f6bb9245SSami Tolvanen 		process(cache, " = ");
576f6bb9245SSami Tolvanen 		process_fmt(cache, "%" PRIu64, value);
577f6bb9245SSami Tolvanen 	}
578f6bb9245SSami Tolvanen }
579f6bb9245SSami Tolvanen 
580f6bb9245SSami Tolvanen static void process_enumeration_type(struct state *state, struct die *cache,
581f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
582f6bb9245SSami Tolvanen {
583f6bb9245SSami Tolvanen 	__process_structure_type(state, cache, die, "enumeration_type",
584f6bb9245SSami Tolvanen 				 process_type, match_enumerator_type);
585f6bb9245SSami Tolvanen }
586f6bb9245SSami Tolvanen 
5870c1c7627SSami Tolvanen static void process_base_type(struct state *state, struct die *cache,
5880c1c7627SSami Tolvanen 			      Dwarf_Die *die)
5895b7780e8SSami Tolvanen {
5900c1c7627SSami Tolvanen 	process(cache, "base_type");
5910c1c7627SSami Tolvanen 	process_fqn(cache, die);
5920c1c7627SSami Tolvanen 	process_byte_size_attr(cache, die);
5930c1c7627SSami Tolvanen 	process_encoding_attr(cache, die);
5940c1c7627SSami Tolvanen 	process_alignment_attr(cache, die);
5950c1c7627SSami Tolvanen }
5960c1c7627SSami Tolvanen 
597f6bb9245SSami Tolvanen static void process_unspecified_type(struct state *state, struct die *cache,
598f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
599f6bb9245SSami Tolvanen {
600f6bb9245SSami Tolvanen 	/*
601f6bb9245SSami Tolvanen 	 * These can be emitted for stand-alone assembly code, which means we
602f6bb9245SSami Tolvanen 	 * might run into them in vmlinux.o.
603f6bb9245SSami Tolvanen 	 */
604f6bb9245SSami Tolvanen 	process(cache, "unspecified_type");
605f6bb9245SSami Tolvanen }
606f6bb9245SSami Tolvanen 
6070c1c7627SSami Tolvanen static void process_cached(struct state *state, struct die *cache,
6080c1c7627SSami Tolvanen 			   Dwarf_Die *die)
6090c1c7627SSami Tolvanen {
6100c1c7627SSami Tolvanen 	struct die_fragment *df;
6110c1c7627SSami Tolvanen 	Dwarf_Die child;
6120c1c7627SSami Tolvanen 
6130c1c7627SSami Tolvanen 	list_for_each_entry(df, &cache->fragments, list) {
6140c1c7627SSami Tolvanen 		switch (df->type) {
6150c1c7627SSami Tolvanen 		case FRAGMENT_STRING:
616d2ffdc1cSSami Tolvanen 			die_debug_b("cache %p STRING '%s'", cache,
617d2ffdc1cSSami Tolvanen 				    df->data.str);
6180c1c7627SSami Tolvanen 			process(NULL, df->data.str);
6190c1c7627SSami Tolvanen 			break;
62006b8b036SSami Tolvanen 		case FRAGMENT_LINEBREAK:
62106b8b036SSami Tolvanen 			process_linebreak(NULL, df->data.linebreak);
62206b8b036SSami Tolvanen 			break;
6230c1c7627SSami Tolvanen 		case FRAGMENT_DIE:
6240c1c7627SSami Tolvanen 			if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu),
6250c1c7627SSami Tolvanen 						(void *)df->data.addr, &child))
6260c1c7627SSami Tolvanen 				error("dwarf_die_addr_die failed");
627d2ffdc1cSSami Tolvanen 			die_debug_b("cache %p DIE addr %" PRIxPTR " tag %x",
628d2ffdc1cSSami Tolvanen 				    cache, df->data.addr, dwarf_tag(&child));
6290c1c7627SSami Tolvanen 			check(process_type(state, NULL, &child));
6300c1c7627SSami Tolvanen 			break;
6310c1c7627SSami Tolvanen 		default:
6320c1c7627SSami Tolvanen 			error("empty die_fragment");
6330c1c7627SSami Tolvanen 		}
6340c1c7627SSami Tolvanen 	}
6355b7780e8SSami Tolvanen }
6365b7780e8SSami Tolvanen 
637f936c129SSami Tolvanen static void state_init(struct state *state)
638f936c129SSami Tolvanen {
639f936c129SSami Tolvanen 	state->expand.expand = true;
640*936cf61cSSami Tolvanen 	state->expand.current_fqn = NULL;
641f936c129SSami Tolvanen 	cache_init(&state->expansion_cache);
642f936c129SSami Tolvanen }
643f936c129SSami Tolvanen 
644f936c129SSami Tolvanen static void expansion_state_restore(struct expansion_state *state,
645f936c129SSami Tolvanen 				    struct expansion_state *saved)
646f936c129SSami Tolvanen {
647f936c129SSami Tolvanen 	state->expand = saved->expand;
648*936cf61cSSami Tolvanen 	state->current_fqn = saved->current_fqn;
649f936c129SSami Tolvanen }
650f936c129SSami Tolvanen 
651f936c129SSami Tolvanen static void expansion_state_save(struct expansion_state *state,
652f936c129SSami Tolvanen 				 struct expansion_state *saved)
653f936c129SSami Tolvanen {
654f936c129SSami Tolvanen 	expansion_state_restore(saved, state);
655f936c129SSami Tolvanen }
656f936c129SSami Tolvanen 
657f936c129SSami Tolvanen static bool is_expanded_type(int tag)
658f936c129SSami Tolvanen {
659f936c129SSami Tolvanen 	return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
660f936c129SSami Tolvanen 	       tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type;
661f936c129SSami Tolvanen }
662f936c129SSami Tolvanen 
6635b7780e8SSami Tolvanen #define PROCESS_TYPE(type)                                \
6645b7780e8SSami Tolvanen 	case DW_TAG_##type##_type:                        \
6650c1c7627SSami Tolvanen 		process_##type##_type(state, cache, die); \
6665b7780e8SSami Tolvanen 		break;
6675b7780e8SSami Tolvanen 
6680c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
6695b7780e8SSami Tolvanen {
670f936c129SSami Tolvanen 	enum die_state want_state = DIE_COMPLETE;
6710c1c7627SSami Tolvanen 	struct die *cache;
672f936c129SSami Tolvanen 	struct expansion_state saved;
6735b7780e8SSami Tolvanen 	int tag = dwarf_tag(die);
6745b7780e8SSami Tolvanen 
675f936c129SSami Tolvanen 	expansion_state_save(&state->expand, &saved);
676f936c129SSami Tolvanen 
6770c1c7627SSami Tolvanen 	/*
678f936c129SSami Tolvanen 	 * Structures and enumeration types are expanded only once per
679f936c129SSami Tolvanen 	 * exported symbol. This is sufficient for detecting ABI changes
680f936c129SSami Tolvanen 	 * within the structure.
681f936c129SSami Tolvanen 	 */
682f936c129SSami Tolvanen 	if (is_expanded_type(tag)) {
683f936c129SSami Tolvanen 		if (cache_was_expanded(&state->expansion_cache, die->addr))
684f936c129SSami Tolvanen 			state->expand.expand = false;
685f936c129SSami Tolvanen 
686f936c129SSami Tolvanen 		if (state->expand.expand)
687f936c129SSami Tolvanen 			cache_mark_expanded(&state->expansion_cache, die->addr);
688f936c129SSami Tolvanen 		else
689f936c129SSami Tolvanen 			want_state = DIE_UNEXPANDED;
690f936c129SSami Tolvanen 	}
691f936c129SSami Tolvanen 
692f936c129SSami Tolvanen 	/*
693f936c129SSami Tolvanen 	 * If we have want_state already cached, use it instead of walking
6940c1c7627SSami Tolvanen 	 * through DWARF.
6950c1c7627SSami Tolvanen 	 */
696f936c129SSami Tolvanen 	cache = die_map_get(die, want_state);
6970c1c7627SSami Tolvanen 
698f936c129SSami Tolvanen 	if (cache->state == want_state) {
699d2ffdc1cSSami Tolvanen 		die_debug_g("cached addr %p tag %x -- %s", die->addr, tag,
700d2ffdc1cSSami Tolvanen 			    die_state_name(cache->state));
701d2ffdc1cSSami Tolvanen 
7020c1c7627SSami Tolvanen 		process_cached(state, cache, die);
7030c1c7627SSami Tolvanen 		die_map_add_die(parent, cache);
704f936c129SSami Tolvanen 
705f936c129SSami Tolvanen 		expansion_state_restore(&state->expand, &saved);
7060c1c7627SSami Tolvanen 		return 0;
7070c1c7627SSami Tolvanen 	}
7080c1c7627SSami Tolvanen 
709d2ffdc1cSSami Tolvanen 	die_debug_g("addr %p tag %x -- %s -> %s", die->addr, tag,
710d2ffdc1cSSami Tolvanen 		    die_state_name(cache->state), die_state_name(want_state));
711d2ffdc1cSSami Tolvanen 
7125b7780e8SSami Tolvanen 	switch (tag) {
71306b8b036SSami Tolvanen 	/* Type modifiers */
71406b8b036SSami Tolvanen 	PROCESS_TYPE(atomic)
71506b8b036SSami Tolvanen 	PROCESS_TYPE(const)
71606b8b036SSami Tolvanen 	PROCESS_TYPE(immutable)
71706b8b036SSami Tolvanen 	PROCESS_TYPE(packed)
71806b8b036SSami Tolvanen 	PROCESS_TYPE(pointer)
71906b8b036SSami Tolvanen 	PROCESS_TYPE(reference)
72006b8b036SSami Tolvanen 	PROCESS_TYPE(restrict)
72106b8b036SSami Tolvanen 	PROCESS_TYPE(rvalue_reference)
72206b8b036SSami Tolvanen 	PROCESS_TYPE(shared)
72306b8b036SSami Tolvanen 	PROCESS_TYPE(volatile)
724f6bb9245SSami Tolvanen 	/* Container types */
725f6bb9245SSami Tolvanen 	PROCESS_TYPE(class)
726f6bb9245SSami Tolvanen 	PROCESS_TYPE(structure)
727f6bb9245SSami Tolvanen 	PROCESS_TYPE(union)
728f6bb9245SSami Tolvanen 	PROCESS_TYPE(enumeration)
729220a0857SSami Tolvanen 	/* Subtypes */
730f6bb9245SSami Tolvanen 	PROCESS_TYPE(enumerator)
731220a0857SSami Tolvanen 	PROCESS_TYPE(formal_parameter)
732f6bb9245SSami Tolvanen 	PROCESS_TYPE(member)
733c772f1d1SSami Tolvanen 	PROCESS_TYPE(subrange)
734f6bb9245SSami Tolvanen 	PROCESS_TYPE(template_type_parameter)
735f6bb9245SSami Tolvanen 	PROCESS_TYPE(variant)
736f6bb9245SSami Tolvanen 	PROCESS_TYPE(variant_part)
73706b8b036SSami Tolvanen 	/* Other types */
738c772f1d1SSami Tolvanen 	PROCESS_TYPE(array)
7395b7780e8SSami Tolvanen 	PROCESS_TYPE(base)
740220a0857SSami Tolvanen 	PROCESS_TYPE(subroutine)
74106b8b036SSami Tolvanen 	PROCESS_TYPE(typedef)
742f6bb9245SSami Tolvanen 	PROCESS_TYPE(unspecified)
7435b7780e8SSami Tolvanen 	default:
744f6bb9245SSami Tolvanen 		error("unexpected type: %x", tag);
7455b7780e8SSami Tolvanen 	}
7465b7780e8SSami Tolvanen 
747d2ffdc1cSSami Tolvanen 	die_debug_r("parent %p cache %p die addr %p tag %x", parent, cache,
748d2ffdc1cSSami Tolvanen 		    die->addr, tag);
749d2ffdc1cSSami Tolvanen 
7500c1c7627SSami Tolvanen 	/* Update cache state and append to the parent (if any) */
7510c1c7627SSami Tolvanen 	cache->tag = tag;
752f936c129SSami Tolvanen 	cache->state = want_state;
7530c1c7627SSami Tolvanen 	die_map_add_die(parent, cache);
7540c1c7627SSami Tolvanen 
755f936c129SSami Tolvanen 	expansion_state_restore(&state->expand, &saved);
7565b7780e8SSami Tolvanen 	return 0;
7575b7780e8SSami Tolvanen }
7585b7780e8SSami Tolvanen 
759f2856884SSami Tolvanen /*
760f2856884SSami Tolvanen  * Exported symbol processing
761f2856884SSami Tolvanen  */
76271378888SSami Tolvanen static struct die *get_symbol_cache(struct state *state, Dwarf_Die *die)
76371378888SSami Tolvanen {
76471378888SSami Tolvanen 	struct die *cache;
76571378888SSami Tolvanen 
76671378888SSami Tolvanen 	cache = die_map_get(die, DIE_SYMBOL);
76771378888SSami Tolvanen 
76871378888SSami Tolvanen 	if (cache->state != DIE_INCOMPLETE)
76971378888SSami Tolvanen 		return NULL; /* We already processed a symbol for this DIE */
77071378888SSami Tolvanen 
77171378888SSami Tolvanen 	cache->tag = dwarf_tag(die);
77271378888SSami Tolvanen 	return cache;
77371378888SSami Tolvanen }
77471378888SSami Tolvanen 
775f2856884SSami Tolvanen static void process_symbol(struct state *state, Dwarf_Die *die,
776f2856884SSami Tolvanen 			   die_callback_t process_func)
777f2856884SSami Tolvanen {
77871378888SSami Tolvanen 	struct die *cache;
77971378888SSami Tolvanen 
78071378888SSami Tolvanen 	symbol_set_die(state->sym, die);
78171378888SSami Tolvanen 
78271378888SSami Tolvanen 	cache = get_symbol_cache(state, die);
78371378888SSami Tolvanen 	if (!cache)
78471378888SSami Tolvanen 		return;
78571378888SSami Tolvanen 
786f2856884SSami Tolvanen 	debug("%s", state->sym->name);
78771378888SSami Tolvanen 	check(process_func(state, cache, die));
78871378888SSami Tolvanen 	cache->state = DIE_SYMBOL;
789f2856884SSami Tolvanen 	if (dump_dies)
790f2856884SSami Tolvanen 		fputs("\n", stderr);
791f2856884SSami Tolvanen }
792f2856884SSami Tolvanen 
7930c1c7627SSami Tolvanen static int __process_subprogram(struct state *state, struct die *cache,
7940c1c7627SSami Tolvanen 				Dwarf_Die *die)
795f2856884SSami Tolvanen {
796220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subprogram");
797f2856884SSami Tolvanen 	return 0;
798f2856884SSami Tolvanen }
799f2856884SSami Tolvanen 
800f2856884SSami Tolvanen static void process_subprogram(struct state *state, Dwarf_Die *die)
801f2856884SSami Tolvanen {
802f2856884SSami Tolvanen 	process_symbol(state, die, __process_subprogram);
803f2856884SSami Tolvanen }
804f2856884SSami Tolvanen 
8050c1c7627SSami Tolvanen static int __process_variable(struct state *state, struct die *cache,
8060c1c7627SSami Tolvanen 			      Dwarf_Die *die)
807f2856884SSami Tolvanen {
8080c1c7627SSami Tolvanen 	process(cache, "variable ");
8090c1c7627SSami Tolvanen 	process_type_attr(state, cache, die);
810f2856884SSami Tolvanen 	return 0;
811f2856884SSami Tolvanen }
812f2856884SSami Tolvanen 
813f2856884SSami Tolvanen static void process_variable(struct state *state, Dwarf_Die *die)
814f2856884SSami Tolvanen {
815f2856884SSami Tolvanen 	process_symbol(state, die, __process_variable);
816f2856884SSami Tolvanen }
817f2856884SSami Tolvanen 
8180c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache,
8190c1c7627SSami Tolvanen 				    Dwarf_Die *die)
820f2856884SSami Tolvanen {
821f2856884SSami Tolvanen 	int tag = dwarf_tag(die);
822f2856884SSami Tolvanen 
823f2856884SSami Tolvanen 	switch (tag) {
824f2856884SSami Tolvanen 	/* Possible containers of exported symbols */
825f2856884SSami Tolvanen 	case DW_TAG_namespace:
826f2856884SSami Tolvanen 	case DW_TAG_class_type:
827f2856884SSami Tolvanen 	case DW_TAG_structure_type:
828f2856884SSami Tolvanen 		return check(process_die_container(
8290c1c7627SSami Tolvanen 			NULL, cache, die, process_exported_symbols, match_all));
830f2856884SSami Tolvanen 
831f2856884SSami Tolvanen 	/* Possible exported symbols */
832f2856884SSami Tolvanen 	case DW_TAG_subprogram:
833f2856884SSami Tolvanen 	case DW_TAG_variable: {
834f2856884SSami Tolvanen 		struct state state;
835f2856884SSami Tolvanen 
836f2856884SSami Tolvanen 		if (!match_export_symbol(&state, die))
837f2856884SSami Tolvanen 			return 0;
838f2856884SSami Tolvanen 
839f936c129SSami Tolvanen 		state_init(&state);
840f936c129SSami Tolvanen 
841f2856884SSami Tolvanen 		if (tag == DW_TAG_subprogram)
842f2856884SSami Tolvanen 			process_subprogram(&state, &state.die);
843f2856884SSami Tolvanen 		else
844f2856884SSami Tolvanen 			process_variable(&state, &state.die);
845f2856884SSami Tolvanen 
846f936c129SSami Tolvanen 		cache_free(&state.expansion_cache);
847f2856884SSami Tolvanen 		return 0;
848f2856884SSami Tolvanen 	}
849f2856884SSami Tolvanen 	default:
850f2856884SSami Tolvanen 		return 0;
851f2856884SSami Tolvanen 	}
852f2856884SSami Tolvanen }
853f2856884SSami Tolvanen 
854f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie)
855f2856884SSami Tolvanen {
8560c1c7627SSami Tolvanen 	check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
857f2856884SSami Tolvanen 				    match_all));
858f936c129SSami Tolvanen 
859f936c129SSami Tolvanen 	cache_free(&srcfile_cache);
860f2856884SSami Tolvanen }
861