xref: /openbsd-src/usr.sbin/btrace/ksyms.c (revision 3e48d9ff1c49af9b8d475e167c5ac25039aa0b36)
1 /*	$OpenBSD: ksyms.c,v 1.10 2024/04/01 22:49:04 jsg Exp $ */
2 
3 /*
4  * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #define _DYN_LOADER	/* needed for AuxInfo */
20 
21 #include <sys/types.h>
22 
23 #include <err.h>
24 #include <fcntl.h>
25 #include <gelf.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "btrace.h"
33 
34 struct sym {
35 	char *sym_name;
36 	unsigned long sym_value;	/* from st_value */
37 	unsigned long sym_size;		/* from st_size */
38 };
39 
40 struct syms {
41 	struct sym *table;
42 	size_t nsymb;
43 };
44 
45 int sym_compare_search(const void *, const void *);
46 int sym_compare_sort(const void *, const void *);
47 
48 struct syms *
kelf_open(const char * path)49 kelf_open(const char *path)
50 {
51 	char *name;
52 	Elf *elf;
53 	Elf_Data *data = NULL;
54 	Elf_Scn	*scn = NULL, *symtab = NULL;
55 	GElf_Sym sym;
56 	GElf_Shdr shdr;
57 	size_t i, shstrndx, strtabndx = SIZE_MAX, symtab_size;
58 	unsigned long diff;
59 	struct sym *tmp;
60 	struct syms *syms = NULL;
61 	int fd;
62 
63 	if (elf_version(EV_CURRENT) == EV_NONE)
64 		errx(1, "elf_version: %s", elf_errmsg(-1));
65 
66 	fd = open(path, O_RDONLY);
67 	if (fd == -1) {
68 		warn("open: %s", path);
69 		return NULL;
70 	}
71 
72 	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
73 		warnx("elf_begin: %s", elf_errmsg(-1));
74 		goto bad;
75 	}
76 
77 	if (elf_kind(elf) != ELF_K_ELF)
78 		goto bad;
79 
80 	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
81 		warnx("elf_getshdrstrndx: %s", elf_errmsg(-1));
82 		goto bad;
83 	}
84 
85 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
86 		if (gelf_getshdr(scn, &shdr) != &shdr) {
87 			warnx("elf_getshdr: %s", elf_errmsg(-1));
88 			goto bad;
89 		}
90 		if ((name = elf_strptr(elf, shstrndx, shdr.sh_name)) == NULL) {
91 			warnx("elf_strptr: %s", elf_errmsg(-1));
92 			goto bad;
93 		}
94 		if (strcmp(name, ELF_SYMTAB) == 0 &&
95 		    shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
96 			symtab = scn;
97 			symtab_size = shdr.sh_size / shdr.sh_entsize;
98 		}
99 		if (strcmp(name, ELF_STRTAB) == 0 &&
100 		    shdr.sh_type == SHT_STRTAB) {
101 			strtabndx = elf_ndxscn(scn);
102 		}
103 	}
104 	if (symtab == NULL) {
105 		warnx("%s: %s: section not found", path, ELF_SYMTAB);
106 		goto bad;
107 	}
108 	if (strtabndx == SIZE_MAX) {
109 		warnx("%s: %s: section not found", path, ELF_STRTAB);
110 		goto bad;
111 	}
112 
113 	data = elf_rawdata(symtab, data);
114 	if (data == NULL)
115 		goto bad;
116 
117 	if ((syms = calloc(1, sizeof(*syms))) == NULL)
118 		err(1, NULL);
119 	syms->table = calloc(symtab_size, sizeof *syms->table);
120 	if (syms->table == NULL)
121 		err(1, NULL);
122 	for (i = 0; i < symtab_size; i++) {
123 		if (gelf_getsym(data, i, &sym) == NULL)
124 			continue;
125 		if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
126 			continue;
127 		name = elf_strptr(elf, strtabndx, sym.st_name);
128 		if (name == NULL)
129 			continue;
130 		syms->table[syms->nsymb].sym_name = strdup(name);
131 		if (syms->table[syms->nsymb].sym_name == NULL)
132 			err(1, NULL);
133 		syms->table[syms->nsymb].sym_value = sym.st_value;
134 		syms->table[syms->nsymb].sym_size = sym.st_size;
135 		syms->nsymb++;
136 	}
137 	tmp = reallocarray(syms->table, syms->nsymb, sizeof *syms->table);
138 	if (tmp == NULL)
139 		err(1, NULL);
140 	syms->table = tmp;
141 
142 	/* Sort symbols in ascending order by address. */
143 	qsort(syms->table, syms->nsymb, sizeof *syms->table, sym_compare_sort);
144 
145 	/*
146 	 * Some functions, particularly those written in assembly, have an
147 	 * st_size of zero.  We can approximate a size for these by assuming
148 	 * that they extend from their st_value to that of the next function.
149 	 */
150 	for (i = 0; i < syms->nsymb; i++) {
151 		if (syms->table[i].sym_size != 0)
152 			continue;
153 		/* Can't do anything for the last symbol. */
154 		if (i + 1 == syms->nsymb)
155 			continue;
156 		diff = syms->table[i + 1].sym_value - syms->table[i].sym_value;
157 		syms->table[i].sym_size = diff;
158 	}
159 
160 bad:
161 	elf_end(elf);
162 	close(fd);
163 	return syms;
164 }
165 
166 void
kelf_close(struct syms * syms)167 kelf_close(struct syms *syms)
168 {
169 	size_t i;
170 
171 	if (syms == NULL)
172 		return;
173 
174 	for (i = 0; i < syms->nsymb; i++)
175 		free(syms->table[i].sym_name);
176 	free(syms->table);
177 	free(syms);
178 }
179 
180 int
kelf_snprintsym(struct syms * syms,char * str,size_t size,unsigned long pc,unsigned long off)181 kelf_snprintsym(struct syms *syms, char *str, size_t size, unsigned long pc,
182     unsigned long off)
183 {
184 	struct sym key = { .sym_value = pc + off };
185 	struct sym *entry;
186 	Elf_Addr offset;
187 
188 	if (syms == NULL)
189 		goto fallback;
190 
191 	entry = bsearch(&key, syms->table, syms->nsymb, sizeof *syms->table,
192 	    sym_compare_search);
193 	if (entry == NULL)
194 		goto fallback;
195 
196 	offset = pc - (entry->sym_value + off);
197 	if (offset != 0) {
198 		return snprintf(str, size, "\n%s+0x%llx",
199 		    entry->sym_name, (unsigned long long)offset);
200 	}
201 
202 	return snprintf(str, size, "\n%s", entry->sym_name);
203 
204 fallback:
205 	return snprintf(str, size, "\n0x%lx", pc);
206 }
207 
208 int
sym_compare_sort(const void * ap,const void * bp)209 sym_compare_sort(const void *ap, const void *bp)
210 {
211 	const struct sym *a = ap, *b = bp;
212 
213 	if (a->sym_value < b->sym_value)
214 		return -1;
215 	return a->sym_value > b->sym_value;
216 }
217 
218 int
sym_compare_search(const void * keyp,const void * entryp)219 sym_compare_search(const void *keyp, const void *entryp)
220 {
221 	const struct sym *entry = entryp, *key = keyp;
222 
223 	if (key->sym_value < entry->sym_value)
224 		return -1;
225 	return key->sym_value >= entry->sym_value + entry->sym_size;
226 }
227