diff options
Diffstat (limited to 'usr.bin/pmdb/elf_syms.c')
-rw-r--r-- | usr.bin/pmdb/elf_syms.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/usr.bin/pmdb/elf_syms.c b/usr.bin/pmdb/elf_syms.c new file mode 100644 index 00000000000..cba56fee07d --- /dev/null +++ b/usr.bin/pmdb/elf_syms.c @@ -0,0 +1,403 @@ +/* $PMDB: elf_syms.c,v 1.18 2002/03/11 23:39:49 art Exp $ */ +/* + * Copyright (c) 2002 Artur Grabowski <art@openbsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <err.h> + +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/mman.h> + +#include <nlist.h> +#ifdef __NetBSD__ +#include <machine/elf_machdep.h> +#define ELFSIZE ARCH_ELFSIZE +#include <sys/exec_elf.h> +#else +#include <elf_abi.h> +#endif +#include <link.h> + +#include "pmdb.h" +#include "symbol.h" +#include "break.h" + +struct elf_symbol_handle { + struct sym_table esh_st; + int esh_fd; + char *esh_strtab; + Elf_Word esh_strsize; + Elf_Sym *esh_symtab; + Elf_Word esh_symsize; + Elf_Addr esh_offs; +}; + +#define ESH_TO_ST(esh) (&(esh)->esh_st) +#define ST_TO_ESH(st) ((struct elf_symbol_handle *)(st)) + +struct sym_table *elf_open(const char *); +void elf_close(struct sym_table *); +char *elf_name_and_off(struct sym_table *, reg, reg *); +int elf_lookup(struct pstate *, const char *, reg *); +void elf_update(struct pstate *); + +struct sym_ops elf_sops = { + elf_open, + elf_close, + elf_name_and_off, + elf_lookup, + elf_update +}; + +int +sym_check_elf(const char *name, struct pstate *ps) +{ + Elf_Ehdr ehdr; + int fd; + + if ((fd = open(name, O_RDONLY)) < 0) + return (-1); + + if (pread(fd, &ehdr, sizeof(Elf_Ehdr), 0) != sizeof(Elf_Ehdr)) + return (-1); + +#ifndef __NetBSD__ + if (!IS_ELF(ehdr) || + ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS || + ehdr.e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr.e_ident[EI_VERSION] != ELF_TARG_VER || + ehdr.e_machine != ELF_TARG_MACH || + ehdr.e_version != ELF_TARG_VER) + return (-1); +#endif + + close(fd); + + ps->ps_sops = &elf_sops; + + return (0); +} + +struct sym_table * +elf_open(const char *name) +{ + struct elf_symbol_handle *esh; + Elf_Off symoff, stroff; + Elf_Ehdr ehdr; + Elf_Shdr *shdr; + int i, fd; + + /* Just a sanity check */ + if (sizeof(reg) != sizeof(Elf_Addr)) + errx(1, "sym_open: sizeof(reg) != sizeof(Elf_Addr)"); + + if ((esh = malloc(sizeof(*esh))) == NULL) { + return (NULL); + } + + memset(esh, 0, sizeof(*esh)); + esh->esh_fd = -1; + + if ((fd = esh->esh_fd = open(name, O_RDONLY)) < 0) { + goto fail; + } + + if (pread(fd, &ehdr, sizeof(Elf_Ehdr), 0) != sizeof(Elf_Ehdr)) { + goto fail; + } +#ifndef __NetBSD__ + if (!IS_ELF(ehdr) || + ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS || + ehdr.e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr.e_ident[EI_VERSION] != ELF_TARG_VER || + ehdr.e_machine != ELF_TARG_MACH || + ehdr.e_version != ELF_TARG_VER) { + goto fail; + } +#endif + + if ((shdr = (Elf_Shdr *)mmap(NULL, ehdr.e_shentsize * ehdr.e_shnum, + PROT_READ, MAP_SHARED, fd, ehdr.e_shoff)) == MAP_FAILED) { + goto fail; + } + + for (i = 0; i < ehdr.e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { + symoff = shdr[i].sh_offset; + esh->esh_symsize = shdr[i].sh_size; + stroff = shdr[shdr[i].sh_link].sh_offset; + esh->esh_strsize = shdr[shdr[i].sh_link].sh_size; + break; + } + } + + munmap(shdr, ehdr.e_shentsize * ehdr.e_shnum); + + if (i == ehdr.e_shnum) { + goto fail; + } + + if ((esh->esh_strtab = mmap(NULL, esh->esh_strsize, PROT_READ, + MAP_SHARED, fd, stroff)) == MAP_FAILED) { + goto fail; + } + + if ((esh->esh_symtab = mmap(NULL, esh->esh_symsize, PROT_READ, + MAP_SHARED, fd, symoff)) == MAP_FAILED) { + goto fail; + } + + return (ESH_TO_ST(esh)); +fail: + + elf_close(ESH_TO_ST(esh)); + return (NULL); +} + +void +elf_close(struct sym_table *st) +{ + struct elf_symbol_handle *esh = ST_TO_ESH(st); + + if (esh->esh_fd != -1) + close(esh->esh_fd); + + munmap(esh->esh_strtab, esh->esh_strsize); + munmap(esh->esh_symtab, esh->esh_symsize); + free(esh); +} + +char * +elf_name_and_off(struct sym_table *st, reg pc, reg *offs) +{ + struct elf_symbol_handle *esh = ST_TO_ESH(st); + Elf_Sym *s, *bests = NULL; + Elf_Addr bestoff = 0; + int nsyms, i; + char *symn; + +#define SYMVAL(S) (unsigned long)((S)->st_value + esh->esh_offs) + + nsyms = esh->esh_symsize / sizeof(Elf_Sym); + + bests = NULL; + for (i = 0; i < nsyms; i++) { + s = &esh->esh_symtab[i]; + + if (s->st_value == 0 || + s->st_shndx == 0 || + (ELF_ST_BIND(s->st_info) != STB_GLOBAL && + ELF_ST_BIND(s->st_info) != STB_WEAK && + ELF_ST_BIND(s->st_info) != STB_LOCAL)) + continue; + symn = &esh->esh_strtab[s->st_name]; + if (SYMVAL(s) <= pc && SYMVAL(s) > bestoff && + symn[0] != '\0' && strcmp(symn, "gcc2_compiled.")) { + bests = s; + bestoff = SYMVAL(s); + } + } + + if ((s = bests) == NULL) + return (NULL); + + *offs = pc - SYMVAL(s); + + return &esh->esh_strtab[s->st_name]; +} + +static Elf_Sym * +elf_lookup_table(struct elf_symbol_handle *esh, const char *name) +{ + int nsyms, i; + char *symn; + Elf_Sym *s = NULL; + + /* XXX - dumb, doesn't follow the rules (weak, hash, etc.). */ + nsyms = esh->esh_symsize / sizeof(Elf_Sym); + for (i = 0; i < nsyms; i++) { + s = &esh->esh_symtab[i]; + symn = &esh->esh_strtab[s->st_name]; + if (strcmp(name, symn) == 0) + break; + } + if (i == nsyms) + return (NULL); + + return (s); +} + +int +elf_lookup(struct pstate *ps, const char *name, reg *res) +{ + struct sym_table *st; + Elf_Sym *s; + + TAILQ_FOREACH(st, &ps->ps_syms, st_list) { + if ((s = elf_lookup_table(ST_TO_ESH(st), name)) != NULL) + break; + } + + if (s != NULL) { + *res = s->st_value + ST_TO_ESH(st)->esh_offs; + return (0); + } + + return (-1); +} + +#ifndef __NetBSD__ +struct elf_object_v1 { + Elf_Addr load_addr; + Elf_Addr load_offs; + char *load_name; + Elf_Dyn *load_dyn; + struct elf_object_v1 *next; + struct elf_object_v1 *prev; + void *load_list; + u_int32_t load_size; + u_long info[DT_NUM + DT_PROCNUM]; + struct elf_object_v1 *dep_next; + int status; + Elf_Phdr *phdrp; + int phdrc; + int refcount; + int obj_type; +#define EOBJ1_LDR 1 +#define EOBJ1_EXE 2 +#define EOBJ1_LIB 3 +#define EOBJ1_DLO 4 +}; +#endif + +/* + * dlopen breakpoint (XXX make this generic?) + */ +int +sym_bkpt(struct pstate *ps, void *arg) +{ + fprintf(stderr, "pmdb: shared lib changed\n"); + + sym_update(ps); + + return BKPT_KEEP_CONT; +} + +/* + * Called after execution started so that we can load any dynamic symbols. + */ +void +elf_update(struct pstate *ps) +{ +#ifndef __NetBSD__ + pid_t pid = ps->ps_pid; + struct elf_object_v1 eobj; + struct sym_table *st; + struct r_debug rdeb; + reg addr; + Elf_Dyn dyn; + static int bkpt_set; + Elf_Sym *s; + + if ((s = elf_lookup_table(ST_TO_ESH(ps->ps_sym_exe), "_DYNAMIC")) == NULL) { + warnx("Can't find _DYNAMIC"); + return; + } + addr = s->st_value + ST_TO_ESH(ps->ps_sym_exe)->esh_offs; + + do { + if (read_from_pid(pid, addr, &dyn, sizeof(dyn)) < 0) { + warnx("Can't read _DYNAMIC"); + return; + } + addr += sizeof(dyn); + } while (dyn.d_tag != 0 && dyn.d_tag != DT_DEBUG); + + if (dyn.d_tag == 0) { + warnx("Can't find DT_DEBUG"); + return; + } + + if (read_from_pid(pid, dyn.d_un.d_ptr, &rdeb, sizeof(rdeb)) < 0) { + warnx("Can't read DT_DEBUG"); + return; + } + + if (rdeb.r_version != 1) { + warn("Can't handle debug map version %d", rdeb.r_version); + return; + } + if (rdeb.r_state != RT_CONSISTENT) { + warn("debug map not consistent: %d", rdeb.r_state); + return; + } + + if (!bkpt_set) { + if (bkpt_add_cb(ps, rdeb.r_brk, sym_bkpt, NULL)) + warn("sym_exec: can't set bkpt"); + bkpt_set = 1; + } + + addr = (Elf_Addr)rdeb.r_map; + while (addr != 0 && addr != -1) { + char fname[MAXPATHLEN]; + int i; + + if (read_from_pid(pid, addr, &eobj, sizeof(eobj)) < 0) { + warnx("Can't read symbols..."); + return; + } + + addr = (Elf_Addr)eobj.next; + + if (eobj.load_name == NULL || eobj.load_name == (char *)-1) + continue; + if (read_from_pid(pid, (Elf_Addr)eobj.load_name, fname, + sizeof(fname)) < 0) { + warnx("Can't read symbols..."); + return; + } + + /* sanity check the file name */ + for (i = 0; i < MAXPATHLEN; i++) + if (fname[i] == '\0') + break; + if (i == MAXPATHLEN) + continue; + + st = st_open(ps, fname); + if (st == NULL) { + warn("symbol loading failed"); + continue; + } + ST_TO_ESH(st)->esh_offs = eobj.load_offs; + } +#endif +} |