xref: /openbsd/usr.sbin/btrace/ksyms.c (revision 3e48d9ff)
1*3e48d9ffSjsg /*	$OpenBSD: ksyms.c,v 1.10 2024/04/01 22:49:04 jsg Exp $ */
223160851Smpi 
323160851Smpi /*
423160851Smpi  * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
523160851Smpi  *
623160851Smpi  * Permission to use, copy, modify, and distribute this software for any
723160851Smpi  * purpose with or without fee is hereby granted, provided that the above
823160851Smpi  * copyright notice and this permission notice appear in all copies.
923160851Smpi  *
1023160851Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1123160851Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1223160851Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1323160851Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1423160851Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1523160851Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1623160851Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1723160851Smpi  */
1823160851Smpi 
195e6104a1Sclaudio #define _DYN_LOADER	/* needed for AuxInfo */
205e6104a1Sclaudio 
215e6104a1Sclaudio #include <sys/types.h>
225e6104a1Sclaudio 
2323160851Smpi #include <err.h>
2423160851Smpi #include <fcntl.h>
2523160851Smpi #include <gelf.h>
26eaddcb2fScheloha #include <stdint.h>
2723160851Smpi #include <stdio.h>
285e6104a1Sclaudio #include <stdlib.h>
2923160851Smpi #include <string.h>
3023160851Smpi #include <unistd.h>
3123160851Smpi 
3223160851Smpi #include "btrace.h"
3323160851Smpi 
34eaddcb2fScheloha struct sym {
35eaddcb2fScheloha 	char *sym_name;
36eaddcb2fScheloha 	unsigned long sym_value;	/* from st_value */
37eaddcb2fScheloha 	unsigned long sym_size;		/* from st_size */
3843ae943eScheloha };
3943ae943eScheloha 
40eaddcb2fScheloha struct syms {
41eaddcb2fScheloha 	struct sym *table;
42eaddcb2fScheloha 	size_t nsymb;
43eaddcb2fScheloha };
44eaddcb2fScheloha 
45eaddcb2fScheloha int sym_compare_search(const void *, const void *);
46eaddcb2fScheloha int sym_compare_sort(const void *, const void *);
4723160851Smpi 
485e6104a1Sclaudio struct syms *
kelf_open(const char * path)495e6104a1Sclaudio kelf_open(const char *path)
5023160851Smpi {
51eaddcb2fScheloha 	char *name;
52eaddcb2fScheloha 	Elf *elf;
53eaddcb2fScheloha 	Elf_Data *data = NULL;
54*3e48d9ffSjsg 	Elf_Scn	*scn = NULL, *symtab = NULL;
55eaddcb2fScheloha 	GElf_Sym sym;
56eaddcb2fScheloha 	GElf_Shdr shdr;
57eaddcb2fScheloha 	size_t i, shstrndx, strtabndx = SIZE_MAX, symtab_size;
58eaddcb2fScheloha 	unsigned long diff;
59eaddcb2fScheloha 	struct sym *tmp;
60eaddcb2fScheloha 	struct syms *syms = NULL;
61eaddcb2fScheloha 	int fd;
6223160851Smpi 
6323160851Smpi 	if (elf_version(EV_CURRENT) == EV_NONE)
6423160851Smpi 		errx(1, "elf_version: %s", elf_errmsg(-1));
6523160851Smpi 
66eaddcb2fScheloha 	fd = open(path, O_RDONLY);
67eaddcb2fScheloha 	if (fd == -1) {
685e6104a1Sclaudio 		warn("open: %s", path);
695e6104a1Sclaudio 		return NULL;
7023160851Smpi 	}
7123160851Smpi 
72eaddcb2fScheloha 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
7323160851Smpi 		warnx("elf_begin: %s", elf_errmsg(-1));
7423160851Smpi 		goto bad;
7523160851Smpi 	}
7623160851Smpi 
77eaddcb2fScheloha 	if (elf_kind(elf) != ELF_K_ELF)
7823160851Smpi 		goto bad;
7923160851Smpi 
80eaddcb2fScheloha 	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
81eaddcb2fScheloha 		warnx("elf_getshdrstrndx: %s", elf_errmsg(-1));
82eaddcb2fScheloha 		goto bad;
83eaddcb2fScheloha 	}
84eaddcb2fScheloha 
85eaddcb2fScheloha 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
86eaddcb2fScheloha 		if (gelf_getshdr(scn, &shdr) != &shdr) {
87eaddcb2fScheloha 			warnx("elf_getshdr: %s", elf_errmsg(-1));
88eaddcb2fScheloha 			goto bad;
89eaddcb2fScheloha 		}
90eaddcb2fScheloha 		if ((name = elf_strptr(elf, shstrndx, shdr.sh_name)) == NULL) {
91eaddcb2fScheloha 			warnx("elf_strptr: %s", elf_errmsg(-1));
92eaddcb2fScheloha 			goto bad;
93eaddcb2fScheloha 		}
94eaddcb2fScheloha 		if (strcmp(name, ELF_SYMTAB) == 0 &&
95eaddcb2fScheloha 		    shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
96eaddcb2fScheloha 			symtab = scn;
97eaddcb2fScheloha 			symtab_size = shdr.sh_size / shdr.sh_entsize;
98eaddcb2fScheloha 		}
99eaddcb2fScheloha 		if (strcmp(name, ELF_STRTAB) == 0 &&
100eaddcb2fScheloha 		    shdr.sh_type == SHT_STRTAB) {
101eaddcb2fScheloha 			strtabndx = elf_ndxscn(scn);
102eaddcb2fScheloha 		}
103eaddcb2fScheloha 	}
104eaddcb2fScheloha 	if (symtab == NULL) {
105eaddcb2fScheloha 		warnx("%s: %s: section not found", path, ELF_SYMTAB);
106eaddcb2fScheloha 		goto bad;
107eaddcb2fScheloha 	}
108eaddcb2fScheloha 	if (strtabndx == SIZE_MAX) {
109eaddcb2fScheloha 		warnx("%s: %s: section not found", path, ELF_STRTAB);
110eaddcb2fScheloha 		goto bad;
111eaddcb2fScheloha 	}
112eaddcb2fScheloha 
113eaddcb2fScheloha 	data = elf_rawdata(symtab, data);
114eaddcb2fScheloha 	if (data == NULL)
11523160851Smpi 		goto bad;
11623160851Smpi 
117eaddcb2fScheloha 	if ((syms = calloc(1, sizeof(*syms))) == NULL)
118eaddcb2fScheloha 		err(1, NULL);
119eaddcb2fScheloha 	syms->table = calloc(symtab_size, sizeof *syms->table);
120eaddcb2fScheloha 	if (syms->table == NULL)
121eaddcb2fScheloha 		err(1, NULL);
122eaddcb2fScheloha 	for (i = 0; i < symtab_size; i++) {
123eaddcb2fScheloha 		if (gelf_getsym(data, i, &sym) == NULL)
124eaddcb2fScheloha 			continue;
125eaddcb2fScheloha 		if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
126eaddcb2fScheloha 			continue;
127eaddcb2fScheloha 		name = elf_strptr(elf, strtabndx, sym.st_name);
128eaddcb2fScheloha 		if (name == NULL)
129eaddcb2fScheloha 			continue;
130eaddcb2fScheloha 		syms->table[syms->nsymb].sym_name = strdup(name);
131eaddcb2fScheloha 		if (syms->table[syms->nsymb].sym_name == NULL)
132eaddcb2fScheloha 			err(1, NULL);
133eaddcb2fScheloha 		syms->table[syms->nsymb].sym_value = sym.st_value;
134eaddcb2fScheloha 		syms->table[syms->nsymb].sym_size = sym.st_size;
135eaddcb2fScheloha 		syms->nsymb++;
136eaddcb2fScheloha 	}
137eaddcb2fScheloha 	tmp = reallocarray(syms->table, syms->nsymb, sizeof *syms->table);
138eaddcb2fScheloha 	if (tmp == NULL)
139eaddcb2fScheloha 		err(1, NULL);
140eaddcb2fScheloha 	syms->table = tmp;
141eaddcb2fScheloha 
142eaddcb2fScheloha 	/* Sort symbols in ascending order by address. */
143eaddcb2fScheloha 	qsort(syms->table, syms->nsymb, sizeof *syms->table, sym_compare_sort);
144eaddcb2fScheloha 
145eaddcb2fScheloha 	/*
146eaddcb2fScheloha 	 * Some functions, particularly those written in assembly, have an
147eaddcb2fScheloha 	 * st_size of zero.  We can approximate a size for these by assuming
148eaddcb2fScheloha 	 * that they extend from their st_value to that of the next function.
149eaddcb2fScheloha 	 */
150eaddcb2fScheloha 	for (i = 0; i < syms->nsymb; i++) {
151eaddcb2fScheloha 		if (syms->table[i].sym_size != 0)
152eaddcb2fScheloha 			continue;
153eaddcb2fScheloha 		/* Can't do anything for the last symbol. */
154eaddcb2fScheloha 		if (i + 1 == syms->nsymb)
155eaddcb2fScheloha 			continue;
156eaddcb2fScheloha 		diff = syms->table[i + 1].sym_value - syms->table[i].sym_value;
157eaddcb2fScheloha 		syms->table[i].sym_size = diff;
158eaddcb2fScheloha 	}
15923160851Smpi 
16023160851Smpi bad:
161eaddcb2fScheloha 	elf_end(elf);
162eaddcb2fScheloha 	close(fd);
163eaddcb2fScheloha 	return syms;
16423160851Smpi }
16523160851Smpi 
16623160851Smpi void
kelf_close(struct syms * syms)1675e6104a1Sclaudio kelf_close(struct syms *syms)
16823160851Smpi {
169eaddcb2fScheloha 	size_t i;
170eaddcb2fScheloha 
1715e6104a1Sclaudio 	if (syms == NULL)
1725e6104a1Sclaudio 		return;
173eaddcb2fScheloha 
174eaddcb2fScheloha 	for (i = 0; i < syms->nsymb; i++)
175eaddcb2fScheloha 		free(syms->table[i].sym_name);
176eaddcb2fScheloha 	free(syms->table);
1775e6104a1Sclaudio 	free(syms);
17823160851Smpi }
17923160851Smpi 
18023160851Smpi int
kelf_snprintsym(struct syms * syms,char * str,size_t size,unsigned long pc,unsigned long off)1815e6104a1Sclaudio kelf_snprintsym(struct syms *syms, char *str, size_t size, unsigned long pc,
1825e6104a1Sclaudio     unsigned long off)
18323160851Smpi {
184eaddcb2fScheloha 	struct sym key = { .sym_value = pc + off };
185eaddcb2fScheloha 	struct sym *entry;
186eaddcb2fScheloha 	Elf_Addr offset;
18723160851Smpi 
1885e6104a1Sclaudio 	if (syms == NULL)
1895e6104a1Sclaudio 		goto fallback;
1905e6104a1Sclaudio 
191eaddcb2fScheloha 	entry = bsearch(&key, syms->table, syms->nsymb, sizeof *syms->table,
192eaddcb2fScheloha 	    sym_compare_search);
193eaddcb2fScheloha 	if (entry == NULL)
19423160851Smpi 		goto fallback;
19523160851Smpi 
196eaddcb2fScheloha 	offset = pc - (entry->sym_value + off);
19781c0b8beScheloha 	if (offset != 0) {
198eaddcb2fScheloha 		return snprintf(str, size, "\n%s+0x%llx",
199eaddcb2fScheloha 		    entry->sym_name, (unsigned long long)offset);
20081c0b8beScheloha 	}
20181c0b8beScheloha 
202eaddcb2fScheloha 	return snprintf(str, size, "\n%s", entry->sym_name);
20323160851Smpi 
20423160851Smpi fallback:
205199e5b12Smpi 	return snprintf(str, size, "\n0x%lx", pc);
20623160851Smpi }
20723160851Smpi 
20823160851Smpi int
sym_compare_sort(const void * ap,const void * bp)209eaddcb2fScheloha sym_compare_sort(const void *ap, const void *bp)
21023160851Smpi {
211eaddcb2fScheloha 	const struct sym *a = ap, *b = bp;
21223160851Smpi 
213eaddcb2fScheloha 	if (a->sym_value < b->sym_value)
214eaddcb2fScheloha 		return -1;
215eaddcb2fScheloha 	return a->sym_value > b->sym_value;
21623160851Smpi }
21723160851Smpi 
218eaddcb2fScheloha int
sym_compare_search(const void * keyp,const void * entryp)219eaddcb2fScheloha sym_compare_search(const void *keyp, const void *entryp)
220eaddcb2fScheloha {
221eaddcb2fScheloha 	const struct sym *entry = entryp, *key = keyp;
22223160851Smpi 
223eaddcb2fScheloha 	if (key->sym_value < entry->sym_value)
224eaddcb2fScheloha 		return -1;
225eaddcb2fScheloha 	return key->sym_value >= entry->sym_value + entry->sym_size;
22623160851Smpi }
227