xref: /linux/scripts/gendwarfksyms/symbols.c (revision ba6ec09911b805778a2fed6d626bfe77b011a717)
1f2856884SSami Tolvanen // SPDX-License-Identifier: GPL-2.0
2f2856884SSami Tolvanen /*
3f2856884SSami Tolvanen  * Copyright (C) 2024 Google LLC
4f2856884SSami Tolvanen  */
5f2856884SSami Tolvanen 
6f2856884SSami Tolvanen #include "gendwarfksyms.h"
7f2856884SSami Tolvanen 
8f2856884SSami Tolvanen #define SYMBOL_HASH_BITS 12
9f2856884SSami Tolvanen 
10e982abf4SSami Tolvanen /* struct symbol_addr -> struct symbol */
11e982abf4SSami Tolvanen static HASHTABLE_DEFINE(symbol_addrs, 1 << SYMBOL_HASH_BITS);
12f2856884SSami Tolvanen /* name -> struct symbol */
13f2856884SSami Tolvanen static HASHTABLE_DEFINE(symbol_names, 1 << SYMBOL_HASH_BITS);
14f2856884SSami Tolvanen 
symbol_addr_hash(const struct symbol_addr * addr)15e982abf4SSami Tolvanen static inline unsigned int symbol_addr_hash(const struct symbol_addr *addr)
16e982abf4SSami Tolvanen {
17e982abf4SSami Tolvanen 	return hash_32(addr->section ^ addr_hash(addr->address));
18e982abf4SSami Tolvanen }
19e982abf4SSami Tolvanen 
__for_each_addr(struct symbol * sym,symbol_callback_t func,void * data)20e982abf4SSami Tolvanen static unsigned int __for_each_addr(struct symbol *sym, symbol_callback_t func,
21e982abf4SSami Tolvanen 				    void *data)
22e982abf4SSami Tolvanen {
23e982abf4SSami Tolvanen 	struct hlist_node *tmp;
24e982abf4SSami Tolvanen 	struct symbol *match = NULL;
25e982abf4SSami Tolvanen 	unsigned int processed = 0;
26e982abf4SSami Tolvanen 
27e982abf4SSami Tolvanen 	hash_for_each_possible_safe(symbol_addrs, match, tmp, addr_hash,
28e982abf4SSami Tolvanen 				    symbol_addr_hash(&sym->addr)) {
29e982abf4SSami Tolvanen 		if (match == sym)
30e982abf4SSami Tolvanen 			continue; /* Already processed */
31e982abf4SSami Tolvanen 
32e982abf4SSami Tolvanen 		if (match->addr.section == sym->addr.section &&
33e982abf4SSami Tolvanen 		    match->addr.address == sym->addr.address) {
34e982abf4SSami Tolvanen 			func(match, data);
35e982abf4SSami Tolvanen 			++processed;
36e982abf4SSami Tolvanen 		}
37e982abf4SSami Tolvanen 	}
38e982abf4SSami Tolvanen 
39e982abf4SSami Tolvanen 	return processed;
40e982abf4SSami Tolvanen }
41e982abf4SSami Tolvanen 
42*fa624569SSami Tolvanen /*
43*fa624569SSami Tolvanen  * For symbols without debugging information (e.g. symbols defined in other
44*fa624569SSami Tolvanen  * TUs), we also match __gendwarfksyms_ptr_<symbol_name> symbols, which the
45*fa624569SSami Tolvanen  * kernel uses to ensure type information is present in the TU that exports
46*fa624569SSami Tolvanen  * the symbol. A __gendwarfksyms_ptr pointer must have the same type as the
47*fa624569SSami Tolvanen  * exported symbol, e.g.:
48*fa624569SSami Tolvanen  *
49*fa624569SSami Tolvanen  *   typeof(symname) *__gendwarf_ptr_symname = &symname;
50*fa624569SSami Tolvanen  */
is_symbol_ptr(const char * name)51*fa624569SSami Tolvanen bool is_symbol_ptr(const char *name)
52*fa624569SSami Tolvanen {
53*fa624569SSami Tolvanen 	return name && !strncmp(name, SYMBOL_PTR_PREFIX, SYMBOL_PTR_PREFIX_LEN);
54*fa624569SSami Tolvanen }
55*fa624569SSami Tolvanen 
for_each(const char * name,symbol_callback_t func,void * data)56f2856884SSami Tolvanen static unsigned int for_each(const char *name, symbol_callback_t func,
57f2856884SSami Tolvanen 			     void *data)
58f2856884SSami Tolvanen {
59f2856884SSami Tolvanen 	struct hlist_node *tmp;
60f2856884SSami Tolvanen 	struct symbol *match;
61f2856884SSami Tolvanen 
62f2856884SSami Tolvanen 	if (!name || !*name)
63f2856884SSami Tolvanen 		return 0;
64*fa624569SSami Tolvanen 	if (is_symbol_ptr(name))
65*fa624569SSami Tolvanen 		name += SYMBOL_PTR_PREFIX_LEN;
66f2856884SSami Tolvanen 
67f2856884SSami Tolvanen 	hash_for_each_possible_safe(symbol_names, match, tmp, name_hash,
68f2856884SSami Tolvanen 				    hash_str(name)) {
69f2856884SSami Tolvanen 		if (strcmp(match->name, name))
70f2856884SSami Tolvanen 			continue;
71f2856884SSami Tolvanen 
72e982abf4SSami Tolvanen 		/* Call func for the match, and all address matches */
73f2856884SSami Tolvanen 		if (func)
74f2856884SSami Tolvanen 			func(match, data);
75f2856884SSami Tolvanen 
76e982abf4SSami Tolvanen 		if (match->addr.section != SHN_UNDEF)
77e982abf4SSami Tolvanen 			return __for_each_addr(match, func, data) + 1;
78e982abf4SSami Tolvanen 
79f2856884SSami Tolvanen 		return 1;
80f2856884SSami Tolvanen 	}
81f2856884SSami Tolvanen 
82f2856884SSami Tolvanen 	return 0;
83f2856884SSami Tolvanen }
84f2856884SSami Tolvanen 
set_crc(struct symbol * sym,void * data)8571378888SSami Tolvanen static void set_crc(struct symbol *sym, void *data)
8671378888SSami Tolvanen {
8771378888SSami Tolvanen 	unsigned long *crc = data;
8871378888SSami Tolvanen 
8971378888SSami Tolvanen 	if (sym->state == SYMBOL_PROCESSED && sym->crc != *crc)
9071378888SSami Tolvanen 		warn("overriding version for symbol %s (crc %lx vs. %lx)",
9171378888SSami Tolvanen 		     sym->name, sym->crc, *crc);
9271378888SSami Tolvanen 
9371378888SSami Tolvanen 	sym->state = SYMBOL_PROCESSED;
9471378888SSami Tolvanen 	sym->crc = *crc;
9571378888SSami Tolvanen }
9671378888SSami Tolvanen 
symbol_set_crc(struct symbol * sym,unsigned long crc)9771378888SSami Tolvanen void symbol_set_crc(struct symbol *sym, unsigned long crc)
9871378888SSami Tolvanen {
9971378888SSami Tolvanen 	if (for_each(sym->name, set_crc, &crc) == 0)
10071378888SSami Tolvanen 		error("no matching symbols: '%s'", sym->name);
10171378888SSami Tolvanen }
10271378888SSami Tolvanen 
set_ptr(struct symbol * sym,void * data)103*fa624569SSami Tolvanen static void set_ptr(struct symbol *sym, void *data)
104*fa624569SSami Tolvanen {
105*fa624569SSami Tolvanen 	sym->ptr_die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
106*fa624569SSami Tolvanen }
107*fa624569SSami Tolvanen 
symbol_set_ptr(struct symbol * sym,Dwarf_Die * ptr)108*fa624569SSami Tolvanen void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr)
109*fa624569SSami Tolvanen {
110*fa624569SSami Tolvanen 	if (for_each(sym->name, set_ptr, ptr) == 0)
111*fa624569SSami Tolvanen 		error("no matching symbols: '%s'", sym->name);
112*fa624569SSami Tolvanen }
113*fa624569SSami Tolvanen 
set_die(struct symbol * sym,void * data)11471378888SSami Tolvanen static void set_die(struct symbol *sym, void *data)
11571378888SSami Tolvanen {
11671378888SSami Tolvanen 	sym->die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
11771378888SSami Tolvanen 	sym->state = SYMBOL_MAPPED;
11871378888SSami Tolvanen }
11971378888SSami Tolvanen 
symbol_set_die(struct symbol * sym,Dwarf_Die * die)12071378888SSami Tolvanen void symbol_set_die(struct symbol *sym, Dwarf_Die *die)
12171378888SSami Tolvanen {
12271378888SSami Tolvanen 	if (for_each(sym->name, set_die, die) == 0)
12371378888SSami Tolvanen 		error("no matching symbols: '%s'", sym->name);
12471378888SSami Tolvanen }
12571378888SSami Tolvanen 
is_exported(const char * name)126f2856884SSami Tolvanen static bool is_exported(const char *name)
127f2856884SSami Tolvanen {
128f2856884SSami Tolvanen 	return for_each(name, NULL, NULL) > 0;
129f2856884SSami Tolvanen }
130f2856884SSami Tolvanen 
symbol_read_exports(FILE * file)131f2856884SSami Tolvanen void symbol_read_exports(FILE *file)
132f2856884SSami Tolvanen {
133f2856884SSami Tolvanen 	struct symbol *sym;
134f2856884SSami Tolvanen 	char *line = NULL;
135f2856884SSami Tolvanen 	char *name = NULL;
136f2856884SSami Tolvanen 	size_t size = 0;
137f2856884SSami Tolvanen 	int nsym = 0;
138f2856884SSami Tolvanen 
139f2856884SSami Tolvanen 	while (getline(&line, &size, file) > 0) {
140f2856884SSami Tolvanen 		if (sscanf(line, "%ms\n", &name) != 1)
141f2856884SSami Tolvanen 			error("malformed input line: %s", line);
142f2856884SSami Tolvanen 
143f2856884SSami Tolvanen 		if (is_exported(name)) {
144f2856884SSami Tolvanen 			/* Ignore duplicates */
145f2856884SSami Tolvanen 			free(name);
146f2856884SSami Tolvanen 			continue;
147f2856884SSami Tolvanen 		}
148f2856884SSami Tolvanen 
149f2856884SSami Tolvanen 		sym = xcalloc(1, sizeof(struct symbol));
150f2856884SSami Tolvanen 		sym->name = name;
151e982abf4SSami Tolvanen 		sym->addr.section = SHN_UNDEF;
152ab443998SSami Tolvanen 		sym->state = SYMBOL_UNPROCESSED;
153f2856884SSami Tolvanen 
154f2856884SSami Tolvanen 		hash_add(symbol_names, &sym->name_hash, hash_str(sym->name));
155f2856884SSami Tolvanen 		++nsym;
156f2856884SSami Tolvanen 
157f2856884SSami Tolvanen 		debug("%s", sym->name);
158f2856884SSami Tolvanen 	}
159f2856884SSami Tolvanen 
160f2856884SSami Tolvanen 	free(line);
161f2856884SSami Tolvanen 	debug("%d exported symbols", nsym);
162f2856884SSami Tolvanen }
163f2856884SSami Tolvanen 
get_symbol(struct symbol * sym,void * arg)164f2856884SSami Tolvanen static void get_symbol(struct symbol *sym, void *arg)
165f2856884SSami Tolvanen {
166f2856884SSami Tolvanen 	struct symbol **res = arg;
167f2856884SSami Tolvanen 
168ab443998SSami Tolvanen 	if (sym->state == SYMBOL_UNPROCESSED)
169f2856884SSami Tolvanen 		*res = sym;
170f2856884SSami Tolvanen }
171f2856884SSami Tolvanen 
symbol_get(const char * name)172f2856884SSami Tolvanen struct symbol *symbol_get(const char *name)
173f2856884SSami Tolvanen {
174f2856884SSami Tolvanen 	struct symbol *sym = NULL;
175f2856884SSami Tolvanen 
176f2856884SSami Tolvanen 	for_each(name, get_symbol, &sym);
177f2856884SSami Tolvanen 	return sym;
178f2856884SSami Tolvanen }
179f2856884SSami Tolvanen 
symbol_for_each(symbol_callback_t func,void * arg)18071378888SSami Tolvanen void symbol_for_each(symbol_callback_t func, void *arg)
18171378888SSami Tolvanen {
18271378888SSami Tolvanen 	struct hlist_node *tmp;
18371378888SSami Tolvanen 	struct symbol *sym;
18471378888SSami Tolvanen 
18571378888SSami Tolvanen 	hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
18671378888SSami Tolvanen 		func(sym, arg);
18771378888SSami Tolvanen 	}
18871378888SSami Tolvanen }
18971378888SSami Tolvanen 
190e982abf4SSami Tolvanen typedef void (*elf_symbol_callback_t)(const char *name, GElf_Sym *sym,
191e982abf4SSami Tolvanen 				      Elf32_Word xndx, void *arg);
192e982abf4SSami Tolvanen 
elf_for_each_global(int fd,elf_symbol_callback_t func,void * arg)193e982abf4SSami Tolvanen static void elf_for_each_global(int fd, elf_symbol_callback_t func, void *arg)
194e982abf4SSami Tolvanen {
195e982abf4SSami Tolvanen 	size_t sym_size;
196e982abf4SSami Tolvanen 	GElf_Shdr shdr_mem;
197e982abf4SSami Tolvanen 	GElf_Shdr *shdr;
198e982abf4SSami Tolvanen 	Elf_Data *xndx_data = NULL;
199e982abf4SSami Tolvanen 	Elf_Scn *scn;
200e982abf4SSami Tolvanen 	Elf *elf;
201e982abf4SSami Tolvanen 
202e982abf4SSami Tolvanen 	if (elf_version(EV_CURRENT) != EV_CURRENT)
203e982abf4SSami Tolvanen 		error("elf_version failed: %s", elf_errmsg(-1));
204e982abf4SSami Tolvanen 
205e982abf4SSami Tolvanen 	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
206e982abf4SSami Tolvanen 	if (!elf)
207e982abf4SSami Tolvanen 		error("elf_begin failed: %s", elf_errmsg(-1));
208e982abf4SSami Tolvanen 
209e982abf4SSami Tolvanen 	scn = elf_nextscn(elf, NULL);
210e982abf4SSami Tolvanen 
211e982abf4SSami Tolvanen 	while (scn) {
212e982abf4SSami Tolvanen 		shdr = gelf_getshdr(scn, &shdr_mem);
213e982abf4SSami Tolvanen 		if (!shdr)
214e982abf4SSami Tolvanen 			error("gelf_getshdr failed: %s", elf_errmsg(-1));
215e982abf4SSami Tolvanen 
216e982abf4SSami Tolvanen 		if (shdr->sh_type == SHT_SYMTAB_SHNDX) {
217e982abf4SSami Tolvanen 			xndx_data = elf_getdata(scn, NULL);
218e982abf4SSami Tolvanen 			if (!xndx_data)
219e982abf4SSami Tolvanen 				error("elf_getdata failed: %s", elf_errmsg(-1));
220e982abf4SSami Tolvanen 			break;
221e982abf4SSami Tolvanen 		}
222e982abf4SSami Tolvanen 
223e982abf4SSami Tolvanen 		scn = elf_nextscn(elf, scn);
224e982abf4SSami Tolvanen 	}
225e982abf4SSami Tolvanen 
226e982abf4SSami Tolvanen 	sym_size = gelf_fsize(elf, ELF_T_SYM, 1, EV_CURRENT);
227e982abf4SSami Tolvanen 	scn = elf_nextscn(elf, NULL);
228e982abf4SSami Tolvanen 
229e982abf4SSami Tolvanen 	while (scn) {
230e982abf4SSami Tolvanen 		shdr = gelf_getshdr(scn, &shdr_mem);
231e982abf4SSami Tolvanen 		if (!shdr)
232e982abf4SSami Tolvanen 			error("gelf_getshdr failed: %s", elf_errmsg(-1));
233e982abf4SSami Tolvanen 
234e982abf4SSami Tolvanen 		if (shdr->sh_type == SHT_SYMTAB) {
235e982abf4SSami Tolvanen 			unsigned int nsyms;
236e982abf4SSami Tolvanen 			unsigned int n;
237e982abf4SSami Tolvanen 			Elf_Data *data = elf_getdata(scn, NULL);
238e982abf4SSami Tolvanen 
239e982abf4SSami Tolvanen 			if (!data)
240e982abf4SSami Tolvanen 				error("elf_getdata failed: %s", elf_errmsg(-1));
241e982abf4SSami Tolvanen 
242e982abf4SSami Tolvanen 			if (shdr->sh_entsize != sym_size)
243e982abf4SSami Tolvanen 				error("expected sh_entsize (%lu) to be %zu",
244e982abf4SSami Tolvanen 				      shdr->sh_entsize, sym_size);
245e982abf4SSami Tolvanen 
246e982abf4SSami Tolvanen 			nsyms = shdr->sh_size / shdr->sh_entsize;
247e982abf4SSami Tolvanen 
248e982abf4SSami Tolvanen 			for (n = 1; n < nsyms; ++n) {
249e982abf4SSami Tolvanen 				const char *name = NULL;
250e982abf4SSami Tolvanen 				Elf32_Word xndx = 0;
251e982abf4SSami Tolvanen 				GElf_Sym sym_mem;
252e982abf4SSami Tolvanen 				GElf_Sym *sym;
253e982abf4SSami Tolvanen 
254e982abf4SSami Tolvanen 				sym = gelf_getsymshndx(data, xndx_data, n,
255e982abf4SSami Tolvanen 						       &sym_mem, &xndx);
256e982abf4SSami Tolvanen 				if (!sym)
257e982abf4SSami Tolvanen 					error("gelf_getsymshndx failed: %s",
258e982abf4SSami Tolvanen 					      elf_errmsg(-1));
259e982abf4SSami Tolvanen 
260e982abf4SSami Tolvanen 				if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
261e982abf4SSami Tolvanen 					continue;
262e982abf4SSami Tolvanen 
263e982abf4SSami Tolvanen 				if (sym->st_shndx != SHN_XINDEX)
264e982abf4SSami Tolvanen 					xndx = sym->st_shndx;
265e982abf4SSami Tolvanen 
266e982abf4SSami Tolvanen 				name = elf_strptr(elf, shdr->sh_link,
267e982abf4SSami Tolvanen 						  sym->st_name);
268e982abf4SSami Tolvanen 				if (!name)
269e982abf4SSami Tolvanen 					error("elf_strptr failed: %s",
270e982abf4SSami Tolvanen 					      elf_errmsg(-1));
271e982abf4SSami Tolvanen 
272e982abf4SSami Tolvanen 				/* Skip empty symbol names */
273e982abf4SSami Tolvanen 				if (*name)
274e982abf4SSami Tolvanen 					func(name, sym, xndx, arg);
275e982abf4SSami Tolvanen 			}
276e982abf4SSami Tolvanen 		}
277e982abf4SSami Tolvanen 
278e982abf4SSami Tolvanen 		scn = elf_nextscn(elf, scn);
279e982abf4SSami Tolvanen 	}
280e982abf4SSami Tolvanen 
281e982abf4SSami Tolvanen 	check(elf_end(elf));
282e982abf4SSami Tolvanen }
283e982abf4SSami Tolvanen 
set_symbol_addr(struct symbol * sym,void * arg)284e982abf4SSami Tolvanen static void set_symbol_addr(struct symbol *sym, void *arg)
285e982abf4SSami Tolvanen {
286e982abf4SSami Tolvanen 	struct symbol_addr *addr = arg;
287e982abf4SSami Tolvanen 
288e982abf4SSami Tolvanen 	if (sym->addr.section == SHN_UNDEF) {
289e982abf4SSami Tolvanen 		sym->addr = *addr;
290e982abf4SSami Tolvanen 		hash_add(symbol_addrs, &sym->addr_hash,
291e982abf4SSami Tolvanen 			 symbol_addr_hash(&sym->addr));
292e982abf4SSami Tolvanen 
293e982abf4SSami Tolvanen 		debug("%s -> { %u, %lx }", sym->name, sym->addr.section,
294e982abf4SSami Tolvanen 		      sym->addr.address);
295e982abf4SSami Tolvanen 	} else if (sym->addr.section != addr->section ||
296e982abf4SSami Tolvanen 		   sym->addr.address != addr->address) {
297e982abf4SSami Tolvanen 		warn("multiple addresses for symbol %s?", sym->name);
298e982abf4SSami Tolvanen 	}
299e982abf4SSami Tolvanen }
300e982abf4SSami Tolvanen 
elf_set_symbol_addr(const char * name,GElf_Sym * sym,Elf32_Word xndx,void * arg)301e982abf4SSami Tolvanen static void elf_set_symbol_addr(const char *name, GElf_Sym *sym,
302e982abf4SSami Tolvanen 				Elf32_Word xndx, void *arg)
303e982abf4SSami Tolvanen {
304e982abf4SSami Tolvanen 	struct symbol_addr addr = { .section = xndx, .address = sym->st_value };
305e982abf4SSami Tolvanen 
306e982abf4SSami Tolvanen 	/* Set addresses for exported symbols */
307e982abf4SSami Tolvanen 	if (addr.section != SHN_UNDEF)
308e982abf4SSami Tolvanen 		for_each(name, set_symbol_addr, &addr);
309e982abf4SSami Tolvanen }
310e982abf4SSami Tolvanen 
symbol_read_symtab(int fd)311e982abf4SSami Tolvanen void symbol_read_symtab(int fd)
312e982abf4SSami Tolvanen {
313e982abf4SSami Tolvanen 	elf_for_each_global(fd, elf_set_symbol_addr, NULL);
314e982abf4SSami Tolvanen }
315e982abf4SSami Tolvanen 
symbol_print_versions(void)31671378888SSami Tolvanen void symbol_print_versions(void)
31771378888SSami Tolvanen {
31871378888SSami Tolvanen 	struct hlist_node *tmp;
31971378888SSami Tolvanen 	struct symbol *sym;
32071378888SSami Tolvanen 
32171378888SSami Tolvanen 	hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
32271378888SSami Tolvanen 		if (sym->state != SYMBOL_PROCESSED)
32371378888SSami Tolvanen 			warn("no information for symbol %s", sym->name);
32471378888SSami Tolvanen 
32571378888SSami Tolvanen 		printf("#SYMVER %s 0x%08lx\n", sym->name, sym->crc);
32671378888SSami Tolvanen 	}
32771378888SSami Tolvanen }
32871378888SSami Tolvanen 
symbol_free(void)329f2856884SSami Tolvanen void symbol_free(void)
330f2856884SSami Tolvanen {
331f2856884SSami Tolvanen 	struct hlist_node *tmp;
332f2856884SSami Tolvanen 	struct symbol *sym;
333f2856884SSami Tolvanen 
334f2856884SSami Tolvanen 	hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
335f2856884SSami Tolvanen 		free((void *)sym->name);
336f2856884SSami Tolvanen 		free(sym);
337f2856884SSami Tolvanen 	}
338f2856884SSami Tolvanen 
339e982abf4SSami Tolvanen 	hash_init(symbol_addrs);
340f2856884SSami Tolvanen 	hash_init(symbol_names);
341f2856884SSami Tolvanen }
342