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