1210688Srpaulo/* 2210688Srpaulo * Copyright (c) 2010 The FreeBSD Foundation 3210688Srpaulo * All rights reserved. 4210688Srpaulo * 5210688Srpaulo * This software was developed by Rui Paulo under sponsorship from the 6210688Srpaulo * FreeBSD Foundation. 7210688Srpaulo * 8210688Srpaulo * Redistribution and use in source and binary forms, with or without 9210688Srpaulo * modification, are permitted provided that the following conditions 10210688Srpaulo * are met: 11210688Srpaulo * 1. Redistributions of source code must retain the above copyright 12210688Srpaulo * notice, this list of conditions and the following disclaimer. 13210688Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 14210688Srpaulo * notice, this list of conditions and the following disclaimer in the 15210688Srpaulo * documentation and/or other materials provided with the distribution. 16210688Srpaulo * 17210688Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18210688Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19210688Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20210688Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21210688Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22210688Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23210688Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24210688Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25210688Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26210688Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27210688Srpaulo * SUCH DAMAGE. 28210688Srpaulo */ 29210688Srpaulo 30210688Srpaulo#include <sys/cdefs.h> 31210688Srpaulo__FBSDID("$FreeBSD$"); 32210688Srpaulo 33210688Srpaulo#include <sys/types.h> 34210688Srpaulo#include <sys/ptrace.h> 35210688Srpaulo#include <sys/wait.h> 36210688Srpaulo#include <machine/_inttypes.h> 37210688Srpaulo 38210688Srpaulo#include <assert.h> 39210688Srpaulo#include <err.h> 40259969Smarkj#include <errno.h> 41259969Smarkj#include <signal.h> 42210688Srpaulo#include <stdio.h> 43210688Srpaulo#include "_libproc.h" 44210688Srpaulo 45210688Srpaulo#if defined(__i386__) || defined(__amd64__) 46210688Srpaulo#define BREAKPOINT_INSTR 0xcc /* int 0x3 */ 47210688Srpaulo#define BREAKPOINT_INSTR_SZ 1 48233402Sgonzo#elif defined(__mips__) 49233402Sgonzo#define BREAKPOINT_INSTR 0xd /* break */ 50233402Sgonzo#define BREAKPOINT_INSTR_SZ 4 51242723Sjhibbits#elif defined(__powerpc__) 52242723Sjhibbits#define BREAKPOINT_INSTR 0x7fe00008 /* trap */ 53242723Sjhibbits#define BREAKPOINT_INSTR_SZ 4 54210688Srpaulo#else 55210688Srpaulo#error "Add support for your architecture" 56210688Srpaulo#endif 57210688Srpaulo 58259969Smarkjstatic int 59259969Smarkjproc_stop(struct proc_handle *phdl) 60259969Smarkj{ 61259969Smarkj int status; 62259969Smarkj 63259969Smarkj if (kill(proc_getpid(phdl), SIGSTOP) == -1) { 64259969Smarkj DPRINTF("kill %d", proc_getpid(phdl)); 65259969Smarkj return (-1); 66259969Smarkj } else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) { 67259969Smarkj DPRINTF("waitpid %d", proc_getpid(phdl)); 68259969Smarkj return (-1); 69259969Smarkj } else if (!WIFSTOPPED(status)) { 70259969Smarkj DPRINTFX("waitpid: unexpected status 0x%x", status); 71259969Smarkj return (-1); 72259969Smarkj } 73259969Smarkj 74259969Smarkj return (0); 75259969Smarkj} 76259969Smarkj 77210688Srpauloint 78210688Srpauloproc_bkptset(struct proc_handle *phdl, uintptr_t address, 79210688Srpaulo unsigned long *saved) 80210688Srpaulo{ 81210688Srpaulo struct ptrace_io_desc piod; 82210688Srpaulo unsigned long paddr, caddr; 83269754Smarkj int ret = 0, stopped; 84210688Srpaulo 85210688Srpaulo *saved = 0; 86210688Srpaulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 87210688Srpaulo phdl->status == PS_IDLE) { 88210688Srpaulo errno = ENOENT; 89210688Srpaulo return (-1); 90210688Srpaulo } 91210688Srpaulo 92259969Smarkj DPRINTFX("adding breakpoint at 0x%lx", address); 93259969Smarkj 94269754Smarkj stopped = 0; 95269754Smarkj if (phdl->status != PS_STOP) { 96259969Smarkj if (proc_stop(phdl) != 0) 97259969Smarkj return (-1); 98269754Smarkj stopped = 1; 99269754Smarkj } 100259969Smarkj 101210688Srpaulo /* 102210688Srpaulo * Read the original instruction. 103210688Srpaulo */ 104210688Srpaulo caddr = address; 105210688Srpaulo paddr = 0; 106210688Srpaulo piod.piod_op = PIOD_READ_I; 107210688Srpaulo piod.piod_offs = (void *)caddr; 108210688Srpaulo piod.piod_addr = &paddr; 109210688Srpaulo piod.piod_len = BREAKPOINT_INSTR_SZ; 110210688Srpaulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 111259895Smarkj DPRINTF("ERROR: couldn't read instruction at address 0x%" 112259895Smarkj PRIuPTR, address); 113259969Smarkj ret = -1; 114259969Smarkj goto done; 115210688Srpaulo } 116210688Srpaulo *saved = paddr; 117210688Srpaulo /* 118210688Srpaulo * Write a breakpoint instruction to that address. 119210688Srpaulo */ 120210688Srpaulo caddr = address; 121210688Srpaulo paddr = BREAKPOINT_INSTR; 122210688Srpaulo piod.piod_op = PIOD_WRITE_I; 123210688Srpaulo piod.piod_offs = (void *)caddr; 124210688Srpaulo piod.piod_addr = &paddr; 125210688Srpaulo piod.piod_len = BREAKPOINT_INSTR_SZ; 126210688Srpaulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 127259895Smarkj DPRINTF("ERROR: couldn't write instruction at address 0x%" 128259895Smarkj PRIuPTR, address); 129259969Smarkj ret = -1; 130259969Smarkj goto done; 131210688Srpaulo } 132210688Srpaulo 133259969Smarkjdone: 134269754Smarkj if (stopped) 135259969Smarkj /* Restart the process if we had to stop it. */ 136269754Smarkj proc_continue(phdl); 137259969Smarkj 138259969Smarkj return (ret); 139210688Srpaulo} 140210688Srpaulo 141210688Srpauloint 142210688Srpauloproc_bkptdel(struct proc_handle *phdl, uintptr_t address, 143210688Srpaulo unsigned long saved) 144210688Srpaulo{ 145210688Srpaulo struct ptrace_io_desc piod; 146210688Srpaulo unsigned long paddr, caddr; 147269754Smarkj int ret = 0, stopped; 148210688Srpaulo 149210688Srpaulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 150210688Srpaulo phdl->status == PS_IDLE) { 151210688Srpaulo errno = ENOENT; 152210688Srpaulo return (-1); 153210688Srpaulo } 154259969Smarkj 155259969Smarkj DPRINTFX("removing breakpoint at 0x%lx", address); 156259969Smarkj 157269754Smarkj stopped = 0; 158269754Smarkj if (phdl->status != PS_STOP) { 159259969Smarkj if (proc_stop(phdl) != 0) 160259969Smarkj return (-1); 161269754Smarkj stopped = 1; 162269754Smarkj } 163259969Smarkj 164210688Srpaulo /* 165210688Srpaulo * Overwrite the breakpoint instruction that we setup previously. 166210688Srpaulo */ 167210688Srpaulo caddr = address; 168210688Srpaulo paddr = saved; 169210688Srpaulo piod.piod_op = PIOD_WRITE_I; 170210688Srpaulo piod.piod_offs = (void *)caddr; 171210688Srpaulo piod.piod_addr = &paddr; 172210688Srpaulo piod.piod_len = BREAKPOINT_INSTR_SZ; 173210688Srpaulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 174259895Smarkj DPRINTF("ERROR: couldn't write instruction at address 0x%" 175259895Smarkj PRIuPTR, address); 176259969Smarkj ret = -1; 177210688Srpaulo } 178259969Smarkj 179269754Smarkj if (stopped) 180259969Smarkj /* Restart the process if we had to stop it. */ 181269754Smarkj proc_continue(phdl); 182210688Srpaulo 183259969Smarkj return (ret); 184210688Srpaulo} 185210688Srpaulo 186210688Srpaulo/* 187210688Srpaulo * Decrement pc so that we delete the breakpoint at the correct 188210688Srpaulo * address, i.e. at the BREAKPOINT_INSTR address. 189210688Srpaulo */ 190210688Srpaulovoid 191210688Srpauloproc_bkptregadj(unsigned long *pc) 192210688Srpaulo{ 193210688Srpaulo *pc = *pc - BREAKPOINT_INSTR_SZ; 194210688Srpaulo} 195210688Srpaulo 196210688Srpaulo/* 197210688Srpaulo * Step over the breakpoint. 198210688Srpaulo */ 199210688Srpauloint 200210688Srpauloproc_bkptexec(struct proc_handle *phdl, unsigned long saved) 201210688Srpaulo{ 202210688Srpaulo unsigned long pc; 203210688Srpaulo unsigned long samesaved; 204210688Srpaulo int status; 205210688Srpaulo 206210688Srpaulo if (proc_regget(phdl, REG_PC, &pc) < 0) { 207259895Smarkj DPRINTFX("ERROR: couldn't get PC register"); 208210688Srpaulo return (-1); 209210688Srpaulo } 210210688Srpaulo proc_bkptregadj(&pc); 211210688Srpaulo if (proc_bkptdel(phdl, pc, saved) < 0) { 212259895Smarkj DPRINTFX("ERROR: couldn't delete breakpoint"); 213210688Srpaulo return (-1); 214210688Srpaulo } 215210688Srpaulo /* 216210688Srpaulo * Go back in time and step over the new instruction just 217210688Srpaulo * set up by proc_bkptdel(). 218210688Srpaulo */ 219210688Srpaulo proc_regset(phdl, REG_PC, pc); 220210688Srpaulo if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) { 221259895Smarkj DPRINTFX("ERROR: ptrace step failed"); 222210688Srpaulo return (-1); 223210688Srpaulo } 224211184Srpaulo proc_wstatus(phdl); 225211184Srpaulo status = proc_getwstat(phdl); 226210688Srpaulo if (!WIFSTOPPED(status)) { 227259895Smarkj DPRINTFX("ERROR: don't know why process stopped"); 228210688Srpaulo return (-1); 229210688Srpaulo } 230210688Srpaulo /* 231210688Srpaulo * Restore the breakpoint. The saved instruction should be 232210688Srpaulo * the same as the one that we were passed in. 233210688Srpaulo */ 234210688Srpaulo if (proc_bkptset(phdl, pc, &samesaved) < 0) { 235259895Smarkj DPRINTFX("ERROR: couldn't restore breakpoint"); 236210688Srpaulo return (-1); 237210688Srpaulo } 238210688Srpaulo assert(samesaved == saved); 239210688Srpaulo 240210688Srpaulo return (0); 241210688Srpaulo} 242