18eb20f36SRui Paulo /* 28eb20f36SRui Paulo * Copyright (c) 2010 The FreeBSD Foundation 38eb20f36SRui Paulo * All rights reserved. 48eb20f36SRui Paulo * 58eb20f36SRui Paulo * This software was developed by Rui Paulo under sponsorship from the 68eb20f36SRui Paulo * FreeBSD Foundation. 78eb20f36SRui Paulo * 88eb20f36SRui Paulo * Redistribution and use in source and binary forms, with or without 98eb20f36SRui Paulo * modification, are permitted provided that the following conditions 108eb20f36SRui Paulo * are met: 118eb20f36SRui Paulo * 1. Redistributions of source code must retain the above copyright 128eb20f36SRui Paulo * notice, this list of conditions and the following disclaimer. 138eb20f36SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 148eb20f36SRui Paulo * notice, this list of conditions and the following disclaimer in the 158eb20f36SRui Paulo * documentation and/or other materials provided with the distribution. 168eb20f36SRui Paulo * 178eb20f36SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 188eb20f36SRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 198eb20f36SRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 208eb20f36SRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 218eb20f36SRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 228eb20f36SRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 238eb20f36SRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 248eb20f36SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 258eb20f36SRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 268eb20f36SRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 278eb20f36SRui Paulo * SUCH DAMAGE. 288eb20f36SRui Paulo */ 298eb20f36SRui Paulo 308eb20f36SRui Paulo #include <sys/cdefs.h> 318eb20f36SRui Paulo __FBSDID("$FreeBSD$"); 328eb20f36SRui Paulo 338eb20f36SRui Paulo #include <sys/types.h> 348eb20f36SRui Paulo #include <sys/ptrace.h> 358eb20f36SRui Paulo #include <sys/wait.h> 368eb20f36SRui Paulo #include <machine/_inttypes.h> 378eb20f36SRui Paulo 388eb20f36SRui Paulo #include <assert.h> 398eb20f36SRui Paulo #include <err.h> 408eb20f36SRui Paulo #include <stdio.h> 418eb20f36SRui Paulo #include <errno.h> 428eb20f36SRui Paulo #include "_libproc.h" 438eb20f36SRui Paulo 448eb20f36SRui Paulo #if defined(__i386__) || defined(__amd64__) 458eb20f36SRui Paulo #define BREAKPOINT_INSTR 0xcc /* int 0x3 */ 468eb20f36SRui Paulo #define BREAKPOINT_INSTR_SZ 1 47f2242861SOleksandr Tymoshenko #elif defined(__mips__) 48f2242861SOleksandr Tymoshenko #define BREAKPOINT_INSTR 0xd /* break */ 49f2242861SOleksandr Tymoshenko #define BREAKPOINT_INSTR_SZ 4 50*c7570492SJustin Hibbits #elif defined(__powerpc__) 51*c7570492SJustin Hibbits #define BREAKPOINT_INSTR 0x7fe00008 /* trap */ 52*c7570492SJustin Hibbits #define BREAKPOINT_INSTR_SZ 4 538eb20f36SRui Paulo #else 548eb20f36SRui Paulo #error "Add support for your architecture" 558eb20f36SRui Paulo #endif 568eb20f36SRui Paulo 578eb20f36SRui Paulo int 588eb20f36SRui Paulo proc_bkptset(struct proc_handle *phdl, uintptr_t address, 598eb20f36SRui Paulo unsigned long *saved) 608eb20f36SRui Paulo { 618eb20f36SRui Paulo struct ptrace_io_desc piod; 628eb20f36SRui Paulo unsigned long paddr, caddr; 638eb20f36SRui Paulo 648eb20f36SRui Paulo *saved = 0; 658eb20f36SRui Paulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 668eb20f36SRui Paulo phdl->status == PS_IDLE) { 678eb20f36SRui Paulo errno = ENOENT; 688eb20f36SRui Paulo return (-1); 698eb20f36SRui Paulo } 708eb20f36SRui Paulo 718eb20f36SRui Paulo /* 728eb20f36SRui Paulo * Read the original instruction. 738eb20f36SRui Paulo */ 748eb20f36SRui Paulo caddr = address; 758eb20f36SRui Paulo paddr = 0; 768eb20f36SRui Paulo piod.piod_op = PIOD_READ_I; 778eb20f36SRui Paulo piod.piod_offs = (void *)caddr; 788eb20f36SRui Paulo piod.piod_addr = &paddr; 798eb20f36SRui Paulo piod.piod_len = BREAKPOINT_INSTR_SZ; 808eb20f36SRui Paulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 818eb20f36SRui Paulo DPRINTF("ERROR: couldn't read instruction at address 0x%" PRIuPTR, 828eb20f36SRui Paulo address); 838eb20f36SRui Paulo return (-1); 848eb20f36SRui Paulo } 858eb20f36SRui Paulo *saved = paddr; 868eb20f36SRui Paulo /* 878eb20f36SRui Paulo * Write a breakpoint instruction to that address. 888eb20f36SRui Paulo */ 898eb20f36SRui Paulo caddr = address; 908eb20f36SRui Paulo paddr = BREAKPOINT_INSTR; 918eb20f36SRui Paulo piod.piod_op = PIOD_WRITE_I; 928eb20f36SRui Paulo piod.piod_offs = (void *)caddr; 938eb20f36SRui Paulo piod.piod_addr = &paddr; 948eb20f36SRui Paulo piod.piod_len = BREAKPOINT_INSTR_SZ; 958eb20f36SRui Paulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 968eb20f36SRui Paulo warn("ERROR: couldn't write instruction at address 0x%" PRIuPTR, 978eb20f36SRui Paulo address); 988eb20f36SRui Paulo return (-1); 998eb20f36SRui Paulo } 1008eb20f36SRui Paulo 1018eb20f36SRui Paulo return (0); 1028eb20f36SRui Paulo } 1038eb20f36SRui Paulo 1048eb20f36SRui Paulo int 1058eb20f36SRui Paulo proc_bkptdel(struct proc_handle *phdl, uintptr_t address, 1068eb20f36SRui Paulo unsigned long saved) 1078eb20f36SRui Paulo { 1088eb20f36SRui Paulo struct ptrace_io_desc piod; 1098eb20f36SRui Paulo unsigned long paddr, caddr; 1108eb20f36SRui Paulo 1118eb20f36SRui Paulo if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || 1128eb20f36SRui Paulo phdl->status == PS_IDLE) { 1138eb20f36SRui Paulo errno = ENOENT; 1148eb20f36SRui Paulo return (-1); 1158eb20f36SRui Paulo } 1168eb20f36SRui Paulo DPRINTF("removing breakpoint at 0x%lx\n", address); 1178eb20f36SRui Paulo /* 1188eb20f36SRui Paulo * Overwrite the breakpoint instruction that we setup previously. 1198eb20f36SRui Paulo */ 1208eb20f36SRui Paulo caddr = address; 1218eb20f36SRui Paulo paddr = saved; 1228eb20f36SRui Paulo piod.piod_op = PIOD_WRITE_I; 1238eb20f36SRui Paulo piod.piod_offs = (void *)caddr; 1248eb20f36SRui Paulo piod.piod_addr = &paddr; 1258eb20f36SRui Paulo piod.piod_len = BREAKPOINT_INSTR_SZ; 1268eb20f36SRui Paulo if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { 1278eb20f36SRui Paulo DPRINTF("ERROR: couldn't write instruction at address 0x%" PRIuPTR, 1288eb20f36SRui Paulo address); 1298eb20f36SRui Paulo return (-1); 1308eb20f36SRui Paulo } 1318eb20f36SRui Paulo 1328eb20f36SRui Paulo return (0); 1338eb20f36SRui Paulo } 1348eb20f36SRui Paulo 1358eb20f36SRui Paulo /* 1368eb20f36SRui Paulo * Decrement pc so that we delete the breakpoint at the correct 1378eb20f36SRui Paulo * address, i.e. at the BREAKPOINT_INSTR address. 1388eb20f36SRui Paulo */ 1398eb20f36SRui Paulo void 1408eb20f36SRui Paulo proc_bkptregadj(unsigned long *pc) 1418eb20f36SRui Paulo { 1428eb20f36SRui Paulo *pc = *pc - BREAKPOINT_INSTR_SZ; 1438eb20f36SRui Paulo } 1448eb20f36SRui Paulo 1458eb20f36SRui Paulo /* 1468eb20f36SRui Paulo * Step over the breakpoint. 1478eb20f36SRui Paulo */ 1488eb20f36SRui Paulo int 1498eb20f36SRui Paulo proc_bkptexec(struct proc_handle *phdl, unsigned long saved) 1508eb20f36SRui Paulo { 1518eb20f36SRui Paulo unsigned long pc; 1528eb20f36SRui Paulo unsigned long samesaved; 1538eb20f36SRui Paulo int status; 1548eb20f36SRui Paulo 1558eb20f36SRui Paulo if (proc_regget(phdl, REG_PC, &pc) < 0) { 1568eb20f36SRui Paulo warn("ERROR: couldn't get PC register"); 1578eb20f36SRui Paulo return (-1); 1588eb20f36SRui Paulo } 1598eb20f36SRui Paulo proc_bkptregadj(&pc); 1608eb20f36SRui Paulo if (proc_bkptdel(phdl, pc, saved) < 0) { 1618eb20f36SRui Paulo warn("ERROR: couldn't delete breakpoint"); 1628eb20f36SRui Paulo return (-1); 1638eb20f36SRui Paulo } 1648eb20f36SRui Paulo /* 1658eb20f36SRui Paulo * Go back in time and step over the new instruction just 1668eb20f36SRui Paulo * set up by proc_bkptdel(). 1678eb20f36SRui Paulo */ 1688eb20f36SRui Paulo proc_regset(phdl, REG_PC, pc); 1698eb20f36SRui Paulo if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) { 1708eb20f36SRui Paulo warn("ERROR: ptrace step failed"); 1718eb20f36SRui Paulo return (-1); 1728eb20f36SRui Paulo } 1734c74b245SRui Paulo proc_wstatus(phdl); 1744c74b245SRui Paulo status = proc_getwstat(phdl); 1758eb20f36SRui Paulo if (!WIFSTOPPED(status)) { 1768eb20f36SRui Paulo warn("ERROR: don't know why process stopped"); 1778eb20f36SRui Paulo return (-1); 1788eb20f36SRui Paulo } 1798eb20f36SRui Paulo /* 1808eb20f36SRui Paulo * Restore the breakpoint. The saved instruction should be 1818eb20f36SRui Paulo * the same as the one that we were passed in. 1828eb20f36SRui Paulo */ 1838eb20f36SRui Paulo if (proc_bkptset(phdl, pc, &samesaved) < 0) { 1848eb20f36SRui Paulo warn("ERROR: couldn't restore breakpoint"); 1858eb20f36SRui Paulo return (-1); 1868eb20f36SRui Paulo } 1878eb20f36SRui Paulo assert(samesaved == saved); 1888eb20f36SRui Paulo 1898eb20f36SRui Paulo return (0); 1908eb20f36SRui Paulo } 191