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