xref: /linux/scripts/gendwarfksyms/dwarf.c (revision ba6ec09911b805778a2fed6d626bfe77b011a717)
1f2856884SSami Tolvanen // SPDX-License-Identifier: GPL-2.0
2f2856884SSami Tolvanen /*
3f2856884SSami Tolvanen  * Copyright (C) 2024 Google LLC
4f2856884SSami Tolvanen  */
5f2856884SSami Tolvanen 
6a9369418SSami Tolvanen #include <assert.h>
75b7780e8SSami Tolvanen #include <inttypes.h>
85b7780e8SSami Tolvanen #include <stdarg.h>
9f2856884SSami Tolvanen #include "gendwarfksyms.h"
10f2856884SSami Tolvanen 
11a9369418SSami Tolvanen /* See get_union_kabi_status */
12a9369418SSami Tolvanen #define KABI_PREFIX "__kabi_"
13a9369418SSami Tolvanen #define KABI_PREFIX_LEN (sizeof(KABI_PREFIX) - 1)
14a9369418SSami Tolvanen #define KABI_RESERVED_PREFIX "reserved"
15a9369418SSami Tolvanen #define KABI_RESERVED_PREFIX_LEN (sizeof(KABI_RESERVED_PREFIX) - 1)
16a9369418SSami Tolvanen #define KABI_RENAMED_PREFIX "renamed"
17a9369418SSami Tolvanen #define KABI_RENAMED_PREFIX_LEN (sizeof(KABI_RENAMED_PREFIX) - 1)
18a9369418SSami Tolvanen #define KABI_IGNORED_PREFIX "ignored"
19a9369418SSami Tolvanen #define KABI_IGNORED_PREFIX_LEN (sizeof(KABI_IGNORED_PREFIX) - 1)
20a9369418SSami Tolvanen 
is_kabi_prefix(const char * name)21a9369418SSami Tolvanen static inline bool is_kabi_prefix(const char *name)
22a9369418SSami Tolvanen {
23a9369418SSami Tolvanen 	return name && !strncmp(name, KABI_PREFIX, KABI_PREFIX_LEN);
24a9369418SSami Tolvanen }
25a9369418SSami Tolvanen 
26a9369418SSami Tolvanen enum kabi_status {
27a9369418SSami Tolvanen 	/* >0 to stop DIE processing */
28a9369418SSami Tolvanen 	KABI_NORMAL = 1,
29a9369418SSami Tolvanen 	KABI_RESERVED,
30a9369418SSami Tolvanen 	KABI_IGNORED,
31a9369418SSami Tolvanen };
32a9369418SSami Tolvanen 
3306b8b036SSami Tolvanen static bool do_linebreak;
3406b8b036SSami Tolvanen static int indentation_level;
3506b8b036SSami Tolvanen 
3606b8b036SSami Tolvanen /* Line breaks and indentation for pretty-printing */
process_linebreak(struct die * cache,int n)3706b8b036SSami Tolvanen static void process_linebreak(struct die *cache, int n)
3806b8b036SSami Tolvanen {
3906b8b036SSami Tolvanen 	indentation_level += n;
4006b8b036SSami Tolvanen 	do_linebreak = true;
4106b8b036SSami Tolvanen 	die_map_add_linebreak(cache, n);
4206b8b036SSami Tolvanen }
4306b8b036SSami Tolvanen 
445b7780e8SSami Tolvanen #define DEFINE_GET_ATTR(attr, type)                                    \
455b7780e8SSami Tolvanen 	static bool get_##attr##_attr(Dwarf_Die *die, unsigned int id, \
465b7780e8SSami Tolvanen 				      type *value)                     \
475b7780e8SSami Tolvanen 	{                                                              \
485b7780e8SSami Tolvanen 		Dwarf_Attribute da;                                    \
495b7780e8SSami Tolvanen 		return dwarf_attr(die, id, &da) &&                     \
505b7780e8SSami Tolvanen 		       !dwarf_form##attr(&da, value);                  \
515b7780e8SSami Tolvanen 	}
525b7780e8SSami Tolvanen 
DEFINE_GET_ATTR(flag,bool)53f936c129SSami Tolvanen DEFINE_GET_ATTR(flag, bool)
545b7780e8SSami Tolvanen DEFINE_GET_ATTR(udata, Dwarf_Word)
555b7780e8SSami Tolvanen 
56f2856884SSami Tolvanen static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value)
57f2856884SSami Tolvanen {
58f2856884SSami Tolvanen 	Dwarf_Attribute da;
59f2856884SSami Tolvanen 
60f2856884SSami Tolvanen 	/* dwarf_formref_die returns a pointer instead of an error value. */
61f2856884SSami Tolvanen 	return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value);
62f2856884SSami Tolvanen }
63f2856884SSami Tolvanen 
64f2856884SSami Tolvanen #define DEFINE_GET_STRING_ATTR(attr)                         \
65f2856884SSami Tolvanen 	static const char *get_##attr##_attr(Dwarf_Die *die) \
66f2856884SSami Tolvanen 	{                                                    \
67f2856884SSami Tolvanen 		Dwarf_Attribute da;                          \
68f2856884SSami Tolvanen 		if (dwarf_attr(die, DW_AT_##attr, &da))      \
69f2856884SSami Tolvanen 			return dwarf_formstring(&da);        \
70f2856884SSami Tolvanen 		return NULL;                                 \
71f2856884SSami Tolvanen 	}
72f2856884SSami Tolvanen 
73f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(name)
DEFINE_GET_STRING_ATTR(linkage_name)74f2856884SSami Tolvanen DEFINE_GET_STRING_ATTR(linkage_name)
75f2856884SSami Tolvanen 
76f2856884SSami Tolvanen static const char *get_symbol_name(Dwarf_Die *die)
77f2856884SSami Tolvanen {
78f2856884SSami Tolvanen 	const char *name;
79f2856884SSami Tolvanen 
80f2856884SSami Tolvanen 	/* rustc uses DW_AT_linkage_name for exported symbols */
81f2856884SSami Tolvanen 	name = get_linkage_name_attr(die);
82f2856884SSami Tolvanen 	if (!name)
83f2856884SSami Tolvanen 		name = get_name_attr(die);
84f2856884SSami Tolvanen 
85f2856884SSami Tolvanen 	return name;
86f2856884SSami Tolvanen }
87f2856884SSami Tolvanen 
match_export_symbol(struct state * state,Dwarf_Die * die)88f2856884SSami Tolvanen static bool match_export_symbol(struct state *state, Dwarf_Die *die)
89f2856884SSami Tolvanen {
90f2856884SSami Tolvanen 	Dwarf_Die *source = die;
91f2856884SSami Tolvanen 	Dwarf_Die origin;
92f2856884SSami Tolvanen 
93f2856884SSami Tolvanen 	/* If the DIE has an abstract origin, use it for type information. */
94f2856884SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin))
95f2856884SSami Tolvanen 		source = &origin;
96f2856884SSami Tolvanen 
97f2856884SSami Tolvanen 	state->sym = symbol_get(get_symbol_name(die));
98f2856884SSami Tolvanen 
99f2856884SSami Tolvanen 	/* Look up using the origin name if there are no matches. */
100f2856884SSami Tolvanen 	if (!state->sym && source != die)
101f2856884SSami Tolvanen 		state->sym = symbol_get(get_symbol_name(source));
102f2856884SSami Tolvanen 
103f2856884SSami Tolvanen 	state->die = *source;
104f2856884SSami Tolvanen 	return !!state->sym;
105f2856884SSami Tolvanen }
106f2856884SSami Tolvanen 
107f936c129SSami Tolvanen /* DW_AT_decl_file -> struct srcfile */
108f936c129SSami Tolvanen static struct cache srcfile_cache;
109f936c129SSami Tolvanen 
is_definition_private(Dwarf_Die * die)110f936c129SSami Tolvanen static bool is_definition_private(Dwarf_Die *die)
111f936c129SSami Tolvanen {
112f936c129SSami Tolvanen 	Dwarf_Word filenum;
113f936c129SSami Tolvanen 	Dwarf_Files *files;
114f936c129SSami Tolvanen 	Dwarf_Die cudie;
115f936c129SSami Tolvanen 	const char *s;
116f936c129SSami Tolvanen 	int res;
117f936c129SSami Tolvanen 
118f936c129SSami Tolvanen 	/*
119f936c129SSami Tolvanen 	 * Definitions in .c files cannot change the public ABI,
120f936c129SSami Tolvanen 	 * so consider them private.
121f936c129SSami Tolvanen 	 */
122f936c129SSami Tolvanen 	if (!get_udata_attr(die, DW_AT_decl_file, &filenum))
123f936c129SSami Tolvanen 		return false;
124f936c129SSami Tolvanen 
125f936c129SSami Tolvanen 	res = cache_get(&srcfile_cache, filenum);
126f936c129SSami Tolvanen 	if (res >= 0)
127f936c129SSami Tolvanen 		return !!res;
128f936c129SSami Tolvanen 
129f936c129SSami Tolvanen 	if (!dwarf_cu_die(die->cu, &cudie, NULL, NULL, NULL, NULL, NULL, NULL))
130f936c129SSami Tolvanen 		error("dwarf_cu_die failed: '%s'", dwarf_errmsg(-1));
131f936c129SSami Tolvanen 
132f936c129SSami Tolvanen 	if (dwarf_getsrcfiles(&cudie, &files, NULL))
133f936c129SSami Tolvanen 		error("dwarf_getsrcfiles failed: '%s'", dwarf_errmsg(-1));
134f936c129SSami Tolvanen 
135f936c129SSami Tolvanen 	s = dwarf_filesrc(files, filenum, NULL, NULL);
136f936c129SSami Tolvanen 	if (!s)
137f936c129SSami Tolvanen 		error("dwarf_filesrc failed: '%s'", dwarf_errmsg(-1));
138f936c129SSami Tolvanen 
139f936c129SSami Tolvanen 	s = strrchr(s, '.');
140f936c129SSami Tolvanen 	res = s && !strcmp(s, ".c");
141f936c129SSami Tolvanen 	cache_set(&srcfile_cache, filenum, res);
142f936c129SSami Tolvanen 
143f936c129SSami Tolvanen 	return !!res;
144f936c129SSami Tolvanen }
145f936c129SSami Tolvanen 
is_kabi_definition(struct die * cache,Dwarf_Die * die)146936cf61cSSami Tolvanen static bool is_kabi_definition(struct die *cache, Dwarf_Die *die)
147f936c129SSami Tolvanen {
148f936c129SSami Tolvanen 	bool value;
149f936c129SSami Tolvanen 
150f936c129SSami Tolvanen 	if (get_flag_attr(die, DW_AT_declaration, &value) && value)
151f936c129SSami Tolvanen 		return false;
152f936c129SSami Tolvanen 
153936cf61cSSami Tolvanen 	if (kabi_is_declonly(cache->fqn))
154936cf61cSSami Tolvanen 		return false;
155936cf61cSSami Tolvanen 
156f936c129SSami Tolvanen 	return !is_definition_private(die);
157f936c129SSami Tolvanen }
158f936c129SSami Tolvanen 
159f2856884SSami Tolvanen /*
160f2856884SSami Tolvanen  * Type string processing
161f2856884SSami Tolvanen  */
process(struct die * cache,const char * s)1620c1c7627SSami Tolvanen static void process(struct die *cache, const char *s)
163f2856884SSami Tolvanen {
164f2856884SSami Tolvanen 	s = s ?: "<null>";
165f2856884SSami Tolvanen 
16606b8b036SSami Tolvanen 	if (dump_dies && do_linebreak) {
16706b8b036SSami Tolvanen 		fputs("\n", stderr);
16806b8b036SSami Tolvanen 		for (int i = 0; i < indentation_level; i++)
16906b8b036SSami Tolvanen 			fputs("  ", stderr);
17006b8b036SSami Tolvanen 		do_linebreak = false;
17106b8b036SSami Tolvanen 	}
172f2856884SSami Tolvanen 	if (dump_dies)
173f2856884SSami Tolvanen 		fputs(s, stderr);
1740c1c7627SSami Tolvanen 
175d2ffdc1cSSami Tolvanen 	if (cache)
176d2ffdc1cSSami Tolvanen 		die_debug_r("cache %p string '%s'", cache, s);
1770c1c7627SSami Tolvanen 	die_map_add_string(cache, s);
178f2856884SSami Tolvanen }
179f2856884SSami Tolvanen 
1805b7780e8SSami Tolvanen #define MAX_FMT_BUFFER_SIZE 128
1815b7780e8SSami Tolvanen 
process_fmt(struct die * cache,const char * fmt,...)1820c1c7627SSami Tolvanen static void process_fmt(struct die *cache, const char *fmt, ...)
1835b7780e8SSami Tolvanen {
1845b7780e8SSami Tolvanen 	char buf[MAX_FMT_BUFFER_SIZE];
1855b7780e8SSami Tolvanen 	va_list args;
1865b7780e8SSami Tolvanen 
1875b7780e8SSami Tolvanen 	va_start(args, fmt);
1885b7780e8SSami Tolvanen 
1895b7780e8SSami Tolvanen 	if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf))
1905b7780e8SSami Tolvanen 		error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE");
1915b7780e8SSami Tolvanen 
1920c1c7627SSami Tolvanen 	process(cache, buf);
1935b7780e8SSami Tolvanen 	va_end(args);
1945b7780e8SSami Tolvanen }
1955b7780e8SSami Tolvanen 
1965b7780e8SSami Tolvanen #define MAX_FQN_SIZE 64
1975b7780e8SSami Tolvanen 
1985b7780e8SSami Tolvanen /* Get a fully qualified name from DWARF scopes */
get_fqn(Dwarf_Die * die)1995b7780e8SSami Tolvanen static char *get_fqn(Dwarf_Die *die)
2005b7780e8SSami Tolvanen {
2015b7780e8SSami Tolvanen 	const char *list[MAX_FQN_SIZE];
2025b7780e8SSami Tolvanen 	Dwarf_Die *scopes = NULL;
2035b7780e8SSami Tolvanen 	bool has_name = false;
2045b7780e8SSami Tolvanen 	char *fqn = NULL;
2055b7780e8SSami Tolvanen 	char *p;
2065b7780e8SSami Tolvanen 	int count = 0;
2075b7780e8SSami Tolvanen 	int len = 0;
2085b7780e8SSami Tolvanen 	int res;
2095b7780e8SSami Tolvanen 	int i;
2105b7780e8SSami Tolvanen 
2115b7780e8SSami Tolvanen 	res = checkp(dwarf_getscopes_die(die, &scopes));
2125b7780e8SSami Tolvanen 	if (!res) {
2135b7780e8SSami Tolvanen 		list[count] = get_name_attr(die);
2145b7780e8SSami Tolvanen 
2155b7780e8SSami Tolvanen 		if (!list[count])
2165b7780e8SSami Tolvanen 			return NULL;
2175b7780e8SSami Tolvanen 
2185b7780e8SSami Tolvanen 		len += strlen(list[count]);
2195b7780e8SSami Tolvanen 		count++;
2205b7780e8SSami Tolvanen 
2215b7780e8SSami Tolvanen 		goto done;
2225b7780e8SSami Tolvanen 	}
2235b7780e8SSami Tolvanen 
2245b7780e8SSami Tolvanen 	for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) {
2255b7780e8SSami Tolvanen 		if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit)
2265b7780e8SSami Tolvanen 			continue;
2275b7780e8SSami Tolvanen 
2285b7780e8SSami Tolvanen 		list[count] = get_name_attr(&scopes[i]);
2295b7780e8SSami Tolvanen 
2305b7780e8SSami Tolvanen 		if (list[count]) {
2315b7780e8SSami Tolvanen 			has_name = true;
2325b7780e8SSami Tolvanen 		} else {
2335b7780e8SSami Tolvanen 			list[count] = "<anonymous>";
2345b7780e8SSami Tolvanen 			has_name = false;
2355b7780e8SSami Tolvanen 		}
2365b7780e8SSami Tolvanen 
2375b7780e8SSami Tolvanen 		len += strlen(list[count]);
2385b7780e8SSami Tolvanen 		count++;
2395b7780e8SSami Tolvanen 
2405b7780e8SSami Tolvanen 		if (i > 0) {
2415b7780e8SSami Tolvanen 			list[count++] = "::";
2425b7780e8SSami Tolvanen 			len += 2;
2435b7780e8SSami Tolvanen 		}
2445b7780e8SSami Tolvanen 	}
2455b7780e8SSami Tolvanen 
2465b7780e8SSami Tolvanen 	free(scopes);
2475b7780e8SSami Tolvanen 
2485b7780e8SSami Tolvanen 	if (count == MAX_FQN_SIZE)
2495b7780e8SSami Tolvanen 		warn("increase MAX_FQN_SIZE: reached the maximum");
2505b7780e8SSami Tolvanen 
2515b7780e8SSami Tolvanen 	/* Consider the DIE unnamed if the last scope doesn't have a name */
2525b7780e8SSami Tolvanen 	if (!has_name)
2535b7780e8SSami Tolvanen 		return NULL;
2545b7780e8SSami Tolvanen done:
2555b7780e8SSami Tolvanen 	fqn = xmalloc(len + 1);
2565b7780e8SSami Tolvanen 	*fqn = '\0';
2575b7780e8SSami Tolvanen 
2585b7780e8SSami Tolvanen 	p = fqn;
2595b7780e8SSami Tolvanen 	for (i = 0; i < count; i++)
2605b7780e8SSami Tolvanen 		p = stpcpy(p, list[i]);
2615b7780e8SSami Tolvanen 
2625b7780e8SSami Tolvanen 	return fqn;
2635b7780e8SSami Tolvanen }
2645b7780e8SSami Tolvanen 
update_fqn(struct die * cache,Dwarf_Die * die)2650c1c7627SSami Tolvanen static void update_fqn(struct die *cache, Dwarf_Die *die)
2665b7780e8SSami Tolvanen {
2670c1c7627SSami Tolvanen 	if (!cache->fqn)
2680c1c7627SSami Tolvanen 		cache->fqn = get_fqn(die) ?: "";
2690c1c7627SSami Tolvanen }
2700c1c7627SSami Tolvanen 
process_fqn(struct die * cache,Dwarf_Die * die)2710c1c7627SSami Tolvanen static void process_fqn(struct die *cache, Dwarf_Die *die)
2720c1c7627SSami Tolvanen {
2730c1c7627SSami Tolvanen 	update_fqn(cache, die);
2740c1c7627SSami Tolvanen 	if (*cache->fqn)
2750c1c7627SSami Tolvanen 		process(cache, " ");
2760c1c7627SSami Tolvanen 	process(cache, cache->fqn);
2775b7780e8SSami Tolvanen }
2785b7780e8SSami Tolvanen 
2795b7780e8SSami Tolvanen #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute)                          \
2800c1c7627SSami Tolvanen 	static void process_##attribute##_attr(struct die *cache,          \
2810c1c7627SSami Tolvanen 					       Dwarf_Die *die)             \
2825b7780e8SSami Tolvanen 	{                                                                  \
2835b7780e8SSami Tolvanen 		Dwarf_Word value;                                          \
2845b7780e8SSami Tolvanen 		if (get_udata_attr(die, DW_AT_##attribute, &value))        \
2850c1c7627SSami Tolvanen 			process_fmt(cache, " " #attribute "(%" PRIu64 ")", \
2860c1c7627SSami Tolvanen 				    value);                                \
2875b7780e8SSami Tolvanen 	}
2885b7780e8SSami Tolvanen 
289f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)2905b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
291f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
2925b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
2935b7780e8SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
294f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
295f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
296f6bb9245SSami Tolvanen DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
2975b7780e8SSami Tolvanen 
298220a0857SSami Tolvanen /* Match functions -- die_match_callback_t */
299220a0857SSami Tolvanen #define DEFINE_MATCH(type)                                     \
300220a0857SSami Tolvanen 	static bool match_##type##_type(Dwarf_Die *die)        \
301220a0857SSami Tolvanen 	{                                                      \
302220a0857SSami Tolvanen 		return dwarf_tag(die) == DW_TAG_##type##_type; \
303220a0857SSami Tolvanen 	}
304220a0857SSami Tolvanen 
305f6bb9245SSami Tolvanen DEFINE_MATCH(enumerator)
306220a0857SSami Tolvanen DEFINE_MATCH(formal_parameter)
307f6bb9245SSami Tolvanen DEFINE_MATCH(member)
308c772f1d1SSami Tolvanen DEFINE_MATCH(subrange)
309220a0857SSami Tolvanen 
310f2856884SSami Tolvanen bool match_all(Dwarf_Die *die)
311f2856884SSami Tolvanen {
312f2856884SSami Tolvanen 	return true;
313f2856884SSami Tolvanen }
314f2856884SSami Tolvanen 
process_die_container(struct state * state,struct die * cache,Dwarf_Die * die,die_callback_t func,die_match_callback_t match)3150c1c7627SSami Tolvanen int process_die_container(struct state *state, struct die *cache,
3160c1c7627SSami Tolvanen 			  Dwarf_Die *die, die_callback_t func,
3170c1c7627SSami Tolvanen 			  die_match_callback_t match)
318f2856884SSami Tolvanen {
319f2856884SSami Tolvanen 	Dwarf_Die current;
320f2856884SSami Tolvanen 	int res;
321f2856884SSami Tolvanen 
322220a0857SSami Tolvanen 	/* Track the first item in lists. */
323220a0857SSami Tolvanen 	if (state)
324220a0857SSami Tolvanen 		state->first_list_item = true;
325220a0857SSami Tolvanen 
326f2856884SSami Tolvanen 	res = checkp(dwarf_child(die, &current));
327f2856884SSami Tolvanen 	while (!res) {
328f2856884SSami Tolvanen 		if (match(&current)) {
329f2856884SSami Tolvanen 			/* <0 = error, 0 = continue, >0 = stop */
3300c1c7627SSami Tolvanen 			res = checkp(func(state, cache, &current));
331f2856884SSami Tolvanen 			if (res)
332220a0857SSami Tolvanen 				goto out;
333f2856884SSami Tolvanen 		}
334f2856884SSami Tolvanen 
335f2856884SSami Tolvanen 		res = checkp(dwarf_siblingof(&current, &current));
336f2856884SSami Tolvanen 	}
337f2856884SSami Tolvanen 
338220a0857SSami Tolvanen 	res = 0;
339220a0857SSami Tolvanen out:
340220a0857SSami Tolvanen 	if (state)
341220a0857SSami Tolvanen 		state->first_list_item = false;
342220a0857SSami Tolvanen 
343220a0857SSami Tolvanen 	return res;
344f2856884SSami Tolvanen }
345f2856884SSami Tolvanen 
3460c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent,
3470c1c7627SSami Tolvanen 			Dwarf_Die *die);
3485b7780e8SSami Tolvanen 
process_type_attr(struct state * state,struct die * cache,Dwarf_Die * die)3490c1c7627SSami Tolvanen static void process_type_attr(struct state *state, struct die *cache,
3500c1c7627SSami Tolvanen 			      Dwarf_Die *die)
3515b7780e8SSami Tolvanen {
3525b7780e8SSami Tolvanen 	Dwarf_Die type;
3535b7780e8SSami Tolvanen 
3545b7780e8SSami Tolvanen 	if (get_ref_die_attr(die, DW_AT_type, &type)) {
3550c1c7627SSami Tolvanen 		check(process_type(state, cache, &type));
3565b7780e8SSami Tolvanen 		return;
3575b7780e8SSami Tolvanen 	}
3585b7780e8SSami Tolvanen 
3595b7780e8SSami Tolvanen 	/* Compilers can omit DW_AT_type -- print out 'void' to clarify */
3600c1c7627SSami Tolvanen 	process(cache, "base_type void");
3615b7780e8SSami Tolvanen }
3625b7780e8SSami Tolvanen 
process_list_comma(struct state * state,struct die * cache)363220a0857SSami Tolvanen static void process_list_comma(struct state *state, struct die *cache)
364220a0857SSami Tolvanen {
365220a0857SSami Tolvanen 	if (state->first_list_item) {
366220a0857SSami Tolvanen 		state->first_list_item = false;
367220a0857SSami Tolvanen 	} else {
368220a0857SSami Tolvanen 		process(cache, " ,");
369220a0857SSami Tolvanen 		process_linebreak(cache, 0);
370220a0857SSami Tolvanen 	}
371220a0857SSami Tolvanen }
372220a0857SSami Tolvanen 
373220a0857SSami Tolvanen /* Comma-separated with DW_AT_type */
__process_list_type(struct state * state,struct die * cache,Dwarf_Die * die,const char * type)374220a0857SSami Tolvanen static void __process_list_type(struct state *state, struct die *cache,
375220a0857SSami Tolvanen 				Dwarf_Die *die, const char *type)
376220a0857SSami Tolvanen {
377220a0857SSami Tolvanen 	const char *name = get_name_attr(die);
378220a0857SSami Tolvanen 
379a9369418SSami Tolvanen 	if (stable) {
380a9369418SSami Tolvanen 		if (is_kabi_prefix(name))
381a9369418SSami Tolvanen 			name = NULL;
382a9369418SSami Tolvanen 		state->kabi.orig_name = NULL;
383a9369418SSami Tolvanen 	}
384a9369418SSami Tolvanen 
385220a0857SSami Tolvanen 	process_list_comma(state, cache);
386220a0857SSami Tolvanen 	process(cache, type);
387220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
388a9369418SSami Tolvanen 
389a9369418SSami Tolvanen 	if (stable && state->kabi.orig_name)
390a9369418SSami Tolvanen 		name = state->kabi.orig_name;
391220a0857SSami Tolvanen 	if (name) {
392220a0857SSami Tolvanen 		process(cache, " ");
393220a0857SSami Tolvanen 		process(cache, name);
394220a0857SSami Tolvanen 	}
395a9369418SSami Tolvanen 
396f6bb9245SSami Tolvanen 	process_accessibility_attr(cache, die);
397f6bb9245SSami Tolvanen 	process_bit_size_attr(cache, die);
398f6bb9245SSami Tolvanen 	process_data_bit_offset_attr(cache, die);
399f6bb9245SSami Tolvanen 	process_data_member_location_attr(cache, die);
400220a0857SSami Tolvanen }
401220a0857SSami Tolvanen 
402220a0857SSami Tolvanen #define DEFINE_PROCESS_LIST_TYPE(type)                                       \
403220a0857SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
404220a0857SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
405220a0857SSami Tolvanen 	{                                                                    \
406220a0857SSami Tolvanen 		__process_list_type(state, cache, die, #type " ");           \
407220a0857SSami Tolvanen 	}
408220a0857SSami Tolvanen 
409220a0857SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(formal_parameter)
DEFINE_PROCESS_LIST_TYPE(member)410f6bb9245SSami Tolvanen DEFINE_PROCESS_LIST_TYPE(member)
411220a0857SSami Tolvanen 
41206b8b036SSami Tolvanen /* Container types with DW_AT_type */
41306b8b036SSami Tolvanen static void __process_type(struct state *state, struct die *cache,
41406b8b036SSami Tolvanen 			   Dwarf_Die *die, const char *type)
41506b8b036SSami Tolvanen {
41606b8b036SSami Tolvanen 	process(cache, type);
41706b8b036SSami Tolvanen 	process_fqn(cache, die);
41806b8b036SSami Tolvanen 	process(cache, " {");
41906b8b036SSami Tolvanen 	process_linebreak(cache, 1);
42006b8b036SSami Tolvanen 	process_type_attr(state, cache, die);
42106b8b036SSami Tolvanen 	process_linebreak(cache, -1);
42206b8b036SSami Tolvanen 	process(cache, "}");
42306b8b036SSami Tolvanen 	process_byte_size_attr(cache, die);
42406b8b036SSami Tolvanen 	process_alignment_attr(cache, die);
42506b8b036SSami Tolvanen }
42606b8b036SSami Tolvanen 
42706b8b036SSami Tolvanen #define DEFINE_PROCESS_TYPE(type)                                            \
42806b8b036SSami Tolvanen 	static void process_##type##_type(struct state *state,               \
42906b8b036SSami Tolvanen 					  struct die *cache, Dwarf_Die *die) \
43006b8b036SSami Tolvanen 	{                                                                    \
43106b8b036SSami Tolvanen 		__process_type(state, cache, die, #type "_type");            \
43206b8b036SSami Tolvanen 	}
43306b8b036SSami Tolvanen 
43406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(atomic)
DEFINE_PROCESS_TYPE(const)43506b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(const)
43606b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(immutable)
43706b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(packed)
43806b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(pointer)
43906b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(reference)
44006b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(restrict)
44106b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(rvalue_reference)
44206b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(shared)
443f6bb9245SSami Tolvanen DEFINE_PROCESS_TYPE(template_type_parameter)
44406b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(volatile)
44506b8b036SSami Tolvanen DEFINE_PROCESS_TYPE(typedef)
44606b8b036SSami Tolvanen 
447c772f1d1SSami Tolvanen static void process_subrange_type(struct state *state, struct die *cache,
448c772f1d1SSami Tolvanen 				  Dwarf_Die *die)
449c772f1d1SSami Tolvanen {
450c772f1d1SSami Tolvanen 	Dwarf_Word count = 0;
451c772f1d1SSami Tolvanen 
452c772f1d1SSami Tolvanen 	if (get_udata_attr(die, DW_AT_count, &count))
453c772f1d1SSami Tolvanen 		process_fmt(cache, "[%" PRIu64 "]", count);
454c772f1d1SSami Tolvanen 	else if (get_udata_attr(die, DW_AT_upper_bound, &count))
455c772f1d1SSami Tolvanen 		process_fmt(cache, "[%" PRIu64 "]", count + 1);
456c772f1d1SSami Tolvanen 	else
457c772f1d1SSami Tolvanen 		process(cache, "[]");
458c772f1d1SSami Tolvanen }
459c772f1d1SSami Tolvanen 
process_array_type(struct state * state,struct die * cache,Dwarf_Die * die)460c772f1d1SSami Tolvanen static void process_array_type(struct state *state, struct die *cache,
461c772f1d1SSami Tolvanen 			       Dwarf_Die *die)
462c772f1d1SSami Tolvanen {
463c772f1d1SSami Tolvanen 	process(cache, "array_type");
464c772f1d1SSami Tolvanen 	/* Array size */
465c772f1d1SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
466c772f1d1SSami Tolvanen 				    match_subrange_type));
467c772f1d1SSami Tolvanen 	process(cache, " {");
468c772f1d1SSami Tolvanen 	process_linebreak(cache, 1);
469c772f1d1SSami Tolvanen 	process_type_attr(state, cache, die);
470c772f1d1SSami Tolvanen 	process_linebreak(cache, -1);
471c772f1d1SSami Tolvanen 	process(cache, "}");
472c772f1d1SSami Tolvanen }
473c772f1d1SSami Tolvanen 
__process_subroutine_type(struct state * state,struct die * cache,Dwarf_Die * die,const char * type)474220a0857SSami Tolvanen static void __process_subroutine_type(struct state *state, struct die *cache,
475220a0857SSami Tolvanen 				      Dwarf_Die *die, const char *type)
476220a0857SSami Tolvanen {
477220a0857SSami Tolvanen 	process(cache, type);
478220a0857SSami Tolvanen 	process(cache, " (");
479220a0857SSami Tolvanen 	process_linebreak(cache, 1);
480220a0857SSami Tolvanen 	/* Parameters */
481220a0857SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
482220a0857SSami Tolvanen 				    match_formal_parameter_type));
483220a0857SSami Tolvanen 	process_linebreak(cache, -1);
484220a0857SSami Tolvanen 	process(cache, ")");
485220a0857SSami Tolvanen 	process_linebreak(cache, 0);
486220a0857SSami Tolvanen 	/* Return type */
487220a0857SSami Tolvanen 	process(cache, "-> ");
488220a0857SSami Tolvanen 	process_type_attr(state, cache, die);
489220a0857SSami Tolvanen }
490220a0857SSami Tolvanen 
process_subroutine_type(struct state * state,struct die * cache,Dwarf_Die * die)491220a0857SSami Tolvanen static void process_subroutine_type(struct state *state, struct die *cache,
492220a0857SSami Tolvanen 				    Dwarf_Die *die)
493220a0857SSami Tolvanen {
494220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subroutine_type");
495220a0857SSami Tolvanen }
496220a0857SSami Tolvanen 
process_variant_type(struct state * state,struct die * cache,Dwarf_Die * die)497f6bb9245SSami Tolvanen static void process_variant_type(struct state *state, struct die *cache,
498f6bb9245SSami Tolvanen 				 Dwarf_Die *die)
499f6bb9245SSami Tolvanen {
500f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
501f6bb9245SSami Tolvanen 	process(cache, "variant {");
502f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
503f6bb9245SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
504f6bb9245SSami Tolvanen 				    match_member_type));
505f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
506f6bb9245SSami Tolvanen 	process(cache, "}");
507f6bb9245SSami Tolvanen 	process_discr_value_attr(cache, die);
508f6bb9245SSami Tolvanen }
509f6bb9245SSami Tolvanen 
process_variant_part_type(struct state * state,struct die * cache,Dwarf_Die * die)510f6bb9245SSami Tolvanen static void process_variant_part_type(struct state *state, struct die *cache,
511f6bb9245SSami Tolvanen 				      Dwarf_Die *die)
512f6bb9245SSami Tolvanen {
513f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
514f6bb9245SSami Tolvanen 	process(cache, "variant_part {");
515f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
516f6bb9245SSami Tolvanen 	check(process_die_container(state, cache, die, process_type,
517f6bb9245SSami Tolvanen 				    match_all));
518f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
519f6bb9245SSami Tolvanen 	process(cache, "}");
520f6bb9245SSami Tolvanen }
521f6bb9245SSami Tolvanen 
get_kabi_status(Dwarf_Die * die,const char ** suffix)522a9369418SSami Tolvanen static int get_kabi_status(Dwarf_Die *die, const char **suffix)
523a9369418SSami Tolvanen {
524a9369418SSami Tolvanen 	const char *name = get_name_attr(die);
525a9369418SSami Tolvanen 
526a9369418SSami Tolvanen 	if (suffix)
527a9369418SSami Tolvanen 		*suffix = NULL;
528a9369418SSami Tolvanen 
529a9369418SSami Tolvanen 	if (is_kabi_prefix(name)) {
530a9369418SSami Tolvanen 		name += KABI_PREFIX_LEN;
531a9369418SSami Tolvanen 
532a9369418SSami Tolvanen 		if (!strncmp(name, KABI_RESERVED_PREFIX,
533a9369418SSami Tolvanen 			     KABI_RESERVED_PREFIX_LEN))
534a9369418SSami Tolvanen 			return KABI_RESERVED;
535a9369418SSami Tolvanen 		if (!strncmp(name, KABI_IGNORED_PREFIX,
536a9369418SSami Tolvanen 			     KABI_IGNORED_PREFIX_LEN))
537a9369418SSami Tolvanen 			return KABI_IGNORED;
538a9369418SSami Tolvanen 
539a9369418SSami Tolvanen 		if (!strncmp(name, KABI_RENAMED_PREFIX,
540a9369418SSami Tolvanen 			     KABI_RENAMED_PREFIX_LEN)) {
541a9369418SSami Tolvanen 			if (suffix) {
542a9369418SSami Tolvanen 				name += KABI_RENAMED_PREFIX_LEN;
543a9369418SSami Tolvanen 				*suffix = name;
544a9369418SSami Tolvanen 			}
545a9369418SSami Tolvanen 			return KABI_RESERVED;
546a9369418SSami Tolvanen 		}
547a9369418SSami Tolvanen 	}
548a9369418SSami Tolvanen 
549a9369418SSami Tolvanen 	return KABI_NORMAL;
550a9369418SSami Tolvanen }
551a9369418SSami Tolvanen 
check_struct_member_kabi_status(struct state * state,struct die * __unused,Dwarf_Die * die)552a9369418SSami Tolvanen static int check_struct_member_kabi_status(struct state *state,
553a9369418SSami Tolvanen 					   struct die *__unused, Dwarf_Die *die)
554a9369418SSami Tolvanen {
555a9369418SSami Tolvanen 	int res;
556a9369418SSami Tolvanen 
557a9369418SSami Tolvanen 	assert(dwarf_tag(die) == DW_TAG_member_type);
558a9369418SSami Tolvanen 
559a9369418SSami Tolvanen 	/*
560a9369418SSami Tolvanen 	 * If the union member is a struct, expect the __kabi field to
561a9369418SSami Tolvanen 	 * be the first member of the structure, i.e..:
562a9369418SSami Tolvanen 	 *
563a9369418SSami Tolvanen 	 * union {
564a9369418SSami Tolvanen 	 * 	type new_member;
565a9369418SSami Tolvanen 	 * 	struct {
566a9369418SSami Tolvanen 	 * 		type __kabi_field;
567a9369418SSami Tolvanen 	 * 	}
568a9369418SSami Tolvanen 	 * };
569a9369418SSami Tolvanen 	 */
570a9369418SSami Tolvanen 	res = get_kabi_status(die, &state->kabi.orig_name);
571a9369418SSami Tolvanen 
572a9369418SSami Tolvanen 	if (res == KABI_RESERVED &&
573a9369418SSami Tolvanen 	    !get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder))
574a9369418SSami Tolvanen 		error("structure member missing a type?");
575a9369418SSami Tolvanen 
576a9369418SSami Tolvanen 	return res;
577a9369418SSami Tolvanen }
578a9369418SSami Tolvanen 
check_union_member_kabi_status(struct state * state,struct die * __unused,Dwarf_Die * die)579a9369418SSami Tolvanen static int check_union_member_kabi_status(struct state *state,
580a9369418SSami Tolvanen 					  struct die *__unused, Dwarf_Die *die)
581a9369418SSami Tolvanen {
582a9369418SSami Tolvanen 	Dwarf_Die type;
583a9369418SSami Tolvanen 	int res;
584a9369418SSami Tolvanen 
585a9369418SSami Tolvanen 	assert(dwarf_tag(die) == DW_TAG_member_type);
586a9369418SSami Tolvanen 
587a9369418SSami Tolvanen 	if (!get_ref_die_attr(die, DW_AT_type, &type))
588a9369418SSami Tolvanen 		error("union member missing a type?");
589a9369418SSami Tolvanen 
590a9369418SSami Tolvanen 	/*
591a9369418SSami Tolvanen 	 * We expect a union with two members. Check if either of them
592a9369418SSami Tolvanen 	 * has a __kabi name prefix, i.e.:
593a9369418SSami Tolvanen 	 *
594a9369418SSami Tolvanen 	 * union {
595a9369418SSami Tolvanen 	 * 	...
596a9369418SSami Tolvanen 	 * 	type memberN; // <- type, N = {0,1}
597a9369418SSami Tolvanen 	 *	...
598a9369418SSami Tolvanen 	 * };
599a9369418SSami Tolvanen 	 *
600a9369418SSami Tolvanen 	 * The member can also be a structure type, in which case we'll
601a9369418SSami Tolvanen 	 * check the first structure member.
602a9369418SSami Tolvanen 	 *
603a9369418SSami Tolvanen 	 * In any case, stop processing after we've seen two members.
604a9369418SSami Tolvanen 	 */
605a9369418SSami Tolvanen 	res = get_kabi_status(die, &state->kabi.orig_name);
606a9369418SSami Tolvanen 
607a9369418SSami Tolvanen 	if (res == KABI_RESERVED)
608a9369418SSami Tolvanen 		state->kabi.placeholder = type;
609a9369418SSami Tolvanen 	if (res != KABI_NORMAL)
610a9369418SSami Tolvanen 		return res;
611a9369418SSami Tolvanen 
612a9369418SSami Tolvanen 	if (dwarf_tag(&type) == DW_TAG_structure_type)
613a9369418SSami Tolvanen 		res = checkp(process_die_container(
614a9369418SSami Tolvanen 			state, NULL, &type, check_struct_member_kabi_status,
615a9369418SSami Tolvanen 			match_member_type));
616a9369418SSami Tolvanen 
617a9369418SSami Tolvanen 	if (res <= KABI_NORMAL && ++state->kabi.members < 2)
618a9369418SSami Tolvanen 		return 0; /* Continue */
619a9369418SSami Tolvanen 
620a9369418SSami Tolvanen 	return res;
621a9369418SSami Tolvanen }
622a9369418SSami Tolvanen 
get_union_kabi_status(Dwarf_Die * die,Dwarf_Die * placeholder,const char ** orig_name)623a9369418SSami Tolvanen static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder,
624a9369418SSami Tolvanen 				 const char **orig_name)
625a9369418SSami Tolvanen {
626a9369418SSami Tolvanen 	struct state state;
627a9369418SSami Tolvanen 	int res;
628a9369418SSami Tolvanen 
629a9369418SSami Tolvanen 	if (!stable)
630a9369418SSami Tolvanen 		return KABI_NORMAL;
631a9369418SSami Tolvanen 
632a9369418SSami Tolvanen 	/*
633a9369418SSami Tolvanen 	 * To maintain a stable kABI, distributions may choose to reserve
634a9369418SSami Tolvanen 	 * space in structs for later use by adding placeholder members,
635a9369418SSami Tolvanen 	 * for example:
636a9369418SSami Tolvanen 	 *
637a9369418SSami Tolvanen 	 * struct s {
638a9369418SSami Tolvanen 	 * 	u32 a;
639a9369418SSami Tolvanen 	 *	// an 8-byte placeholder for future use
640a9369418SSami Tolvanen 	 * 	u64 __kabi_reserved_0;
641a9369418SSami Tolvanen 	 * };
642a9369418SSami Tolvanen 	 *
643a9369418SSami Tolvanen 	 * When the reserved member is taken into use, the type change
644a9369418SSami Tolvanen 	 * would normally cause the symbol version to change as well, but
645a9369418SSami Tolvanen 	 * if the replacement uses the following convention, gendwarfksyms
646a9369418SSami Tolvanen 	 * continues to use the placeholder type for versioning instead,
647a9369418SSami Tolvanen 	 * thus maintaining the same symbol version:
648a9369418SSami Tolvanen 	 *
649a9369418SSami Tolvanen 	 * struct s {
650a9369418SSami Tolvanen 	 * 	u32 a;
651a9369418SSami Tolvanen 	 *	union {
652a9369418SSami Tolvanen 	 * 		// placeholder replaced with a new member `b`
653a9369418SSami Tolvanen 	 * 		struct t b;
654a9369418SSami Tolvanen 	 * 		struct {
655a9369418SSami Tolvanen 	 * 			// the placeholder type that is still
656a9369418SSami Tolvanen 	 *			// used for versioning
657a9369418SSami Tolvanen 	 * 			u64 __kabi_reserved_0;
658a9369418SSami Tolvanen 	 * 		};
659a9369418SSami Tolvanen 	 * 	};
660a9369418SSami Tolvanen 	 * };
661a9369418SSami Tolvanen 	 *
662a9369418SSami Tolvanen 	 * I.e., as long as the replaced member is in a union, and the
663a9369418SSami Tolvanen 	 * placeholder has a __kabi_reserved name prefix, we'll continue
664a9369418SSami Tolvanen 	 * to use the placeholder type (here u64) for version calculation
665a9369418SSami Tolvanen 	 * instead of the union type.
666a9369418SSami Tolvanen 	 *
667a9369418SSami Tolvanen 	 * It's also possible to ignore new members from versioning if
668a9369418SSami Tolvanen 	 * they've been added to alignment holes, for example, by
669a9369418SSami Tolvanen 	 * including them in a union with another member that uses the
670a9369418SSami Tolvanen 	 * __kabi_ignored name prefix:
671a9369418SSami Tolvanen 	 *
672a9369418SSami Tolvanen 	 * struct s {
673a9369418SSami Tolvanen 	 * 	u32 a;
674a9369418SSami Tolvanen 	 *	// an alignment hole is used to add `n`
675a9369418SSami Tolvanen 	 * 	union {
676a9369418SSami Tolvanen 	 * 		u32 n;
677a9369418SSami Tolvanen 	 *		// hide the entire union member from versioning
678a9369418SSami Tolvanen 	 * 		u8 __kabi_ignored_0;
679a9369418SSami Tolvanen 	 * 	};
680a9369418SSami Tolvanen 	 * 	u64 b;
681a9369418SSami Tolvanen 	 * };
682a9369418SSami Tolvanen 	 *
683a9369418SSami Tolvanen 	 * Note that the user of this feature is responsible for ensuring
684a9369418SSami Tolvanen 	 * that the structure actually remains ABI compatible.
685a9369418SSami Tolvanen 	 */
686a9369418SSami Tolvanen 	memset(&state.kabi, 0, sizeof(struct kabi_state));
687a9369418SSami Tolvanen 
688a9369418SSami Tolvanen 	res = checkp(process_die_container(&state, NULL, die,
689a9369418SSami Tolvanen 					   check_union_member_kabi_status,
690a9369418SSami Tolvanen 					   match_member_type));
691a9369418SSami Tolvanen 
692a9369418SSami Tolvanen 	if (res == KABI_RESERVED) {
693a9369418SSami Tolvanen 		if (placeholder)
694a9369418SSami Tolvanen 			*placeholder = state.kabi.placeholder;
695a9369418SSami Tolvanen 		if (orig_name)
696a9369418SSami Tolvanen 			*orig_name = state.kabi.orig_name;
697a9369418SSami Tolvanen 	}
698a9369418SSami Tolvanen 
699a9369418SSami Tolvanen 	return res;
700a9369418SSami Tolvanen }
701a9369418SSami Tolvanen 
is_kabi_ignored(Dwarf_Die * die)702a9369418SSami Tolvanen static bool is_kabi_ignored(Dwarf_Die *die)
703a9369418SSami Tolvanen {
704a9369418SSami Tolvanen 	Dwarf_Die type;
705a9369418SSami Tolvanen 
706a9369418SSami Tolvanen 	if (!stable)
707a9369418SSami Tolvanen 		return false;
708a9369418SSami Tolvanen 
709a9369418SSami Tolvanen 	if (!get_ref_die_attr(die, DW_AT_type, &type))
710a9369418SSami Tolvanen 		error("member missing a type?");
711a9369418SSami Tolvanen 
712a9369418SSami Tolvanen 	return dwarf_tag(&type) == DW_TAG_union_type &&
713a9369418SSami Tolvanen 	       checkp(get_union_kabi_status(&type, NULL, NULL)) == KABI_IGNORED;
714a9369418SSami Tolvanen }
715a9369418SSami Tolvanen 
___process_structure_type(struct state * state,struct die * cache,Dwarf_Die * die)716f6bb9245SSami Tolvanen static int ___process_structure_type(struct state *state, struct die *cache,
717f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
718f6bb9245SSami Tolvanen {
719f6bb9245SSami Tolvanen 	switch (dwarf_tag(die)) {
720f6bb9245SSami Tolvanen 	case DW_TAG_member:
721a9369418SSami Tolvanen 		if (is_kabi_ignored(die))
722a9369418SSami Tolvanen 			return 0;
723a9369418SSami Tolvanen 		return check(process_type(state, cache, die));
724f6bb9245SSami Tolvanen 	case DW_TAG_variant_part:
725f6bb9245SSami Tolvanen 		return check(process_type(state, cache, die));
726f6bb9245SSami Tolvanen 	case DW_TAG_class_type:
727f6bb9245SSami Tolvanen 	case DW_TAG_enumeration_type:
728f6bb9245SSami Tolvanen 	case DW_TAG_structure_type:
729f6bb9245SSami Tolvanen 	case DW_TAG_template_type_parameter:
730f6bb9245SSami Tolvanen 	case DW_TAG_union_type:
731f6bb9245SSami Tolvanen 	case DW_TAG_subprogram:
732f6bb9245SSami Tolvanen 		/* Skip non-member types, including member functions */
733f6bb9245SSami Tolvanen 		return 0;
734f6bb9245SSami Tolvanen 	default:
735f6bb9245SSami Tolvanen 		error("unexpected structure_type child: %x", dwarf_tag(die));
736f6bb9245SSami Tolvanen 	}
737f6bb9245SSami Tolvanen }
738f6bb9245SSami Tolvanen 
__process_structure_type(struct state * state,struct die * cache,Dwarf_Die * die,const char * type,die_callback_t process_func,die_match_callback_t match_func)739f6bb9245SSami Tolvanen static void __process_structure_type(struct state *state, struct die *cache,
740f6bb9245SSami Tolvanen 				     Dwarf_Die *die, const char *type,
741f6bb9245SSami Tolvanen 				     die_callback_t process_func,
742f6bb9245SSami Tolvanen 				     die_match_callback_t match_func)
743f6bb9245SSami Tolvanen {
744f936c129SSami Tolvanen 	bool expand;
745f936c129SSami Tolvanen 
746f6bb9245SSami Tolvanen 	process(cache, type);
747f6bb9245SSami Tolvanen 	process_fqn(cache, die);
748f6bb9245SSami Tolvanen 	process(cache, " {");
749f6bb9245SSami Tolvanen 	process_linebreak(cache, 1);
750f6bb9245SSami Tolvanen 
751936cf61cSSami Tolvanen 	expand = state->expand.expand && is_kabi_definition(cache, die);
752f936c129SSami Tolvanen 
753f936c129SSami Tolvanen 	if (expand) {
754936cf61cSSami Tolvanen 		state->expand.current_fqn = cache->fqn;
755f6bb9245SSami Tolvanen 		check(process_die_container(state, cache, die, process_func,
756f6bb9245SSami Tolvanen 					    match_func));
757f936c129SSami Tolvanen 	}
758f6bb9245SSami Tolvanen 
759f6bb9245SSami Tolvanen 	process_linebreak(cache, -1);
760f6bb9245SSami Tolvanen 	process(cache, "}");
761f6bb9245SSami Tolvanen 
762f936c129SSami Tolvanen 	if (expand) {
763f6bb9245SSami Tolvanen 		process_byte_size_attr(cache, die);
764f6bb9245SSami Tolvanen 		process_alignment_attr(cache, die);
765f6bb9245SSami Tolvanen 	}
766f936c129SSami Tolvanen }
767f6bb9245SSami Tolvanen 
768f6bb9245SSami Tolvanen #define DEFINE_PROCESS_STRUCTURE_TYPE(structure)                        \
769f6bb9245SSami Tolvanen 	static void process_##structure##_type(                         \
770f6bb9245SSami Tolvanen 		struct state *state, struct die *cache, Dwarf_Die *die) \
771f6bb9245SSami Tolvanen 	{                                                               \
772f6bb9245SSami Tolvanen 		__process_structure_type(state, cache, die,             \
773f6bb9245SSami Tolvanen 					 #structure "_type",            \
774f6bb9245SSami Tolvanen 					 ___process_structure_type,     \
775f6bb9245SSami Tolvanen 					 match_all);                    \
776f6bb9245SSami Tolvanen 	}
777f6bb9245SSami Tolvanen 
778f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(class)
DEFINE_PROCESS_STRUCTURE_TYPE(structure)779f6bb9245SSami Tolvanen DEFINE_PROCESS_STRUCTURE_TYPE(structure)
780a9369418SSami Tolvanen 
781a9369418SSami Tolvanen static void process_union_type(struct state *state, struct die *cache,
782a9369418SSami Tolvanen 			       Dwarf_Die *die)
783a9369418SSami Tolvanen {
784a9369418SSami Tolvanen 	Dwarf_Die placeholder;
785a9369418SSami Tolvanen 
786a9369418SSami Tolvanen 	int res = checkp(get_union_kabi_status(die, &placeholder,
787a9369418SSami Tolvanen 					       &state->kabi.orig_name));
788a9369418SSami Tolvanen 
789a9369418SSami Tolvanen 	if (res == KABI_RESERVED)
790a9369418SSami Tolvanen 		check(process_type(state, cache, &placeholder));
791a9369418SSami Tolvanen 	if (res > KABI_NORMAL)
792a9369418SSami Tolvanen 		return;
793a9369418SSami Tolvanen 
794a9369418SSami Tolvanen 	__process_structure_type(state, cache, die, "union_type",
795a9369418SSami Tolvanen 				 ___process_structure_type, match_all);
796a9369418SSami Tolvanen }
797f6bb9245SSami Tolvanen 
process_enumerator_type(struct state * state,struct die * cache,Dwarf_Die * die)798f6bb9245SSami Tolvanen static void process_enumerator_type(struct state *state, struct die *cache,
799f6bb9245SSami Tolvanen 				    Dwarf_Die *die)
800f6bb9245SSami Tolvanen {
801936cf61cSSami Tolvanen 	bool overridden = false;
802f6bb9245SSami Tolvanen 	Dwarf_Word value;
803f6bb9245SSami Tolvanen 
804936cf61cSSami Tolvanen 	if (stable) {
805936cf61cSSami Tolvanen 		/* Get the fqn before we process anything */
806936cf61cSSami Tolvanen 		update_fqn(cache, die);
807936cf61cSSami Tolvanen 
808936cf61cSSami Tolvanen 		if (kabi_is_enumerator_ignored(state->expand.current_fqn,
809936cf61cSSami Tolvanen 					       cache->fqn))
810936cf61cSSami Tolvanen 			return;
811936cf61cSSami Tolvanen 
812936cf61cSSami Tolvanen 		overridden = kabi_get_enumerator_value(
813936cf61cSSami Tolvanen 			state->expand.current_fqn, cache->fqn, &value);
814936cf61cSSami Tolvanen 	}
815936cf61cSSami Tolvanen 
816f6bb9245SSami Tolvanen 	process_list_comma(state, cache);
817f6bb9245SSami Tolvanen 	process(cache, "enumerator");
818f6bb9245SSami Tolvanen 	process_fqn(cache, die);
819f6bb9245SSami Tolvanen 
820936cf61cSSami Tolvanen 	if (overridden || get_udata_attr(die, DW_AT_const_value, &value)) {
821f6bb9245SSami Tolvanen 		process(cache, " = ");
822f6bb9245SSami Tolvanen 		process_fmt(cache, "%" PRIu64, value);
823f6bb9245SSami Tolvanen 	}
824f6bb9245SSami Tolvanen }
825f6bb9245SSami Tolvanen 
process_enumeration_type(struct state * state,struct die * cache,Dwarf_Die * die)826f6bb9245SSami Tolvanen static void process_enumeration_type(struct state *state, struct die *cache,
827f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
828f6bb9245SSami Tolvanen {
829f6bb9245SSami Tolvanen 	__process_structure_type(state, cache, die, "enumeration_type",
830f6bb9245SSami Tolvanen 				 process_type, match_enumerator_type);
831f6bb9245SSami Tolvanen }
832f6bb9245SSami Tolvanen 
process_base_type(struct state * state,struct die * cache,Dwarf_Die * die)8330c1c7627SSami Tolvanen static void process_base_type(struct state *state, struct die *cache,
8340c1c7627SSami Tolvanen 			      Dwarf_Die *die)
8355b7780e8SSami Tolvanen {
8360c1c7627SSami Tolvanen 	process(cache, "base_type");
8370c1c7627SSami Tolvanen 	process_fqn(cache, die);
8380c1c7627SSami Tolvanen 	process_byte_size_attr(cache, die);
8390c1c7627SSami Tolvanen 	process_encoding_attr(cache, die);
8400c1c7627SSami Tolvanen 	process_alignment_attr(cache, die);
8410c1c7627SSami Tolvanen }
8420c1c7627SSami Tolvanen 
process_unspecified_type(struct state * state,struct die * cache,Dwarf_Die * die)843f6bb9245SSami Tolvanen static void process_unspecified_type(struct state *state, struct die *cache,
844f6bb9245SSami Tolvanen 				     Dwarf_Die *die)
845f6bb9245SSami Tolvanen {
846f6bb9245SSami Tolvanen 	/*
847f6bb9245SSami Tolvanen 	 * These can be emitted for stand-alone assembly code, which means we
848f6bb9245SSami Tolvanen 	 * might run into them in vmlinux.o.
849f6bb9245SSami Tolvanen 	 */
850f6bb9245SSami Tolvanen 	process(cache, "unspecified_type");
851f6bb9245SSami Tolvanen }
852f6bb9245SSami Tolvanen 
process_cached(struct state * state,struct die * cache,Dwarf_Die * die)8530c1c7627SSami Tolvanen static void process_cached(struct state *state, struct die *cache,
8540c1c7627SSami Tolvanen 			   Dwarf_Die *die)
8550c1c7627SSami Tolvanen {
8560c1c7627SSami Tolvanen 	struct die_fragment *df;
8570c1c7627SSami Tolvanen 	Dwarf_Die child;
8580c1c7627SSami Tolvanen 
8590c1c7627SSami Tolvanen 	list_for_each_entry(df, &cache->fragments, list) {
8600c1c7627SSami Tolvanen 		switch (df->type) {
8610c1c7627SSami Tolvanen 		case FRAGMENT_STRING:
862d2ffdc1cSSami Tolvanen 			die_debug_b("cache %p STRING '%s'", cache,
863d2ffdc1cSSami Tolvanen 				    df->data.str);
8640c1c7627SSami Tolvanen 			process(NULL, df->data.str);
8650c1c7627SSami Tolvanen 			break;
86606b8b036SSami Tolvanen 		case FRAGMENT_LINEBREAK:
86706b8b036SSami Tolvanen 			process_linebreak(NULL, df->data.linebreak);
86806b8b036SSami Tolvanen 			break;
8690c1c7627SSami Tolvanen 		case FRAGMENT_DIE:
8700c1c7627SSami Tolvanen 			if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu),
8710c1c7627SSami Tolvanen 						(void *)df->data.addr, &child))
8720c1c7627SSami Tolvanen 				error("dwarf_die_addr_die failed");
873d2ffdc1cSSami Tolvanen 			die_debug_b("cache %p DIE addr %" PRIxPTR " tag %x",
874d2ffdc1cSSami Tolvanen 				    cache, df->data.addr, dwarf_tag(&child));
8750c1c7627SSami Tolvanen 			check(process_type(state, NULL, &child));
8760c1c7627SSami Tolvanen 			break;
8770c1c7627SSami Tolvanen 		default:
8780c1c7627SSami Tolvanen 			error("empty die_fragment");
8790c1c7627SSami Tolvanen 		}
8800c1c7627SSami Tolvanen 	}
8815b7780e8SSami Tolvanen }
8825b7780e8SSami Tolvanen 
state_init(struct state * state)883f936c129SSami Tolvanen static void state_init(struct state *state)
884f936c129SSami Tolvanen {
885f936c129SSami Tolvanen 	state->expand.expand = true;
886936cf61cSSami Tolvanen 	state->expand.current_fqn = NULL;
887f936c129SSami Tolvanen 	cache_init(&state->expansion_cache);
888f936c129SSami Tolvanen }
889f936c129SSami Tolvanen 
expansion_state_restore(struct expansion_state * state,struct expansion_state * saved)890f936c129SSami Tolvanen static void expansion_state_restore(struct expansion_state *state,
891f936c129SSami Tolvanen 				    struct expansion_state *saved)
892f936c129SSami Tolvanen {
893f936c129SSami Tolvanen 	state->expand = saved->expand;
894936cf61cSSami Tolvanen 	state->current_fqn = saved->current_fqn;
895f936c129SSami Tolvanen }
896f936c129SSami Tolvanen 
expansion_state_save(struct expansion_state * state,struct expansion_state * saved)897f936c129SSami Tolvanen static void expansion_state_save(struct expansion_state *state,
898f936c129SSami Tolvanen 				 struct expansion_state *saved)
899f936c129SSami Tolvanen {
900f936c129SSami Tolvanen 	expansion_state_restore(saved, state);
901f936c129SSami Tolvanen }
902f936c129SSami Tolvanen 
is_expanded_type(int tag)903f936c129SSami Tolvanen static bool is_expanded_type(int tag)
904f936c129SSami Tolvanen {
905f936c129SSami Tolvanen 	return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
906f936c129SSami Tolvanen 	       tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type;
907f936c129SSami Tolvanen }
908f936c129SSami Tolvanen 
9095b7780e8SSami Tolvanen #define PROCESS_TYPE(type)                                \
9105b7780e8SSami Tolvanen 	case DW_TAG_##type##_type:                        \
9110c1c7627SSami Tolvanen 		process_##type##_type(state, cache, die); \
9125b7780e8SSami Tolvanen 		break;
9135b7780e8SSami Tolvanen 
process_type(struct state * state,struct die * parent,Dwarf_Die * die)9140c1c7627SSami Tolvanen static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
9155b7780e8SSami Tolvanen {
916f936c129SSami Tolvanen 	enum die_state want_state = DIE_COMPLETE;
9170c1c7627SSami Tolvanen 	struct die *cache;
918f936c129SSami Tolvanen 	struct expansion_state saved;
9195b7780e8SSami Tolvanen 	int tag = dwarf_tag(die);
9205b7780e8SSami Tolvanen 
921f936c129SSami Tolvanen 	expansion_state_save(&state->expand, &saved);
922f936c129SSami Tolvanen 
9230c1c7627SSami Tolvanen 	/*
924f936c129SSami Tolvanen 	 * Structures and enumeration types are expanded only once per
925f936c129SSami Tolvanen 	 * exported symbol. This is sufficient for detecting ABI changes
926f936c129SSami Tolvanen 	 * within the structure.
927f936c129SSami Tolvanen 	 */
928f936c129SSami Tolvanen 	if (is_expanded_type(tag)) {
929f936c129SSami Tolvanen 		if (cache_was_expanded(&state->expansion_cache, die->addr))
930f936c129SSami Tolvanen 			state->expand.expand = false;
931f936c129SSami Tolvanen 
932f936c129SSami Tolvanen 		if (state->expand.expand)
933f936c129SSami Tolvanen 			cache_mark_expanded(&state->expansion_cache, die->addr);
934f936c129SSami Tolvanen 		else
935f936c129SSami Tolvanen 			want_state = DIE_UNEXPANDED;
936f936c129SSami Tolvanen 	}
937f936c129SSami Tolvanen 
938f936c129SSami Tolvanen 	/*
939f936c129SSami Tolvanen 	 * If we have want_state already cached, use it instead of walking
9400c1c7627SSami Tolvanen 	 * through DWARF.
9410c1c7627SSami Tolvanen 	 */
942f936c129SSami Tolvanen 	cache = die_map_get(die, want_state);
9430c1c7627SSami Tolvanen 
944f936c129SSami Tolvanen 	if (cache->state == want_state) {
945d2ffdc1cSSami Tolvanen 		die_debug_g("cached addr %p tag %x -- %s", die->addr, tag,
946d2ffdc1cSSami Tolvanen 			    die_state_name(cache->state));
947d2ffdc1cSSami Tolvanen 
9480c1c7627SSami Tolvanen 		process_cached(state, cache, die);
9490c1c7627SSami Tolvanen 		die_map_add_die(parent, cache);
950f936c129SSami Tolvanen 
951f936c129SSami Tolvanen 		expansion_state_restore(&state->expand, &saved);
9520c1c7627SSami Tolvanen 		return 0;
9530c1c7627SSami Tolvanen 	}
9540c1c7627SSami Tolvanen 
955d2ffdc1cSSami Tolvanen 	die_debug_g("addr %p tag %x -- %s -> %s", die->addr, tag,
956d2ffdc1cSSami Tolvanen 		    die_state_name(cache->state), die_state_name(want_state));
957d2ffdc1cSSami Tolvanen 
9585b7780e8SSami Tolvanen 	switch (tag) {
95906b8b036SSami Tolvanen 	/* Type modifiers */
96006b8b036SSami Tolvanen 	PROCESS_TYPE(atomic)
96106b8b036SSami Tolvanen 	PROCESS_TYPE(const)
96206b8b036SSami Tolvanen 	PROCESS_TYPE(immutable)
96306b8b036SSami Tolvanen 	PROCESS_TYPE(packed)
96406b8b036SSami Tolvanen 	PROCESS_TYPE(pointer)
96506b8b036SSami Tolvanen 	PROCESS_TYPE(reference)
96606b8b036SSami Tolvanen 	PROCESS_TYPE(restrict)
96706b8b036SSami Tolvanen 	PROCESS_TYPE(rvalue_reference)
96806b8b036SSami Tolvanen 	PROCESS_TYPE(shared)
96906b8b036SSami Tolvanen 	PROCESS_TYPE(volatile)
970f6bb9245SSami Tolvanen 	/* Container types */
971f6bb9245SSami Tolvanen 	PROCESS_TYPE(class)
972f6bb9245SSami Tolvanen 	PROCESS_TYPE(structure)
973f6bb9245SSami Tolvanen 	PROCESS_TYPE(union)
974f6bb9245SSami Tolvanen 	PROCESS_TYPE(enumeration)
975220a0857SSami Tolvanen 	/* Subtypes */
976f6bb9245SSami Tolvanen 	PROCESS_TYPE(enumerator)
977220a0857SSami Tolvanen 	PROCESS_TYPE(formal_parameter)
978f6bb9245SSami Tolvanen 	PROCESS_TYPE(member)
979c772f1d1SSami Tolvanen 	PROCESS_TYPE(subrange)
980f6bb9245SSami Tolvanen 	PROCESS_TYPE(template_type_parameter)
981f6bb9245SSami Tolvanen 	PROCESS_TYPE(variant)
982f6bb9245SSami Tolvanen 	PROCESS_TYPE(variant_part)
98306b8b036SSami Tolvanen 	/* Other types */
984c772f1d1SSami Tolvanen 	PROCESS_TYPE(array)
9855b7780e8SSami Tolvanen 	PROCESS_TYPE(base)
986220a0857SSami Tolvanen 	PROCESS_TYPE(subroutine)
98706b8b036SSami Tolvanen 	PROCESS_TYPE(typedef)
988f6bb9245SSami Tolvanen 	PROCESS_TYPE(unspecified)
9895b7780e8SSami Tolvanen 	default:
990f6bb9245SSami Tolvanen 		error("unexpected type: %x", tag);
9915b7780e8SSami Tolvanen 	}
9925b7780e8SSami Tolvanen 
993d2ffdc1cSSami Tolvanen 	die_debug_r("parent %p cache %p die addr %p tag %x", parent, cache,
994d2ffdc1cSSami Tolvanen 		    die->addr, tag);
995d2ffdc1cSSami Tolvanen 
9960c1c7627SSami Tolvanen 	/* Update cache state and append to the parent (if any) */
9970c1c7627SSami Tolvanen 	cache->tag = tag;
998f936c129SSami Tolvanen 	cache->state = want_state;
9990c1c7627SSami Tolvanen 	die_map_add_die(parent, cache);
10000c1c7627SSami Tolvanen 
1001f936c129SSami Tolvanen 	expansion_state_restore(&state->expand, &saved);
10025b7780e8SSami Tolvanen 	return 0;
10035b7780e8SSami Tolvanen }
10045b7780e8SSami Tolvanen 
1005f2856884SSami Tolvanen /*
1006f2856884SSami Tolvanen  * Exported symbol processing
1007f2856884SSami Tolvanen  */
get_symbol_cache(struct state * state,Dwarf_Die * die)100871378888SSami Tolvanen static struct die *get_symbol_cache(struct state *state, Dwarf_Die *die)
100971378888SSami Tolvanen {
101071378888SSami Tolvanen 	struct die *cache;
101171378888SSami Tolvanen 
101271378888SSami Tolvanen 	cache = die_map_get(die, DIE_SYMBOL);
101371378888SSami Tolvanen 
101471378888SSami Tolvanen 	if (cache->state != DIE_INCOMPLETE)
101571378888SSami Tolvanen 		return NULL; /* We already processed a symbol for this DIE */
101671378888SSami Tolvanen 
101771378888SSami Tolvanen 	cache->tag = dwarf_tag(die);
101871378888SSami Tolvanen 	return cache;
101971378888SSami Tolvanen }
102071378888SSami Tolvanen 
process_symbol(struct state * state,Dwarf_Die * die,die_callback_t process_func)1021f2856884SSami Tolvanen static void process_symbol(struct state *state, Dwarf_Die *die,
1022f2856884SSami Tolvanen 			   die_callback_t process_func)
1023f2856884SSami Tolvanen {
102471378888SSami Tolvanen 	struct die *cache;
102571378888SSami Tolvanen 
102671378888SSami Tolvanen 	symbol_set_die(state->sym, die);
102771378888SSami Tolvanen 
102871378888SSami Tolvanen 	cache = get_symbol_cache(state, die);
102971378888SSami Tolvanen 	if (!cache)
103071378888SSami Tolvanen 		return;
103171378888SSami Tolvanen 
1032f2856884SSami Tolvanen 	debug("%s", state->sym->name);
103371378888SSami Tolvanen 	check(process_func(state, cache, die));
103471378888SSami Tolvanen 	cache->state = DIE_SYMBOL;
1035f2856884SSami Tolvanen 	if (dump_dies)
1036f2856884SSami Tolvanen 		fputs("\n", stderr);
1037f2856884SSami Tolvanen }
1038f2856884SSami Tolvanen 
__process_subprogram(struct state * state,struct die * cache,Dwarf_Die * die)10390c1c7627SSami Tolvanen static int __process_subprogram(struct state *state, struct die *cache,
10400c1c7627SSami Tolvanen 				Dwarf_Die *die)
1041f2856884SSami Tolvanen {
1042220a0857SSami Tolvanen 	__process_subroutine_type(state, cache, die, "subprogram");
1043f2856884SSami Tolvanen 	return 0;
1044f2856884SSami Tolvanen }
1045f2856884SSami Tolvanen 
process_subprogram(struct state * state,Dwarf_Die * die)1046f2856884SSami Tolvanen static void process_subprogram(struct state *state, Dwarf_Die *die)
1047f2856884SSami Tolvanen {
1048f2856884SSami Tolvanen 	process_symbol(state, die, __process_subprogram);
1049f2856884SSami Tolvanen }
1050f2856884SSami Tolvanen 
__process_variable(struct state * state,struct die * cache,Dwarf_Die * die)10510c1c7627SSami Tolvanen static int __process_variable(struct state *state, struct die *cache,
10520c1c7627SSami Tolvanen 			      Dwarf_Die *die)
1053f2856884SSami Tolvanen {
10540c1c7627SSami Tolvanen 	process(cache, "variable ");
10550c1c7627SSami Tolvanen 	process_type_attr(state, cache, die);
1056f2856884SSami Tolvanen 	return 0;
1057f2856884SSami Tolvanen }
1058f2856884SSami Tolvanen 
process_variable(struct state * state,Dwarf_Die * die)1059f2856884SSami Tolvanen static void process_variable(struct state *state, Dwarf_Die *die)
1060f2856884SSami Tolvanen {
1061f2856884SSami Tolvanen 	process_symbol(state, die, __process_variable);
1062f2856884SSami Tolvanen }
1063f2856884SSami Tolvanen 
save_symbol_ptr(struct state * state)1064*fa624569SSami Tolvanen static void save_symbol_ptr(struct state *state)
1065*fa624569SSami Tolvanen {
1066*fa624569SSami Tolvanen 	Dwarf_Die ptr_type;
1067*fa624569SSami Tolvanen 	Dwarf_Die type;
1068*fa624569SSami Tolvanen 
1069*fa624569SSami Tolvanen 	if (!get_ref_die_attr(&state->die, DW_AT_type, &ptr_type) ||
1070*fa624569SSami Tolvanen 	    dwarf_tag(&ptr_type) != DW_TAG_pointer_type)
1071*fa624569SSami Tolvanen 		error("%s must be a pointer type!",
1072*fa624569SSami Tolvanen 		      get_symbol_name(&state->die));
1073*fa624569SSami Tolvanen 
1074*fa624569SSami Tolvanen 	if (!get_ref_die_attr(&ptr_type, DW_AT_type, &type))
1075*fa624569SSami Tolvanen 		error("%s pointer missing a type attribute?",
1076*fa624569SSami Tolvanen 		      get_symbol_name(&state->die));
1077*fa624569SSami Tolvanen 
1078*fa624569SSami Tolvanen 	/*
1079*fa624569SSami Tolvanen 	 * Save the symbol pointer DIE in case the actual symbol is
1080*fa624569SSami Tolvanen 	 * missing from the DWARF. Clang, for example, intentionally
1081*fa624569SSami Tolvanen 	 * omits external symbols from the debugging information.
1082*fa624569SSami Tolvanen 	 */
1083*fa624569SSami Tolvanen 	if (dwarf_tag(&type) == DW_TAG_subroutine_type)
1084*fa624569SSami Tolvanen 		symbol_set_ptr(state->sym, &type);
1085*fa624569SSami Tolvanen 	else
1086*fa624569SSami Tolvanen 		symbol_set_ptr(state->sym, &ptr_type);
1087*fa624569SSami Tolvanen }
1088*fa624569SSami Tolvanen 
process_exported_symbols(struct state * unused,struct die * cache,Dwarf_Die * die)10890c1c7627SSami Tolvanen static int process_exported_symbols(struct state *unused, struct die *cache,
10900c1c7627SSami Tolvanen 				    Dwarf_Die *die)
1091f2856884SSami Tolvanen {
1092f2856884SSami Tolvanen 	int tag = dwarf_tag(die);
1093f2856884SSami Tolvanen 
1094f2856884SSami Tolvanen 	switch (tag) {
1095f2856884SSami Tolvanen 	/* Possible containers of exported symbols */
1096f2856884SSami Tolvanen 	case DW_TAG_namespace:
1097f2856884SSami Tolvanen 	case DW_TAG_class_type:
1098f2856884SSami Tolvanen 	case DW_TAG_structure_type:
1099f2856884SSami Tolvanen 		return check(process_die_container(
11000c1c7627SSami Tolvanen 			NULL, cache, die, process_exported_symbols, match_all));
1101f2856884SSami Tolvanen 
1102f2856884SSami Tolvanen 	/* Possible exported symbols */
1103f2856884SSami Tolvanen 	case DW_TAG_subprogram:
1104f2856884SSami Tolvanen 	case DW_TAG_variable: {
1105f2856884SSami Tolvanen 		struct state state;
1106f2856884SSami Tolvanen 
1107f2856884SSami Tolvanen 		if (!match_export_symbol(&state, die))
1108f2856884SSami Tolvanen 			return 0;
1109f2856884SSami Tolvanen 
1110f936c129SSami Tolvanen 		state_init(&state);
1111f936c129SSami Tolvanen 
1112*fa624569SSami Tolvanen 		if (is_symbol_ptr(get_symbol_name(&state.die)))
1113*fa624569SSami Tolvanen 			save_symbol_ptr(&state);
1114*fa624569SSami Tolvanen 		else if (tag == DW_TAG_subprogram)
1115f2856884SSami Tolvanen 			process_subprogram(&state, &state.die);
1116f2856884SSami Tolvanen 		else
1117f2856884SSami Tolvanen 			process_variable(&state, &state.die);
1118f2856884SSami Tolvanen 
1119f936c129SSami Tolvanen 		cache_free(&state.expansion_cache);
1120f2856884SSami Tolvanen 		return 0;
1121f2856884SSami Tolvanen 	}
1122f2856884SSami Tolvanen 	default:
1123f2856884SSami Tolvanen 		return 0;
1124f2856884SSami Tolvanen 	}
1125f2856884SSami Tolvanen }
1126f2856884SSami Tolvanen 
process_symbol_ptr(struct symbol * sym,void * arg)1127*fa624569SSami Tolvanen static void process_symbol_ptr(struct symbol *sym, void *arg)
1128*fa624569SSami Tolvanen {
1129*fa624569SSami Tolvanen 	struct state state;
1130*fa624569SSami Tolvanen 	Dwarf *dwarf = arg;
1131*fa624569SSami Tolvanen 
1132*fa624569SSami Tolvanen 	if (sym->state != SYMBOL_UNPROCESSED || !sym->ptr_die_addr)
1133*fa624569SSami Tolvanen 		return;
1134*fa624569SSami Tolvanen 
1135*fa624569SSami Tolvanen 	debug("%s", sym->name);
1136*fa624569SSami Tolvanen 	state_init(&state);
1137*fa624569SSami Tolvanen 	state.sym = sym;
1138*fa624569SSami Tolvanen 
1139*fa624569SSami Tolvanen 	if (!dwarf_die_addr_die(dwarf, (void *)sym->ptr_die_addr, &state.die))
1140*fa624569SSami Tolvanen 		error("dwarf_die_addr_die failed for symbol ptr: '%s'",
1141*fa624569SSami Tolvanen 		      sym->name);
1142*fa624569SSami Tolvanen 
1143*fa624569SSami Tolvanen 	if (dwarf_tag(&state.die) == DW_TAG_subroutine_type)
1144*fa624569SSami Tolvanen 		process_subprogram(&state, &state.die);
1145*fa624569SSami Tolvanen 	else
1146*fa624569SSami Tolvanen 		process_variable(&state, &state.die);
1147*fa624569SSami Tolvanen 
1148*fa624569SSami Tolvanen 	cache_free(&state.expansion_cache);
1149*fa624569SSami Tolvanen }
1150*fa624569SSami Tolvanen 
process_cu(Dwarf_Die * cudie)1151f2856884SSami Tolvanen void process_cu(Dwarf_Die *cudie)
1152f2856884SSami Tolvanen {
11530c1c7627SSami Tolvanen 	check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
1154f2856884SSami Tolvanen 				    match_all));
1155f936c129SSami Tolvanen 
1156*fa624569SSami Tolvanen 	symbol_for_each(process_symbol_ptr, dwarf_cu_getdwarf(cudie->cu));
1157*fa624569SSami Tolvanen 
1158f936c129SSami Tolvanen 	cache_free(&srcfile_cache);
1159f2856884SSami Tolvanen }
1160