Home | History | Annotate | Line # | Download | only in dist
proc_bkpt.c revision 1.1
      1 /*
      2  * Copyright (c) 2010 The FreeBSD Foundation
      3  * All rights reserved.
      4  *
      5  * This software was developed by Rui Paulo under sponsorship from the
      6  * FreeBSD Foundation.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __FBSDID("$FreeBSD: head/lib/libproc/proc_bkpt.c 287106 2015-08-24 12:17:15Z andrew $");
     32 
     33 #include <sys/types.h>
     34 #include <sys/ptrace.h>
     35 #include <sys/wait.h>
     36 #include <machine/_inttypes.h>
     37 
     38 #include <assert.h>
     39 #include <err.h>
     40 #include <errno.h>
     41 #include <signal.h>
     42 #include <stdio.h>
     43 #include "_libproc.h"
     44 
     45 #if defined(__aarch64__)
     46 #define	AARCH64_BRK		0xd4200000
     47 #define	AARCH64_BRK_IMM16_SHIFT	5
     48 #define	AARCH64_BRK_IMM16_VAL	(0xd << AARCH64_BRK_IMM16_SHIFT)
     49 #define	BREAKPOINT_INSTR	(AARCH64_BRK | AARCH64_BRK_IMM16_VAL)
     50 #define	BREAKPOINT_INSTR_SZ	4
     51 #elif defined(__amd64__) || defined(__i386__)
     52 #define	BREAKPOINT_INSTR	0xcc	/* int 0x3 */
     53 #define	BREAKPOINT_INSTR_SZ	1
     54 #define	BREAKPOINT_ADJUST_SZ	BREAKPOINT_INSTR_SZ
     55 #elif defined(__arm__)
     56 #define	BREAKPOINT_INSTR	0xe7ffffff	/* bkpt */
     57 #define	BREAKPOINT_INSTR_SZ	4
     58 #elif defined(__mips__)
     59 #define	BREAKPOINT_INSTR	0xd	/* break */
     60 #define	BREAKPOINT_INSTR_SZ	4
     61 #elif defined(__powerpc__)
     62 #define	BREAKPOINT_INSTR	0x7fe00008	/* trap */
     63 #define	BREAKPOINT_INSTR_SZ	4
     64 #else
     65 #error "Add support for your architecture"
     66 #endif
     67 
     68 static int
     69 proc_stop(struct proc_handle *phdl)
     70 {
     71 	int status;
     72 
     73 	if (kill(proc_getpid(phdl), SIGSTOP) == -1) {
     74 		DPRINTF("kill %d", proc_getpid(phdl));
     75 		return (-1);
     76 	} else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) {
     77 		DPRINTF("waitpid %d", proc_getpid(phdl));
     78 		return (-1);
     79 	} else if (!WIFSTOPPED(status)) {
     80 		DPRINTFX("waitpid: unexpected status 0x%x", status);
     81 		return (-1);
     82 	}
     83 
     84 	return (0);
     85 }
     86 
     87 int
     88 proc_bkptset(struct proc_handle *phdl, uintptr_t address,
     89     unsigned long *saved)
     90 {
     91 	struct ptrace_io_desc piod;
     92 	unsigned long paddr, caddr;
     93 	int ret = 0, stopped;
     94 
     95 	*saved = 0;
     96 	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
     97 	    phdl->status == PS_IDLE) {
     98 		errno = ENOENT;
     99 		return (-1);
    100 	}
    101 
    102 	DPRINTFX("adding breakpoint at 0x%lx", address);
    103 
    104 	stopped = 0;
    105 	if (phdl->status != PS_STOP) {
    106 		if (proc_stop(phdl) != 0)
    107 			return (-1);
    108 		stopped = 1;
    109 	}
    110 
    111 	/*
    112 	 * Read the original instruction.
    113 	 */
    114 	caddr = address;
    115 	paddr = 0;
    116 	piod.piod_op = PIOD_READ_I;
    117 	piod.piod_offs = (void *)caddr;
    118 	piod.piod_addr = &paddr;
    119 	piod.piod_len  = BREAKPOINT_INSTR_SZ;
    120 	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
    121 		DPRINTF("ERROR: couldn't read instruction at address 0x%"
    122 		    PRIuPTR, address);
    123 		ret = -1;
    124 		goto done;
    125 	}
    126 	*saved = paddr;
    127 	/*
    128 	 * Write a breakpoint instruction to that address.
    129 	 */
    130 	caddr = address;
    131 	paddr = BREAKPOINT_INSTR;
    132 	piod.piod_op = PIOD_WRITE_I;
    133 	piod.piod_offs = (void *)caddr;
    134 	piod.piod_addr = &paddr;
    135 	piod.piod_len  = BREAKPOINT_INSTR_SZ;
    136 	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
    137 		DPRINTF("ERROR: couldn't write instruction at address 0x%"
    138 		    PRIuPTR, address);
    139 		ret = -1;
    140 		goto done;
    141 	}
    142 
    143 done:
    144 	if (stopped)
    145 		/* Restart the process if we had to stop it. */
    146 		proc_continue(phdl);
    147 
    148 	return (ret);
    149 }
    150 
    151 int
    152 proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
    153     unsigned long saved)
    154 {
    155 	struct ptrace_io_desc piod;
    156 	unsigned long paddr, caddr;
    157 	int ret = 0, stopped;
    158 
    159 	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
    160 	    phdl->status == PS_IDLE) {
    161 		errno = ENOENT;
    162 		return (-1);
    163 	}
    164 
    165 	DPRINTFX("removing breakpoint at 0x%lx", address);
    166 
    167 	stopped = 0;
    168 	if (phdl->status != PS_STOP) {
    169 		if (proc_stop(phdl) != 0)
    170 			return (-1);
    171 		stopped = 1;
    172 	}
    173 
    174 	/*
    175 	 * Overwrite the breakpoint instruction that we setup previously.
    176 	 */
    177 	caddr = address;
    178 	paddr = saved;
    179 	piod.piod_op = PIOD_WRITE_I;
    180 	piod.piod_offs = (void *)caddr;
    181 	piod.piod_addr = &paddr;
    182 	piod.piod_len  = BREAKPOINT_INSTR_SZ;
    183 	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
    184 		DPRINTF("ERROR: couldn't write instruction at address 0x%"
    185 		    PRIuPTR, address);
    186 		ret = -1;
    187 	}
    188 
    189 	if (stopped)
    190 		/* Restart the process if we had to stop it. */
    191 		proc_continue(phdl);
    192 
    193 	return (ret);
    194 }
    195 
    196 /*
    197  * Decrement pc so that we delete the breakpoint at the correct
    198  * address, i.e. at the BREAKPOINT_INSTR address.
    199  *
    200  * This is only needed on some architectures where the pc value
    201  * when reading registers points at the instruction after the
    202  * breakpoint, e.g. x86.
    203  */
    204 void
    205 proc_bkptregadj(unsigned long *pc)
    206 {
    207 
    208 	(void)pc;
    209 #ifdef BREAKPOINT_ADJUST_SZ
    210 	*pc = *pc - BREAKPOINT_ADJUST_SZ;
    211 #endif
    212 }
    213 
    214 /*
    215  * Step over the breakpoint.
    216  */
    217 int
    218 proc_bkptexec(struct proc_handle *phdl, unsigned long saved)
    219 {
    220 	unsigned long pc;
    221 	unsigned long samesaved;
    222 	int status;
    223 
    224 	if (proc_regget(phdl, REG_PC, &pc) < 0) {
    225 		DPRINTFX("ERROR: couldn't get PC register");
    226 		return (-1);
    227 	}
    228 	proc_bkptregadj(&pc);
    229 	if (proc_bkptdel(phdl, pc, saved) < 0) {
    230 		DPRINTFX("ERROR: couldn't delete breakpoint");
    231 		return (-1);
    232 	}
    233 	/*
    234 	 * Go back in time and step over the new instruction just
    235 	 * set up by proc_bkptdel().
    236 	 */
    237 	proc_regset(phdl, REG_PC, pc);
    238 	if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) {
    239 		DPRINTFX("ERROR: ptrace step failed");
    240 		return (-1);
    241 	}
    242 	proc_wstatus(phdl);
    243 	status = proc_getwstat(phdl);
    244 	if (!WIFSTOPPED(status)) {
    245 		DPRINTFX("ERROR: don't know why process stopped");
    246 		return (-1);
    247 	}
    248 	/*
    249 	 * Restore the breakpoint. The saved instruction should be
    250 	 * the same as the one that we were passed in.
    251 	 */
    252 	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
    253 		DPRINTFX("ERROR: couldn't restore breakpoint");
    254 		return (-1);
    255 	}
    256 	assert(samesaved == saved);
    257 
    258 	return (0);
    259 }
    260