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