xref: /openbsd-src/usr.sbin/btrace/ksyms.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /*	$OpenBSD: ksyms.c,v 1.2 2020/08/13 11:35:21 mpi 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 #include <assert.h>
20 #include <err.h>
21 #include <fcntl.h>
22 #include <gelf.h>
23 #include <paths.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "btrace.h"
29 
30 int		 kfd = -1;
31 Elf		*kelf;
32 Elf_Scn		*ksymtab;
33 size_t		 kstrtabndx, knsymb;
34 
35 int		 kelf_parse(void);
36 
37 int
38 kelf_open(void)
39 {
40 	int error;
41 
42 	assert(kfd == -1);
43 
44 	if (elf_version(EV_CURRENT) == EV_NONE)
45 		errx(1, "elf_version: %s", elf_errmsg(-1));
46 
47 	kfd = open(_PATH_KSYMS, O_RDONLY);
48 	if (kfd == -1) {
49 		warn("open");
50 		return 1;
51 	}
52 
53 	if ((kelf = elf_begin(kfd, ELF_C_READ, NULL)) == NULL) {
54 		warnx("elf_begin: %s", elf_errmsg(-1));
55 		error = 1;
56 		goto bad;
57 	}
58 
59 	if (elf_kind(kelf) != ELF_K_ELF) {
60 		error = 1;
61 		goto bad;
62 	}
63 
64 	error = kelf_parse();
65 	if (error)
66 		goto bad;
67 
68 	return 0;
69 
70 bad:
71 	kelf_close();
72 	return error;
73 }
74 
75 void
76 kelf_close(void)
77 {
78 	elf_end(kelf);
79 	kelf = NULL;
80 	close(kfd);
81 	kfd = -1;
82 }
83 
84 int
85 kelf_snprintsym(char *str, size_t size, unsigned long pc)
86 {
87 	GElf_Sym	 sym;
88 	Elf_Data	*data = NULL;
89 	Elf_Addr	 offset, bestoff = 0;
90 	size_t		 i, bestidx = 0;
91 	char		*name;
92 	int		 cnt;
93 
94 	data = elf_rawdata(ksymtab, data);
95 	if (data == NULL)
96 		goto fallback;
97 
98 	for (i = 0; i < knsymb; i++) {
99 		if (gelf_getsym(data, i, &sym) == NULL)
100 			continue;
101 		if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
102 			continue;
103 		if (pc >= sym.st_value) {
104 			if (pc < (sym.st_value + sym.st_size))
105 				break;
106 			/* Workaround for symbols w/o size, usually asm ones. */
107 			if (sym.st_size == 0 && sym.st_value > bestoff) {
108 				bestidx = i;
109 				bestoff = sym.st_value;
110 			}
111 		}
112 	}
113 
114 	if (i == knsymb) {
115 		if (bestidx == 0 || gelf_getsym(data, bestidx, &sym) == NULL)
116 			goto fallback;
117 	}
118 
119 	name = elf_strptr(kelf, kstrtabndx, sym.st_name);
120 	if (name != NULL)
121 		cnt = snprintf(str, size, "\n%s", name);
122 	else
123 		cnt = snprintf(str, size, "\n0x%llx", sym.st_value);
124 
125 	offset = pc - sym.st_value;
126 	if (offset != 0)
127 		cnt += snprintf(str + cnt, size - cnt, "+0x%llx", offset);
128 
129 	return cnt;
130 
131 fallback:
132 	return snprintf(str, size, "\n0x%lx", pc);
133 }
134 
135 int
136 kelf_parse(void)
137 {
138 	GElf_Shdr	 shdr;
139 	Elf_Scn		*scn, *scnctf;
140 	char		*name;
141 	size_t		 shstrndx;
142 
143 	if (elf_getshdrstrndx(kelf, &shstrndx) != 0) {
144 		warnx("elf_getshdrstrndx: %s", elf_errmsg(-1));
145 		return 1;
146 	}
147 
148 	scn = scnctf = NULL;
149 	while ((scn = elf_nextscn(kelf, scn)) != NULL) {
150 		if (gelf_getshdr(scn, &shdr) != &shdr) {
151 			warnx("elf_getshdr: %s", elf_errmsg(-1));
152 			return 1;
153 		}
154 
155 		if ((name = elf_strptr(kelf, shstrndx, shdr.sh_name)) == NULL) {
156 			warnx("elf_strptr: %s", elf_errmsg(-1));
157 			return 1;
158 		}
159 
160 		if (strcmp(name, ELF_SYMTAB) == 0 &&
161 		    shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
162 			ksymtab = scn;
163 			knsymb = shdr.sh_size / shdr.sh_entsize;
164 		}
165 
166 		if (strcmp(name, ELF_STRTAB) == 0 &&
167 		    shdr.sh_type == SHT_STRTAB) {
168 			kstrtabndx = elf_ndxscn(scn);
169 		}
170 	}
171 
172 	if (ksymtab == NULL)
173 		warnx("symbol table not found");
174 
175 	return 0;
176 }
177