xref: /linux/scripts/gendwarfksyms/dwarf.c (revision f936c129fd4c3ce495768374ea48e5b736655046)
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 
30*f936c129SSami 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 
84*f936c129SSami Tolvanen /* DW_AT_decl_file -> struct srcfile */
85*f936c129SSami Tolvanen static struct cache srcfile_cache;
86*f936c129SSami Tolvanen 
87*f936c129SSami Tolvanen static bool is_definition_private(Dwarf_Die *die)
88*f936c129SSami Tolvanen {
89*f936c129SSami Tolvanen 	Dwarf_Word filenum;
90*f936c129SSami Tolvanen 	Dwarf_Files *files;
91*f936c129SSami Tolvanen 	Dwarf_Die cudie;
92*f936c129SSami Tolvanen 	const char *s;
93*f936c129SSami Tolvanen 	int res;
94*f936c129SSami Tolvanen 
95*f936c129SSami Tolvanen 	/*
96*f936c129SSami Tolvanen 	 * Definitions in .c files cannot change the public ABI,
97*f936c129SSami Tolvanen 	 * so consider them private.
98*f936c129SSami Tolvanen 	 */
99*f936c129SSami Tolvanen 	if (!get_udata_attr(die, DW_AT_decl_file, &filenum))
100*f936c129SSami Tolvanen 		return false;
101*f936c129SSami Tolvanen 
102*f936c129SSami Tolvanen 	res = cache_get(&srcfile_cache, filenum);
103*f936c129SSami Tolvanen 	if (res >= 0)
104*f936c129SSami Tolvanen 		return !!res;
105*f936c129SSami Tolvanen 
106*f936c129SSami Tolvanen 	if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL))
107*f936c129SSami Tolvanen 		error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1));
108*f936c129SSami Tolvanen 
109*f936c129SSami Tolvanen 	if (dwarf_getsrcfiles(&cudie, &files, NULL))
110*f936c129SSami Tolvanen 		error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1));
111*f936c129SSami Tolvanen 
112*f936c129SSami Tolvanen 	s = dwarf_filesrc(files, filenum, NULL, NULL);
113*f936c129SSami Tolvanen 	if (!s)
114*f936c129SSami Tolvanen 		error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1));
115*f936c129SSami Tolvanen 
116*f936c129SSami Tolvanen 	s = strrchr(s, '.');
117*f936c129SSami Tolvanen 	res = s && !strcmp(s, ".c");
118*f936c129SSami Tolvanen 	cache_set(&srcfile_cache, filenum, res);
119*f936c129SSami Tolvanen 
120*f936c129SSami Tolvanen 	return !!res;
121*f936c129SSami Tolvanen }
122*f936c129SSami Tolvanen 
123*f936c129SSami Tolvanen static bool is_kabi_definition(Dwarf_Die *die)
124*f936c129SSami Tolvanen {
125*f936c129SSami Tolvanen 	bool value;
126*f936c129SSami Tolvanen 
127*f936c129SSami Tolvanen 	if (get_flag_attr(die, DW_AT_declaration, &value) && value)
128*f936c129SSami Tolvanen 		return false;
129*f936c129SSami Tolvanen 
130*f936c129SSami Tolvanen 	return !is_definition_private(die);
131*f936c129SSami Tolvanen }
132*f936c129SSami Tolvanen 
133f2856884SSami Tolvanen /*
134f2856884SSami Tolvanen  * Type string processing
135f2856884SSami Tolvanen  */
1360c1c7627SSami Tolvanen static void process(struct die *cache, const char *s)
137f2856884SSami Tolvanen {
138f2856884SSami Tolvanen 	s = s ?: "<null>";
139f2856884SSami Tolvanen 
14006b8b036SSami Tolvanen 	if (dump_dies && do_linebreak) {
14106b8b036SSami Tolvanen 		fputs("\n", stderr);
14206b8b036SSami Tolvanen 		for (int i = 0; i < indentation_level; i++)
14306b8b036SSami Tolvanen 			fputs("  ", stderr);
14406b8b036SSami Tolvanen 		do_linebreak = false;
14506b8b036SSami Tolvanen 	}
146f2856884SSami Tolvanen 	if (dump_dies)
147f2856884SSami Tolvanen 		fputs(s, stderr);
1480c1c7627SSami Tolvanen 
1490c1c7627SSami Tolvanen 	die_map_add_string(cache, s);
150f2856884SSami Tolvanen }
151f2856884SSami Tolvanen 
1525b7780e8SSami Tolvanen #define MAX_FMT_BUFFER_SIZE 128
1535b7780e8SSami Tolvanen 
1540c1c7627SSami Tolvanen static void process_fmt(struct die *cache, const char *fmt, ...)
1555b7780e8SSami Tolvanen {
1565b7780e8SSami Tolvanen 	char buf[MAX_FMT_BUFFER_SIZE];
1575b7780e8SSami Tolvanen 	va_list args;
1585b7780e8SSami Tolvanen 
1595b7780e8SSami Tolvanen 	va_start(args, fmt);
1605b7780e8SSami Tolvanen 
1615b7780e8SSami Tolvanen 	if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf))
1625b7780e8SSami Tolvanen 		error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE");
1635b7780e8SSami Tolvanen 
1640c1c7627SSami Tolvanen 	process(cache, buf);
1655b7780e8SSami Tolvanen 	va_end(args);
1665b7780e8SSami Tolvanen }
1675b7780e8SSami Tolvanen 
1685b7780e8SSami Tolvanen #define MAX_FQN_SIZE 64
1695b7780e8SSami Tolvanen 
1705b7780e8SSami Tolvanen /* Get a fully qualified name from DWARF scopes */
1715b7780e8SSami Tolvanen static char *get_fqn(Dwarf_Die *die)
1725b7780e8SSami Tolvanen {
1735b7780e8SSami Tolvanen 	const char *list[MAX_FQN_SIZE];
1745b7780e8SSami Tolvanen 	Dwarf_Die *scopes = NULL;
1755b7780e8SSami Tolvanen 	bool has_name = false;
1765b7780e8SSami Tolvanen 	char *fqn = NULL;
1775b7780e8SSami Tolvanen 	char *p;
1785b7780e8SSami Tolvanen 	int count = 0;
1795b7780e8SSami Tolvanen 	int len = 0;
1805b7780e8SSami Tolvanen 	int res;
1815b7780e8SSami Tolvanen 	int i;
1825b7780e8SSami Tolvanen 
1835b7780e8SSami Tolvanen 	res = checkp(dwarf_getscopes_die(die, &scopes));
1845b7780e8SSami Tolvanen 	if (!res) {
1855b7780e8SSami Tolvanen 		list[count] = get_name_attr(die);
1865b7780e8SSami Tolvanen 
1875b7780e8SSami Tolvanen 		if (!list[count])
1885b7780e8SSami Tolvanen 			return NULL;
1895b7780e8SSami Tolvanen 
1905b7780e8SSami Tolvanen 		len += strlen(list[count]);
1915b7780e8SSami Tolvanen 		count++;
1925b7780e8SSami Tolvanen 
1935b7780e8SSami Tolvanen 		goto done;
1945b7780e8SSami Tolvanen 	}
1955b7780e8SSami Tolvanen 
1965b7780e8SSami Tolvanen 	for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) {
1975b7780e8SSami Tolvanen 		if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit)
1985b7780e8SSami Tolvanen 			continue;
1995b7780e8SSami Tolvanen 
2005b7780e8SSami Tolvanen 		list[count] = get_name_attr(&scopes[i]);
2015b7780e8SSami Tolvanen 
2025b7780e8SSami Tolvanen 		if (list[count]) {
2035b7780e8SSami Tolvanen 			has_name = true;
2045b7780e8SSami Tolvanen 		} else {
2055b7780e8SSami Tolvanen 			list[count] = "<anonymous>";
2065b7780e8SSami Tolvanen 			has_name = false;
2075b7780e8SSami Tolvanen 		}
2085b7780e8SSami Tolvanen 
2095b7780e8SSami Tolvanen 		len += strlen(list[count]);
2105b7780e8SSami Tolvanen 		count++;
2115b7780e8SSami Tolvanen 
2125b7780e8SSami Tolvanen 		if (i > 0) {
2135b7780e8SSami Tolvanen 			list[count++] = "::";
2145b7780e8SSami Tolvanen 			len += 2;
2155b7780e8SSami Tolvanen 		}
2165b7780e8SSami Tolvanen 	}
2175b7780e8SSami Tolvanen 
2185b7780e8SSami Tolvanen 	free(scopes);
2195b7780e8SSami Tolvanen 
2205b7780e8SSami Tolvanen 	if (count == MAX_FQN_SIZE)
2215b7780e8SSami Tolvanen 		warn("increase MAX_FQN_SIZE: reached the maximum");
2225b7780e8SSami Tolvanen 
2235b7780e8SSami Tolvanen 	/* Consider the DIE unnamed if the last scope doesn't have a name */
2245b7780e8SSami Tolvanen 	if (!has_name)
2255b7780e8SSami Tolvanen 		return NULL;
2265b7780e8SSami Tolvanen done:
2275b7780e8SSami Tolvanen 	fqn = xmalloc(len + 1);
2285b7780e8SSami Tolvanen 	*fqn = '\0';
2295b7780e8SSami Tolvanen 
2305b7780e8SSami Tolvanen 	p = fqn;
2315b7780e8SSami Tolvanen 	for (i = 0; i < count; i++)
2325b7780e8SSami Tolvanen 		p = stpcpy(p, list[i]);
2335b7780e8SSami Tolvanen 
2345b7780e8SSami Tolvanen 	return fqn;
2355b7780e8SSami Tolvanen }
2365b7780e8SSami Tolvanen 
2370c1c7627SSami Tolvanen static void update_fqn(struct die *cache, Dwarf_Die *die)
2385b7780e8SSami Tolvanen {
2390c1c7627SSami Tolvanen 	if (!cache->fqn)
2400c1c7627SSami Tolvanen 		cache->fqn = get_fqn(die) ?: "";
2410c1c7627SSami Tolvanen }
2420c1c7627SSami Tolvanen 
2430c1c7627SSami Tolvanen static void process_fqn(struct die *cache, Dwarf_Die *die)
2440c1c7627SSami Tolvanen {
2450c1c7627SSami Tolvanen 	update_fqn(cache, die);
2460c1c7627SSami Tolvanen 	if (*cache->fqn)
2470c1c7627SSami Tolvanen 		process(cache, " ");
2480c1c7627SSami Tolvanen 	process(cache, cache->fqn);
2495b7780e8SSami Tolvanen }
2505b7780e8SSami Tolvanen 
2515b7780e8SSami Tolvanen #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute)                          \
2520c1c7627SSami Tolvanen 	static void process_##attribute##_attr(struct die *cache,          \
2530c1c7627SSami Tolvanen 					       Dwarf_Die *die)             \
2545b7780e8SSami Tolvanen 	{                                                                  \
2555b7780e8SSami Tolvanen 		Dwarf_Word value;                                          \
2565b7780e8SSami Tolvanen 		if (get_udata_attr(die, DW_AT_##attribute, &value))        \
2570c1c7627SSami Tolvanen 			process_fmt(cache, " " #attribute "(%" PRIu64 ")", \
2580c1c7627SSami Tolvanen 				    value);                                \
2595b7780e8SSami Tolvanen 	}
2605b7780e8SSami Tolvanen 
261f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
2625b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
263f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
2645b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
2655b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
266f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
267f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
268f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
2695b7780e8SSami Tolvanen 
270220a0857SSami Tolvanen /* Match functions -- die_match_callback_t */
271220a0857SSami Tolvanen #define DEFINE_MATCH(type)                                     \
272220a0857SSami Tolvanen 	static bool match_##type##_type(Dwarf_Die *die)        \
273220a0857SSami Tolvanen 	{                                                      \
274220a0857SSami Tolvanen 		return dwarf_tag(die) == DW_TAG_##type##_type; \
275220a0857SSami Tolvanen 	}
276220a0857SSami Tolvanen 
277f6bb9245SSami Tolvanen DEFINE_MATCH(enumerator)
278220a0857SSami Tolvanen DEFINE_MATCH(formal_parameter)
279f6bb9245SSami Tolvanen DEFINE_MATCH(member)
280c772f1d1SSami Tolvanen DEFINE_MATCH(subrange)
281220a0857SSami Tolvanen 
282f2856884SSami Tolvanen bool match_all(Dwarf_Die *die)
283f2856884SSami Tolvanen {
284f2856884SSami Tolvanen 	return true;
285f2856884SSami Tolvanen }
286f2856884SSami Tolvanen 
2870c1c7627SSami Tolvanen int process_die_container(struct state *state, struct die *cache,
2880c1c7627SSami Tolvanen 			  Dwarf_Die *die, die_callback_t func,
2890c1c7627SSami Tolvanen 			  die_match_callback_t match)
290f2856884SSami Tolvanen {
291f2856884SSami Tolvanen 	Dwarf_Die current;
292f2856884SSami Tolvanen 	int res;
293f2856884SSami Tolvanen 
294220a0857SSami Tolvanen 	/* Track the first item in lists. */
295220a0857SSami Tolvanen 	if (state)
296220a0857SSami Tolvanen 		state->first_list_item = true;
297220a0857SSami Tolvanen 
298f2856884SSami Tolvanen 	res = checkp(dwarf_child(die, &current));
299f2856884SSami Tolvanen 	while (!res) {
300f2856884SSami Tolvanen 		if (match(&current)) {
301f2856884SSami Tolvanen 			/* <0 = error, 0 = continue, >0 = stop */
3020c1c7627SSami Tolvanen 			res = checkp(func(state, cache, &current));
303f2856884SSami Tolvanen 			if (res)
304220a0857SSami Tolvanen 				goto out;
305f2856884SSami Tolvanen 		}
306f2856884SSami Tolvanen 
307f2856884SSami Tolvanen 		res = checkp(dwarf_siblingof(&current, &current));
308f2856884SSami Tolvanen 	}
309f2856884SSami Tolvanen 
310220a0857SSami Tolvanen 	res = 0;
311220a0857SSami Tolvanen out:
312220a0857SSami Tolvanen 	if (state)
313220a0857SSami Tolvanen 		state->first_list_item = false;
314220a0857SSami Tolvanen 
315220a0857SSami Tolvanen 	return res;
316f2856884SSami Tolvanen }
317f2856884SSami Tolvanen 
3180c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent,
3190c1c7627SSami Tolvanen 			Dwarf_Die *die);
3205b7780e8SSami Tolvanen 
3210c1c7627SSami Tolvanen static void process_type_attr(struct state *state, struct die *cache,
3220c1c7627SSami Tolvanen 			      Dwarf_Die *die)
3235b7780e8SSami Tolvanen {
3245b7780e8SSami Tolvanen 	Dwarf_Die type;
3255b7780e8SSami Tolvanen 
3265b7780e8SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_type, &type)) {
3270c1c7627SSami Tolvanen 		check(process_type(state, cache, &type));
3285b7780e8SSami Tolvanen 		return;
3295b7780e8SSami Tolvanen 	}
3305b7780e8SSami Tolvanen 
3315b7780e8SSami Tolvanen 	/* Compilers can omit DW_AT_type -- print out 'void' to clarify */
3320c1c7627SSami Tolvanen 	process(cache, "base_type void");
3335b7780e8SSami Tolvanen }
3345b7780e8SSami Tolvanen 
335220a0857SSami Tolvanen static void process_list_comma(struct state *state, struct die *cache)
336220a0857SSami Tolvanen {
337220a0857SSami Tolvanen 	if (state->first_list_item) {
338220a0857SSami Tolvanen 		state->first_list_item = false;
339220a0857SSami Tolvanen 	} else {
340220a0857SSami Tolvanen 		process(cache, " ,");
341220a0857SSami Tolvanen 		process_linebreak(cache, 0);
342220a0857SSami Tolvanen 	}
343220a0857SSami Tolvanen }
344220a0857SSami Tolvanen 
345220a0857SSami Tolvanen /* Comma-separated with DW_AT_type */
346220a0857SSami Tolvanen static void __process_list_type(struct state *state, struct die *cache,
347220a0857SSami Tolvanen 				Dwarf_Die *die, const char *type)
348220a0857SSami Tolvanen {
349220a0857SSami Tolvanen 	const char *name = get_name_attr(die);
350220a0857SSami Tolvanen 
351220a0857SSami Tolvanen 	process_list_comma(state, cache);
352220a0857SSami Tolvanen 	process(cache, type);
353220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
354220a0857SSami Tolvanen 	if (name) {
355220a0857SSami Tolvanen 		process(cache, " ");
356220a0857SSami Tolvanen 		process(cache, name);
357220a0857SSami Tolvanen 	}
358f6bb9245SSami Tolvanen 	process_accessibility_attr(cache, die);
359f6bb9245SSami Tolvanen 	process_bit_size_attr(cache, die);
360f6bb9245SSami Tolvanen 	process_data_bit_offset_attr(cache, die);
361f6bb9245SSami Tolvanen 	process_data_member_location_attr(cache, die);
362220a0857SSami Tolvanen }
363220a0857SSami Tolvanen 
364220a0857SSami Tolvanen #define DEFINE_PROCESS_LIST_TYPE(type)                                       \
365220a0857SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
366220a0857SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
367220a0857SSami Tolvanen 	{                                                                    \
368220a0857SSami Tolvanen 		__process_list_type(state, cache, die, #type " ");           \
369220a0857SSami Tolvanen 	}
370220a0857SSami Tolvanen 
371220a0857SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(formal_parameter)
372f6bb9245SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(member)
373220a0857SSami Tolvanen 
37406b8b036SSami Tolvanen /* Container types with DW_AT_type */
37506b8b036SSami Tolvanen static void __process_type(struct state *state, struct die *cache,
37606b8b036SSami Tolvanen 			   Dwarf_Die *die, const char *type)
37706b8b036SSami Tolvanen {
37806b8b036SSami Tolvanen 	process(cache, type);
37906b8b036SSami Tolvanen 	process_fqn(cache, die);
38006b8b036SSami Tolvanen 	process(cache, " {");
38106b8b036SSami Tolvanen 	process_linebreak(cache, 1);
38206b8b036SSami Tolvanen 	process_type_attr(state, cache, die);
38306b8b036SSami Tolvanen 	process_linebreak(cache, -1);
38406b8b036SSami Tolvanen 	process(cache, "}");
38506b8b036SSami Tolvanen 	process_byte_size_attr(cache, die);
38606b8b036SSami Tolvanen 	process_alignment_attr(cache, die);
38706b8b036SSami Tolvanen }
38806b8b036SSami Tolvanen 
38906b8b036SSami Tolvanen #define DEFINE_PROCESS_TYPE(type)                                            \
39006b8b036SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
39106b8b036SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
39206b8b036SSami Tolvanen 	{                                                                    \
39306b8b036SSami Tolvanen 		__process_type(state, cache, die, #type "_type");            \
39406b8b036SSami Tolvanen 	}
39506b8b036SSami Tolvanen 
39606b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(atomic)
39706b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(const)
39806b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(immutable)
39906b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(packed)
40006b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(pointer)
40106b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(reference)
40206b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(restrict)
40306b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(rvalue_reference)
40406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(shared)
405f6bb9245SSami Tolvanen DEFINE_PROCESS_TYPE(template_type_parameter)
40606b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(volatile)
40706b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(typedef)
40806b8b036SSami Tolvanen 
409c772f1d1SSami Tolvanen static void process_subrange_type(struct state *state, struct die *cache,
410c772f1d1SSami Tolvanen 				  Dwarf_Die *die)
411c772f1d1SSami Tolvanen {
412c772f1d1SSami Tolvanen 	Dwarf_Word count = 0;
413c772f1d1SSami Tolvanen 
414c772f1d1SSami Tolvanen 	if (get_udata_attr(die, DW_AT_count, &count))
415c772f1d1SSami Tolvanen 		process_fmt(cache, "[%" PRIu64 "]", count);
416c772f1d1SSami Tolvanen 	else if (get_udata_attr(die, DW_AT_upper_bound, &count))
417c772f1d1SSami Tolvanen 		process_fmt(cache, "[%" PRIu64 "]", count + 1);
418c772f1d1SSami Tolvanen 	else
419c772f1d1SSami Tolvanen 		process(cache, "[]");
420c772f1d1SSami Tolvanen }
421c772f1d1SSami Tolvanen 
422c772f1d1SSami Tolvanen static void process_array_type(struct state *state, struct die *cache,
423c772f1d1SSami Tolvanen 			       Dwarf_Die *die)
424c772f1d1SSami Tolvanen {
425c772f1d1SSami Tolvanen 	process(cache, "array_type");
426c772f1d1SSami Tolvanen 	/* Array size */
427c772f1d1SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
428c772f1d1SSami Tolvanen 				    match_subrange_type));
429c772f1d1SSami Tolvanen 	process(cache, " {");
430c772f1d1SSami Tolvanen 	process_linebreak(cache, 1);
431c772f1d1SSami Tolvanen 	process_type_attr(state, cache, die);
432c772f1d1SSami Tolvanen 	process_linebreak(cache, -1);
433c772f1d1SSami Tolvanen 	process(cache, "}");
434c772f1d1SSami Tolvanen }
435c772f1d1SSami Tolvanen 
436220a0857SSami Tolvanen static void __process_subroutine_type(struct state *state, struct die *cache,
437220a0857SSami Tolvanen 				      Dwarf_Die *die, const char *type)
438220a0857SSami Tolvanen {
439220a0857SSami Tolvanen 	process(cache, type);
440220a0857SSami Tolvanen 	process(cache, " (");
441220a0857SSami Tolvanen 	process_linebreak(cache, 1);
442220a0857SSami Tolvanen 	/* Parameters */
443220a0857SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
444220a0857SSami Tolvanen 				    match_formal_parameter_type));
445220a0857SSami Tolvanen 	process_linebreak(cache, -1);
446220a0857SSami Tolvanen 	process(cache, ")");
447220a0857SSami Tolvanen 	process_linebreak(cache, 0);
448220a0857SSami Tolvanen 	/* Return type */
449220a0857SSami Tolvanen 	process(cache, "-> ");
450220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
451220a0857SSami Tolvanen }
452220a0857SSami Tolvanen 
453220a0857SSami Tolvanen static void process_subroutine_type(struct state *state, struct die *cache,
454220a0857SSami Tolvanen 				    Dwarf_Die *die)
455220a0857SSami Tolvanen {
456220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subroutine_type");
457220a0857SSami Tolvanen }
458220a0857SSami Tolvanen 
459f6bb9245SSami Tolvanen static void process_variant_type(struct state *state, struct die *cache,
460f6bb9245SSami Tolvanen 				 Dwarf_Die *die)
461f6bb9245SSami Tolvanen {
462f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
463f6bb9245SSami Tolvanen 	process(cache, "variant {");
464f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
465f6bb9245SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
466f6bb9245SSami Tolvanen 				    match_member_type));
467f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
468f6bb9245SSami Tolvanen 	process(cache, "}");
469f6bb9245SSami Tolvanen 	process_discr_value_attr(cache, die);
470f6bb9245SSami Tolvanen }
471f6bb9245SSami Tolvanen 
472f6bb9245SSami Tolvanen static void process_variant_part_type(struct state *state, struct die *cache,
473f6bb9245SSami Tolvanen 				      Dwarf_Die *die)
474f6bb9245SSami Tolvanen {
475f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
476f6bb9245SSami Tolvanen 	process(cache, "variant_part {");
477f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
478f6bb9245SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
479f6bb9245SSami Tolvanen 				    match_all));
480f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
481f6bb9245SSami Tolvanen 	process(cache, "}");
482f6bb9245SSami Tolvanen }
483f6bb9245SSami Tolvanen 
484f6bb9245SSami Tolvanen static int ___process_structure_type(struct state *state, struct die *cache,
485f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
486f6bb9245SSami Tolvanen {
487f6bb9245SSami Tolvanen 	switch (dwarf_tag(die)) {
488f6bb9245SSami Tolvanen 	case DW_TAG_member:
489f6bb9245SSami Tolvanen 	case DW_TAG_variant_part:
490f6bb9245SSami Tolvanen 		return check(process_type(state, cache, die));
491f6bb9245SSami Tolvanen 	case DW_TAG_class_type:
492f6bb9245SSami Tolvanen 	case DW_TAG_enumeration_type:
493f6bb9245SSami Tolvanen 	case DW_TAG_structure_type:
494f6bb9245SSami Tolvanen 	case DW_TAG_template_type_parameter:
495f6bb9245SSami Tolvanen 	case DW_TAG_union_type:
496f6bb9245SSami Tolvanen 	case DW_TAG_subprogram:
497f6bb9245SSami Tolvanen 		/* Skip non-member types, including member functions */
498f6bb9245SSami Tolvanen 		return 0;
499f6bb9245SSami Tolvanen 	default:
500f6bb9245SSami Tolvanen 		error("unexpected structure_type child: %x", dwarf_tag(die));
501f6bb9245SSami Tolvanen 	}
502f6bb9245SSami Tolvanen }
503f6bb9245SSami Tolvanen 
504f6bb9245SSami Tolvanen static void __process_structure_type(struct state *state, struct die *cache,
505f6bb9245SSami Tolvanen 				     Dwarf_Die *die, const char *type,
506f6bb9245SSami Tolvanen 				     die_callback_t process_func,
507f6bb9245SSami Tolvanen 				     die_match_callback_t match_func)
508f6bb9245SSami Tolvanen {
509*f936c129SSami Tolvanen 	bool expand;
510*f936c129SSami Tolvanen 
511f6bb9245SSami Tolvanen 	process(cache, type);
512f6bb9245SSami Tolvanen 	process_fqn(cache, die);
513f6bb9245SSami Tolvanen 	process(cache, " {");
514f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
515f6bb9245SSami Tolvanen 
516*f936c129SSami Tolvanen 	expand = state->expand.expand && is_kabi_definition(die);
517*f936c129SSami Tolvanen 
518*f936c129SSami Tolvanen 	if (expand) {
519f6bb9245SSami Tolvanen 		check(process_die_container(state, cache, die, process_func,
520f6bb9245SSami Tolvanen 					    match_func));
521*f936c129SSami Tolvanen 	}
522f6bb9245SSami Tolvanen 
523f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
524f6bb9245SSami Tolvanen 	process(cache, "}");
525f6bb9245SSami Tolvanen 
526*f936c129SSami Tolvanen 	if (expand) {
527f6bb9245SSami Tolvanen 		process_byte_size_attr(cache, die);
528f6bb9245SSami Tolvanen 		process_alignment_attr(cache, die);
529f6bb9245SSami Tolvanen 	}
530*f936c129SSami Tolvanen }
531f6bb9245SSami Tolvanen 
532f6bb9245SSami Tolvanen #define DEFINE_PROCESS_STRUCTURE_TYPE(structure)                        \
533f6bb9245SSami Tolvanen 	static void process_##structure##_type(                         \
534f6bb9245SSami Tolvanen 		struct state *state, struct die *cache, Dwarf_Die *die) \
535f6bb9245SSami Tolvanen 	{                                                               \
536f6bb9245SSami Tolvanen 		__process_structure_type(state, cache, die,             \
537f6bb9245SSami Tolvanen 					 #structure "_type",            \
538f6bb9245SSami Tolvanen 					 ___process_structure_type,     \
539f6bb9245SSami Tolvanen 					 match_all);                    \
540f6bb9245SSami Tolvanen 	}
541f6bb9245SSami Tolvanen 
542f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(class)
543f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(structure)
544f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(union)
545f6bb9245SSami Tolvanen 
546f6bb9245SSami Tolvanen static void process_enumerator_type(struct state *state, struct die *cache,
547f6bb9245SSami Tolvanen 				    Dwarf_Die *die)
548f6bb9245SSami Tolvanen {
549f6bb9245SSami Tolvanen 	Dwarf_Word value;
550f6bb9245SSami Tolvanen 
551f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
552f6bb9245SSami Tolvanen 	process(cache, "enumerator");
553f6bb9245SSami Tolvanen 	process_fqn(cache, die);
554f6bb9245SSami Tolvanen 
555f6bb9245SSami Tolvanen 	if (get_udata_attr(die, DW_AT_const_value, &value)) {
556f6bb9245SSami Tolvanen 		process(cache, " = ");
557f6bb9245SSami Tolvanen 		process_fmt(cache, "%" PRIu64, value);
558f6bb9245SSami Tolvanen 	}
559f6bb9245SSami Tolvanen }
560f6bb9245SSami Tolvanen 
561f6bb9245SSami Tolvanen static void process_enumeration_type(struct state *state, struct die *cache,
562f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
563f6bb9245SSami Tolvanen {
564f6bb9245SSami Tolvanen 	__process_structure_type(state, cache, die, "enumeration_type",
565f6bb9245SSami Tolvanen 				 process_type, match_enumerator_type);
566f6bb9245SSami Tolvanen }
567f6bb9245SSami Tolvanen 
5680c1c7627SSami Tolvanen static void process_base_type(struct state *state, struct die *cache,
5690c1c7627SSami Tolvanen 			      Dwarf_Die *die)
5705b7780e8SSami Tolvanen {
5710c1c7627SSami Tolvanen 	process(cache, "base_type");
5720c1c7627SSami Tolvanen 	process_fqn(cache, die);
5730c1c7627SSami Tolvanen 	process_byte_size_attr(cache, die);
5740c1c7627SSami Tolvanen 	process_encoding_attr(cache, die);
5750c1c7627SSami Tolvanen 	process_alignment_attr(cache, die);
5760c1c7627SSami Tolvanen }
5770c1c7627SSami Tolvanen 
578f6bb9245SSami Tolvanen static void process_unspecified_type(struct state *state, struct die *cache,
579f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
580f6bb9245SSami Tolvanen {
581f6bb9245SSami Tolvanen 	/*
582f6bb9245SSami Tolvanen 	 * These can be emitted for stand-alone assembly code, which means we
583f6bb9245SSami Tolvanen 	 * might run into them in vmlinux.o.
584f6bb9245SSami Tolvanen 	 */
585f6bb9245SSami Tolvanen 	process(cache, "unspecified_type");
586f6bb9245SSami Tolvanen }
587f6bb9245SSami Tolvanen 
5880c1c7627SSami Tolvanen static void process_cached(struct state *state, struct die *cache,
5890c1c7627SSami Tolvanen 			   Dwarf_Die *die)
5900c1c7627SSami Tolvanen {
5910c1c7627SSami Tolvanen 	struct die_fragment *df;
5920c1c7627SSami Tolvanen 	Dwarf_Die child;
5930c1c7627SSami Tolvanen 
5940c1c7627SSami Tolvanen 	list_for_each_entry(df, &cache->fragments, list) {
5950c1c7627SSami Tolvanen 		switch (df->type) {
5960c1c7627SSami Tolvanen 		case FRAGMENT_STRING:
5970c1c7627SSami Tolvanen 			process(NULL, df->data.str);
5980c1c7627SSami Tolvanen 			break;
59906b8b036SSami Tolvanen 		case FRAGMENT_LINEBREAK:
60006b8b036SSami Tolvanen 			process_linebreak(NULL, df->data.linebreak);
60106b8b036SSami Tolvanen 			break;
6020c1c7627SSami Tolvanen 		case FRAGMENT_DIE:
6030c1c7627SSami Tolvanen 			if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu),
6040c1c7627SSami Tolvanen 						(void *)df->data.addr, &child))
6050c1c7627SSami Tolvanen 				error("dwarf_die_addr_die failed");
6060c1c7627SSami Tolvanen 			check(process_type(state, NULL, &child));
6070c1c7627SSami Tolvanen 			break;
6080c1c7627SSami Tolvanen 		default:
6090c1c7627SSami Tolvanen 			error("empty die_fragment");
6100c1c7627SSami Tolvanen 		}
6110c1c7627SSami Tolvanen 	}
6125b7780e8SSami Tolvanen }
6135b7780e8SSami Tolvanen 
614*f936c129SSami Tolvanen static void state_init(struct state *state)
615*f936c129SSami Tolvanen {
616*f936c129SSami Tolvanen 	state->expand.expand = true;
617*f936c129SSami Tolvanen 	cache_init(&state->expansion_cache);
618*f936c129SSami Tolvanen }
619*f936c129SSami Tolvanen 
620*f936c129SSami Tolvanen static void expansion_state_restore(struct expansion_state *state,
621*f936c129SSami Tolvanen 				    struct expansion_state *saved)
622*f936c129SSami Tolvanen {
623*f936c129SSami Tolvanen 	state->expand = saved->expand;
624*f936c129SSami Tolvanen }
625*f936c129SSami Tolvanen 
626*f936c129SSami Tolvanen static void expansion_state_save(struct expansion_state *state,
627*f936c129SSami Tolvanen 				 struct expansion_state *saved)
628*f936c129SSami Tolvanen {
629*f936c129SSami Tolvanen 	expansion_state_restore(saved, state);
630*f936c129SSami Tolvanen }
631*f936c129SSami Tolvanen 
632*f936c129SSami Tolvanen static bool is_expanded_type(int tag)
633*f936c129SSami Tolvanen {
634*f936c129SSami Tolvanen 	return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
635*f936c129SSami Tolvanen 	       tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type;
636*f936c129SSami Tolvanen }
637*f936c129SSami Tolvanen 
6385b7780e8SSami Tolvanen #define PROCESS_TYPE(type)                                \
6395b7780e8SSami Tolvanen 	case DW_TAG_##type##_type:                        \
6400c1c7627SSami Tolvanen 		process_##type##_type(state, cache, die); \
6415b7780e8SSami Tolvanen 		break;
6425b7780e8SSami Tolvanen 
6430c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
6445b7780e8SSami Tolvanen {
645*f936c129SSami Tolvanen 	enum die_state want_state = DIE_COMPLETE;
6460c1c7627SSami Tolvanen 	struct die *cache;
647*f936c129SSami Tolvanen 	struct expansion_state saved;
6485b7780e8SSami Tolvanen 	int tag = dwarf_tag(die);
6495b7780e8SSami Tolvanen 
650*f936c129SSami Tolvanen 	expansion_state_save(&state->expand, &saved);
651*f936c129SSami Tolvanen 
6520c1c7627SSami Tolvanen 	/*
653*f936c129SSami Tolvanen 	 * Structures and enumeration types are expanded only once per
654*f936c129SSami Tolvanen 	 * exported symbol. This is sufficient for detecting ABI changes
655*f936c129SSami Tolvanen 	 * within the structure.
656*f936c129SSami Tolvanen 	 */
657*f936c129SSami Tolvanen 	if (is_expanded_type(tag)) {
658*f936c129SSami Tolvanen 		if (cache_was_expanded(&state->expansion_cache, die->addr))
659*f936c129SSami Tolvanen 			state->expand.expand = false;
660*f936c129SSami Tolvanen 
661*f936c129SSami Tolvanen 		if (state->expand.expand)
662*f936c129SSami Tolvanen 			cache_mark_expanded(&state->expansion_cache, die->addr);
663*f936c129SSami Tolvanen 		else
664*f936c129SSami Tolvanen 			want_state = DIE_UNEXPANDED;
665*f936c129SSami Tolvanen 	}
666*f936c129SSami Tolvanen 
667*f936c129SSami Tolvanen 	/*
668*f936c129SSami Tolvanen 	 * If we have want_state already cached, use it instead of walking
6690c1c7627SSami Tolvanen 	 * through DWARF.
6700c1c7627SSami Tolvanen 	 */
671*f936c129SSami Tolvanen 	cache = die_map_get(die, want_state);
6720c1c7627SSami Tolvanen 
673*f936c129SSami Tolvanen 	if (cache->state == want_state) {
6740c1c7627SSami Tolvanen 		process_cached(state, cache, die);
6750c1c7627SSami Tolvanen 		die_map_add_die(parent, cache);
676*f936c129SSami Tolvanen 
677*f936c129SSami Tolvanen 		expansion_state_restore(&state->expand, &saved);
6780c1c7627SSami Tolvanen 		return 0;
6790c1c7627SSami Tolvanen 	}
6800c1c7627SSami Tolvanen 
6815b7780e8SSami Tolvanen 	switch (tag) {
68206b8b036SSami Tolvanen 	/* Type modifiers */
68306b8b036SSami Tolvanen 	PROCESS_TYPE(atomic)
68406b8b036SSami Tolvanen 	PROCESS_TYPE(const)
68506b8b036SSami Tolvanen 	PROCESS_TYPE(immutable)
68606b8b036SSami Tolvanen 	PROCESS_TYPE(packed)
68706b8b036SSami Tolvanen 	PROCESS_TYPE(pointer)
68806b8b036SSami Tolvanen 	PROCESS_TYPE(reference)
68906b8b036SSami Tolvanen 	PROCESS_TYPE(restrict)
69006b8b036SSami Tolvanen 	PROCESS_TYPE(rvalue_reference)
69106b8b036SSami Tolvanen 	PROCESS_TYPE(shared)
69206b8b036SSami Tolvanen 	PROCESS_TYPE(volatile)
693f6bb9245SSami Tolvanen 	/* Container types */
694f6bb9245SSami Tolvanen 	PROCESS_TYPE(class)
695f6bb9245SSami Tolvanen 	PROCESS_TYPE(structure)
696f6bb9245SSami Tolvanen 	PROCESS_TYPE(union)
697f6bb9245SSami Tolvanen 	PROCESS_TYPE(enumeration)
698220a0857SSami Tolvanen 	/* Subtypes */
699f6bb9245SSami Tolvanen 	PROCESS_TYPE(enumerator)
700220a0857SSami Tolvanen 	PROCESS_TYPE(formal_parameter)
701f6bb9245SSami Tolvanen 	PROCESS_TYPE(member)
702c772f1d1SSami Tolvanen 	PROCESS_TYPE(subrange)
703f6bb9245SSami Tolvanen 	PROCESS_TYPE(template_type_parameter)
704f6bb9245SSami Tolvanen 	PROCESS_TYPE(variant)
705f6bb9245SSami Tolvanen 	PROCESS_TYPE(variant_part)
70606b8b036SSami Tolvanen 	/* Other types */
707c772f1d1SSami Tolvanen 	PROCESS_TYPE(array)
7085b7780e8SSami Tolvanen 	PROCESS_TYPE(base)
709220a0857SSami Tolvanen 	PROCESS_TYPE(subroutine)
71006b8b036SSami Tolvanen 	PROCESS_TYPE(typedef)
711f6bb9245SSami Tolvanen 	PROCESS_TYPE(unspecified)
7125b7780e8SSami Tolvanen 	default:
713f6bb9245SSami Tolvanen 		error("unexpected type: %x", tag);
7145b7780e8SSami Tolvanen 	}
7155b7780e8SSami Tolvanen 
7160c1c7627SSami Tolvanen 	/* Update cache state and append to the parent (if any) */
7170c1c7627SSami Tolvanen 	cache->tag = tag;
718*f936c129SSami Tolvanen 	cache->state = want_state;
7190c1c7627SSami Tolvanen 	die_map_add_die(parent, cache);
7200c1c7627SSami Tolvanen 
721*f936c129SSami Tolvanen 	expansion_state_restore(&state->expand, &saved);
7225b7780e8SSami Tolvanen 	return 0;
7235b7780e8SSami Tolvanen }
7245b7780e8SSami Tolvanen 
725f2856884SSami Tolvanen /*
726f2856884SSami Tolvanen  * Exported symbol processing
727f2856884SSami Tolvanen  */
728f2856884SSami Tolvanen static void process_symbol(struct state *state, Dwarf_Die *die,
729f2856884SSami Tolvanen 			   die_callback_t process_func)
730f2856884SSami Tolvanen {
731f2856884SSami Tolvanen 	debug("%s", state->sym->name);
7320c1c7627SSami Tolvanen 	check(process_func(state, NULL, die));
733f2856884SSami Tolvanen 	if (dump_dies)
734f2856884SSami Tolvanen 		fputs("\n", stderr);
735f2856884SSami Tolvanen }
736f2856884SSami Tolvanen 
7370c1c7627SSami Tolvanen static int __process_subprogram(struct state *state, struct die *cache,
7380c1c7627SSami Tolvanen 				Dwarf_Die *die)
739f2856884SSami Tolvanen {
740220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subprogram");
741f2856884SSami Tolvanen 	return 0;
742f2856884SSami Tolvanen }
743f2856884SSami Tolvanen 
744f2856884SSami Tolvanen static void process_subprogram(struct state *state, Dwarf_Die *die)
745f2856884SSami Tolvanen {
746f2856884SSami Tolvanen 	process_symbol(state, die, __process_subprogram);
747f2856884SSami Tolvanen }
748f2856884SSami Tolvanen 
7490c1c7627SSami Tolvanen static int __process_variable(struct state *state, struct die *cache,
7500c1c7627SSami Tolvanen 			      Dwarf_Die *die)
751f2856884SSami Tolvanen {
7520c1c7627SSami Tolvanen 	process(cache, "variable ");
7530c1c7627SSami Tolvanen 	process_type_attr(state, cache, die);
754f2856884SSami Tolvanen 	return 0;
755f2856884SSami Tolvanen }
756f2856884SSami Tolvanen 
757f2856884SSami Tolvanen static void process_variable(struct state *state, Dwarf_Die *die)
758f2856884SSami Tolvanen {
759f2856884SSami Tolvanen 	process_symbol(state, die, __process_variable);
760f2856884SSami Tolvanen }
761f2856884SSami Tolvanen 
7620c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache,
7630c1c7627SSami Tolvanen 				    Dwarf_Die *die)
764f2856884SSami Tolvanen {
765f2856884SSami Tolvanen 	int tag = dwarf_tag(die);
766f2856884SSami Tolvanen 
767f2856884SSami Tolvanen 	switch (tag) {
768f2856884SSami Tolvanen 	/* Possible containers of exported symbols */
769f2856884SSami Tolvanen 	case DW_TAG_namespace:
770f2856884SSami Tolvanen 	case DW_TAG_class_type:
771f2856884SSami Tolvanen 	case DW_TAG_structure_type:
772f2856884SSami Tolvanen 		return check(process_die_container(
7730c1c7627SSami Tolvanen 			NULL, cache, die, process_exported_symbols, match_all));
774f2856884SSami Tolvanen 
775f2856884SSami Tolvanen 	/* Possible exported symbols */
776f2856884SSami Tolvanen 	case DW_TAG_subprogram:
777f2856884SSami Tolvanen 	case DW_TAG_variable: {
778f2856884SSami Tolvanen 		struct state state;
779f2856884SSami Tolvanen 
780f2856884SSami Tolvanen 		if (!match_export_symbol(&state, die))
781f2856884SSami Tolvanen 			return 0;
782f2856884SSami Tolvanen 
783*f936c129SSami Tolvanen 		state_init(&state);
784*f936c129SSami Tolvanen 
785f2856884SSami Tolvanen 		if (tag == DW_TAG_subprogram)
786f2856884SSami Tolvanen 			process_subprogram(&state, &state.die);
787f2856884SSami Tolvanen 		else
788f2856884SSami Tolvanen 			process_variable(&state, &state.die);
789f2856884SSami Tolvanen 
790*f936c129SSami Tolvanen 		cache_free(&state.expansion_cache);
791f2856884SSami Tolvanen 		return 0;
792f2856884SSami Tolvanen 	}
793f2856884SSami Tolvanen 	default:
794f2856884SSami Tolvanen 		return 0;
795f2856884SSami Tolvanen 	}
796f2856884SSami Tolvanen }
797f2856884SSami Tolvanen 
798f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie)
799f2856884SSami Tolvanen {
8000c1c7627SSami Tolvanen 	check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
801f2856884SSami Tolvanen 				    match_all));
802*f936c129SSami Tolvanen 
803*f936c129SSami Tolvanen 	cache_free(&srcfile_cache);
804f2856884SSami Tolvanen }
805