1
0
mirror of https://github.com/openbsd/src.git synced 2026-04-29 16:47:15 +00:00

Teach btrace(8) how to resolve addresses in callstacks to symbols.

Overall design comes from deraadt@. The calculation method of DSO
address in traced process virtual address space was suggested by
kettenis@.  Many others provided their feedback and ideas in earlier
versions of this change.

OK claudio@
This commit is contained in:
sashan
2025-09-22 07:49:43 +00:00
parent ba3af0f64a
commit adfd21e9d9
5 changed files with 420 additions and 151 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: dt_dev.c,v 1.45 2025/07/31 15:07:59 bluhm Exp $ */
/* $OpenBSD: dt_dev.c,v 1.46 2025/09/22 07:49:43 sashan Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -25,6 +25,13 @@
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/vnode.h>
#include <uvm/uvm.h>
#include <uvm/uvm_map.h>
#include <uvm/uvm_vnode.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/fcntl.h>
#include <machine/intr.h>
@@ -162,7 +169,7 @@ int dt_ioctl_record_start(struct dt_softc *);
void dt_ioctl_record_stop(struct dt_softc *);
int dt_ioctl_probe_enable(struct dt_softc *, struct dtioc_req *);
int dt_ioctl_probe_disable(struct dt_softc *, struct dtioc_req *);
int dt_ioctl_get_auxbase(struct dt_softc *, struct dtioc_getaux *);
int dt_ioctl_rd_vnode(struct dt_softc *, struct dtioc_rdvn *);
int dt_ring_copy(struct dt_cpubuf *, struct uio *, size_t, size_t *);
@@ -299,7 +306,7 @@ dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
case DTIOCRECORD:
case DTIOCPRBENABLE:
case DTIOCPRBDISABLE:
case DTIOCGETAUXBASE:
case DTIOCRDVNODE:
/* root only ioctl(2) */
break;
default:
@@ -323,8 +330,8 @@ dtioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
case DTIOCPRBDISABLE:
error = dt_ioctl_probe_disable(sc, (struct dtioc_req *)addr);
break;
case DTIOCGETAUXBASE:
error = dt_ioctl_get_auxbase(sc, (struct dtioc_getaux *)addr);
case DTIOCRDVNODE:
error = dt_ioctl_rd_vnode(sc, (struct dtioc_rdvn *)addr);
break;
default:
KASSERT(0);
@@ -652,39 +659,76 @@ dt_ioctl_probe_disable(struct dt_softc *sc, struct dtioc_req *dtrq)
}
int
dt_ioctl_get_auxbase(struct dt_softc *sc, struct dtioc_getaux *dtga)
dt_ioctl_rd_vnode(struct dt_softc *sc, struct dtioc_rdvn *dtrv)
{
struct uio uio;
struct iovec iov;
struct process *pr;
struct process *ps;
struct proc *p = curproc;
AuxInfo auxv[ELF_AUX_ENTRIES];
int i, error;
boolean_t ok;
struct vm_map_entry *e;
int err = 0;
int fd;
struct uvm_vnode *uvn;
struct vnode *vn;
struct file *fp;
dtga->dtga_auxbase = 0;
if ((pr = prfind(dtga->dtga_pid)) == NULL)
if ((ps = prfind(dtrv->dtrv_pid)) == NULL)
return ESRCH;
iov.iov_base = auxv;
iov.iov_len = sizeof(auxv);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = pr->ps_auxinfo;
uio.uio_resid = sizeof(auxv);
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_procp = p;
uio.uio_rw = UIO_READ;
vm_map_lock_read(&ps->ps_vmspace->vm_map);
error = process_domem(p, pr, &uio, PT_READ_D);
if (error)
return error;
ok = uvm_map_lookup_entry(&ps->ps_vmspace->vm_map,
(vaddr_t)dtrv->dtrv_va, &e);
if (ok == 0 || (e->etype & UVM_ET_OBJ) == 0 ||
(e->protection & PROT_EXEC) == 0 ||
!UVM_OBJ_IS_VNODE(e->object.uvm_obj)) {
err = ENOENT;
vn = NULL;
DPRINTF("%s no mapping for %p\n", __func__, dtrv->dtrv_va);
} else {
uvn = (struct uvm_vnode *)e->object.uvm_obj;
vn = uvn->u_vnode;
vref(vn);
for (i = 0; i < ELF_AUX_ENTRIES; i++)
if (auxv[i].au_id == AUX_base)
dtga->dtga_auxbase = auxv[i].au_v;
dtrv->dtrv_len = (size_t)uvn->u_size;
dtrv->dtrv_start = (caddr_t)e->start;
dtrv->dtrv_offset = (caddr_t)e->offset;
}
return 0;
vm_map_unlock_read(&ps->ps_vmspace->vm_map);
if (vn != NULL) {
fdplock(p->p_fd);
err = falloc(p, &fp, &fd);
fdpunlock(p->p_fd);
if (err != 0) {
vrele(vn);
DPRINTF("%s fdopen failed (%d)\n", __func__, err);
return err;
}
err = VOP_OPEN(vn, O_RDONLY, p->p_p->ps_ucred, p);
if (err == 0) {
fp->f_flag = FREAD;
fp->f_type = DTYPE_VNODE;
fp->f_ops = &vnops;
fp->f_data = vn;
fp->f_offset = 0;
dtrv->dtrv_fd = fd;
fdplock(p->p_fd);
fdinsert(p->p_fd, fd, UF_EXCLOSE, fp);
fdpunlock(p->p_fd);
FRELE(fp, p);
} else {
DPRINTF("%s vopen() failed (%d)\n", __func__,
err);
vrele(vn);
fdplock(p->p_fd);
fdremove(p->p_fd, fd);
fdpunlock(p->p_fd);
FRELE(fp, p);
}
}
return err;
}
struct dt_probe *

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: dtvar.h,v 1.23 2025/08/14 13:04:48 mpi Exp $ */
/* $OpenBSD: dtvar.h,v 1.24 2025/09/22 07:49:43 sashan Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -22,6 +22,7 @@
#include <sys/ioccom.h>
#include <sys/stacktrace.h>
#include <sys/time.h>
#include <sys/syslimits.h>
/*
* Length of provider/probe/function names, including NUL.
@@ -121,18 +122,25 @@ struct dtioc_stat {
uint64_t dtst_recurevt; /* recursive events */
};
struct dtioc_getaux {
pid_t dtga_pid; /* process to inspect */
unsigned long dtga_auxbase; /* AUX_base value */
struct dtioc_rdvn {
pid_t dtrv_pid; /* process to inspect */
int dtrv_fd; /* where to dump data */
caddr_t dtrv_va;
/* programm counter in inspected process */
caddr_t dtrv_offset;
/* comes from vm_map_entry::offset */
caddr_t dtrv_start; /* end address for section */
size_t dtrv_len; /* the length of ELF file */
};
#define DTIOCGPLIST _IOWR('D', 1, struct dtioc_probe)
#define DTIOCGSTATS _IOR('D', 2, struct dtioc_stat)
#define DTIOCRECORD _IOW('D', 3, int)
#define DTIOCPRBENABLE _IOW('D', 4, struct dtioc_req)
#define DTIOCPRBDISABLE _IOW('D', 5, struct dtioc_req)
#define DTIOCPRBDISABLE _IOW('D', 5, struct dtioc_req)
#define DTIOCGARGS _IOWR('D', 6, struct dtioc_arg)
#define DTIOCGETAUXBASE _IOWR('D', 7, struct dtioc_getaux)
/* _IOWR('D', 7, struct dtioc_getaux) was DTIOCGETAUXBASE */
#define DTIOCRDVNODE _IOWR('D', 8, struct dtioc_rdvn)
#ifdef _KERNEL

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: btrace.c,v 1.97 2025/05/18 20:09:58 schwarze Exp $ */
/* $OpenBSD: btrace.c,v 1.98 2025/09/22 07:49:43 sashan Exp $ */
/*
* Copyright (c) 2019 - 2023 Martin Pieuchot <mpi@openbsd.org>
@@ -39,6 +39,8 @@
#include <dev/dt/dtvar.h>
#include <gelf.h>
#include "btrace.h"
#include "bt_parser.h"
@@ -120,7 +122,7 @@ struct dt_evt bt_devt; /* fake event for BEGIN/END */
#define EVENT_END (unsigned int)(-1)
uint64_t bt_filtered; /* # of events filtered out */
struct syms *kelf, *uelf;
struct syms *kelf;
char **vargs;
int nargs = 0;
@@ -157,9 +159,6 @@ main(int argc, char *argv[])
case 'n':
noaction = 1;
break;
case 'p':
uelf = kelf_open(optarg);
break;
case 'v':
verbose++;
break;
@@ -611,7 +610,7 @@ rules_setup(int fd)
}
if (dokstack)
kelf = kelf_open(_PATH_KSYMS);
kelf = kelf_open_kernel(_PATH_KSYMS);
/* Initialize "fake" event for BEGIN/END */
bt_devt.dtev_pbn = EVENT_BEGIN;
@@ -699,9 +698,6 @@ rules_teardown(int fd)
}
kelf_close(kelf);
kelf = NULL;
kelf_close(uelf);
uelf = NULL;
/* Update "fake" event for BEGIN/END */
bt_devt.dtev_pbn = EVENT_END;
@@ -806,7 +802,7 @@ builtin_nsecs(struct dt_evt *dtev)
}
const char *
builtin_stack(struct dt_evt *dtev, int kernel, unsigned long offset)
builtin_stack(struct dt_evt *dtev, int kernel)
{
struct stacktrace *st = &dtev->dtev_kstack;
static char buf[4096];
@@ -829,11 +825,11 @@ builtin_stack(struct dt_evt *dtev, int kernel, unsigned long offset)
int l;
if (!kernel)
l = kelf_snprintsym(uelf, bp, sz - 1, st->st_pc[i],
offset);
l = kelf_snprintsym_proc(dtfd, dtev->dtev_pid, bp, sz - 1,
st->st_pc[i]);
else
l = kelf_snprintsym(kelf, bp, sz - 1, st->st_pc[i],
offset);
l = kelf_snprintsym_kernel(kelf, bp, sz - 1,
st->st_pc[i]);
if (l < 0)
break;
if (l >= sz - 1) {
@@ -1805,10 +1801,10 @@ ba2str(struct bt_arg *ba, struct dt_evt *dtev)
str = "";
break;
case B_AT_BI_KSTACK:
str = builtin_stack(dtev, 1, 0);
str = builtin_stack(dtev, 1);
break;
case B_AT_BI_USTACK:
str = builtin_stack(dtev, 0, dt_get_offset(dtev->dtev_pid));
str = builtin_stack(dtev, 0);
break;
case B_AT_BI_COMM:
str = dtev->dtev_comm;
@@ -2103,30 +2099,3 @@ debug_dump_filter(struct bt_rule *r)
debug_dump_expr(SLIST_FIRST(&bs->bs_args));
debugx("/\n");
}
unsigned long
dt_get_offset(pid_t pid)
{
static struct dtioc_getaux cache[32];
static int next;
struct dtioc_getaux *aux = NULL;
int i;
for (i = 0; i < 32; i++) {
if (cache[i].dtga_pid != pid)
continue;
aux = cache + i;
break;
}
if (aux == NULL) {
aux = &cache[next++];
next %= 32;
aux->dtga_pid = pid;
if (ioctl(dtfd, DTIOCGETAUXBASE, aux))
aux->dtga_auxbase = 0;
}
return aux->dtga_auxbase;
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: btrace.h,v 1.15 2024/05/21 05:00:48 jsg Exp $ */
/* $OpenBSD: btrace.h,v 1.16 2025/09/22 07:49:43 sashan Exp $ */
/*
* Copyright (c) 2019 - 2020 Martin Pieuchot <mpi@openbsd.org>
@@ -19,6 +19,8 @@
#ifndef BTRACE_H
#define BTRACE_H
#include <dev/dt/dtvar.h>
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
@@ -37,10 +39,13 @@ unsigned long dt_get_offset(pid_t);
/* ksyms.c */
struct syms;
struct syms *kelf_open(const char *);
struct syms *kelf_open_kernel(const char *);
struct syms *kelf_load_syms(void *, struct syms *);
void kelf_close(struct syms *);
int kelf_snprintsym(struct syms *, char *, size_t,
unsigned long, unsigned long);
int kelf_snprintsym_proc(int, pid_t, char *, size_t,
unsigned long);
int kelf_snprintsym_kernel(struct syms *, char *, size_t,
unsigned long);
/* map.c */
struct map;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: ksyms.c,v 1.10 2024/04/01 22:49:04 jsg Exp $ */
/* $OpenBSD: ksyms.c,v 1.11 2025/09/22 07:49:43 sashan Exp $ */
/*
* Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
@@ -28,6 +28,10 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <dev/dt/dtvar.h>
#include <sys/queue.h>
#include "btrace.h"
@@ -42,54 +46,108 @@ struct syms {
size_t nsymb;
};
int sym_compare_search(const void *, const void *);
int sym_compare_sort(const void *, const void *);
struct shlib_syms {
struct syms *sls_syms;
caddr_t sls_start;
caddr_t sls_end;
LIST_ENTRY(shlib_syms) sls_le;
};
struct syms *
kelf_open(const char *path)
static LIST_HEAD(table, shlib_syms) shlib_lh = LIST_HEAD_INITIALIZER(table);
static int sym_compare_search(const void *, const void *);
static int sym_compare_sort(const void *, const void *);
static struct shlib_syms *
read_syms(Elf *elf, const void *offset, const void *dtrv_va)
{
char *name;
Elf *elf;
Elf_Data *data = NULL;
Elf_Scn *scn = NULL, *symtab = NULL;
GElf_Sym sym;
GElf_Shdr shdr;
size_t i, shstrndx, strtabndx = SIZE_MAX, symtab_size;
GElf_Phdr phdr;
size_t phnum, i;
size_t shstrndx, strtabndx = SIZE_MAX, symtab_size;
unsigned long diff;
struct sym *tmp;
struct syms *syms = NULL;
int fd;
uintptr_t base_addr = 0;
struct shlib_syms *sls;
void *v_start, *v_end;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(1, "elf_version: %s", elf_errmsg(-1));
fd = open(path, O_RDONLY);
if (fd == -1) {
warn("open: %s", path);
if (elf_kind(elf) != ELF_K_ELF) {
warnx("elf_keind() != ELF_K_ELF");
return NULL;
}
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
warnx("elf_begin: %s", elf_errmsg(-1));
goto bad;
}
if (elf_kind(elf) != ELF_K_ELF)
goto bad;
if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
warnx("elf_getshdrstrndx: %s", elf_errmsg(-1));
goto bad;
return NULL;
}
if (elf_getphdrnum(elf, &phnum) != 0) {
warnx("elf_getphdrnum: %s", elf_errmsg(-1));
return NULL;
}
/*
* calculate base_addr where DSO got loaded to at traced process.
* We need to combine
* - address of .text segment (.dtrv_start)
* - offset of .text segment (.dtrv_offset)
* - program address we are resolving (.dtrv_va)
* - .p_offset and .p_vaddr from elf header
*
* the offset comes from caller, caller did a math for us already:
* offset = .dtrv_offset + (.dtrv_va - .dtrv_start)
* Here we also use offset to find program header such offset
* fits into range <.p_offset, .p_offset + .p_filesz). The matching
* program header provides the last two pieces: .p_vaddr and .p_offset
* to calculate base_addr:
* (dtrv_va - .p_vaddr) - (offset - .p_offset)
*/
v_start = NULL;
v_end = NULL;
for (i = 0; i < phnum && base_addr == 0 && v_start == NULL; i++) {
if (gelf_getphdr(elf, i, &phdr) == NULL) {
warnx("gelf_getphdr(%zu): %s", i, elf_errmsg(-1));
continue;
}
if (base_addr == 0 && (void *)phdr.p_offset <= offset &&
offset < (void *)(phdr.p_offset + phdr.p_filesz))
base_addr =
(uintptr_t)((dtrv_va - (void *)phdr.p_vaddr) -
(offset - (void *)phdr.p_offset));
/*
* while walking the headers, find virtual address of .text
*/
if ((phdr.p_flags & PF_X) != 0) {
v_start = (void *)phdr.p_vaddr;
v_end = (void *)(phdr.p_vaddr + phdr.p_memsz);
}
}
sls = malloc(sizeof (struct shlib_syms));
if (sls == NULL) {
err(1, "malloc");
} else {
sls->sls_start = v_start + base_addr;
sls->sls_end = v_end + base_addr;
sls->sls_syms = NULL;
}
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("elf_getshdr: %s", elf_errmsg(-1));
goto bad;
return sls;
}
if ((name = elf_strptr(elf, shstrndx, shdr.sh_name)) == NULL) {
warnx("elf_strptr: %s", elf_errmsg(-1));
goto bad;
return sls;
}
if (strcmp(name, ELF_SYMTAB) == 0 &&
shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
@@ -102,23 +160,25 @@ kelf_open(const char *path)
}
}
if (symtab == NULL) {
warnx("%s: %s: section not found", path, ELF_SYMTAB);
goto bad;
warnx("%s: %s: section not found", __func__, ELF_SYMTAB);
return sls;
}
if (strtabndx == SIZE_MAX) {
warnx("%s: %s: section not found", path, ELF_STRTAB);
goto bad;
warnx("%s: %s: section not found", __func__, ELF_STRTAB);
return sls;
}
data = elf_rawdata(symtab, data);
if (data == NULL)
goto bad;
if (data == NULL) {
warnx("%s elf_rwadata() unable to read syms from\n", __func__);
return sls;
}
if ((syms = calloc(1, sizeof(*syms))) == NULL)
err(1, NULL);
syms->table = calloc(symtab_size, sizeof *syms->table);
if (syms->table == NULL)
err(1, NULL);
err(1, "calloc");
for (i = 0; i < symtab_size; i++) {
if (gelf_getsym(data, i, &sym) == NULL)
continue;
@@ -127,16 +187,18 @@ kelf_open(const char *path)
name = elf_strptr(elf, strtabndx, sym.st_name);
if (name == NULL)
continue;
if (sym.st_value == 0)
continue;
syms->table[syms->nsymb].sym_name = strdup(name);
if (syms->table[syms->nsymb].sym_name == NULL)
err(1, NULL);
syms->table[syms->nsymb].sym_value = sym.st_value;
syms->table[syms->nsymb].sym_value = sym.st_value + base_addr;
syms->table[syms->nsymb].sym_size = sym.st_size;
syms->nsymb++;
}
tmp = reallocarray(syms->table, syms->nsymb, sizeof *syms->table);
if (tmp == NULL)
err(1, NULL);
err(1, "reallocarray");
syms->table = tmp;
/* Sort symbols in ascending order by address. */
@@ -157,55 +219,125 @@ kelf_open(const char *path)
syms->table[i].sym_size = diff;
}
bad:
elf_end(elf);
close(fd);
return syms;
sls->sls_syms = syms;
return sls;
}
void
kelf_close(struct syms *syms)
static struct shlib_syms *
read_syms_buf(char *elfbuf, size_t elfbuf_sz, caddr_t offset, caddr_t dtrv_va)
{
Elf *elf;
struct shlib_syms *sls;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(1, "elf_version: %s", elf_errmsg(-1));
if ((elf = elf_memory(elfbuf, elfbuf_sz)) == NULL) {
warnx("elf_memory: %s", elf_errmsg(-1));
return NULL;
}
sls = read_syms(elf, offset, dtrv_va);
elf_end(elf);
return sls;
}
static void
free_syms(struct syms *syms)
{
size_t i;
if (syms == NULL)
return;
if (syms != NULL) {
for (i = 0; i < syms->nsymb; i++)
free(syms->table[i].sym_name);
for (i = 0; i < syms->nsymb; i++)
free(syms->table[i].sym_name);
free(syms->table);
free(syms);
free(syms->table);
free(syms);
}
}
int
kelf_snprintsym(struct syms *syms, char *str, size_t size, unsigned long pc,
unsigned long off)
static struct shlib_syms *
load_syms(int dtdev, pid_t pid, caddr_t pc)
{
struct sym key = { .sym_value = pc + off };
struct sym *entry;
Elf_Addr offset;
struct shlib_syms *new_sls, *mark_sls, *sls;
struct dtioc_rdvn dtrv;
char *elfbuf;
if (syms == NULL)
goto fallback;
entry = bsearch(&key, syms->table, syms->nsymb, sizeof *syms->table,
sym_compare_search);
if (entry == NULL)
goto fallback;
offset = pc - (entry->sym_value + off);
if (offset != 0) {
return snprintf(str, size, "\n%s+0x%llx",
entry->sym_name, (unsigned long long)offset);
memset(&dtrv, 0, sizeof (dtrv));
dtrv.dtrv_pid = pid;
dtrv.dtrv_va = pc;
dtrv.dtrv_fd = 0;
if (ioctl(dtdev, DTIOCRDVNODE, &dtrv)) {
warn("DTIOCRDVNODE fails for %p\n", pc);
return NULL;
}
return snprintf(str, size, "\n%s", entry->sym_name);
elfbuf = mmap(NULL, dtrv.dtrv_len, PROT_READ, MAP_PRIVATE,
dtrv.dtrv_fd, 0);
if (elfbuf == MAP_FAILED) {
warn("mmap");
close(dtrv.dtrv_fd);
return NULL;
}
/*
* calculate offset we use to determine the base_addr where DSO got
* loaded at in traced process.
*/
dtrv.dtrv_offset += (dtrv.dtrv_va - dtrv.dtrv_start);
new_sls = read_syms_buf(elfbuf, dtrv.dtrv_len, dtrv.dtrv_offset,
dtrv.dtrv_va);
munmap(elfbuf, dtrv.dtrv_len);
close(dtrv.dtrv_fd);
if (new_sls == NULL) {
warn("%s malloc(shlib_syms))", __func__);
return NULL;
}
fallback:
return snprintf(str, size, "\n0x%lx", pc);
/*
* Keep list of symbol tables sorted ascending from address.
*/
mark_sls = NULL;
LIST_FOREACH(sls, &shlib_lh, sls_le) {
mark_sls = sls;
if (new_sls->sls_start < sls->sls_start)
break;
}
if (mark_sls == NULL)
LIST_INSERT_HEAD(&shlib_lh, new_sls, sls_le);
else if (new_sls->sls_start > mark_sls->sls_start)
LIST_INSERT_AFTER(mark_sls, new_sls, sls_le);
else
LIST_INSERT_BEFORE(mark_sls, new_sls, sls_le);
return new_sls;
}
int
static struct shlib_syms *
find_shlib(caddr_t pc)
{
struct shlib_syms *sls, *match_sls;
match_sls = NULL;
LIST_FOREACH(sls, &shlib_lh, sls_le) {
if (sls->sls_start > pc)
break;
match_sls = sls;
}
/*
* program counter must fit <sls_start, sls_end> range,
* if it does not, then the address has not been resolved
* yet.
*/
if (match_sls == NULL || match_sls->sls_end <= pc)
match_sls = NULL;
return match_sls;
}
static int
sym_compare_sort(const void *ap, const void *bp)
{
const struct sym *a = ap, *b = bp;
@@ -215,7 +347,7 @@ sym_compare_sort(const void *ap, const void *bp)
return a->sym_value > b->sym_value;
}
int
static int
sym_compare_search(const void *keyp, const void *entryp)
{
const struct sym *entry = entryp, *key = keyp;
@@ -224,3 +356,114 @@ sym_compare_search(const void *keyp, const void *entryp)
return -1;
return key->sym_value >= entry->sym_value + entry->sym_size;
}
struct syms *
kelf_open_kernel(const char *path)
{
struct shlib_syms *sls;
struct syms *syms;
Elf *elf;
int fd;
fd = open(path, O_RDONLY);
if (fd == -1)
return NULL;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(1, "elf_version: %s", elf_errmsg(-1));
elf = elf_begin(fd, ELF_C_READ, NULL);
if (elf == NULL) {
close(fd);
return NULL;
}
sls = read_syms(elf, 0, 0);
elf_end(elf);
close(fd);
if (sls != NULL) {
syms = sls->sls_syms;
free(sls);
} else {
syms = NULL;
}
return syms;
}
void
kelf_open(void)
{
LIST_INIT(&shlib_lh);
}
void
kelf_close(struct syms *ksyms)
{
struct shlib_syms *sls;
while (!LIST_EMPTY(&shlib_lh)) {
sls = LIST_FIRST(&shlib_lh);
LIST_REMOVE(sls, sls_le);
free_syms(sls->sls_syms);
free(sls);
}
free_syms(ksyms);
}
int
kelf_snprintsym_proc(int dtfd, pid_t pid, char *str, size_t size,
unsigned long pc)
{
struct shlib_syms *sls;
struct sym key = { .sym_value = pc };
struct sym *entry;
Elf_Addr offset;
*str = '\0';
if (pc == 0)
return 0;
if ((sls = find_shlib((caddr_t)key.sym_value)) == NULL)
sls = load_syms(dtfd, pid, (caddr_t)key.sym_value);
if (sls == NULL || sls->sls_syms == NULL)
return snprintf(str, size, "\n0x%lx", pc);
entry = bsearch(&key, sls->sls_syms->table, sls->sls_syms->nsymb,
sizeof *sls->sls_syms->table, sym_compare_search);
if (entry == NULL)
return snprintf(str, size, "\n0x%lx", pc);
offset = pc - entry->sym_value;
if (offset != 0) {
return snprintf(str, size, "\n%s+0x%llx",
entry->sym_name, (unsigned long long)offset);
}
return snprintf(str, size, "\n%s", entry->sym_name);
}
int
kelf_snprintsym_kernel(struct syms *syms, char *str, size_t size,
unsigned long pc)
{
struct sym key = { .sym_value = pc };
struct sym *entry;
Elf_Addr offset;
entry = bsearch(&key, syms->table, syms->nsymb, sizeof *syms->table,
sym_compare_search);
if (entry == NULL)
return snprintf(str, size, "\n0x%lx", pc);
offset = pc - entry->sym_value;
if (offset != 0) {
return snprintf(str, size, "\n%s+0x%llx",
entry->sym_name, (unsigned long long)offset);
}
return snprintf(str, size, "\n%s", entry->sym_name);
}