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