diff options
Diffstat (limited to 'usr.bin/pmdb')
28 files changed, 4091 insertions, 0 deletions
diff --git a/usr.bin/pmdb/Makefile b/usr.bin/pmdb/Makefile new file mode 100644 index 00000000000..6b3194b9c44 --- /dev/null +++ b/usr.bin/pmdb/Makefile @@ -0,0 +1,46 @@ +# $PMDB: Makefile,v 1.10 2002/03/07 14:27:08 art Exp $ + +.include <bsd.own.mk> + +BINDIR=/usr/local/bin + +PROG=pmdb +SRCS=pmdb.c symbol.c clit.c process.c signal.c break.c + +# support for NetBSD +.if defined(OBJECT_FMT) +.if ${OBJECT_FMT} == "ELF" +ELF_TOOLCHAIN=yes +.else +ELF_TOOLCHAIN=no +.endif +.endif + +.if (${ELF_TOOLCHAIN} == "yes") +SRCS+=elf_syms.c +CPPFLAGS+=-DPMDB_ELF +.else +SRCS+=aout_syms.c +CPPFLAGS+=-DPMDB_AOUT +.endif + +LDADD=-ledit -lcurses +DPADD=${LIBEDIT} ${LIBTERMCAP} + +CPPFLAGS+=-I${.CURDIR} + +.if exists(arch/${MACHINE}/Makefile.inc) +.PATH: ${.CURDIR}/arch/${MACHINE} +.include "arch/${MACHINE}/Makefile.inc" +CPPFLAGS+=-I${.CURDIR}/arch/${MACHINE} +.endif + +.if (${MACHINE_ARCH} != ${MACHINE}) && exists(arch/${MACHINE_ARCH}/Makefile.inc) +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH} +.include arch/${MACHINE_ARCH}/Makefile.inc +CPPFLAGS+=-I${.CURDIR}/arch/${MACHINE_ARCH} +.endif + +COPTS=-Wall -Wno-uninitialized + +.include <bsd.prog.mk> diff --git a/usr.bin/pmdb/aout_syms.c b/usr.bin/pmdb/aout_syms.c new file mode 100644 index 00000000000..9af258f0786 --- /dev/null +++ b/usr.bin/pmdb/aout_syms.c @@ -0,0 +1,348 @@ +/* $PMDB: aout_syms.c,v 1.17 2002/03/11 21:46:12 art Exp $ */ +/* + * Copyright (c) 2002 Federico Schwindt <fgsch@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 <sys/types.h> +#include <a.out.h> +#include <link.h> + +#include "pmdb.h" +#include "symbol.h" + +#if defined(__OpenBSD__) && (OpenBSD < 200106) +/* OpenBSD prior to 2.9 have a broken pread on big-endian archs. */ +#define IGNORE_PREAD_ERRORS +#endif + +struct aout_symbol_handle { + struct sym_table ash_st; + int ash_fd; + char *ash_strtab; + u_int32_t ash_strsize; + struct nlist *ash_symtab; + int ash_symsize; + int ash_offs; +}; + +#define ASH_TO_ST(ash) (&(ash)->ash_st) +#define ST_TO_ASH(st) ((struct aout_symbol_handle *)(st)) + +struct sym_table *aout_open(const char *); +void aout_close(struct sym_table *); +char *aout_name_and_off(struct sym_table *, reg, reg *); +int aout_lookup(struct pstate *, const char *, reg *); +void aout_update(struct pstate *); + +struct sym_ops aout_sops = { + aout_open, + aout_close, + aout_name_and_off, + aout_lookup, + aout_update +}; + +int +sym_check_aout(const char *name, struct pstate *ps) +{ + struct exec ahdr; + int fd; + + if ((fd = open(name, O_RDONLY)) < 0) + return (-1); + + if (pread(fd, &ahdr, sizeof(ahdr), 0) != sizeof(ahdr)) { +#ifndef IGNORE_PREAD_ERRORS + return (-1); +#endif + } + + if (N_BADMAG(ahdr)) { + return (-1); + } + + close(fd); + + ps->ps_sops = &aout_sops; + + return (0); +} + +struct sym_table * +aout_open(const char *name) +{ + struct aout_symbol_handle *ash; + u_int32_t symoff, stroff; + struct exec ahdr; + + if ((ash = malloc(sizeof(*ash))) == NULL) { + return NULL; + } + + memset(ash, 0, sizeof(*ash)); + ash->ash_fd = -1; + + if ((ash->ash_fd = open(name, O_RDONLY)) < 0) { + warn("open(%s)", name); + goto fail; + } + + if (pread(ash->ash_fd, &ahdr, sizeof(ahdr), 0) != sizeof(ahdr)) { +#ifndef IGNORE_PREAD_ERRORS + warn("pread(header)"); + goto fail; +#endif + } + + if (N_BADMAG(ahdr)) { + warnx("Bad magic."); + goto fail; + } + + symoff = N_SYMOFF(ahdr); + ash->ash_symsize = ahdr.a_syms; + stroff = N_STROFF(ahdr); + + if (pread(ash->ash_fd, &ash->ash_strsize, sizeof(u_int32_t), + stroff) != sizeof(u_int32_t)) { +#ifndef IGNORE_PREAD_ERRORS + warn("pread(strsize)"); + goto fail; +#endif + } + + if ((ash->ash_strtab = mmap(NULL, ash->ash_strsize, PROT_READ, + MAP_SHARED, ash->ash_fd, stroff)) == MAP_FAILED) { + warn("mmap(strtab)"); + goto fail; + } + + if ((ash->ash_symtab = mmap(NULL, ash->ash_symsize, PROT_READ, + MAP_SHARED, ash->ash_fd, symoff)) == MAP_FAILED) { + warn("mmap(symtab)"); + goto fail; + } + + return (ASH_TO_ST(ash)); +fail: + + aout_close(ASH_TO_ST(ash)); + return (NULL); +} + +void +aout_close(struct sym_table *st) +{ + struct aout_symbol_handle *ash = ST_TO_ASH(st); + + if (ash->ash_fd != -1) + close(ash->ash_fd); + + munmap(ash->ash_strtab, ash->ash_strsize); + munmap(ash->ash_symtab, ash->ash_symsize); + free(ash); +} + +char * +aout_name_and_off(struct sym_table *st, reg pc, reg *offs) +{ + struct aout_symbol_handle *ash = ST_TO_ASH(st); + struct nlist *s, *bests = NULL; + int bestoff = 0; + int nsyms, i; + char *symn; + +#define SYMVAL(S) (unsigned long)((S)->n_value + ash->ash_offs) + + nsyms = ash->ash_symsize / sizeof(struct nlist); + + bests = NULL; + for (i = 0; i < nsyms; i++) { + s = &ash->ash_symtab[i]; + + if (s->n_value == 0 || + s->n_un.n_strx == 0) + continue; + + symn = &ash->ash_strtab[s->n_un.n_strx]; + 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 &ash->ash_strtab[s->n_un.n_strx]; +} + +static struct nlist * +aout_lookup_table(struct aout_symbol_handle *ash, const char *name) +{ + int nsyms, i; + char *symn; + struct nlist *s = NULL; + + nsyms = ash->ash_symsize / sizeof(struct nlist); + for (i = 0; i < nsyms; i++) { + s = &ash->ash_symtab[i]; + symn = &ash->ash_strtab[s->n_un.n_strx]; + if (strcmp(name, symn) == 0) + break; + } + + if (i == nsyms) + return (NULL); + + return (s); +} + +int +aout_lookup(struct pstate *ps, const char *name, reg *res) +{ + struct sym_table *st; + struct nlist *s; + int first = 1; + char *sname = (char *)name; + +restart: + TAILQ_FOREACH(st, &ps->ps_syms, st_list) { + if ((s = aout_lookup_table(ST_TO_ASH(st), sname)) != NULL) + break; + } + + if (!first) + free(sname); + + if (s == NULL) { + if (first) { + asprintf(&sname, "_%s", sname); + if (sname != NULL) { + first = 0; + goto restart; + } + } + + return (-1); + } + + *res = s->n_value + ST_TO_ASH(st)->ash_offs; + return (0); +} + +/* + * Called after execution started so that we can load any dynamic symbols. + */ +void +aout_update(struct pstate *ps) +{ + pid_t pid = ps->ps_pid; + struct _dynamic dyn; + struct so_debug sdeb; + struct section_dispatch_table sdt; + struct so_map som; + off_t somp; + reg addr; + struct nlist *s; + + if ((s = aout_lookup_table(ST_TO_ASH(ps->ps_sym_exe), "__DYNAMIC")) == NULL) { + warnx("Can't find __DYNAMIC"); + return; + } + addr = s->n_value + ST_TO_ASH(ps->ps_sym_exe)->ash_offs; + + if (read_from_pid(pid, addr, &dyn, sizeof(dyn)) < 0) { + warn("Can't read __DYNAMIC"); + return; + } + + if (dyn.d_version != LD_VERSION_BSD) { + warn("Can't handle __DYNAMIC version %d", dyn.d_version); + return; + } + + if (read_from_pid(pid, (off_t)(reg)dyn.d_debug, &sdeb, sizeof(sdeb)) < 0) { + warn("Can't read __DYNAMIC.d_debug"); + return; + } + + if (sdeb.dd_version != 0) { + warn("Can't handle so_debug version %d", sdeb.dd_version); + return; + } + + if (read_from_pid(pid, (off_t)(reg)dyn.d_un.d_sdt, &sdt, sizeof(sdt)) < 0) { + warn("Can't read section dispatch table"); + return; + } + + somp = (off_t)(reg)sdt.sdt_loaded; + while (somp) { + struct sym_table *st; + char fname[MAXPATHLEN]; + int i; + + if (read_from_pid(pid, somp, &som, sizeof(som)) < 0) { + warn("Can't read so_map"); + break; + } + somp = (off_t)(reg)som.som_next; + if (read_from_pid(pid, (off_t)(reg)som.som_path, fname, + sizeof(fname)) < 0) { + warn("Can't read so filename"); + continue; + } + + /* sanity check the file name */ + for (i = 0; i < MAXPATHLEN; i++) + if (fname[i] == '\0') + break; + if (i == MAXPATHLEN) { + warnx("so filename invalid"); + continue; + } + + st = st_open(ps, fname); + if (st == NULL) { + warn("symbol loading failed"); + continue; + } + ST_TO_ASH(st)->ash_offs = (int)som.som_addr; + } +} diff --git a/usr.bin/pmdb/arch/alpha/Makefile.inc b/usr.bin/pmdb/arch/alpha/Makefile.inc new file mode 100644 index 00000000000..90bf092b2e2 --- /dev/null +++ b/usr.bin/pmdb/arch/alpha/Makefile.inc @@ -0,0 +1 @@ +SRCS+=alpha.c alpha_trace.c diff --git a/usr.bin/pmdb/arch/alpha/alpha.c b/usr.bin/pmdb/arch/alpha/alpha.c new file mode 100644 index 00000000000..a09f59f036e --- /dev/null +++ b/usr.bin/pmdb/arch/alpha/alpha.c @@ -0,0 +1,70 @@ +/* $PMDB: alpha.c,v 1.4 2002/02/21 02:20:51 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 <sys/param.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> + +#include <string.h> + +#include "pmdb.h" + +/* + * This happens to be the same order as in struct reg. + */ + +static const char *md_reg_names[] = { + "v0", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", + "a0", "a1", "a2", "a3", "a4", "a5", + "t8", "t9", "t10", "t11", + "ra", + "t12", + "at", "gp", "sp", "zero" +}; + +struct md_def md_def = { md_reg_names, 32, 31 }; + +void +md_def_init(void) +{ + /* nothing */ +} + +int +md_getregs(struct pstate *ps, reg *regs) +{ + struct reg r; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return -1; + + memcpy(regs, &r, sizeof(r)); + + return 0; +}
\ No newline at end of file diff --git a/usr.bin/pmdb/arch/alpha/alpha_instruction.h b/usr.bin/pmdb/arch/alpha/alpha_instruction.h new file mode 100644 index 00000000000..631aad909ea --- /dev/null +++ b/usr.bin/pmdb/arch/alpha/alpha_instruction.h @@ -0,0 +1,750 @@ +/* $PMDB: alpha_instruction.h,v 1.3 2002/01/30 23:14:34 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. + */ + +/* + * Copyright (c) 1999 Christopher G. Demetriou. 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou + * for the NetBSD Project. + * 4. 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 BY THE AUTHOR ``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. + */ + +/* + * Mach Operating System + * Copyright (c) 1993,1992 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* + * File: alpha_instruction.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 11/91 + * + * Alpha Instruction set definition + * + * Reference: "Alpha System Reference Manual", V4.0, April 1991 + * + */ + +#ifndef _ALPHA_INSTRUCTION_H_ +#define _ALPHA_INSTRUCTION_H_ 1 + +#if !defined(ASSEMBLER) + +/* + * All instructions are in one of five formats: + * Memory, Branch, Operate, Floating-point Operate, PAL + * + * The original Mach sources attempted to use 'smarter' names + * for registers, which reflected source and destination. These + * definitions use the names from the Architecture Reference Manual, + * both for clarity and because you can't differentiate between + * 'source' and 'destinations' for some types of instructions (loads + * and stores; they'd be correct for one, but swapped for the other). + */ + + +typedef union { + /* + * All instructions are 32 bits wide + */ + unsigned int bits; + + /* + * Generic instruction pseudo format; look at + * opcode to see how to interpret the rest. + */ + struct { + unsigned bits:26, + opcode:6; + } generic_format; + + /* + * Memory instructions contain a 16 bit + * signed immediate value and two register + * specifiers + */ + struct { + signed short displacement; + unsigned rb : 5, + ra : 5, + opcode : 6; + } mem_format; + + /* + * Branch instruction contain a 21 bit offset, + * which is sign-extended, shifted and combined + * with the PC to form a 64 bit destination address. + * + * In computed jump instructions the opcode is further + * specified in the offset field, the rest of it is + * used as branch target hint. The destination of the + * jump is the source register. + */ + struct { + signed int displacement : 21; + unsigned ra : 5, + opcode : 6; + } branch_format; + + struct { + signed int hint : 14; + unsigned action : 2, + rb : 5, + ra : 5, + opcode : 6; + } jump_format; + + + /* + * Operate instructions are of two types, with + * a second source register or with a literal + * specifier. Bit 12 sez which is which. + */ + struct { + unsigned rc : 5, + function : 7, + is_lit : 1, + sbz_or_litlo : 3, + rb_or_lithi : 5, + ra : 5, + opcode : 6; + } operate_generic_format; + + struct { + unsigned rc : 5, + function : 7, + zero : 1, + sbz : 3, + rb : 5, + ra : 5, + opcode : 6; + } operate_reg_format; + + struct { + unsigned rc : 5, + function : 7, + one : 1, + literal : 8, + ra : 5, + opcode : 6; + } operate_lit_format; + + + /* + * Floating point operate instruction are quite + * uniform in the encoding. As for the semantics.. + */ + struct { + unsigned fc : 5, + function : 11, + fb : 5, + fa : 5, + opcode : 6; + } float_format; + + + /* + * PAL instructions just define the major opcode + */ + + struct { + unsigned function : 26, + opcode : 6; + } pal_format; + +} alpha_instruction; + +#endif /* !defined(ASSEMBLER) */ + +/* + * + * Encoding of regular instructions (Appendix C op cit) + * + */ + + /* OPCODE, bits 26..31 */ + +#define op_pal 0x00 /* see PAL sub-table */ + /* 1..7 reserved */ +#define op_lda 0x08 +#define op_ldah 0x09 +#define op_ldbu 0x0a +#define op_ldq_u 0x0b +#define op_ldwu 0x0c +#define op_stw 0x0d +#define op_stb 0x0e +#define op_stq_u 0x0f + +#define op_arit 0x10 /* see ARIT sub-table */ +#define op_logical 0x11 /* see LOGICAL sub-table */ +#define op_bit 0x12 /* see BIT sub-table */ +#define op_mul 0x13 /* see MUL sub-table */ + /* reserved */ +#define op_vax_float 0x15 /* see FLOAT sub-table */ +#define op_ieee_float 0x16 /* see FLOAT sub-table */ +#define op_any_float 0x17 /* see FLOAT sub-table */ + +#define op_special 0x18 /* see SPECIAL sub-table */ +#define op_pal19 0x19 /* reserved for pal code */ +#define op_j 0x1a /* see JUMP sub-table */ +#define op_pal1b 0x1b /* reserved for pal code */ +#define op_intmisc 0x1c /* see INTMISC sub-table */ +#define op_pal1d 0x1d /* reserved for pal code */ +#define op_pal1e 0x1e /* reserved for pal code */ +#define op_pal1f 0x1f /* reserved for pal code */ + +#define op_ldf 0x20 +#define op_ldg 0x21 +#define op_lds 0x22 +#define op_ldt 0x23 +#define op_stf 0x24 +#define op_stg 0x25 +#define op_sts 0x26 +#define op_stt 0x27 +#define op_ldl 0x28 +#define op_ldq 0x29 +#define op_ldl_l 0x2a +#define op_ldq_l 0x2b +#define op_stl 0x2c +#define op_stq 0x2d +#define op_stl_c 0x2e +#define op_stq_c 0x2f +#define op_br 0x30 +#define op_fbeq 0x31 +#define op_fblt 0x32 +#define op_fble 0x33 +#define op_bsr 0x34 +#define op_fbne 0x35 +#define op_fbge 0x36 +#define op_fbgt 0x37 +#define op_blbc 0x38 +#define op_beq 0x39 +#define op_blt 0x3a +#define op_ble 0x3b +#define op_blbs 0x3c +#define op_bne 0x3d +#define op_bge 0x3e +#define op_bgt 0x3f + + + /* PAL, "function" opcodes (bits 0..25) */ +/* + * What we will implement is TBD. These are the unprivileged ones + * that we probably have to support for compat reasons. + */ + +/* See <machine/pal.h> */ + + /* ARIT, "function" opcodes (bits 5..11) */ + +#define op_addl 0x00 +#define op_s4addl 0x02 +#define op_subl 0x09 +#define op_s4subl 0x0b +#define op_cmpbge 0x0f +#define op_s8addl 0x12 +#define op_s8subl 0x1b +#define op_cmpult 0x1d +#define op_addq 0x20 +#define op_s4addq 0x22 +#define op_subq 0x29 +#define op_s4subq 0x2b +#define op_cmpeq 0x2d +#define op_s8addq 0x32 +#define op_s8subq 0x3b +#define op_cmpule 0x3d +#define op_addl_v 0x40 +#define op_subl_v 0x49 +#define op_cmplt 0x4d +#define op_addq_v 0x60 +#define op_subq_v 0x69 +#define op_cmple 0x6d + + + /* LOGICAL, "function" opcodes (bits 5..11) */ + +#define op_and 0x00 +#define op_andnot 0x08 /* bic */ +#define op_cmovlbs 0x14 +#define op_cmovlbc 0x16 +#define op_or 0x20 /* bis */ +#define op_cmoveq 0x24 +#define op_cmovne 0x26 +#define op_ornot 0x28 +#define op_xor 0x40 +#define op_cmovlt 0x44 +#define op_cmovge 0x46 +#define op_xornot 0x48 /* eqv */ +#define op_amask 0x61 +#define op_cmovle 0x64 +#define op_cmovgt 0x66 +#define op_implver 0x6c + + /* BIT, "function" opcodes (bits 5..11) */ + +#define op_mskbl 0x02 +#define op_extbl 0x06 +#define op_insbl 0x0b +#define op_mskwl 0x12 +#define op_extwl 0x16 +#define op_inswl 0x1b +#define op_mskll 0x22 +#define op_extll 0x26 +#define op_insll 0x2b +#define op_zap 0x30 +#define op_zapnot 0x31 +#define op_mskql 0x32 +#define op_srl 0x34 +#define op_extql 0x36 +#define op_sll 0x39 +#define op_insql 0x3b +#define op_sra 0x3c +#define op_mskwh 0x52 +#define op_inswh 0x57 +#define op_extwh 0x5a +#define op_msklh 0x62 +#define op_inslh 0x67 +#define op_extlh 0x6a +#define op_extqh 0x7a +#define op_insqh 0x77 +#define op_mskqh 0x72 + + /* MUL, "function" opcodes (bits 5..11) */ + +#define op_mull 0x00 +#define op_mulq_v 0x60 +#define op_mull_v 0x40 +#define op_umulh 0x30 +#define op_mulq 0x20 + + + /* SPECIAL, "displacement" opcodes (bits 0..15) */ + +#define op_trapb 0x0000 +#define op_excb 0x0400 +#define op_mb 0x4000 +#define op_wmb 0x4400 +#define op_fetch 0x8000 +#define op_fetch_m 0xa000 +#define op_rpcc 0xc000 +#define op_rc 0xe000 +#define op_ecb 0xe800 +#define op_rs 0xf000 +#define op_wh64 0xf800 + + /* JUMP, "action" opcodes (bits 14..15) */ + +#define op_jmp 0x0 +#define op_jsr 0x1 +#define op_ret 0x2 +#define op_jcr 0x3 + + /* INTMISC, "function" opcodes (operate format) */ + +#define op_sextb 0x00 +#define op_sextw 0x01 +#define op_ctpop 0x30 +#define op_perr 0x31 +#define op_ctlz 0x32 +#define op_cttz 0x33 +#define op_unpkbw 0x34 +#define op_unpkbl 0x35 +#define op_pkwb 0x36 +#define op_pklb 0x37 +#define op_minsb8 0x38 +#define op_minsw4 0x39 +#define op_minub8 0x3a +#define op_minuw4 0x3b +#define op_maxub8 0x3c +#define op_maxuw4 0x3d +#define op_maxsb8 0x3e +#define op_maxsw4 0x3f +#define op_ftoit 0x70 +#define op_ftois 0x78 + +/* + * + * Encoding of floating point instructions (pagg. C-5..6 op cit) + * + * Load and store operations use opcodes op_ldf..op_stt + */ + + /* any FLOAT, "function" opcodes (bits 5..11) */ + +#define op_cvtlq 0x010 +#define op_cpys 0x020 +#define op_cpysn 0x021 +#define op_cpyse 0x022 +#define op_mt_fpcr 0x024 +#define op_mf_fpcr 0x025 +#define op_fcmoveq 0x02a +#define op_fcmovne 0x02b +#define op_fcmovlt 0x02c +#define op_fcmovge 0x02d +#define op_fcmovle 0x02e +#define op_fcmovgt 0x02f +#define op_cvtql 0x030 +#define op_cvtql_v 0x130 +#define op_cvtql_sv 0x330 + + + /* ieee FLOAT, "function" opcodes (bits 5..11) */ + +#define op_adds_c 0x000 +#define op_subs_c 0x001 +#define op_muls_c 0x002 +#define op_divs_c 0x003 +#define op_addt_c 0x020 +#define op_subt_c 0x021 +#define op_mult_c 0x022 +#define op_divt_c 0x023 +#define op_cvtts_c 0x02c +#define op_cvttq_c 0x02f +#define op_cvtqs_c 0x03c +#define op_cvtqt_c 0x03e +#define op_adds_m 0x040 +#define op_subs_m 0x041 +#define op_muls_m 0x042 +#define op_divs_m 0x043 +#define op_addt_m 0x060 +#define op_subt_m 0x061 +#define op_mult_m 0x062 +#define op_divt_m 0x063 +#define op_cvtts_m 0x06c +#define op_cvtqs_m 0x07c +#define op_cvtqt_m 0x07e +#define op_adds 0x080 +#define op_subs 0x081 +#define op_muls 0x082 +#define op_divs 0x083 +#define op_addt 0x0a0 +#define op_subt 0x0a1 +#define op_mult 0x0a2 +#define op_divt 0x0a3 +#define op_cmptun 0x0a4 +#define op_cmpteq 0x0a5 +#define op_cmptlt 0x0a6 +#define op_cmptle 0x0a7 +#define op_cvtts 0x0ac +#define op_cvttq 0x0af +#define op_cvtqs 0x0bc +#define op_cvtqt 0x0be +#define op_adds_d 0x0c0 +#define op_subs_d 0x0c1 +#define op_muls_d 0x0c2 +#define op_divs_d 0x0c3 +#define op_addt_d 0x0e0 +#define op_subt_d 0x0e1 +#define op_mult_d 0x0e2 +#define op_divt_d 0x0e3 +#define op_cvtts_d 0x0ec +#define op_cvtqs_d 0x0fc +#define op_cvtqt_d 0x0fe +#define op_adds_uc 0x100 +#define op_subs_uc 0x101 +#define op_muls_uc 0x102 +#define op_divs_uc 0x103 +#define op_addt_uc 0x120 +#define op_subt_uc 0x121 +#define op_mult_uc 0x122 +#define op_divt_uc 0x123 +#define op_cvtts_uc 0x12c +#define op_cvttq_vc 0x12f +#define op_adds_um 0x140 +#define op_subs_um 0x141 +#define op_muls_um 0x142 +#define op_divs_um 0x143 +#define op_addt_um 0x160 +#define op_subt_um 0x161 +#define op_mult_um 0x162 +#define op_divt_um 0x163 +#define op_cvtts_um 0x16c +#define op_adds_u 0x180 +#define op_subs_u 0x181 +#define op_muls_u 0x182 +#define op_divs_u 0x183 +#define op_addt_u 0x1a0 +#define op_subt_u 0x1a1 +#define op_mult_u 0x1a2 +#define op_divt_u 0x1a3 +#define op_cvtts_u 0x1ac +#define op_cvttq_v 0x1af +#define op_adds_ud 0x1c0 +#define op_subs_ud 0x1c1 +#define op_muls_ud 0x1c2 +#define op_divs_ud 0x1c3 +#define op_addt_ud 0x1e0 +#define op_subt_ud 0x1e1 +#define op_mult_ud 0x1e2 +#define op_divt_ud 0x1e3 +#define op_cvtts_ud 0x1ec +#define op_adds_suc 0x500 +#define op_subs_suc 0x501 +#define op_muls_suc 0x502 +#define op_divs_suc 0x503 +#define op_addt_suc 0x520 +#define op_subt_suc 0x521 +#define op_mult_suc 0x522 +#define op_divt_suc 0x523 +#define op_cvtts_suc 0x52c +#define op_cvttq_svc 0x52f +#define op_adds_sum 0x540 +#define op_subs_sum 0x541 +#define op_muls_sum 0x542 +#define op_divs_sum 0x543 +#define op_addt_sum 0x560 +#define op_subt_sum 0x561 +#define op_mult_sum 0x562 +#define op_divt_sum 0x563 +#define op_cvtts_sum 0x56c +#define op_adds_su 0x580 +#define op_subs_su 0x581 +#define op_muls_su 0x582 +#define op_divs_su 0x583 +#define op_addt_su 0x5a0 +#define op_subt_su 0x5a1 +#define op_mult_su 0x5a2 +#define op_divt_su 0x5a3 +#define op_cmptun_su 0x5a4 +#define op_cmpteq_su 0x5a5 +#define op_cmptlt_su 0x5a6 +#define op_cmptle_su 0x5a7 +#define op_cvtts_su 0x5ac +#define op_cvttq_sv 0x5af +#define op_adds_sud 0x5c0 +#define op_subs_sud 0x5c1 +#define op_muls_sud 0x5c2 +#define op_divs_sud 0x5c3 +#define op_addt_sud 0x5e0 +#define op_subt_sud 0x5e1 +#define op_mult_sud 0x5e2 +#define op_divt_sud 0x5e3 +#define op_cvtts_sud 0x5ec +#define op_adds_suic 0x700 +#define op_subs_suic 0x701 +#define op_muls_suic 0x702 +#define op_divs_suic 0x703 +#define op_addt_suic 0x720 +#define op_subt_suic 0x721 +#define op_mult_suic 0x722 +#define op_divt_suic 0x723 +#define op_cvtts_suic 0x72c +#define op_cvttq_svic 0x72f +#define op_cvtqs_suic 0x73c +#define op_cvtqt_suic 0x73e +#define op_adds_suim 0x740 +#define op_subs_suim 0x741 +#define op_muls_suim 0x742 +#define op_divs_suim 0x743 +#define op_addt_suim 0x760 +#define op_subt_suim 0x761 +#define op_mult_suim 0x762 +#define op_divt_suim 0x763 +#define op_cvtts_suim 0x76c +#define op_cvtqs_suim 0x77c +#define op_cvtqt_suim 0x77e +#define op_adds_sui 0x780 +#define op_subs_sui 0x781 +#define op_muls_sui 0x782 +#define op_divs_sui 0x783 +#define op_addt_sui 0x7a0 +#define op_subt_sui 0x7a1 +#define op_mult_sui 0x7a2 +#define op_divt_sui 0x7a3 +#define op_cvtts_sui 0x7ac +#define op_cvttq_svi 0x7af +#define op_cvtqs_sui 0x7bc +#define op_cvtqt_sui 0x7be +#define op_adds_suid 0x7c0 +#define op_subs_suid 0x7c1 +#define op_muls_suid 0x7c2 +#define op_divs_suid 0x7c3 +#define op_addt_suid 0x7e0 +#define op_subt_suid 0x7e1 +#define op_mult_suid 0x7e2 +#define op_divt_suid 0x7e3 +#define op_cvtts_suid 0x7ec +#define op_cvtqs_suid 0x7fc +#define op_cvtqt_suid 0x7fe + + + /* vax FLOAT, "function" opcodes (bits 5..11) */ + +#define op_addf_c 0x000 +#define op_subf_c 0x001 +#define op_mulf_c 0x002 +#define op_divf_c 0x003 +#define op_cvtdg_c 0x01e +#define op_addg_c 0x020 +#define op_subg_c 0x021 +#define op_mulg_c 0x022 +#define op_divg_c 0x023 +#define op_cvtgf_c 0x02c +#define op_cvtgd_c 0x02d +#define op_cvtgqg_c 0x02f +#define op_cvtqf_c 0x03c +#define op_cvtqg_c 0x03e +#define op_addf 0x080 +#define op_subf 0x081 +#define op_mulf 0x082 +#define op_divf 0x083 +#define op_cvtdg 0x09e +#define op_addg 0x0a0 +#define op_subg 0x0a1 +#define op_mulg 0x0a2 +#define op_divg 0x0a3 +#define op_cmpgeq 0x0a5 +#define op_cmpglt 0x0a6 +#define op_cmpgle 0x0a7 +#define op_cvtgf 0x0ac +#define op_cvtgd 0x0ad +#define op_cvtgq 0x0af +#define op_cvtqf 0x0bc +#define op_cvtqg 0x0be +#define op_addf_uc 0x100 +#define op_subf_uc 0x101 +#define op_mulf_uc 0x102 +#define op_divf_uc 0x103 +#define op_cvtdg_uc 0x11e +#define op_addg_uc 0x120 +#define op_subg_uc 0x121 +#define op_mulg_uc 0x122 +#define op_divg_uc 0x123 +#define op_cvtgf_uc 0x12c +#define op_cvtgd_uc 0x12d +#define op_cvtgqg_vc 0x12f +#define op_addf_u 0x180 +#define op_subf_u 0x181 +#define op_mulf_u 0x182 +#define op_divf_u 0x183 +#define op_cvtdg_u 0x19e +#define op_addg_u 0x1a0 +#define op_subg_u 0x1a1 +#define op_mulg_u 0x1a2 +#define op_divg_u 0x1a3 +#define op_cvtgf_u 0x1ac +#define op_cvtgd_u 0x1ad +#define op_cvtgqg_v 0x1af +#define op_addf_sc 0x400 +#define op_subf_sc 0x401 +#define op_mulf_sc 0x402 +#define op_divf_sc 0x403 +#define op_cvtdg_sc 0x41e +#define op_addg_sc 0x420 +#define op_subg_sc 0x421 +#define op_mulg_sc 0x422 +#define op_divg_sc 0x423 +#define op_cvtgf_sc 0x42c +#define op_cvtgd_sc 0x42d +#define op_cvtgqg_sc 0x42f +#define op_cvtqf_sc 0x43c +#define op_cvtqg_sc 0x43e +#define op_addf_s 0x480 +#define op_subf_s 0x481 +#define op_mulf_s 0x482 +#define op_divf_s 0x483 +#define op_cvtdg_s 0x49e +#define op_addg_s 0x4a0 +#define op_subg_s 0x4a1 +#define op_mulg_s 0x4a2 +#define op_divg_s 0x4a3 +#define op_cmpgeq_s 0x4a5 +#define op_cmpglt_s 0x4a6 +#define op_cmpgle_s 0x4a7 +#define op_cvtgf_s 0x4ac +#define op_cvtgd_s 0x4ad +#define op_cvtgqg_s 0x4af +#define op_cvtqf_s 0x4bc +#define op_cvtqg_s 0x4be +#define op_addf_suc 0x500 +#define op_subf_suc 0x501 +#define op_mulf_suc 0x502 +#define op_divf_suc 0x503 +#define op_cvtdg_suc 0x51e +#define op_addg_suc 0x520 +#define op_subg_suc 0x521 +#define op_mulg_suc 0x522 +#define op_divg_suc 0x523 +#define op_cvtgf_suc 0x52c +#define op_cvtgd_suc 0x52d +#define op_cvtgqg_svc 0x52f +#define op_addf_su 0x580 +#define op_subf_su 0x581 +#define op_mulf_su 0x582 +#define op_divf_su 0x583 +#define op_cvtdg_su 0x59e +#define op_addg_su 0x5a0 +#define op_subg_su 0x5a1 +#define op_mulg_su 0x5a2 +#define op_divg_su 0x5a3 +#define op_cvtgf_su 0x5ac +#define op_cvtgd_su 0x5ad +#define op_cvtgqg_sv 0x5af + +#endif /* _ALPHA_INSTRUCTION_H_ */ diff --git a/usr.bin/pmdb/arch/alpha/alpha_trace.c b/usr.bin/pmdb/arch/alpha/alpha_trace.c new file mode 100644 index 00000000000..a0c4f6c73bd --- /dev/null +++ b/usr.bin/pmdb/arch/alpha/alpha_trace.c @@ -0,0 +1,343 @@ +/* $PMDB: alpha_trace.c,v 1.5 2002/03/10 10:52:16 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. + */ + +/* + * Copyright (c) 1997 Niklas Hallqvist. All rights reserverd. + * Copyright (c) 1997 Theo de Raadt. All rights reserverd. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niklas Hallqvist and + * Theo de Raadt. + * 4. 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 BY THE AUTHOR ``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 <sys/param.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> + +#include <stdio.h> +#include <string.h> + +#include "pmdb.h" +#include "alpha_instruction.h" +#include "symbol.h" + +struct opcode { + enum opc_fmt { OPC_PAL, OPC_RES, OPC_MEM, OPC_OP, OPC_BR } opc_fmt; + char *opc_name; + int opc_print; +}; + +#define inst_return(ins) (((ins) & 0xfc000000) == 0x68000000) + +const struct opcode opcode[] = { + { OPC_PAL, "call_pal", 0 }, /* 00 */ + { OPC_RES, "opc01", 0 }, /* 01 */ + { OPC_RES, "opc02", 0 }, /* 02 */ + { OPC_RES, "opc03", 0 }, /* 03 */ + { OPC_RES, "opc04", 0 }, /* 04 */ + { OPC_RES, "opc05", 0 }, /* 05 */ + { OPC_RES, "opc06", 0 }, /* 06 */ + { OPC_RES, "opc07", 0 }, /* 07 */ + { OPC_MEM, "lda", 1 }, /* 08 */ + { OPC_MEM, "ldah", 1 }, /* 09 */ + { OPC_RES, "opc0a", 0 }, /* 0A */ + { OPC_MEM, "ldq_u", 1 }, /* 0B */ + { OPC_RES, "opc0c", 0 }, /* 0C */ + { OPC_RES, "opc0d", 0 }, /* 0D */ + { OPC_RES, "opc0e", 0 }, /* 0E */ + { OPC_MEM, "stq_u", 1 }, /* 0F */ + { OPC_OP, "inta", 0 }, /* 10 */ + { OPC_OP, "intl", 0 }, /* 11 */ + { OPC_OP, "ints", 0 }, /* 12 */ + { OPC_OP, "intm", 0 }, /* 13 */ + { OPC_RES, "opc14", 0 }, /* 14 */ + { OPC_OP, "fltv", 1 }, /* 15 */ + { OPC_OP, "flti", 1 }, /* 16 */ + { OPC_OP, "fltl", 1 }, /* 17 */ + { OPC_MEM, "misc", 0 }, /* 18 */ + { OPC_PAL, "pal19", 0 }, /* 19 */ + { OPC_MEM, "jsr", 0 }, /* 1A */ + { OPC_PAL, "pal1b", 0 }, /* 1B */ + { OPC_RES, "opc1c", 0 }, /* 1C */ + { OPC_PAL, "pal1d", 0 }, /* 1D */ + { OPC_PAL, "pal1e", 0 }, /* 1E */ + { OPC_PAL, "pal1f", 0 }, /* 1F */ + { OPC_MEM, "ldf", 1 }, /* 20 */ + { OPC_MEM, "ldg", 1 }, /* 21 */ + { OPC_MEM, "lds", 1 }, /* 22 */ + { OPC_MEM, "ldt", 1 }, /* 23 */ + { OPC_MEM, "stf", 1 }, /* 24 */ + { OPC_MEM, "stg", 1 }, /* 25 */ + { OPC_MEM, "sts", 1 }, /* 26 */ + { OPC_MEM, "stt", 1 }, /* 27 */ + { OPC_MEM, "ldl", 1 }, /* 28 */ + { OPC_MEM, "ldq", 1 }, /* 29 */ + { OPC_MEM, "ldl_l", 1 }, /* 2A */ + { OPC_MEM, "ldq_l", 1 }, /* 2B */ + { OPC_MEM, "stl", 1 }, /* 2C */ + { OPC_MEM, "stq", 1 }, /* 2D */ + { OPC_MEM, "stl_c", 1 }, /* 2E */ + { OPC_MEM, "stq_c", 1 }, /* 2F */ + { OPC_BR, "br", 1 }, /* 30 */ + { OPC_BR, "fbeq", 1 }, /* 31 */ + { OPC_BR, "fblt", 1 }, /* 32 */ + { OPC_BR, "fble", 1 }, /* 33 */ + { OPC_BR, "bsr", 1 }, /* 34 */ + { OPC_BR, "fbne", 1 }, /* 35 */ + { OPC_BR, "fbge", 1 }, /* 36 */ + { OPC_BR, "fbgt", 1 }, /* 37 */ + { OPC_BR, "blbc", 1 }, /* 38 */ + { OPC_BR, "beq", 1 }, /* 39 */ + { OPC_BR, "blt", 1 }, /* 3A */ + { OPC_BR, "ble", 1 }, /* 3B */ + { OPC_BR, "blbs", 1 }, /* 3C */ + { OPC_BR, "bne", 1 }, /* 3D */ + { OPC_BR, "bge", 1 }, /* 3E */ + { OPC_BR, "bgt", 1 }, /* 3F */ +}; + +int +inst_load(int ins) +{ + alpha_instruction insn; + + insn.bits = ins; + + /* Loads. */ + if (insn.mem_format.opcode == op_ldbu || + insn.mem_format.opcode == op_ldq_u || + insn.mem_format.opcode == op_ldwu) + return (1); + if ((insn.mem_format.opcode >= op_ldf) && + (insn.mem_format.opcode <= op_ldt)) + return (1); + if ((insn.mem_format.opcode >= op_ldl) && + (insn.mem_format.opcode <= op_ldq_l)) + return (1); + + /* Prefetches. */ + if (insn.mem_format.opcode == op_special) { + /* Note: MB is treated as a store. */ + if ((insn.mem_format.displacement == (short)op_fetch) || + (insn.mem_format.displacement == (short)op_fetch_m)) + return (1); + } + + return (0); +} + +static __inline int sext __P((u_int)); +static __inline int rega __P((u_int)); +static __inline int regb __P((u_int)); +static __inline int regc __P((u_int)); +static __inline int disp __P((u_int)); + +static __inline int +sext(x) + u_int x; +{ + return ((x & 0x8000) ? -(-x & 0xffff) : (x & 0xffff)); +} + +static __inline int +rega(x) + u_int x; +{ + return ((x >> 21) & 0x1f); +} + +static __inline int +regb(x) + u_int x; +{ + return ((x >> 16) & 0x1f); +} + +static __inline int +regc(x) + u_int x; +{ + return (x & 0x1f); +} + +static __inline int +disp(x) + u_int x; +{ + return (sext(x & 0xffff)); +} + +/* + * XXX There are a couple of problems with this code: + * + * The argument list printout code is likely to get confused. + * + * It relies on the conventions of gcc code generation. + * + * It uses heuristics to calculate the framesize, and might get it wrong. + * + * It doesn't yet use the framepointer if available. + * + * The address argument can only be used for pointing at trapframes + * since a frame pointer of its own serves no good on the alpha, + * you need a pc value too. + * + * The heuristics used for tracing through a trap relies on having + * symbols available. + */ +int +md_getframe(struct pstate *ps, int framec, struct md_frame *fram) +{ + reg frame; + int i, framesize; + reg pc, ra; + u_int inst; + char *name; + char namebuf[1024]; + reg offset; + reg slot[32]; + struct reg regs; + int count; + + bzero(slot, sizeof(slot)); + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)®s, 0) != 0) + return -1; + + for (i = 0; i < 32; i++) + slot[i] = -1; + + frame = regs.r_regs[R_SP]; + pc = regs.r_regs[R_ZERO]; /* Ieeek. on drugs. */ + ra = regs.r_regs[R_RA]; + + for (count = 0; count < framec + 1; count++) { + /* XXX - better out of bounds check needed. */ + if (pc < 0x1000 || pc == 0xffffffffffffffff) { + return -1; + } + + name = sym_name_and_offset(ps, pc, namebuf, + sizeof(namebuf), &offset); + if (!name) { + /* Limit the search for procedure start */ + offset = 65536; + } + + framesize = 0; + for (i = sizeof (int); i <= offset; i += sizeof (int)) { + if (read_from_pid(ps->ps_pid, pc - i, &inst, sizeof(inst)) < 0) + return -1; + + /* + * If by chance we don't have any symbols we have to + * get out somehow anyway. Check for the preceding + * procedure return in that case. + */ + if (name == NULL && inst_return(inst)) + break; + + /* + * Disassemble to get the needed info for the frame. + */ + if ((inst & 0xffff0000) == 0x23de0000) { + /* lda sp,n(sp) */ + framesize -= disp(inst) / sizeof (u_long); + } else if ((inst & 0xfc1f0000) == 0xb41e0000) { + /* stq X,n(sp) */ + slot[rega(inst)] = frame + disp(inst); + } else if ((inst & 0xfc000fe0) == 0x44000400 && + rega(inst) == regb(inst)) { + /* bis X,X,Y (aka mov X,Y) */ + /* zero is hardwired */ + if (rega(inst) != 31) + slot[rega(inst)] = slot[regc(inst)]; + slot[regc(inst)] = 0; + /* + * XXX In here we might special case a frame + * pointer setup, i.e. mov sp, fp. + */ + } else if (inst_load(inst)) { + /* clobbers a register */ + slot[rega(inst)] = 0; + } else if (opcode[inst >> 26].opc_fmt == OPC_OP) { + /* clobbers a register */ + slot[regc(inst)] = 0; + } + /* + * XXX Recognize more reg clobbering instructions and + * set slot[reg] = 0 then too. + */ + } + + fram->pc = pc; + fram->fp = frame; + + /* Look for the return address if recorded. */ + if (slot[R_RA]) { + if (slot[R_RA] == -1) + ra = regs.r_regs[R_RA]; + else + if (read_from_pid(ps->ps_pid, (off_t)slot[R_RA], + &ra, sizeof(ra)) < 0) + return -1; + } else { + break; + } + + /* Advance to the next frame. */ + frame += framesize * sizeof(u_long); + if (pc == ra) { + break; + } + pc = ra; + } + + return count == framec + 1 ? 0 : -1; +} diff --git a/usr.bin/pmdb/arch/alpha/pmdb_machdep.h b/usr.bin/pmdb/arch/alpha/pmdb_machdep.h new file mode 100644 index 00000000000..6ff260d09d0 --- /dev/null +++ b/usr.bin/pmdb/arch/alpha/pmdb_machdep.h @@ -0,0 +1,3 @@ +#define BREAKPOINT { 0x80, 0, 0, 0 } +#define BREAKPOINT_LEN 4 +#define BREAKPOINT_DECR_PC 4 diff --git a/usr.bin/pmdb/arch/i386/Makefile.inc b/usr.bin/pmdb/arch/i386/Makefile.inc new file mode 100644 index 00000000000..7226cf7cbb4 --- /dev/null +++ b/usr.bin/pmdb/arch/i386/Makefile.inc @@ -0,0 +1,3 @@ +# $PMDB: Makefile.inc,v 1.2 2002/01/30 23:20:13 fgs Exp $ + +SRCS+= i386.c diff --git a/usr.bin/pmdb/arch/i386/i386.c b/usr.bin/pmdb/arch/i386/i386.c new file mode 100644 index 00000000000..2612ef63568 --- /dev/null +++ b/usr.bin/pmdb/arch/i386/i386.c @@ -0,0 +1,91 @@ +/* $PMDB: i386.c,v 1.5 2002/02/21 01:54:44 art Exp $ */ +/* + * Copyright (c) 2002 Federico Schwindt <fgsch@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 <sys/param.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> +#include "pmdb.h" + +/* + * No frame for x86? + */ +struct frame { + int fp; + int pc; +}; + +static const char *md_reg_names[] = { + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", "%eip", + "%eflags", "%cs", "%ss", "%ds", "%es", "%fs", "%gs" +}; + +struct md_def md_def = { md_reg_names, 16, 8 }; + +void +md_def_init(void) +{ + /* no need to do anything */ +} + +int +md_getframe(struct pstate *ps, int frame, struct md_frame *fram) +{ + struct frame fr; + struct reg r; + int count; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return (-1); + + fr.fp = r.r_ebp; + fr.pc = r.r_eip; + for (count = 0; count < frame; count++) { + if (read_from_pid(ps->ps_pid, fr.fp, &fr, sizeof(fr)) < 0) + return (-1); + + if (fr.pc < 0x1000) + return (-1); + } + + fram->pc = fr.pc; + fram->fp = fr.fp; + + return 0; +} + +int +md_getregs(struct pstate *ps, reg *regs) +{ + struct reg r; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return -1; + + memcpy(regs, &r, sizeof(r)); + + return 0; +} diff --git a/usr.bin/pmdb/arch/i386/pmdb_machdep.h b/usr.bin/pmdb/arch/i386/pmdb_machdep.h new file mode 100644 index 00000000000..9c25e38ee6f --- /dev/null +++ b/usr.bin/pmdb/arch/i386/pmdb_machdep.h @@ -0,0 +1,3 @@ +#define BREAKPOINT { 0xcc } +#define BREAKPOINT_LEN 1 +#define BREAKPOINT_DECR_PC 1 diff --git a/usr.bin/pmdb/arch/sparc/Makefile.inc b/usr.bin/pmdb/arch/sparc/Makefile.inc new file mode 100644 index 00000000000..7e104c0feee --- /dev/null +++ b/usr.bin/pmdb/arch/sparc/Makefile.inc @@ -0,0 +1 @@ +SRCS+=sparc.c diff --git a/usr.bin/pmdb/arch/sparc/pmdb_machdep.h b/usr.bin/pmdb/arch/sparc/pmdb_machdep.h new file mode 100644 index 00000000000..d47a3b78c47 --- /dev/null +++ b/usr.bin/pmdb/arch/sparc/pmdb_machdep.h @@ -0,0 +1,3 @@ +#define BREAKPOINT { 0x91, 0xd0, 0x20, 0x01 } +#define BREAKPOINT_LEN 4 +#define BREAKPOINT_DECR_PC 0 diff --git a/usr.bin/pmdb/arch/sparc/sparc.c b/usr.bin/pmdb/arch/sparc/sparc.c new file mode 100644 index 00000000000..70aeb7687b8 --- /dev/null +++ b/usr.bin/pmdb/arch/sparc/sparc.c @@ -0,0 +1,107 @@ +/* $PMDB: sparc.c,v 1.4 2002/03/05 12:04:26 art Exp $ */ +/* + * Copyright (c) 2002 Federico Schwindt <fgsch@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 <sys/param.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> +#include "pmdb.h" + +static const char *md_reg_names[] = { + "%pc", "%npc", /* %y */ + "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%o6", "%o7", + "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", +}; + +struct md_def md_def = { md_reg_names, 18, 0 }; + +#define next_frame(f) ((struct frame*)(f->fr_fp)) + +void +md_def_init(void) +{ + /* no need to do anything */ +} + +int +md_getframe(struct pstate *ps, int frame, struct md_frame *fram) +{ + struct frame fr; + struct reg r; + reg fp, pc; + int i; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return -1; + + if (frame == 0) { + fram->pc = r.r_pc; + fram->fp = r.r_out[6]; + return 0; + } + + fp = r.r_out[6]; + pc = r.r_out[7]; + + for (i = 1; i < frame; i++) { + if (fp < 8192 || (fp & 7) != 0) + return -1; + + if (read_from_pid(ps->ps_pid, fp, &fr, sizeof(fr)) < 0) + return -1; + fp = (unsigned long)next_frame((&fr)); + pc = fr.fr_pc; + } + fram->pc = pc; + fram->fp = fp; + + fram->nargs = 6; /* XXX - don't know the real number */ + for (i = 0; i < 6; i++) { + fram->args[i] = fr.fr_arg[i]; + } + + return 0; +} + +int +md_getregs(struct pstate *ps, reg *regs) +{ + struct reg r; + int i; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return -1; + regs[0] = r.r_pc; + regs[1] = r.r_npc; + for (i = 0; i < 8; i++) { + regs[2 + i] = r.r_out[i]; + } + for (i = 0; i < 8; i++) { + regs[10 + i] = r.r_global[i]; + } + + return 0; +} diff --git a/usr.bin/pmdb/arch/sparc64/Makefile.inc b/usr.bin/pmdb/arch/sparc64/Makefile.inc new file mode 100644 index 00000000000..3257538c715 --- /dev/null +++ b/usr.bin/pmdb/arch/sparc64/Makefile.inc @@ -0,0 +1 @@ +SRCS+=sparc64.c diff --git a/usr.bin/pmdb/arch/sparc64/pmdb_machdep.h b/usr.bin/pmdb/arch/sparc64/pmdb_machdep.h new file mode 100644 index 00000000000..d47a3b78c47 --- /dev/null +++ b/usr.bin/pmdb/arch/sparc64/pmdb_machdep.h @@ -0,0 +1,3 @@ +#define BREAKPOINT { 0x91, 0xd0, 0x20, 0x01 } +#define BREAKPOINT_LEN 4 +#define BREAKPOINT_DECR_PC 0 diff --git a/usr.bin/pmdb/arch/sparc64/sparc64.c b/usr.bin/pmdb/arch/sparc64/sparc64.c new file mode 100644 index 00000000000..d4f44a56402 --- /dev/null +++ b/usr.bin/pmdb/arch/sparc64/sparc64.c @@ -0,0 +1,122 @@ +/* $PMDB: sparc64.c,v 1.6 2002/03/07 01:10:52 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 <sys/param.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> +#include "pmdb.h" + +static const char *md_reg_names[] = { + "%pc", "%npc", /* %y */ + "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%o6", "%o7", + "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", +}; + +struct md_def md_def = { md_reg_names, 18, 0 }; + +void +md_def_init(void) +{ + /* no need to do anything */ +} + +int +md_getframe(struct pstate *ps, int frame, struct md_frame *fram) +{ + struct frame64 fr; + struct reg r; + reg fp, pc; + reg *outs; + int i; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return (-1); + + if (frame == 0) { + pc = r.r_pc; + fp = r.r_out[6] + BIAS; + /* + * XXX - we need some kind of heuristics here to decide + * if the function has done a save or not and then pick + * the in registers. the problem is just that there are + * no in registers in PT_GETREGS. + */ + outs = (reg *)&r.r_out; + goto out; + } + + fp = r.r_out[6] + BIAS; + pc = r.r_out[7]; + + for (i = 1; i < frame; i++) { + /* Too low or unaligned frame pointer? */ + if (fp < 8192 || (fp & 7) != 0) + return (-1); + + if (read_from_pid(ps->ps_pid, fp, &fr, sizeof(fr)) < 0) + return -1; + fp = (unsigned long)v9next_frame((&fr)); + pc = fr.fr_pc; + + /* Too low or unaligned pc ? */ + if ((pc < 8192) || (pc & 3) != 0) + return (-1); + + outs = (reg *)&fr.fr_arg; + } + +out: + fram->pc = pc; + fram->fp = fp; + + fram->nargs = 6; /* XXX - don't know the real number */ + for (i = 0; i < 6; i++) { + fram->args[i] = fr.fr_arg[i]; + } + + return 0; +} + +int +md_getregs(struct pstate *ps, reg *regs) +{ + struct reg r; + int i; + + if (ptrace(PT_GETREGS, ps->ps_pid, (caddr_t)&r, 0) != 0) + return -1; + regs[0] = r.r_pc; + regs[1] = r.r_npc; + for (i = 0; i < 8; i++) { + regs[2 + i] = r.r_out[i]; + } + for (i = 0; i < 8; i++) { + regs[10 + i] = r.r_global[i]; + } + + return 0; +}
\ No newline at end of file diff --git a/usr.bin/pmdb/break.c b/usr.bin/pmdb/break.c new file mode 100644 index 00000000000..e7eaa61077a --- /dev/null +++ b/usr.bin/pmdb/break.c @@ -0,0 +1,301 @@ +/* $PMDB: break.c,v 1.7 2002/03/12 11:28:28 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 <sys/types.h> +#include <sys/ptrace.h> +#include <sys/wait.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <err.h> +#include <errno.h> +#include <string.h> + +#include "pmdb.h" +#include "symbol.h" +#include "pmdb_machdep.h" +#include "break.h" + +struct callback { + TAILQ_ENTRY(callback) cb_list; + int (*cb_fun)(struct pstate *, void *); + void *cb_arg; +}; + +struct breakpoint { + TAILQ_ENTRY(breakpoint) bkpt_list; + TAILQ_HEAD(,callback) bkpt_cbs; /* list of all callbacks */ + char bkpt_old[BREAKPOINT_LEN]; /* old contents at bkpt */ + reg bkpt_pc; +}; + +static char bkpt_insn[BREAKPOINT_LEN] = BREAKPOINT; + +/* + * Find a breakpoint at this address. + */ +struct breakpoint * +bkpt_find_at_pc(struct pstate *ps, reg pc) +{ + struct breakpoint *bkpt; + + TAILQ_FOREACH(bkpt, &ps->ps_bkpts, bkpt_list) + if (bkpt->bkpt_pc == pc) + break; + + return (bkpt); +} + +/* + * Enable this breakpoint. + */ +static int +bkpt_enable(struct pstate *ps, struct breakpoint *bkpt) +{ + reg pc = bkpt->bkpt_pc; + + if (read_from_pid(ps->ps_pid, pc, &bkpt->bkpt_old, BREAKPOINT_LEN)) { + warn("Can't read process contents at 0x%lx", pc); + return (-1); + } + if (write_to_pid(ps->ps_pid, pc, &bkpt_insn, BREAKPOINT_LEN)) { + warn("Can't write breakpoint at 0x%lx, attempting backout.", pc); + if (write_to_pid(ps->ps_pid, pc, &bkpt->bkpt_old, + BREAKPOINT_LEN)) + warn("Backout failed, process unstable"); + return (-1); + } + return (0); +} + +/* + * Create a new breakpoint and enable it. + */ +int +bkpt_add_cb(struct pstate *ps, reg pc, int (*fun)(struct pstate *, void *), + void *arg) +{ + struct breakpoint *bkpt; + struct callback *cb; + + if ((bkpt = bkpt_find_at_pc(ps, pc)) == NULL) { + bkpt = emalloc(sizeof(*bkpt)); + TAILQ_INIT(&bkpt->bkpt_cbs); + TAILQ_INSERT_TAIL(&ps->ps_bkpts, bkpt, bkpt_list); + bkpt->bkpt_pc = pc; + if (bkpt_enable(ps, bkpt)) { + free(bkpt); + return (-1); + } + } + + cb = emalloc(sizeof(*cb)); + cb->cb_fun = fun; + cb->cb_arg = arg; + TAILQ_INSERT_TAIL(&bkpt->bkpt_cbs, cb, cb_list); + + return (0); +} + +/* + * Disable and delete a breakpoint. + */ +void +bkpt_delete(struct pstate *ps, struct breakpoint *bkpt) +{ + TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list); + + if (write_to_pid(ps->ps_pid, bkpt->bkpt_pc, &bkpt->bkpt_old, + BREAKPOINT_LEN)) + warn("Breakpoint removal failed, process unstable"); + + free(bkpt); +} + +/* + * Normal standard breakpoint. Keep it. + */ +static int +bkpt_normal(struct pstate *ps, void *arg) +{ + return (BKPT_KEEP_STOP); +} + +/* + * Single-step callback for "stepping over" a breakpoint (we restore the + * breakpoint instruction to what it was, single-step over it and then + * call this function). + */ +static int +sstep_bkpt_readd(struct pstate *ps, void *arg) +{ + reg pc = (reg)arg; + + bkpt_add_cb(ps, pc, bkpt_normal, NULL); + + return (0); /* let the process continue */ +} + +/* + * Return 0 for stop, 1 for silent continue. + */ +int +bkpt_check(struct pstate *ps) +{ + struct breakpoint *bkpt; + struct callback *cb; + TAILQ_HEAD(,callback) sstep_cbs; + reg *rg, pc; + int ret; + int didsome = 0; + int stop = 0; + + /* Requeue all single-step callbacks because bkpts can add ssteps. */ + TAILQ_INIT(&sstep_cbs); + while ((cb = TAILQ_FIRST(&ps->ps_sstep_cbs)) != NULL) { + TAILQ_REMOVE(&ps->ps_sstep_cbs, cb, cb_list); + TAILQ_INSERT_TAIL(&sstep_cbs, cb, cb_list); + } + + /* + * The default is to stop. Unless we do some processing and none + * of the callbacks require a stop. + */ + rg = alloca(sizeof(*rg) * md_def.nregs); + if (md_getregs(ps, rg)) + err(1, "bkpt_check: Can't get registers."); + + pc = rg[md_def.pcoff]; + pc -= BREAKPOINT_DECR_PC; + + bkpt = bkpt_find_at_pc(ps, pc); + if (bkpt == NULL) + goto sstep; + + ps->ps_npc = pc; + + while ((cb = TAILQ_FIRST(&bkpt->bkpt_cbs)) != NULL) { + didsome = 1; + TAILQ_REMOVE(&bkpt->bkpt_cbs, cb, cb_list); + ret = (*cb->cb_fun)(ps, cb->cb_arg); + free(cb); + switch (ret) { + case BKPT_DEL_STOP: + stop = 1; + case BKPT_DEL_CONT: + break; + case BKPT_KEEP_STOP: + stop = 1; + case BKPT_KEEP_CONT: + sstep_set(ps, sstep_bkpt_readd, (void *)bkpt->bkpt_pc); + break; + default: + errx(1, "unkonwn bkpt_fun return, internal error"); + } + } + + bkpt_delete(ps, bkpt); + +sstep: + + while ((cb = TAILQ_FIRST(&sstep_cbs)) != NULL) { + didsome = 1; + TAILQ_REMOVE(&sstep_cbs, cb, cb_list); + stop |= (*cb->cb_fun)(ps, cb->cb_arg); + free(cb); + } + ps->ps_flags &= ~PSF_STEP; + + return (didsome && !stop); +} + +int +cmd_bkpt_add(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + char *ep, *bkpt_name; + reg pc; + + if (ps->ps_state != STOPPED && ps->ps_state != LOADED) { + fprintf(stderr, "Process not loaded and stopped %d\n", + ps->ps_state); + return (0); + } + + bkpt_name = argv[1]; + pc = strtol(bkpt_name, &ep, 0); + if (bkpt_name[0] == '\0' || *ep != '\0' || pc < 1) { + if (sym_lookup(ps, bkpt_name, &pc)) { + warnx("%s is not a valid pc", bkpt_name); + return (0); + } + } + + if (bkpt_add_cb(ps, pc, bkpt_normal, 0)) + warn("Can't set break point"); + + return (0); +} + +static int +sstep_normal(struct pstate *ps, void *arg) +{ + return (1); /* stop the command line. */ +} + +int +cmd_sstep(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + + if (ps->ps_state != STOPPED) { + fprintf(stderr, "Process not loaded and stopped %d\n", + ps->ps_state); + return 0; + } + + if (sstep_set(ps, sstep_normal, NULL)) + warn("Can't set single step"); + + return (cmd_process_cont(argc, argv, arg)); +} + +int +sstep_set(struct pstate *ps, int (*fun)(struct pstate *, void *), void *arg) +{ + struct callback *cb; + + cb = emalloc(sizeof(*cb)); + cb->cb_fun = fun; + cb->cb_arg = arg; + TAILQ_INSERT_TAIL(&ps->ps_sstep_cbs, cb, cb_list); + + ps->ps_flags |= PSF_STEP; + + return (0); +} diff --git a/usr.bin/pmdb/break.h b/usr.bin/pmdb/break.h new file mode 100644 index 00000000000..a4513e0ddfc --- /dev/null +++ b/usr.bin/pmdb/break.h @@ -0,0 +1,46 @@ +/* $PMDB: break.h,v 1.4 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. + */ + +/* + * Breakpoint handling. + */ +int bkpt_add_cb(struct pstate *, reg, int (*)(struct pstate *, void *), void *); +int bkpt_check(struct pstate *); +int cmd_bkpt_add(int, char **, void *); + +/* + * Single step handling. + */ +int sstep_set(struct pstate *, int (*)(struct pstate *, void *), void *); +int cmd_sstep(int, char **, void *); + +/* + * Return values from the bkpt_fun + */ +#define BKPT_DEL_STOP 1 /* delete this bkpt and stop */ +#define BKPT_DEL_CONT 2 /* delete this bkpt and continue */ +#define BKPT_KEEP_STOP 3 /* keep this bkpt and stop */ +#define BKPT_KEEP_CONT 4 /* keep this bkpt and cont */ diff --git a/usr.bin/pmdb/clit.c b/usr.bin/pmdb/clit.c new file mode 100644 index 00000000000..3dab6a9231a --- /dev/null +++ b/usr.bin/pmdb/clit.c @@ -0,0 +1,261 @@ +/* $PMDB: clit.c,v 1.11 2002/03/07 13:57:28 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 <stdlib.h> +#include <stdio.h> +#include <histedit.h> +#include <err.h> +#include <string.h> + +#include "clit.h" + +extern char *__progname; + +char *prompt_add; + +static char * +prompt(EditLine *el) +{ + static char p[64]; + + snprintf(p, sizeof(p), "%s%s> ", __progname, + prompt_add ? prompt_add : ""); + + return p; +} + +/* + * Returns number of commands that (at least partially) match "name". + */ +static int +name_to_cmd(const char *name, struct clit *cmds, int ncmds, struct clit **res) +{ + int i, len, ret; + + len = strlen(name); + ret = 0; + + for (i = 0; i < ncmds; i++) { + if (strncmp(cmds[i].cmd, name, len) == 0) { + *res = &cmds[i]; + ret++; + } + } + + return ret; +} + +struct clitenv { + struct clit *cmds; + int ncmds; + EditLine *el; + History *hist; +}; + +int +cmd_help(int argc, char **argv, void *arg) +{ + struct clitenv *env = arg; + struct clit *cmds = env->cmds, *cmdp; + int ncmds = env->ncmds; + int i, res; + + if (argc > 1) { + res = name_to_cmd(argv[1], cmds, ncmds, &cmdp); + if (res == 1) { + printf("%s\t%s\n", cmdp->cmd, cmdp->help); + } else { + fprintf(stderr, "%s command: %s\n", + res == 0 ? "unknown" : "ambiguous", argv[1]); + } + + return 0; + } + for (i = 0; i < ncmds; i++) { + cmdp = &cmds[i]; + + printf("%s\t%s\n", cmdp->cmd, cmdp->help); + } + + return 0; +} + +/* + * XXX - there is no way to push external args into this function. + */ +unsigned char +complt(EditLine *el, int ch) +{ + const LineInfo *line; + char str[1024]; + int len, ret; + + line = el_line(el); + if (line->cursor != line->lastchar) + return CC_ERROR; + + len = line->lastchar - line->buffer; + + if (len >= 1023) + return CC_ERROR; + + memcpy(str, line->buffer, len); + str[len] = '\0'; + + ret = cmd_complt(str, sizeof(str)); + + el_push(el, &str[len]); + + return ret ? CC_ERROR : CC_REDISPLAY; +} + +void * +cmdinit(struct clit *cmds, int ncmds) +{ + struct clitenv *env; +#ifdef __NetBSD__ + HistEvent ev; +#endif + + if ((env = malloc(sizeof(*env))) == NULL) + err(1, "Can't init cmd interpreter."); + + env->cmds = cmds; + env->ncmds = ncmds; + + env->hist = history_init(); +#ifdef __NetBSD__ + history(env->hist, &ev, H_SETSIZE, 100); +#else + history(env->hist, H_EVENT, 100); +#endif + +#ifdef __NetBSD__ + env->el = el_init(__progname, stdin, stdout, stderr); +#else + env->el = el_init(__progname, stdin, stdout); +#endif + el_set(env->el, EL_EDITOR, "emacs"); + el_set(env->el, EL_PROMPT, prompt); + el_set(env->el, EL_HIST, history, env->hist); + el_set(env->el, EL_ADDFN, "complt", "complete", complt); + el_set(env->el, EL_BIND, "\t", "complt"); + el_source(env->el, NULL); + + /* XXX - EL_SIGNAL ? */ + + return env; +} + +void +cmdend(void *arg) +{ + struct clitenv *env = arg; + + el_end(env->el); + history_end(env->hist); + + free(env); +} + +int +cmdloop(void *arg) +{ + struct clitenv *env = arg; + EditLine *el = env->el; + History *hist = env->hist; + const char *elline; + int cnt; + char **argv; + int maxargs = 16; /* XXX */ + int stop; + + stop = 0; + + if ((argv = malloc(sizeof(char *) * maxargs)) == NULL) + err(1, "malloc"); + + while (!stop && (elline = el_gets(el, &cnt)) != NULL) { + char *line, *orgline; + struct clit *cmdp; + char **ap; + int argc, res; +#ifdef __NetBSD__ + HistEvent ev; +#endif + + memset(argv, 0, sizeof(argv)); + +#ifdef __NetBSD__ + history(hist, &ev, H_ENTER, elline); +#else + history(hist, H_ENTER, elline); +#endif + + orgline = line = strdup(elline); + + argc = 0; + for (ap = argv; (*ap = strsep(&line, " \t\n")) != NULL;) { + if (**ap != '\0') { + ++ap; + if (++argc == maxargs) + break; + } + } + if (argc == maxargs) { + fprintf(stderr, "Too many arguments\n"); + goto cmdout; + } + if (!argc) + goto cmdout; + + /* + * Editline commands. + */ + if (el_parse(el, argc, argv) != -1) + goto cmdout; + + if ((res = name_to_cmd(argv[0], env->cmds, env->ncmds, + &cmdp)) == 1) { + if (argc - 1 > cmdp->maxargc) + fprintf(stderr, "Too many arguments\n"); + else if (argc - 1 < cmdp->minargc) + fprintf(stderr, "Too few arguments\n"); + else + stop = (*cmdp->handler)(argc, argv, + cmdp->arg ? cmdp->arg : env); + } else { + fprintf(stderr, "%s command: %s\n", + res == 0 ? "unknown" : "ambiguous", argv[0]); + } +cmdout: + free(orgline); + } + free(argv); + + return stop; +} + diff --git a/usr.bin/pmdb/clit.h b/usr.bin/pmdb/clit.h new file mode 100644 index 00000000000..d0479ec0354 --- /dev/null +++ b/usr.bin/pmdb/clit.h @@ -0,0 +1,52 @@ +/* $PMDB: clit.h,v 1.8 2002/03/07 13:57:28 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. + */ + +struct clit { + const char *cmd; + const char *help; + int minargc; + int maxargc; + int (*handler)(int argc, char **argv, void *); + void *arg; +}; + +char *prompt_add; + +int cmd_help(int, char **, void *); + +void *cmdinit(struct clit *, int); +int cmdloop(void *); +void cmdend(void *); + +/* + * This function must be defined by the calling code. Sorry, but there is + * no way to pass arguments to it or pass this function in some arguments. + * + * Fills in the possible completions into buf. Returns != 0 when there are + * no possible completions. May whack buf, but the "returned" + * string should be appended to the string that was in buf. + */ +int cmd_complt(char *buf, size_t buflen); 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 +} diff --git a/usr.bin/pmdb/pmdb.1 b/usr.bin/pmdb/pmdb.1 new file mode 100644 index 00000000000..e2ecd5ae8de --- /dev/null +++ b/usr.bin/pmdb/pmdb.1 @@ -0,0 +1,110 @@ +.\" $PMDB: pmdb.1,v 1.2 2002/02/20 15:16:27 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. +.\" +.Dd Feb 20, 2002 +.Dt PMDB 1 +.Os +.Sh NAME +.Nm pmdb +.Nd debugger. +.Sh SYNOPSIS +.Nm pmdb +.Ar program Op Ar ... +.Sh DESCRIPTION +The +.Nm +utility can be used to see what is happening inside a running process or +to catch program crashes and examine the state at the time of the crash. +The only way to start +.Nm +at this moment is to specify the name of the prgram to be debugged and all +its arguments on the command line. +The program is controlled from a command line which usually gives the +prompt "pmdb>". +.Sh PROCESS STATES +A loaded program can be in one of three possible states: +.Bl -tag -width RUNNING +.It LOADED +This is the initial state. +The program is not running, it can't be examined (because it doesn't have +any state). +The only thing that can be done to the process is to start it with the +.Ic run +command. +.It RUNNING +When a process is +.Ic RUNNING , +the only way to affect it is through signals sent to it. +Unless a signal is ignored with the +.Ic signal ignore +command, it will be catched by pmdb and the process will go into the +.Ic STOPPED +state. +.It STOPPED +A stopped process can be examined, changed and restarted with the +.Ic continue +command. +.El +.Sh COMMANDS +.Bl -tag -width continue +.It regs +Show the contents of the processor registers at the moment the process was +.Ic STOPPED . +.It trace +Show the function call trace of the currently +.Ic STOPPED +process. +.It run +Start running a +.Ic LOADED +process. +.It continue +Continue a +.Ic STOPPED +process. +.It kill +Unconditionally kills the debugged process and puts it in the +.Ic LOADED +state. +.It signal Ar ignore|stop Ar signum|signame +Sets the signal state for the specified signal to either ignore it and +pass it to the process or to stop the process. +.It sigstate +Shows which signals are currently ignored. +.It help +Shows a short help. +.It quit +Kills the process (if necessary) and exits +.Nm . +.It exit +Alias for +.Ic quit . +.El +.Sh HISTORY +The +.Nm +debugger was written because the author believed that +.Xr gdb 1 +was too to bloated and hairy to run on OpenBSD/sparc64. diff --git a/usr.bin/pmdb/pmdb.c b/usr.bin/pmdb/pmdb.c new file mode 100644 index 00000000000..5f7740f89e9 --- /dev/null +++ b/usr.bin/pmdb/pmdb.c @@ -0,0 +1,348 @@ +/* $PMDB: pmdb.c,v 1.41 2002/03/12 14:24:30 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 <sys/types.h> +#include <sys/ptrace.h> +#include <sys/wait.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <err.h> +#include <errno.h> +#include <string.h> + +#include <sys/endian.h> + +#include "pmdb.h" +#include "symbol.h" +#include "clit.h" +#include "break.h" + +static int cmd_show_registers(int, char **, void *); +static int cmd_show_backtrace(int, char **, void *); +static int cmd_quit(int, char **, void *); + +struct clit cmds[] = { + /* debugging info commands. */ + { "regs", "show registers", 0, 0, cmd_show_registers, (void *)-1 }, + { "trace", "show backtrace", 0, 0, cmd_show_backtrace, (void *)-1 }, + + /* Process handling commands. */ + { "run", "run process", 0, 0, cmd_process_run, (void *)-1 }, + { "continue", "continue process", 0, 0, cmd_process_cont, (void *)-1 }, + { "kill", "kill process", 0, 0, cmd_process_kill, (void *)-1 }, + + /* signal handling commands. */ + { "signal", "ignore signal", 2, 2, cmd_signal_ignore, (void *)-1 }, + { "sigstate", "show signal state", 0, 0, cmd_signal_show, (void *)-1 }, + + /* breakpoints */ + { "break", "set breakpoint", 1, 1, cmd_bkpt_add, (void *)-1 }, + { "step", "single step one insn", 0, 0, cmd_sstep, (void *)-1 }, + + /* misc commands. */ + { "help", "print help", 0, 1, cmd_help, NULL }, + { "quit", "quit", 0, 0, cmd_quit, (void *)-1 }, + { "exit", "quit", 0, 0, cmd_quit, (void *)-1 }, +}; + +int +main(int argc, char **argv) +{ + extern const char *__progname; + struct pstate ps; + int i, ncmds; + int status; + void *cm; + char *pmenv; + int level; + + if (argc < 2) { + fprintf(stderr, "Usage: %s <program> args\n", __progname); + exit(1); + } + + if ((pmenv = getenv("IN_PMDB")) != NULL) { + level = atoi(pmenv); + level++; + } else + level = 0; + + if (level > 0) + asprintf(&prompt_add, "(%d)", level); + asprintf(&pmenv, "%d", level); + setenv("IN_PMDB", pmenv, 1); + + ps.ps_pid = 0; + ps.ps_state = NONE; + ps.ps_argc = --argc; + ps.ps_argv = ++argv; + ps.ps_flags = 0; + ps.ps_signum = 0; + ps.ps_npc = 1; + TAILQ_INIT(&ps.ps_bkpts); + TAILQ_INIT(&ps.ps_sstep_cbs); + + signal(SIGINT, SIG_IGN); + + ncmds = sizeof(cmds)/sizeof(cmds[0]); + + for (i = 0; i < ncmds; i++) + if (cmds[i].arg == (void *)-1) + cmds[i].arg = &ps; + + md_def_init(); + init_sigstate(&ps); + + process_load(&ps); + + cm = cmdinit(cmds, ncmds); + while (ps.ps_state != TERMINATED) { + int signum; + int stopped; + int cont; + + if (ps.ps_state == STOPPED) { + sym_update(&ps); + } + + if (ps.ps_state != RUNNING && cmdloop(cm) == 0) { + cmd_quit(0, NULL, &ps); + } + + if (ps.ps_state == TERMINATED) + break; + + if (wait(&status) == 0) + err(1, "wait"); + if (WIFEXITED(status)) { + if ((ps.ps_flags & PSF_KILL) == 0) { + ps.ps_state = NONE; + } else { + ps.ps_state = TERMINATED; + } + fprintf(stderr, "process exited with status %d\n", + WEXITSTATUS(status)); + continue; + } + if (WIFSIGNALED(status)) { + signum = WTERMSIG(status); + stopped = 0; + } else { + signum = WSTOPSIG(status); + stopped = 1; + } + cont = 0; + if (stopped) + cont = bkpt_check(&ps); + process_signal(&ps, signum, stopped, cont); + } + + cmdend(cm); + + sym_destroy(&ps); + + return (0); +} + +/* XXX - move to some other file. */ +int +read_from_pid(pid_t pid, off_t from, void *to, size_t size) +{ + struct ptrace_io_desc piod; + + piod.piod_op = PIOD_READ_D; + piod.piod_offs = (void *)(long)from; + piod.piod_addr = to; + piod.piod_len = size; + + return (ptrace(PT_IO, pid, (caddr_t)&piod, 0) != size); +} + + +int +write_to_pid(pid_t pid, off_t to, void *from, size_t size) +{ + struct ptrace_io_desc piod; + + piod.piod_op = PIOD_WRITE_D; + piod.piod_offs = (void *)(long)to; + piod.piod_addr = from; + piod.piod_len = size; + + return (ptrace(PT_IO, pid, (caddr_t)&piod, 0) != size); +} + +static int +cmd_show_registers(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + char buf[256]; + int i; + reg *rg; + + if (ps->ps_state != STOPPED) { + fprintf(stderr, "process not stopped\n"); + return 0; + } + + rg = alloca(sizeof(*rg) * md_def.nregs); + + if (md_getregs(ps, rg)) + err(1, "can't get registers"); + for (i = 0; i < md_def.nregs; i++) + printf("%s:\t0x%.*lx\t%s\n", md_def.md_reg_names[i], + (int)(sizeof(reg) * 2), (long)rg[i], + sym_print(ps, rg[i], buf, sizeof(buf))); + return 0; +} + +static int +cmd_show_backtrace(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + int i; + + if (ps->ps_state != STOPPED) { + fprintf(stderr, "process not stopped\n"); + return 0; + } + + /* no more than 100 frames */ + for (i = 0; i < 100; i++) { + struct md_frame mfr; + char namebuf[1024], *name; + reg offs; + int j; + + mfr.nargs = -1; + + if (md_getframe(ps, i, &mfr)) + break; + + name = sym_name_and_offset(ps, mfr.pc, namebuf, + sizeof(namebuf), &offs); + if (name == NULL) { + snprintf(namebuf, sizeof(namebuf), "0x%lx", mfr.pc); + name = namebuf; + } + + printf("%s(", name); + for (j = 0; j < mfr.nargs; j++) { + printf("0x%lx", mfr.args[j]); + if (j < mfr.nargs - 1) + printf(", "); + } + printf(")+0x%lx\n", offs); + } + return 0; +} + +static int +cmd_quit(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + + ps->ps_flags |= PSF_KILL; + + if (process_kill(ps)) + return 1; + + ps->ps_state = TERMINATED; + return 1; +} + +/* + * Perform command completion. + * Pretty simple. if there are spaces in "buf", the last string is a symbol + * otherwise it's a command. + */ +int +cmd_complt(char *buf, size_t buflen) +{ + struct clit *match; + char *start; + int command; + int i, j, len; + int onlymatch; + + command = (strchr(buf, ' ') == NULL); + + if (!command) { + /* XXX - can't handle symbols yet. */ + return -1; + } + + start = buf; + len = strlen(buf); + + match = NULL; + for (i = 0; i < sizeof(cmds) / sizeof(cmds[i]); i++) { + if (strncmp(start, cmds[i].cmd, len) == 0) { + struct clit *cmdp; + + cmdp = &cmds[i]; + if (match == NULL) { + onlymatch = 1; + match = cmdp; + strlcpy(buf, match->cmd, buflen); + continue; + } + onlymatch = 0; + for (j = len; j < buflen; j++) { + if (buf[j] != cmdp->cmd[j]) { + buf[j] = '\0'; + break; + } + if (cmdp->cmd[j] == '\0') + break; + } + } + } + + /* + * Be nice. If there could be arguments for this command and it's + * the only match append a space. + */ + if (match && onlymatch /*&& match->maxargc > 0*/) + strlcat(buf, " ", buflen); + + return (match && onlymatch) ? 0 : -1; +} + +/* + * The "stadard" wrapper + */ +void * +emalloc(size_t sz) +{ + void *ret; + if ((ret = malloc(sz)) == NULL) + err(1, "malloc"); + return (ret); +} diff --git a/usr.bin/pmdb/pmdb.h b/usr.bin/pmdb/pmdb.h new file mode 100644 index 00000000000..18d5cf023c7 --- /dev/null +++ b/usr.bin/pmdb/pmdb.h @@ -0,0 +1,119 @@ +/* $PMDB: pmdb.h,v 1.26 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 <sys/signal.h> /* for NSIG */ +#include <sys/queue.h> +#include <sys/ptrace.h> +#include <err.h> + +/* XXX - ugh, yuck, bleah. */ +#ifndef PT_STEP +#define PT_STEP PT_CONTINUE +#endif + +/* + * Process handling. + */ + +struct breakpoint; +struct callback; +struct sym_table; +struct sym_ops; + +/* XXX - should be machdep some day. */ +typedef unsigned long reg; + +/* The state for a debugged process. */ +struct pstate { + pid_t ps_pid; + enum { NONE, LOADED, RUNNING, STOPPED, TERMINATED } ps_state; + int ps_argc; + char **ps_argv; + int ps_flags; + int ps_signum; + int ps_sigstate[NSIG]; + reg ps_npc; + TAILQ_HEAD(,sym_table) ps_syms; /* all symbols tables in a list */ + struct sym_table *ps_sym_exe; /* symbol table for the executable */ + struct sym_ops *ps_sops; /* operations on symbol tables */ + TAILQ_HEAD(,breakpoint) ps_bkpts; /* breakpoints */ + TAILQ_HEAD(,callback) ps_sstep_cbs; /* single step actions */ +}; + +/* flags in ps_flags */ +#define PSF_SYMBOLS 0x02 /* basic symbols loaded */ +#define PSF_KILL 0x04 /* kill this process asap */ +#define PSF_STEP 0x08 /* next continue should sstep */ + +/* ps_sigstate */ +#define SS_STOP 0x00 +#define SS_IGNORE 0x01 + +/* misc helper functions */ +int process_kill(struct pstate *); +int read_from_pid(pid_t pid, off_t from, void *to, size_t size); +int write_to_pid(pid_t pid, off_t to, void *from, size_t size); + +/* process.c */ +int process_load(struct pstate *); +int cmd_process_run(int, char **, void *); +int cmd_process_cont(int, char **, void *); +int cmd_process_kill(int, char **, void *); + +/* signal.c */ +void init_sigstate(struct pstate *); +void process_signal(struct pstate *, int, int, int); +int cmd_signal_ignore(int, char **, void *); +int cmd_signal_show(int, char **, void *); + +/* + * Machine dependent stuff. + */ +/* register names */ +struct md_def { + const char **md_reg_names; /* array of register names */ + const int nregs; /* number of registers */ + const int pcoff; /* offset of the pc */ +}; +extern struct md_def md_def; +void md_def_init(void); + +#define MDF_MAX_ARGS 16 + +struct md_frame { + reg pc, fp; + int nargs; + reg args[MDF_MAX_ARGS]; +}; + +/* + * Return the registers for the process "ps" in the frame "frame". + */ +int md_getframe(struct pstate *, int, struct md_frame *); +int md_getregs(struct pstate *, reg *); + +/* misc */ +void *emalloc(size_t); diff --git a/usr.bin/pmdb/process.c b/usr.bin/pmdb/process.c new file mode 100644 index 00000000000..727613b57be --- /dev/null +++ b/usr.bin/pmdb/process.c @@ -0,0 +1,169 @@ +/* $PMDB: process.c,v 1.19 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 <sys/types.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <err.h> + +#include "pmdb.h" +#include "symbol.h" +#include "break.h" + +int +process_load(struct pstate *ps) +{ + int status; + + if (ps->ps_state == LOADED) + return (0); + + switch (ps->ps_pid = fork()) { + case 0: + if (ptrace(PT_TRACE_ME, getpid(), NULL, 0) != 0) + err(1, "ptrace(PT_TRACE_ME)"); + execvp(*ps->ps_argv, ps->ps_argv); + err(1, "exec"); + /* NOTREACHED */ + case -1: + err(1, "fork"); + /* NOTREACHED */ + default: + break; + } + + if ((ps->ps_flags & PSF_SYMBOLS) == 0) { + sym_init_exec(ps, ps->ps_argv[0]); + ps->ps_flags |= PSF_SYMBOLS; + } + + if (wait(&status) == 0) + err(1, "wait"); + + ps->ps_state = LOADED; + return 0; +} + +int +process_kill(struct pstate *ps) +{ + switch(ps->ps_state) { + case LOADED: + case RUNNING: + case STOPPED: + if (ptrace(PT_KILL, ps->ps_pid, NULL, 0) != 0) + err(1, "ptrace(PT_KILL)"); + return 1; + default: + return 0; + } +} + +int +cmd_process_kill(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + + process_kill(ps); + + return 1; +} + +int +process_bkpt_main(struct pstate *ps, void *arg) +{ + sym_update(ps); + + return BKPT_DEL_CONT; +} + +int +cmd_process_run(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + + if (ps->ps_state == NONE) { + reg main_addr; + + process_load(ps); + if (sym_lookup(ps, "main", &main_addr)) + warnx("no main"); + else if (bkpt_add_cb(ps, main_addr, process_bkpt_main, NULL)) + warn("no bkpt at main 0x%lx", main_addr); + } + + if (ps->ps_state != LOADED) { + fprintf(stderr, "Process already running.\n"); + return 0; + } + + /* + * XXX - there isn't really any difference between STOPPED and + * LOADED, we should probably get rid of one. + */ + ps->ps_state = STOPPED; + ps->ps_signum = 0; + + return (cmd_process_cont(argc, argv, arg)); +} + +int +cmd_process_cont(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + int signum; + int req = (ps->ps_flags & PSF_STEP) ? PT_STEP : PT_CONTINUE; + + if (ps->ps_state != STOPPED) { + fprintf(stderr, "Process not loaded and stopped %d\n", + ps->ps_state); + return (0); + } + + /* Catch SIGINT and SIGTRAP, pass all other signals. */ + switch (ps->ps_signum) { + case SIGINT: + case SIGTRAP: + signum = 0; + break; + default: + signum = ps->ps_signum; + break; + } + + if (ptrace(req, ps->ps_pid, (caddr_t)ps->ps_npc, signum) != 0) { + err(1, "ptrace(%s)", req == PT_STEP ? "PT_STEP":"PT_CONTINUE"); + } + + ps->ps_state = RUNNING; + ps->ps_npc = 1; + + return (1); +} diff --git a/usr.bin/pmdb/signal.c b/usr.bin/pmdb/signal.c new file mode 100644 index 00000000000..cac71ce737e --- /dev/null +++ b/usr.bin/pmdb/signal.c @@ -0,0 +1,166 @@ +/* $PMDB: signal.c,v 1.10 2002/03/07 13:56:56 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 <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <sys/wait.h> + +#include "pmdb.h" + +void +init_sigstate(struct pstate *ps) +{ + int i; + + for (i = 1; i < NSIG; i++) + ps->ps_sigstate[i] = SS_STOP; + + /* XXX - add more default ignored signals. */ + ps->ps_sigstate[SIGALRM] = SS_IGNORE; + ps->ps_sigstate[SIGCHLD] = SS_IGNORE; +} + +void +process_signal(struct pstate *ps, int signum, int stopped, int force_ignore) +{ + int ignore, status; + + if (stopped && (ps->ps_sigstate[signum] == SS_IGNORE || force_ignore)) + ignore = 1; + else + ignore = 0; + + if (force_ignore && ignore) + signum = 0; + + ps->ps_signum = signum; + + if (!stopped) { + /* + * Process terminated. + */ + /* Let it be restarted if it wasn't a forced termination. */ + if ((ps->ps_flags & PSF_KILL) == 0) + ps->ps_state = NONE; + else + ps->ps_state = TERMINATED; + /* + * Wait for it as a parent. + * XXX - only if we're the real parent. + */ + wait(&status); + } else { + ps->ps_state = STOPPED; + } + + if (!ignore) { + fprintf(stderr, "PBMD %s child. signal: %s\n", + stopped ? "stopping" : "terminating", + sys_signame[signum]); + } else { + cmd_process_cont(0, NULL, ps); + } +} + +int +cmd_signal_ignore(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + int signum; + long l; + char *ep; + char *signame = argv[2]; + int newstate; + + if (!strcmp(argv[1], "ignore")) { + newstate = SS_IGNORE; + } else if (!strcmp(argv[1], "stop")) { + newstate = SS_STOP; + } else { + goto usage; + } + + l = strtol(signame, &ep, 0); + if (signame[0] == '\0' || *ep != '\0' || l < 1 || l > NSIG) { + if (!strncmp("SIG", signame, 3)) + signame += 3; + for (signum = 1; signum < NSIG; signum++) { + if (!strcmp(sys_signame[signum], signame)) + break; + } + } else { + signum = l; + } + + switch (signum) { + case SIGINT: + case SIGSTOP: + case SIGKILL: + fprintf(stderr, "%s can't be ignored\n", signame); + goto usage; + case NSIG: + fprintf(stderr, "%s is not a valid signal\n", signame); + goto usage; + default: + break; + } + + ps->ps_sigstate[signum] = newstate; + + return 0; +usage: + fprintf(stderr, "Usage: signal <ignore|stop> <signum|signame>\n"); + return 0; +} + +int +cmd_signal_show(int argc, char **argv, void *arg) +{ + struct pstate *ps = arg; + int i; + + for (i = 1; i < NSIG; i++) { + char *state; + + switch (ps->ps_sigstate[i]) { + case SS_STOP: + state = "stop"; + break; + case SS_IGNORE: + state = "ignore"; + break; + default: + state = "error"; + break; + } + printf("%2d %-6s\t%s\n", i, sys_signame[i], state); + } + + return 0; +} diff --git a/usr.bin/pmdb/symbol.c b/usr.bin/pmdb/symbol.c new file mode 100644 index 00000000000..8f6d1e6f64e --- /dev/null +++ b/usr.bin/pmdb/symbol.c @@ -0,0 +1,160 @@ +/* $PMDB: symbol.c,v 1.5 2002/03/07 14:27:08 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <err.h> + +#include "pmdb.h" +#include "symbol.h" + +/* + * Initialize the executable and the symbol table. + */ +void +sym_init_exec(struct pstate *ps, const char *name) +{ + ps->ps_sops = NULL; + ps->ps_sym_exe = NULL; + TAILQ_INIT(&ps->ps_syms); + +#ifdef PMDB_ELF + if (sym_check_elf(name, ps)) +#endif +#ifdef PMDB_AOUT + if (sym_check_aout(name, ps)) +#endif + warnx("sym_init_exec: %s is not a supported file format", name); + + if (ps->ps_sops) { + ps->ps_sym_exe = st_open(ps, name); + if (ps->ps_sym_exe) + ps->ps_sym_exe->st_flags |= ST_EXEC; + } +} + +/* + * Destroy all symbol tables. + */ +void +sym_destroy(struct pstate *ps) +{ + struct sym_table *st; + + while ((st = TAILQ_FIRST(&ps->ps_syms)) != NULL) { + TAILQ_REMOVE(&ps->ps_syms, st, st_list); + (*ps->ps_sops->sop_close)(st); + } + ps->ps_sym_exe = NULL; +} + +/* + * We have reasons to believe that the symbol tables we have are not consistent + * with the running binary. Update. + */ +void +sym_update(struct pstate *ps) +{ + (*ps->ps_sops->sop_update)(ps); +} + +char * +sym_name_and_offset(struct pstate *ps, reg pc, char *nam, size_t len, reg *off) +{ + struct sym_table *st; + int bestoffisset = 0; + reg bestoff, noff; + char *res; + + TAILQ_FOREACH(st, &ps->ps_syms, st_list) { + res = (*ps->ps_sops->sop_name_and_off)(st, pc, &noff); + if (res == NULL) + continue; + if (noff < bestoff || !bestoffisset) { + bestoffisset = 1; + bestoff = noff; + strlcpy(nam, res, len); + } + } + + if (!bestoffisset || !strcmp(nam, "_end")) + return (NULL); + + *off = bestoff; + return (nam); +} + +int +sym_lookup(struct pstate *ps, const char *name, reg *res) +{ + /* + * We let the sop do the table walking itself since it might have + * preferences about what symbols to pick (weak and stuff). + */ + return ((*ps->ps_sops->sop_lookup)(ps, name, res)); +} + +char * +sym_print(struct pstate *ps, reg pc, char *buf, size_t buflen) +{ + char namebuf[1024], *name; + reg offs; + + name = sym_name_and_offset(ps, pc, namebuf, sizeof(namebuf), &offs); + if (name == NULL) { + snprintf(buf, buflen, "0x%lx", pc); + } else { + snprintf(buf, buflen, "%s+0x%lx(0x%lx)", name, offs, pc); + } + + return (buf); +} + +/* + * Open a symbol table and install it in the list. Don't do anything if + * it's already there. + */ +struct sym_table * +st_open(struct pstate *ps, const char *name) +{ + struct sym_table *st; + + TAILQ_FOREACH(st, &ps->ps_syms, st_list) { + if (!strcmp(name, st->st_fname)) + return (st); + } + + warnx("Loading symbols from %s", name); + + if ((st = (*ps->ps_sops->sop_open)(name)) != NULL) { + TAILQ_INSERT_TAIL(&ps->ps_syms, st, st_list); + strlcpy(st->st_fname, name, sizeof(st->st_fname)); + } + + return (st); +} + diff --git a/usr.bin/pmdb/symbol.h b/usr.bin/pmdb/symbol.h new file mode 100644 index 00000000000..638e48150ff --- /dev/null +++ b/usr.bin/pmdb/symbol.h @@ -0,0 +1,61 @@ +/* $PMDB: symbol.h,v 1.7 2002/03/07 14:27:08 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 <sys/param.h> + +struct sym_table { + TAILQ_ENTRY(sym_table) st_list; + char st_fname[MAXPATHLEN]; + int st_flags; +}; + +/* Flags in st_flags */ +#define ST_EXEC 0x01 /* this is the executable */ + +struct sym_ops { + struct sym_table *(*sop_open)(const char *); + void (*sop_close)(struct sym_table *); + char *(*sop_name_and_off)(struct sym_table *, reg, reg *); + int (*sop_lookup)(struct pstate *, const char *, reg *); + void (*sop_update)(struct pstate *); +}; + +void sym_init_exec(struct pstate *, const char *); +void sym_destroy(struct pstate *); +void sym_update(struct pstate *); +char *sym_name_and_offset(struct pstate *, reg, char *, size_t, reg *); +int sym_lookup(struct pstate *, const char *, reg *); +char *sym_print(struct pstate *, reg, char *, size_t); + +/* Internal for symbol handlers only. */ +struct sym_table *st_open(struct pstate *, const char *); + +#ifdef PMDB_ELF +int sym_check_elf(const char *, struct pstate *); +#endif +#ifdef PMDB_AOUT +int sym_check_aout(const char *, struct pstate *); +#endif |