1 /* $OpenBSD: ksyms.c,v 1.4 2021/02/10 00:34:57 deraadt 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 if (cnt < 0) 125 return cnt; 126 127 offset = pc - sym.st_value; 128 if (offset != 0) { 129 int l; 130 131 l = snprintf(str + cnt, size > (size_t)cnt ? size - cnt : 0, 132 "+0x%llx", (unsigned long long)offset); 133 if (l < 0) 134 return l; 135 cnt += l; 136 } 137 138 return cnt; 139 140 fallback: 141 return snprintf(str, size, "\n0x%lx", pc); 142 } 143 144 int 145 kelf_parse(void) 146 { 147 GElf_Shdr shdr; 148 Elf_Scn *scn, *scnctf; 149 char *name; 150 size_t shstrndx; 151 152 if (elf_getshdrstrndx(kelf, &shstrndx) != 0) { 153 warnx("elf_getshdrstrndx: %s", elf_errmsg(-1)); 154 return 1; 155 } 156 157 scn = scnctf = NULL; 158 while ((scn = elf_nextscn(kelf, scn)) != NULL) { 159 if (gelf_getshdr(scn, &shdr) != &shdr) { 160 warnx("elf_getshdr: %s", elf_errmsg(-1)); 161 return 1; 162 } 163 164 if ((name = elf_strptr(kelf, shstrndx, shdr.sh_name)) == NULL) { 165 warnx("elf_strptr: %s", elf_errmsg(-1)); 166 return 1; 167 } 168 169 if (strcmp(name, ELF_SYMTAB) == 0 && 170 shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { 171 ksymtab = scn; 172 knsymb = shdr.sh_size / shdr.sh_entsize; 173 } 174 175 if (strcmp(name, ELF_STRTAB) == 0 && 176 shdr.sh_type == SHT_STRTAB) { 177 kstrtabndx = elf_ndxscn(scn); 178 } 179 } 180 181 if (ksymtab == NULL) 182 warnx("symbol table not found"); 183 184 return 0; 185 } 186