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