1316713Smarkj/*- 2316713Smarkj * Copyright (c) 2010 The FreeBSD Foundation 3316713Smarkj * All rights reserved. 4316713Smarkj * 5210688Srpaulo * This software was developed by Rui Paulo under sponsorship from the 6316713Smarkj * FreeBSD Foundation. 7316713Smarkj * 8316713Smarkj * Redistribution and use in source and binary forms, with or without 9316713Smarkj * modification, are permitted provided that the following conditions 10316713Smarkj * are met: 11316713Smarkj * 1. Redistributions of source code must retain the above copyright 12316713Smarkj * notice, this list of conditions and the following disclaimer. 13316713Smarkj * 2. Redistributions in binary form must reproduce the above copyright 14316713Smarkj * notice, this list of conditions and the following disclaimer in the 15316713Smarkj * documentation and/or other materials provided with the distribution. 16316713Smarkj * 17316713Smarkj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18316713Smarkj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19316713Smarkj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20316713Smarkj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21316713Smarkj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22316713Smarkj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23316713Smarkj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24316713Smarkj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25316713Smarkj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26316713Smarkj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27316713Smarkj * SUCH DAMAGE. 28316713Smarkj */ 29210688Srpaulo 30210688Srpaulo#include <sys/cdefs.h> 31210688Srpaulo__FBSDID("$FreeBSD: stable/11/lib/libproc/proc_bkpt.c 316713 2017-04-11 17:36:19Z markj $"); 32210688Srpaulo 33210688Srpaulo#include <sys/types.h> 34210688Srpaulo#include <sys/ptrace.h> 35210688Srpaulo#include <sys/wait.h> 36210688Srpaulo 37210688Srpaulo#include <assert.h> 38210688Srpaulo#include <err.h> 39257670Smarkj#include <errno.h> 40257670Smarkj#include <signal.h> 41210688Srpaulo#include <stdio.h> 42316713Smarkj 43210688Srpaulo#include "_libproc.h" 44210688Srpaulo 45285003Sbr#if defined(__aarch64__) 46285003Sbr#define AARCH64_BRK 0xd4200000 47285003Sbr#define AARCH64_BRK_IMM16_SHIFT 5 48285003Sbr#define AARCH64_BRK_IMM16_VAL (0xd << AARCH64_BRK_IMM16_SHIFT) 49285003Sbr#define BREAKPOINT_INSTR (AARCH64_BRK | AARCH64_BRK_IMM16_VAL) 50285003Sbr#define BREAKPOINT_INSTR_SZ 4 51285003Sbr#elif defined(__amd64__) || defined(__i386__) 52285003Sbr#define BREAKPOINT_INSTR 0xcc /* int 0x3 */ 53210688Srpaulo#define BREAKPOINT_INSTR_SZ 1 54287106Sandrew#define BREAKPOINT_ADJUST_SZ BREAKPOINT_INSTR_SZ 55285003Sbr#elif defined(__arm__) 56285003Sbr#define BREAKPOINT_INSTR 0xe7ffffff /* bkpt */ 57285003Sbr#define BREAKPOINT_INSTR_SZ 4 58233402Sgonzo#elif defined(__mips__) 59285003Sbr#define BREAKPOINT_INSTR 0xd /* break */ 60233402Sgonzo#define BREAKPOINT_INSTR_SZ 4 61242723Sjhibbits#elif defined(__powerpc__) 62285003Sbr#define BREAKPOINT_INSTR 0x7fe00008 /* trap */ 63285003Sbr#define BREAKPOINT_INSTR_SZ 4 64294662Sbr#elif defined(__riscv__) 65294662Sbr#define BREAKPOINT_INSTR 0x00100073 /* sbreak */ 66294662Sbr#define BREAKPOINT_INSTR_SZ 4 67210688Srpaulo#else 68210688Srpaulo#error "Add support for your architecture" 69210688Srpaulo#endif 70210688Srpaulo 71257670Smarkjstatic int 72257670Smarkjproc_stop(struct proc_handle *phdl) 73257670Smarkj{ 74257670Smarkj int status; 75257670Smarkj 76257670Smarkj if (kill(proc_getpid(phdl), SIGSTOP) == -1) { 77257670Smarkj DPRINTF("kill %d", proc_getpid(phdl)); 78257670Smarkj return (-1); 79257670Smarkj } else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) { 80257670Smarkj DPRINTF("waitpid %d", proc_getpid(phdl)); 81257670Smarkj return (-1); 82257670Smarkj } else if (!WIFSTOPPED(status)) { 83257670Smarkj DPRINTFX("waitpid: unexpected status 0x%x", status); 84257670Smarkj return (-1); 85257670Smarkj } 86257670Smarkj 87257670Smarkj return (0); 88257670Smarkj} 89257670Smarkj 90210688Srpauloint 91210688Srpauloproc_bkptset(struct proc_handle *phdl, uintptr_t address, 92210688Srpaulo unsigned long *saved) 93210688Srpaulo{ 94210688Srpaulo struct ptrace_io_desc piod; 95210688Srpaulo unsigned long paddr, caddr; 96265308Smarkj int ret = 0, stopped; 97210688Srpaulo 98210688Srpaulo *saved = 0; 99210688Srpaulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 100210688Srpaulo phdl->status == PS_IDLE) { 101210688Srpaulo errno = ENOENT; 102210688Srpaulo return (-1); 103210688Srpaulo } 104210688Srpaulo 105257670Smarkj DPRINTFX("adding breakpoint at 0x%lx", address); 106257670Smarkj 107265308Smarkj stopped = 0; 108265308Smarkj if (phdl->status != PS_STOP) { 109257670Smarkj if (proc_stop(phdl) != 0) 110257670Smarkj return (-1); 111265308Smarkj stopped = 1; 112265308Smarkj } 113257670Smarkj 114210688Srpaulo /* 115210688Srpaulo * Read the original instruction. 116210688Srpaulo */ 117210688Srpaulo caddr = address; 118210688Srpaulo paddr = 0; 119210688Srpaulo piod.piod_op = PIOD_READ_I; 120210688Srpaulo piod.piod_offs = (void *)caddr; 121210688Srpaulo piod.piod_addr = &paddr; 122210688Srpaulo piod.piod_len = BREAKPOINT_INSTR_SZ; 123210688Srpaulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 124316713Smarkj DPRINTF("ERROR: couldn't read instruction at address 0x%jx", 125316713Smarkj (uintmax_t)address); 126257670Smarkj ret = -1; 127257670Smarkj goto done; 128210688Srpaulo } 129210688Srpaulo *saved = paddr; 130210688Srpaulo /* 131210688Srpaulo * Write a breakpoint instruction to that address. 132210688Srpaulo */ 133210688Srpaulo caddr = address; 134210688Srpaulo paddr = BREAKPOINT_INSTR; 135210688Srpaulo piod.piod_op = PIOD_WRITE_I; 136210688Srpaulo piod.piod_offs = (void *)caddr; 137210688Srpaulo piod.piod_addr = &paddr; 138210688Srpaulo piod.piod_len = BREAKPOINT_INSTR_SZ; 139210688Srpaulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 140316713Smarkj DPRINTF("ERROR: couldn't write instruction at address 0x%jx", 141316713Smarkj (uintmax_t)address); 142257670Smarkj ret = -1; 143257670Smarkj goto done; 144210688Srpaulo } 145210688Srpaulo 146257670Smarkjdone: 147265308Smarkj if (stopped) 148257670Smarkj /* Restart the process if we had to stop it. */ 149265308Smarkj proc_continue(phdl); 150257670Smarkj 151257670Smarkj return (ret); 152210688Srpaulo} 153210688Srpaulo 154210688Srpauloint 155210688Srpauloproc_bkptdel(struct proc_handle *phdl, uintptr_t address, 156210688Srpaulo unsigned long saved) 157210688Srpaulo{ 158210688Srpaulo struct ptrace_io_desc piod; 159210688Srpaulo unsigned long paddr, caddr; 160265308Smarkj int ret = 0, stopped; 161210688Srpaulo 162210688Srpaulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 163210688Srpaulo phdl->status == PS_IDLE) { 164210688Srpaulo errno = ENOENT; 165210688Srpaulo return (-1); 166210688Srpaulo } 167257670Smarkj 168257670Smarkj DPRINTFX("removing breakpoint at 0x%lx", address); 169257670Smarkj 170265308Smarkj stopped = 0; 171265308Smarkj if (phdl->status != PS_STOP) { 172257670Smarkj if (proc_stop(phdl) != 0) 173257670Smarkj return (-1); 174265308Smarkj stopped = 1; 175265308Smarkj } 176257670Smarkj 177210688Srpaulo /* 178210688Srpaulo * Overwrite the breakpoint instruction that we setup previously. 179210688Srpaulo */ 180210688Srpaulo caddr = address; 181210688Srpaulo paddr = saved; 182210688Srpaulo piod.piod_op = PIOD_WRITE_I; 183210688Srpaulo piod.piod_offs = (void *)caddr; 184210688Srpaulo piod.piod_addr = &paddr; 185210688Srpaulo piod.piod_len = BREAKPOINT_INSTR_SZ; 186210688Srpaulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 187316713Smarkj DPRINTF("ERROR: couldn't write instruction at address 0x%jx", 188316713Smarkj (uintmax_t)address); 189257670Smarkj ret = -1; 190210688Srpaulo } 191257670Smarkj 192265308Smarkj if (stopped) 193257670Smarkj /* Restart the process if we had to stop it. */ 194265308Smarkj proc_continue(phdl); 195316713Smarkj 196257670Smarkj return (ret); 197210688Srpaulo} 198210688Srpaulo 199210688Srpaulo/* 200210688Srpaulo * Decrement pc so that we delete the breakpoint at the correct 201210688Srpaulo * address, i.e. at the BREAKPOINT_INSTR address. 202287106Sandrew * 203287106Sandrew * This is only needed on some architectures where the pc value 204287106Sandrew * when reading registers points at the instruction after the 205287106Sandrew * breakpoint, e.g. x86. 206210688Srpaulo */ 207210688Srpaulovoid 208210688Srpauloproc_bkptregadj(unsigned long *pc) 209210688Srpaulo{ 210287106Sandrew 211287106Sandrew (void)pc; 212287106Sandrew#ifdef BREAKPOINT_ADJUST_SZ 213287106Sandrew *pc = *pc - BREAKPOINT_ADJUST_SZ; 214287106Sandrew#endif 215210688Srpaulo} 216210688Srpaulo 217210688Srpaulo/* 218210688Srpaulo * Step over the breakpoint. 219210688Srpaulo */ 220210688Srpauloint 221210688Srpauloproc_bkptexec(struct proc_handle *phdl, unsigned long saved) 222210688Srpaulo{ 223210688Srpaulo unsigned long pc; 224210688Srpaulo unsigned long samesaved; 225210688Srpaulo int status; 226210688Srpaulo 227210688Srpaulo if (proc_regget(phdl, REG_PC, &pc) < 0) { 228257222Smarkj DPRINTFX("ERROR: couldn't get PC register"); 229210688Srpaulo return (-1); 230210688Srpaulo } 231210688Srpaulo proc_bkptregadj(&pc); 232210688Srpaulo if (proc_bkptdel(phdl, pc, saved) < 0) { 233257222Smarkj DPRINTFX("ERROR: couldn't delete breakpoint"); 234210688Srpaulo return (-1); 235210688Srpaulo } 236210688Srpaulo /* 237210688Srpaulo * Go back in time and step over the new instruction just 238210688Srpaulo * set up by proc_bkptdel(). 239210688Srpaulo */ 240210688Srpaulo proc_regset(phdl, REG_PC, pc); 241210688Srpaulo if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) { 242257222Smarkj DPRINTFX("ERROR: ptrace step failed"); 243210688Srpaulo return (-1); 244210688Srpaulo } 245211184Srpaulo proc_wstatus(phdl); 246211184Srpaulo status = proc_getwstat(phdl); 247210688Srpaulo if (!WIFSTOPPED(status)) { 248257222Smarkj DPRINTFX("ERROR: don't know why process stopped"); 249210688Srpaulo return (-1); 250210688Srpaulo } 251210688Srpaulo /* 252210688Srpaulo * Restore the breakpoint. The saved instruction should be 253210688Srpaulo * the same as the one that we were passed in. 254210688Srpaulo */ 255210688Srpaulo if (proc_bkptset(phdl, pc, &samesaved) < 0) { 256257222Smarkj DPRINTFX("ERROR: couldn't restore breakpoint"); 257210688Srpaulo return (-1); 258210688Srpaulo } 259210688Srpaulo assert(samesaved == saved); 260210688Srpaulo 261210688Srpaulo return (0); 262210688Srpaulo} 263