1fcf9fc10SMark Johnston /*- 25e53a4f9SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 35e53a4f9SPedro F. Giffuni * 48eb20f36SRui Paulo * Copyright (c) 2010 The FreeBSD Foundation 58eb20f36SRui Paulo * All rights reserved. 68eb20f36SRui Paulo * 78eb20f36SRui Paulo * This software was developed by Rui Paulo under sponsorship from the 88eb20f36SRui Paulo * FreeBSD Foundation. 98eb20f36SRui Paulo * 108eb20f36SRui Paulo * Redistribution and use in source and binary forms, with or without 118eb20f36SRui Paulo * modification, are permitted provided that the following conditions 128eb20f36SRui Paulo * are met: 138eb20f36SRui Paulo * 1. Redistributions of source code must retain the above copyright 148eb20f36SRui Paulo * notice, this list of conditions and the following disclaimer. 158eb20f36SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 168eb20f36SRui Paulo * notice, this list of conditions and the following disclaimer in the 178eb20f36SRui Paulo * documentation and/or other materials provided with the distribution. 188eb20f36SRui Paulo * 198eb20f36SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 208eb20f36SRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218eb20f36SRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228eb20f36SRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 238eb20f36SRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 248eb20f36SRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 258eb20f36SRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 268eb20f36SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 278eb20f36SRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 288eb20f36SRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 298eb20f36SRui Paulo * SUCH DAMAGE. 308eb20f36SRui Paulo */ 318eb20f36SRui Paulo 328eb20f36SRui Paulo #include <sys/cdefs.h> 338eb20f36SRui Paulo __FBSDID("$FreeBSD$"); 348eb20f36SRui Paulo 358eb20f36SRui Paulo #include <sys/types.h> 368eb20f36SRui Paulo #include <sys/ptrace.h> 378eb20f36SRui Paulo #include <sys/wait.h> 388eb20f36SRui Paulo 398eb20f36SRui Paulo #include <assert.h> 408eb20f36SRui Paulo #include <err.h> 418eb20f36SRui Paulo #include <errno.h> 42813b2694SMark Johnston #include <signal.h> 43813b2694SMark Johnston #include <stdio.h> 44fcf9fc10SMark Johnston 458eb20f36SRui Paulo #include "_libproc.h" 468eb20f36SRui Paulo 4727e54fb5SRuslan Bukin #if defined(__aarch64__) 4827e54fb5SRuslan Bukin #define AARCH64_BRK 0xd4200000 4927e54fb5SRuslan Bukin #define AARCH64_BRK_IMM16_SHIFT 5 5027e54fb5SRuslan Bukin #define AARCH64_BRK_IMM16_VAL (0xd << AARCH64_BRK_IMM16_SHIFT) 5127e54fb5SRuslan Bukin #define BREAKPOINT_INSTR (AARCH64_BRK | AARCH64_BRK_IMM16_VAL) 5227e54fb5SRuslan Bukin #define BREAKPOINT_INSTR_SZ 4 5327e54fb5SRuslan Bukin #elif defined(__amd64__) || defined(__i386__) 548eb20f36SRui Paulo #define BREAKPOINT_INSTR 0xcc /* int 0x3 */ 558eb20f36SRui Paulo #define BREAKPOINT_INSTR_SZ 1 56e3c074a0SAndrew Turner #define BREAKPOINT_ADJUST_SZ BREAKPOINT_INSTR_SZ 5727e54fb5SRuslan Bukin #elif defined(__arm__) 5827e54fb5SRuslan Bukin #define BREAKPOINT_INSTR 0xe7ffffff /* bkpt */ 5927e54fb5SRuslan Bukin #define BREAKPOINT_INSTR_SZ 4 60f2242861SOleksandr Tymoshenko #elif defined(__mips__) 61f2242861SOleksandr Tymoshenko #define BREAKPOINT_INSTR 0xd /* break */ 62f2242861SOleksandr Tymoshenko #define BREAKPOINT_INSTR_SZ 4 63c7570492SJustin Hibbits #elif defined(__powerpc__) 64c7570492SJustin Hibbits #define BREAKPOINT_INSTR 0x7fe00008 /* trap */ 65c7570492SJustin Hibbits #define BREAKPOINT_INSTR_SZ 4 66ca20f8ecSRuslan Bukin #elif defined(__riscv) 677dd3aed9SRuslan Bukin #define BREAKPOINT_INSTR 0x00100073 /* sbreak */ 687dd3aed9SRuslan Bukin #define BREAKPOINT_INSTR_SZ 4 698eb20f36SRui Paulo #else 708eb20f36SRui Paulo #error "Add support for your architecture" 718eb20f36SRui Paulo #endif 728eb20f36SRui Paulo 731d290950SRuslan Bukin /* 741d290950SRuslan Bukin * Use 4-bytes holder for breakpoint instruction on all the platforms. 751d290950SRuslan Bukin * Works for x86 as well until it is endian-little platform. 761d290950SRuslan Bukin * (We are coping one byte only on x86 from this 4-bytes piece of 771d290950SRuslan Bukin * memory). 781d290950SRuslan Bukin */ 791d290950SRuslan Bukin typedef uint32_t instr_t; 801d290950SRuslan Bukin 81813b2694SMark Johnston static int 82813b2694SMark Johnston proc_stop(struct proc_handle *phdl) 83813b2694SMark Johnston { 84813b2694SMark Johnston int status; 85813b2694SMark Johnston 86813b2694SMark Johnston if (kill(proc_getpid(phdl), SIGSTOP) == -1) { 87813b2694SMark Johnston DPRINTF("kill %d", proc_getpid(phdl)); 88813b2694SMark Johnston return (-1); 89813b2694SMark Johnston } else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) { 90813b2694SMark Johnston DPRINTF("waitpid %d", proc_getpid(phdl)); 91813b2694SMark Johnston return (-1); 92813b2694SMark Johnston } else if (!WIFSTOPPED(status)) { 93813b2694SMark Johnston DPRINTFX("waitpid: unexpected status 0x%x", status); 94813b2694SMark Johnston return (-1); 95813b2694SMark Johnston } 96813b2694SMark Johnston 97813b2694SMark Johnston return (0); 98813b2694SMark Johnston } 99813b2694SMark Johnston 1008eb20f36SRui Paulo int 1018eb20f36SRui Paulo proc_bkptset(struct proc_handle *phdl, uintptr_t address, 1028eb20f36SRui Paulo unsigned long *saved) 1038eb20f36SRui Paulo { 1048eb20f36SRui Paulo struct ptrace_io_desc piod; 10592f92525SMark Johnston int ret = 0, stopped; 1061d290950SRuslan Bukin instr_t instr; 1078eb20f36SRui Paulo 1088eb20f36SRui Paulo *saved = 0; 1098eb20f36SRui Paulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 1108eb20f36SRui Paulo phdl->status == PS_IDLE) { 1118eb20f36SRui Paulo errno = ENOENT; 1128eb20f36SRui Paulo return (-1); 1138eb20f36SRui Paulo } 1148eb20f36SRui Paulo 115813b2694SMark Johnston DPRINTFX("adding breakpoint at 0x%lx", address); 116813b2694SMark Johnston 11792f92525SMark Johnston stopped = 0; 11892f92525SMark Johnston if (phdl->status != PS_STOP) { 119813b2694SMark Johnston if (proc_stop(phdl) != 0) 120813b2694SMark Johnston return (-1); 12192f92525SMark Johnston stopped = 1; 12292f92525SMark Johnston } 123813b2694SMark Johnston 1248eb20f36SRui Paulo /* 1258eb20f36SRui Paulo * Read the original instruction. 1268eb20f36SRui Paulo */ 1271d290950SRuslan Bukin instr = 0; 1288eb20f36SRui Paulo piod.piod_op = PIOD_READ_I; 129*0ccd9d15SBrooks Davis piod.piod_offs = (void *)address; 1301d290950SRuslan Bukin piod.piod_addr = &instr; 1318eb20f36SRui Paulo piod.piod_len = BREAKPOINT_INSTR_SZ; 1328eb20f36SRui Paulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 133fcf9fc10SMark Johnston DPRINTF("ERROR: couldn't read instruction at address 0x%jx", 134fcf9fc10SMark Johnston (uintmax_t)address); 135813b2694SMark Johnston ret = -1; 136813b2694SMark Johnston goto done; 1378eb20f36SRui Paulo } 1381d290950SRuslan Bukin *saved = instr; 1398eb20f36SRui Paulo /* 1408eb20f36SRui Paulo * Write a breakpoint instruction to that address. 1418eb20f36SRui Paulo */ 1421d290950SRuslan Bukin instr = BREAKPOINT_INSTR; 1438eb20f36SRui Paulo piod.piod_op = PIOD_WRITE_I; 144*0ccd9d15SBrooks Davis piod.piod_offs = (void *)address; 1451d290950SRuslan Bukin piod.piod_addr = &instr; 1468eb20f36SRui Paulo piod.piod_len = BREAKPOINT_INSTR_SZ; 1478eb20f36SRui Paulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 148fcf9fc10SMark Johnston DPRINTF("ERROR: couldn't write instruction at address 0x%jx", 149fcf9fc10SMark Johnston (uintmax_t)address); 150813b2694SMark Johnston ret = -1; 151813b2694SMark Johnston goto done; 1528eb20f36SRui Paulo } 1538eb20f36SRui Paulo 154813b2694SMark Johnston done: 15592f92525SMark Johnston if (stopped) 156813b2694SMark Johnston /* Restart the process if we had to stop it. */ 15792f92525SMark Johnston proc_continue(phdl); 158813b2694SMark Johnston 159813b2694SMark Johnston return (ret); 1608eb20f36SRui Paulo } 1618eb20f36SRui Paulo 1628eb20f36SRui Paulo int 1638eb20f36SRui Paulo proc_bkptdel(struct proc_handle *phdl, uintptr_t address, 1648eb20f36SRui Paulo unsigned long saved) 1658eb20f36SRui Paulo { 1668eb20f36SRui Paulo struct ptrace_io_desc piod; 16792f92525SMark Johnston int ret = 0, stopped; 1681d290950SRuslan Bukin instr_t instr; 1698eb20f36SRui Paulo 1708eb20f36SRui Paulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 1718eb20f36SRui Paulo phdl->status == PS_IDLE) { 1728eb20f36SRui Paulo errno = ENOENT; 1738eb20f36SRui Paulo return (-1); 1748eb20f36SRui Paulo } 175813b2694SMark Johnston 176813b2694SMark Johnston DPRINTFX("removing breakpoint at 0x%lx", address); 177813b2694SMark Johnston 17892f92525SMark Johnston stopped = 0; 17992f92525SMark Johnston if (phdl->status != PS_STOP) { 180813b2694SMark Johnston if (proc_stop(phdl) != 0) 181813b2694SMark Johnston return (-1); 18292f92525SMark Johnston stopped = 1; 18392f92525SMark Johnston } 184813b2694SMark Johnston 1858eb20f36SRui Paulo /* 1868eb20f36SRui Paulo * Overwrite the breakpoint instruction that we setup previously. 1878eb20f36SRui Paulo */ 1881d290950SRuslan Bukin instr = saved; 1898eb20f36SRui Paulo piod.piod_op = PIOD_WRITE_I; 190*0ccd9d15SBrooks Davis piod.piod_offs = (void *)address; 1911d290950SRuslan Bukin piod.piod_addr = &instr; 1928eb20f36SRui Paulo piod.piod_len = BREAKPOINT_INSTR_SZ; 1938eb20f36SRui Paulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 194fcf9fc10SMark Johnston DPRINTF("ERROR: couldn't write instruction at address 0x%jx", 195fcf9fc10SMark Johnston (uintmax_t)address); 196813b2694SMark Johnston ret = -1; 1978eb20f36SRui Paulo } 1988eb20f36SRui Paulo 19992f92525SMark Johnston if (stopped) 200813b2694SMark Johnston /* Restart the process if we had to stop it. */ 20192f92525SMark Johnston proc_continue(phdl); 202813b2694SMark Johnston 203813b2694SMark Johnston return (ret); 2048eb20f36SRui Paulo } 2058eb20f36SRui Paulo 2068eb20f36SRui Paulo /* 2078eb20f36SRui Paulo * Decrement pc so that we delete the breakpoint at the correct 2088eb20f36SRui Paulo * address, i.e. at the BREAKPOINT_INSTR address. 209e3c074a0SAndrew Turner * 210e3c074a0SAndrew Turner * This is only needed on some architectures where the pc value 211e3c074a0SAndrew Turner * when reading registers points at the instruction after the 212e3c074a0SAndrew Turner * breakpoint, e.g. x86. 2138eb20f36SRui Paulo */ 2148eb20f36SRui Paulo void 2158eb20f36SRui Paulo proc_bkptregadj(unsigned long *pc) 2168eb20f36SRui Paulo { 217e3c074a0SAndrew Turner 218e3c074a0SAndrew Turner (void)pc; 219e3c074a0SAndrew Turner #ifdef BREAKPOINT_ADJUST_SZ 220e3c074a0SAndrew Turner *pc = *pc - BREAKPOINT_ADJUST_SZ; 221e3c074a0SAndrew Turner #endif 2228eb20f36SRui Paulo } 2238eb20f36SRui Paulo 2248eb20f36SRui Paulo /* 2258eb20f36SRui Paulo * Step over the breakpoint. 2268eb20f36SRui Paulo */ 2278eb20f36SRui Paulo int 2288eb20f36SRui Paulo proc_bkptexec(struct proc_handle *phdl, unsigned long saved) 2298eb20f36SRui Paulo { 2308eb20f36SRui Paulo unsigned long pc; 2318eb20f36SRui Paulo unsigned long samesaved; 2328eb20f36SRui Paulo int status; 2338eb20f36SRui Paulo 2348eb20f36SRui Paulo if (proc_regget(phdl, REG_PC, &pc) < 0) { 23530e81f7eSMark Johnston DPRINTFX("ERROR: couldn't get PC register"); 2368eb20f36SRui Paulo return (-1); 2378eb20f36SRui Paulo } 2388eb20f36SRui Paulo proc_bkptregadj(&pc); 2398eb20f36SRui Paulo if (proc_bkptdel(phdl, pc, saved) < 0) { 24030e81f7eSMark Johnston DPRINTFX("ERROR: couldn't delete breakpoint"); 2418eb20f36SRui Paulo return (-1); 2428eb20f36SRui Paulo } 2438eb20f36SRui Paulo /* 2448eb20f36SRui Paulo * Go back in time and step over the new instruction just 2458eb20f36SRui Paulo * set up by proc_bkptdel(). 2468eb20f36SRui Paulo */ 2478eb20f36SRui Paulo proc_regset(phdl, REG_PC, pc); 2488eb20f36SRui Paulo if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) { 24930e81f7eSMark Johnston DPRINTFX("ERROR: ptrace step failed"); 2508eb20f36SRui Paulo return (-1); 2518eb20f36SRui Paulo } 2524c74b245SRui Paulo proc_wstatus(phdl); 2534c74b245SRui Paulo status = proc_getwstat(phdl); 2548eb20f36SRui Paulo if (!WIFSTOPPED(status)) { 25530e81f7eSMark Johnston DPRINTFX("ERROR: don't know why process stopped"); 2568eb20f36SRui Paulo return (-1); 2578eb20f36SRui Paulo } 2588eb20f36SRui Paulo /* 2598eb20f36SRui Paulo * Restore the breakpoint. The saved instruction should be 2608eb20f36SRui Paulo * the same as the one that we were passed in. 2618eb20f36SRui Paulo */ 2628eb20f36SRui Paulo if (proc_bkptset(phdl, pc, &samesaved) < 0) { 26330e81f7eSMark Johnston DPRINTFX("ERROR: couldn't restore breakpoint"); 2648eb20f36SRui Paulo return (-1); 2658eb20f36SRui Paulo } 2668eb20f36SRui Paulo assert(samesaved == saved); 2678eb20f36SRui Paulo 2688eb20f36SRui Paulo return (0); 2698eb20f36SRui Paulo } 270