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:
@@ -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 *
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user