Home | History | Annotate | Line # | Download | only in dist
proc_bkpt.c revision 1.4
      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 #ifdef __FBSDID
     32 __FBSDID("$FreeBSD: head/lib/libproc/proc_bkpt.c 287106 2015-08-24 12:17:15Z andrew $");
     33 #else
     34 __RCSID("$NetBSD: proc_bkpt.c,v 1.4 2015/09/25 19:09:38 christos Exp $");
     35 #endif
     36 
     37 #include <sys/types.h>
     38 #include <sys/ptrace.h>
     39 #include <sys/wait.h>
     40 
     41 #include <assert.h>
     42 #include <string.h>
     43 #include <err.h>
     44 #include <errno.h>
     45 #include <inttypes.h>
     46 #include <signal.h>
     47 #include <stdio.h>
     48 #include "_libproc.h"
     49 
     50 #ifndef PTRACE_BREAKPOINT
     51 #error "Add support for your architecture"
     52 #endif
     53 
     54 static int
     55 proc_stop(struct proc_handle *phdl)
     56 {
     57 	int status;
     58 
     59 	if (kill(proc_getpid(phdl), SIGSTOP) == -1) {
     60 		DPRINTF("kill %d", proc_getpid(phdl));
     61 		return (-1);
     62 	} else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) {
     63 		DPRINTF("waitpid %d", proc_getpid(phdl));
     64 		return (-1);
     65 	} else if (!WIFSTOPPED(status)) {
     66 		DPRINTFX("waitpid: unexpected status 0x%x", status);
     67 		return (-1);
     68 	}
     69 
     70 	return (0);
     71 }
     72 
     73 int
     74 proc_bkptset(struct proc_handle *phdl, uintptr_t address,
     75     proc_breakpoint_t *saved)
     76 {
     77 	struct ptrace_io_desc piod;
     78 	unsigned long paddr, caddr;
     79 	int ret = 0, stopped;
     80 	proc_breakpoint_t copy;
     81 
     82 	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
     83 	    phdl->status == PS_IDLE) {
     84 		errno = ENOENT;
     85 		return (-1);
     86 	}
     87 
     88 	DPRINTFX("adding breakpoint at 0x%lx", address);
     89 
     90 	stopped = 0;
     91 	if (phdl->status != PS_STOP) {
     92 		if (proc_stop(phdl) != 0)
     93 			return (-1);
     94 		stopped = 1;
     95 	}
     96 
     97 	/*
     98 	 * Read the original instruction.
     99 	 */
    100 	caddr = address;
    101 	paddr = 0;
    102 	piod.piod_op = PIOD_READ_I;
    103 	piod.piod_offs = (void *)caddr;
    104 	piod.piod_addr = (void *)saved->data;
    105 	piod.piod_len  = sizeof(saved->data);
    106 	if (ptrace(PT_IO, proc_getpid(phdl), &piod, 0) < 0) {
    107 		DPRINTF("ERROR: couldn't read instruction at address 0x%"
    108 		    PRIuPTR, address);
    109 		ret = -1;
    110 		goto done;
    111 	}
    112 	/*
    113 	 * Write a breakpoint instruction to that address.
    114 	 */
    115 	caddr = address;
    116 	piod.piod_op = PIOD_WRITE_I;
    117 	piod.piod_offs = (void *)caddr;
    118 	piod.piod_addr = (void *)PTRACE_BREAKPOINT;
    119 	piod.piod_len  = sizeof(PTRACE_BREAKPOINT);
    120 	if (ptrace(PT_IO, proc_getpid(phdl), &piod, 0) < 0) {
    121 		DPRINTF("ERROR: couldn't write instruction at address 0x%"
    122 		    PRIuPTR, address);
    123 		ret = -1;
    124 		goto done;
    125 	}
    126 
    127 done:
    128 	if (stopped)
    129 		/* Restart the process if we had to stop it. */
    130 		proc_continue(phdl);
    131 
    132 	return (ret);
    133 }
    134 
    135 int
    136 proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
    137     proc_breakpoint_t *saved)
    138 {
    139 	struct ptrace_io_desc piod;
    140 	unsigned long paddr, caddr;
    141 	int ret = 0, stopped;
    142 
    143 	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
    144 	    phdl->status == PS_IDLE) {
    145 		errno = ENOENT;
    146 		return (-1);
    147 	}
    148 
    149 	DPRINTFX("removing breakpoint at 0x%lx", address);
    150 
    151 	stopped = 0;
    152 	if (phdl->status != PS_STOP) {
    153 		if (proc_stop(phdl) != 0)
    154 			return (-1);
    155 		stopped = 1;
    156 	}
    157 
    158 	/*
    159 	 * Overwrite the breakpoint instruction that we setup previously.
    160 	 */
    161 	caddr = address;
    162 	piod.piod_op = PIOD_WRITE_I;
    163 	piod.piod_offs = (void *)caddr;
    164 	piod.piod_addr = (void *)saved->data;
    165 	piod.piod_len  = sizeof(saved->data);
    166 	if (ptrace(PT_IO, proc_getpid(phdl), &piod, 0) < 0) {
    167 		DPRINTF("ERROR: couldn't write instruction at address 0x%"
    168 		    PRIuPTR, address);
    169 		ret = -1;
    170 	}
    171 
    172 	if (stopped)
    173 		/* Restart the process if we had to stop it. */
    174 		proc_continue(phdl);
    175 
    176 	return (ret);
    177 }
    178 
    179 /*
    180  * Decrement pc so that we delete the breakpoint at the correct
    181  * address, i.e. at the breakpoint instruction address.
    182  *
    183  * This is only needed on some architectures where the pc value
    184  * when reading registers points at the instruction after the
    185  * breakpoint, e.g. x86.
    186  */
    187 void
    188 proc_bkptregadj(unsigned long *pc)
    189 {
    190 
    191 	(void)pc;
    192 #ifdef PTRACE_BREAKPOINT_ADJ
    193 	*pc = *pc - PTRACE_BREAKPOINT_ADJ;
    194 #endif
    195 }
    196 
    197 /*
    198  * Step over the breakpoint.
    199  */
    200 int
    201 proc_bkptexec(struct proc_handle *phdl, proc_breakpoint_t *saved)
    202 {
    203 	unsigned long pc;
    204 	proc_breakpoint_t samesaved;
    205 	int status;
    206 
    207 	if (proc_regget(phdl, REG_PC, &pc) < 0) {
    208 		DPRINTFX("ERROR: couldn't get PC register");
    209 		return (-1);
    210 	}
    211 	proc_bkptregadj(&pc);
    212 	if (proc_bkptdel(phdl, pc, saved) < 0) {
    213 		DPRINTFX("ERROR: couldn't delete breakpoint");
    214 		return (-1);
    215 	}
    216 	/*
    217 	 * Go back in time and step over the new instruction just
    218 	 * set up by proc_bkptdel().
    219 	 */
    220 	proc_regset(phdl, REG_PC, pc);
    221 	if (ptrace(PT_STEP, proc_getpid(phdl), (void *)(intptr_t)1, 0) < 0) {
    222 		DPRINTFX("ERROR: ptrace step failed");
    223 		return (-1);
    224 	}
    225 	proc_wstatus(phdl);
    226 	status = proc_getwstat(phdl);
    227 	if (!WIFSTOPPED(status)) {
    228 		DPRINTFX("ERROR: don't know why process stopped");
    229 		return (-1);
    230 	}
    231 	/*
    232 	 * Restore the breakpoint. The saved instruction should be
    233 	 * the same as the one that we were passed in.
    234 	 */
    235 	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
    236 		DPRINTFX("ERROR: couldn't restore breakpoint");
    237 		return (-1);
    238 	}
    239 
    240 	assert(memcmp(saved, &samesaved, sizeof(samesaved)) == 0);
    241 
    242 	return (0);
    243 }
    244