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