xref: /freebsd/lib/libproc/proc_bkpt.c (revision 8eb20f364fe0e4a0cbbbce6b8456a75324ae95d8)
1*8eb20f36SRui Paulo /*
2*8eb20f36SRui Paulo  * Copyright (c) 2010 The FreeBSD Foundation
3*8eb20f36SRui Paulo  * All rights reserved.
4*8eb20f36SRui Paulo  *
5*8eb20f36SRui Paulo  * This software was developed by Rui Paulo under sponsorship from the
6*8eb20f36SRui Paulo  * FreeBSD Foundation.
7*8eb20f36SRui Paulo  *
8*8eb20f36SRui Paulo  * Redistribution and use in source and binary forms, with or without
9*8eb20f36SRui Paulo  * modification, are permitted provided that the following conditions
10*8eb20f36SRui Paulo  * are met:
11*8eb20f36SRui Paulo  * 1. Redistributions of source code must retain the above copyright
12*8eb20f36SRui Paulo  *    notice, this list of conditions and the following disclaimer.
13*8eb20f36SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
14*8eb20f36SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
15*8eb20f36SRui Paulo  *    documentation and/or other materials provided with the distribution.
16*8eb20f36SRui Paulo  *
17*8eb20f36SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*8eb20f36SRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*8eb20f36SRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*8eb20f36SRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*8eb20f36SRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*8eb20f36SRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*8eb20f36SRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*8eb20f36SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*8eb20f36SRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*8eb20f36SRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*8eb20f36SRui Paulo  * SUCH DAMAGE.
28*8eb20f36SRui Paulo  */
29*8eb20f36SRui Paulo 
30*8eb20f36SRui Paulo #include <sys/cdefs.h>
31*8eb20f36SRui Paulo __FBSDID("$FreeBSD$");
32*8eb20f36SRui Paulo 
33*8eb20f36SRui Paulo #include <sys/types.h>
34*8eb20f36SRui Paulo #include <sys/ptrace.h>
35*8eb20f36SRui Paulo #include <sys/wait.h>
36*8eb20f36SRui Paulo #include <machine/_inttypes.h>
37*8eb20f36SRui Paulo 
38*8eb20f36SRui Paulo #include <assert.h>
39*8eb20f36SRui Paulo #include <err.h>
40*8eb20f36SRui Paulo #include <stdio.h>
41*8eb20f36SRui Paulo #include <errno.h>
42*8eb20f36SRui Paulo #include "_libproc.h"
43*8eb20f36SRui Paulo 
44*8eb20f36SRui Paulo #if defined(__i386__) || defined(__amd64__)
45*8eb20f36SRui Paulo #define BREAKPOINT_INSTR	0xcc	/* int 0x3 */
46*8eb20f36SRui Paulo #define	BREAKPOINT_INSTR_SZ	1
47*8eb20f36SRui Paulo #else
48*8eb20f36SRui Paulo #error "Add support for your architecture"
49*8eb20f36SRui Paulo #endif
50*8eb20f36SRui Paulo 
51*8eb20f36SRui Paulo int
52*8eb20f36SRui Paulo proc_bkptset(struct proc_handle *phdl, uintptr_t address,
53*8eb20f36SRui Paulo     unsigned long *saved)
54*8eb20f36SRui Paulo {
55*8eb20f36SRui Paulo 	struct ptrace_io_desc piod;
56*8eb20f36SRui Paulo 	unsigned long paddr, caddr;
57*8eb20f36SRui Paulo 
58*8eb20f36SRui Paulo 	*saved = 0;
59*8eb20f36SRui Paulo 	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
60*8eb20f36SRui Paulo 	    phdl->status == PS_IDLE) {
61*8eb20f36SRui Paulo 		errno = ENOENT;
62*8eb20f36SRui Paulo 		return (-1);
63*8eb20f36SRui Paulo 	}
64*8eb20f36SRui Paulo 
65*8eb20f36SRui Paulo 	/*
66*8eb20f36SRui Paulo 	 * Read the original instruction.
67*8eb20f36SRui Paulo 	 */
68*8eb20f36SRui Paulo 	caddr = address;
69*8eb20f36SRui Paulo 	paddr = 0;
70*8eb20f36SRui Paulo 	piod.piod_op = PIOD_READ_I;
71*8eb20f36SRui Paulo 	piod.piod_offs = (void *)caddr;
72*8eb20f36SRui Paulo 	piod.piod_addr = &paddr;
73*8eb20f36SRui Paulo 	piod.piod_len  = BREAKPOINT_INSTR_SZ;
74*8eb20f36SRui Paulo 	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
75*8eb20f36SRui Paulo 		DPRINTF("ERROR: couldn't read instruction at address 0x%" PRIuPTR,
76*8eb20f36SRui Paulo 		    address);
77*8eb20f36SRui Paulo 		return (-1);
78*8eb20f36SRui Paulo 	}
79*8eb20f36SRui Paulo 	*saved = paddr;
80*8eb20f36SRui Paulo 	/*
81*8eb20f36SRui Paulo 	 * Write a breakpoint instruction to that address.
82*8eb20f36SRui Paulo 	 */
83*8eb20f36SRui Paulo 	caddr = address;
84*8eb20f36SRui Paulo 	paddr = BREAKPOINT_INSTR;
85*8eb20f36SRui Paulo 	piod.piod_op = PIOD_WRITE_I;
86*8eb20f36SRui Paulo 	piod.piod_offs = (void *)caddr;
87*8eb20f36SRui Paulo 	piod.piod_addr = &paddr;
88*8eb20f36SRui Paulo 	piod.piod_len  = BREAKPOINT_INSTR_SZ;
89*8eb20f36SRui Paulo 	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
90*8eb20f36SRui Paulo 		warn("ERROR: couldn't write instruction at address 0x%" PRIuPTR,
91*8eb20f36SRui Paulo 		    address);
92*8eb20f36SRui Paulo 		return (-1);
93*8eb20f36SRui Paulo 	}
94*8eb20f36SRui Paulo 
95*8eb20f36SRui Paulo 	return (0);
96*8eb20f36SRui Paulo }
97*8eb20f36SRui Paulo 
98*8eb20f36SRui Paulo int
99*8eb20f36SRui Paulo proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
100*8eb20f36SRui Paulo     unsigned long saved)
101*8eb20f36SRui Paulo {
102*8eb20f36SRui Paulo 	struct ptrace_io_desc piod;
103*8eb20f36SRui Paulo 	unsigned long paddr, caddr;
104*8eb20f36SRui Paulo 
105*8eb20f36SRui Paulo 	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
106*8eb20f36SRui Paulo 	    phdl->status == PS_IDLE) {
107*8eb20f36SRui Paulo 		errno = ENOENT;
108*8eb20f36SRui Paulo 		return (-1);
109*8eb20f36SRui Paulo 	}
110*8eb20f36SRui Paulo 	DPRINTF("removing breakpoint at 0x%lx\n", address);
111*8eb20f36SRui Paulo 	/*
112*8eb20f36SRui Paulo 	 * Overwrite the breakpoint instruction that we setup previously.
113*8eb20f36SRui Paulo 	 */
114*8eb20f36SRui Paulo 	caddr = address;
115*8eb20f36SRui Paulo 	paddr = saved;
116*8eb20f36SRui Paulo 	piod.piod_op = PIOD_WRITE_I;
117*8eb20f36SRui Paulo 	piod.piod_offs = (void *)caddr;
118*8eb20f36SRui Paulo 	piod.piod_addr = &paddr;
119*8eb20f36SRui Paulo 	piod.piod_len  = BREAKPOINT_INSTR_SZ;
120*8eb20f36SRui Paulo 	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
121*8eb20f36SRui Paulo 		DPRINTF("ERROR: couldn't write instruction at address 0x%" PRIuPTR,
122*8eb20f36SRui Paulo 		    address);
123*8eb20f36SRui Paulo 		return (-1);
124*8eb20f36SRui Paulo 	}
125*8eb20f36SRui Paulo 
126*8eb20f36SRui Paulo 	return (0);
127*8eb20f36SRui Paulo }
128*8eb20f36SRui Paulo 
129*8eb20f36SRui Paulo /*
130*8eb20f36SRui Paulo  * Decrement pc so that we delete the breakpoint at the correct
131*8eb20f36SRui Paulo  * address, i.e. at the BREAKPOINT_INSTR address.
132*8eb20f36SRui Paulo  */
133*8eb20f36SRui Paulo void
134*8eb20f36SRui Paulo proc_bkptregadj(unsigned long *pc)
135*8eb20f36SRui Paulo {
136*8eb20f36SRui Paulo 	*pc = *pc - BREAKPOINT_INSTR_SZ;
137*8eb20f36SRui Paulo }
138*8eb20f36SRui Paulo 
139*8eb20f36SRui Paulo /*
140*8eb20f36SRui Paulo  * Step over the breakpoint.
141*8eb20f36SRui Paulo  */
142*8eb20f36SRui Paulo int
143*8eb20f36SRui Paulo proc_bkptexec(struct proc_handle *phdl, unsigned long saved)
144*8eb20f36SRui Paulo {
145*8eb20f36SRui Paulo 	unsigned long pc;
146*8eb20f36SRui Paulo 	unsigned long samesaved;
147*8eb20f36SRui Paulo 	int status;
148*8eb20f36SRui Paulo 
149*8eb20f36SRui Paulo 	if (proc_regget(phdl, REG_PC, &pc) < 0) {
150*8eb20f36SRui Paulo 		warn("ERROR: couldn't get PC register");
151*8eb20f36SRui Paulo 		return (-1);
152*8eb20f36SRui Paulo 	}
153*8eb20f36SRui Paulo 	proc_bkptregadj(&pc);
154*8eb20f36SRui Paulo 	if (proc_bkptdel(phdl, pc, saved) < 0) {
155*8eb20f36SRui Paulo 		warn("ERROR: couldn't delete breakpoint");
156*8eb20f36SRui Paulo 		return (-1);
157*8eb20f36SRui Paulo 	}
158*8eb20f36SRui Paulo 	/*
159*8eb20f36SRui Paulo 	 * Go back in time and step over the new instruction just
160*8eb20f36SRui Paulo 	 * set up by proc_bkptdel().
161*8eb20f36SRui Paulo 	 */
162*8eb20f36SRui Paulo 	proc_regset(phdl, REG_PC, pc);
163*8eb20f36SRui Paulo 	if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) {
164*8eb20f36SRui Paulo 		warn("ERROR: ptrace step failed");
165*8eb20f36SRui Paulo 		return (-1);
166*8eb20f36SRui Paulo 	}
167*8eb20f36SRui Paulo 	status = proc_wstatus(phdl);
168*8eb20f36SRui Paulo 	if (!WIFSTOPPED(status)) {
169*8eb20f36SRui Paulo 		warn("ERROR: don't know why process stopped");
170*8eb20f36SRui Paulo 		return (-1);
171*8eb20f36SRui Paulo 	}
172*8eb20f36SRui Paulo 	/*
173*8eb20f36SRui Paulo 	 * Restore the breakpoint. The saved instruction should be
174*8eb20f36SRui Paulo 	 * the same as the one that we were passed in.
175*8eb20f36SRui Paulo 	 */
176*8eb20f36SRui Paulo 	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
177*8eb20f36SRui Paulo 		warn("ERROR: couldn't restore breakpoint");
178*8eb20f36SRui Paulo 		return (-1);
179*8eb20f36SRui Paulo 	}
180*8eb20f36SRui Paulo 	assert(samesaved == saved);
181*8eb20f36SRui Paulo 
182*8eb20f36SRui Paulo 	return (0);
183*8eb20f36SRui Paulo }
184