proc_bkpt.c revision 1.1.1.1
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: head/lib/libproc/proc_bkpt.c 287106 2015-08-24 12:17:15Z andrew $");
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(__aarch64__)
46#define	AARCH64_BRK		0xd4200000
47#define	AARCH64_BRK_IMM16_SHIFT	5
48#define	AARCH64_BRK_IMM16_VAL	(0xd << AARCH64_BRK_IMM16_SHIFT)
49#define	BREAKPOINT_INSTR	(AARCH64_BRK | AARCH64_BRK_IMM16_VAL)
50#define	BREAKPOINT_INSTR_SZ	4
51#elif defined(__amd64__) || defined(__i386__)
52#define	BREAKPOINT_INSTR	0xcc	/* int 0x3 */
53#define	BREAKPOINT_INSTR_SZ	1
54#define	BREAKPOINT_ADJUST_SZ	BREAKPOINT_INSTR_SZ
55#elif defined(__arm__)
56#define	BREAKPOINT_INSTR	0xe7ffffff	/* bkpt */
57#define	BREAKPOINT_INSTR_SZ	4
58#elif defined(__mips__)
59#define	BREAKPOINT_INSTR	0xd	/* break */
60#define	BREAKPOINT_INSTR_SZ	4
61#elif defined(__powerpc__)
62#define	BREAKPOINT_INSTR	0x7fe00008	/* trap */
63#define	BREAKPOINT_INSTR_SZ	4
64#else
65#error "Add support for your architecture"
66#endif
67
68static int
69proc_stop(struct proc_handle *phdl)
70{
71	int status;
72
73	if (kill(proc_getpid(phdl), SIGSTOP) == -1) {
74		DPRINTF("kill %d", proc_getpid(phdl));
75		return (-1);
76	} else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) {
77		DPRINTF("waitpid %d", proc_getpid(phdl));
78		return (-1);
79	} else if (!WIFSTOPPED(status)) {
80		DPRINTFX("waitpid: unexpected status 0x%x", status);
81		return (-1);
82	}
83
84	return (0);
85}
86
87int
88proc_bkptset(struct proc_handle *phdl, uintptr_t address,
89    unsigned long *saved)
90{
91	struct ptrace_io_desc piod;
92	unsigned long paddr, caddr;
93	int ret = 0, stopped;
94
95	*saved = 0;
96	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
97	    phdl->status == PS_IDLE) {
98		errno = ENOENT;
99		return (-1);
100	}
101
102	DPRINTFX("adding breakpoint at 0x%lx", address);
103
104	stopped = 0;
105	if (phdl->status != PS_STOP) {
106		if (proc_stop(phdl) != 0)
107			return (-1);
108		stopped = 1;
109	}
110
111	/*
112	 * Read the original instruction.
113	 */
114	caddr = address;
115	paddr = 0;
116	piod.piod_op = PIOD_READ_I;
117	piod.piod_offs = (void *)caddr;
118	piod.piod_addr = &paddr;
119	piod.piod_len  = BREAKPOINT_INSTR_SZ;
120	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
121		DPRINTF("ERROR: couldn't read instruction at address 0x%"
122		    PRIuPTR, address);
123		ret = -1;
124		goto done;
125	}
126	*saved = paddr;
127	/*
128	 * Write a breakpoint instruction to that address.
129	 */
130	caddr = address;
131	paddr = BREAKPOINT_INSTR;
132	piod.piod_op = PIOD_WRITE_I;
133	piod.piod_offs = (void *)caddr;
134	piod.piod_addr = &paddr;
135	piod.piod_len  = BREAKPOINT_INSTR_SZ;
136	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
137		DPRINTF("ERROR: couldn't write instruction at address 0x%"
138		    PRIuPTR, address);
139		ret = -1;
140		goto done;
141	}
142
143done:
144	if (stopped)
145		/* Restart the process if we had to stop it. */
146		proc_continue(phdl);
147
148	return (ret);
149}
150
151int
152proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
153    unsigned long saved)
154{
155	struct ptrace_io_desc piod;
156	unsigned long paddr, caddr;
157	int ret = 0, stopped;
158
159	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
160	    phdl->status == PS_IDLE) {
161		errno = ENOENT;
162		return (-1);
163	}
164
165	DPRINTFX("removing breakpoint at 0x%lx", address);
166
167	stopped = 0;
168	if (phdl->status != PS_STOP) {
169		if (proc_stop(phdl) != 0)
170			return (-1);
171		stopped = 1;
172	}
173
174	/*
175	 * Overwrite the breakpoint instruction that we setup previously.
176	 */
177	caddr = address;
178	paddr = saved;
179	piod.piod_op = PIOD_WRITE_I;
180	piod.piod_offs = (void *)caddr;
181	piod.piod_addr = &paddr;
182	piod.piod_len  = BREAKPOINT_INSTR_SZ;
183	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
184		DPRINTF("ERROR: couldn't write instruction at address 0x%"
185		    PRIuPTR, address);
186		ret = -1;
187	}
188
189	if (stopped)
190		/* Restart the process if we had to stop it. */
191		proc_continue(phdl);
192
193	return (ret);
194}
195
196/*
197 * Decrement pc so that we delete the breakpoint at the correct
198 * address, i.e. at the BREAKPOINT_INSTR address.
199 *
200 * This is only needed on some architectures where the pc value
201 * when reading registers points at the instruction after the
202 * breakpoint, e.g. x86.
203 */
204void
205proc_bkptregadj(unsigned long *pc)
206{
207
208	(void)pc;
209#ifdef BREAKPOINT_ADJUST_SZ
210	*pc = *pc - BREAKPOINT_ADJUST_SZ;
211#endif
212}
213
214/*
215 * Step over the breakpoint.
216 */
217int
218proc_bkptexec(struct proc_handle *phdl, unsigned long saved)
219{
220	unsigned long pc;
221	unsigned long samesaved;
222	int status;
223
224	if (proc_regget(phdl, REG_PC, &pc) < 0) {
225		DPRINTFX("ERROR: couldn't get PC register");
226		return (-1);
227	}
228	proc_bkptregadj(&pc);
229	if (proc_bkptdel(phdl, pc, saved) < 0) {
230		DPRINTFX("ERROR: couldn't delete breakpoint");
231		return (-1);
232	}
233	/*
234	 * Go back in time and step over the new instruction just
235	 * set up by proc_bkptdel().
236	 */
237	proc_regset(phdl, REG_PC, pc);
238	if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) {
239		DPRINTFX("ERROR: ptrace step failed");
240		return (-1);
241	}
242	proc_wstatus(phdl);
243	status = proc_getwstat(phdl);
244	if (!WIFSTOPPED(status)) {
245		DPRINTFX("ERROR: don't know why process stopped");
246		return (-1);
247	}
248	/*
249	 * Restore the breakpoint. The saved instruction should be
250	 * the same as the one that we were passed in.
251	 */
252	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
253		DPRINTFX("ERROR: couldn't restore breakpoint");
254		return (-1);
255	}
256	assert(samesaved == saved);
257
258	return (0);
259}
260