proc_bkpt.c revision 211184
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 211184 2010-08-11 17:33:26Z rpaulo $");
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 <stdio.h>
41#include <errno.h>
42#include "_libproc.h"
43
44#if defined(__i386__) || defined(__amd64__)
45#define BREAKPOINT_INSTR	0xcc	/* int 0x3 */
46#define	BREAKPOINT_INSTR_SZ	1
47#else
48#error "Add support for your architecture"
49#endif
50
51int
52proc_bkptset(struct proc_handle *phdl, uintptr_t address,
53    unsigned long *saved)
54{
55	struct ptrace_io_desc piod;
56	unsigned long paddr, caddr;
57
58	*saved = 0;
59	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
60	    phdl->status == PS_IDLE) {
61		errno = ENOENT;
62		return (-1);
63	}
64
65	/*
66	 * Read the original instruction.
67	 */
68	caddr = address;
69	paddr = 0;
70	piod.piod_op = PIOD_READ_I;
71	piod.piod_offs = (void *)caddr;
72	piod.piod_addr = &paddr;
73	piod.piod_len  = BREAKPOINT_INSTR_SZ;
74	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
75		DPRINTF("ERROR: couldn't read instruction at address 0x%" PRIuPTR,
76		    address);
77		return (-1);
78	}
79	*saved = paddr;
80	/*
81	 * Write a breakpoint instruction to that address.
82	 */
83	caddr = address;
84	paddr = BREAKPOINT_INSTR;
85	piod.piod_op = PIOD_WRITE_I;
86	piod.piod_offs = (void *)caddr;
87	piod.piod_addr = &paddr;
88	piod.piod_len  = BREAKPOINT_INSTR_SZ;
89	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
90		warn("ERROR: couldn't write instruction at address 0x%" PRIuPTR,
91		    address);
92		return (-1);
93	}
94
95	return (0);
96}
97
98int
99proc_bkptdel(struct proc_handle *phdl, uintptr_t address,
100    unsigned long saved)
101{
102	struct ptrace_io_desc piod;
103	unsigned long paddr, caddr;
104
105	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
106	    phdl->status == PS_IDLE) {
107		errno = ENOENT;
108		return (-1);
109	}
110	DPRINTF("removing breakpoint at 0x%lx\n", address);
111	/*
112	 * Overwrite the breakpoint instruction that we setup previously.
113	 */
114	caddr = address;
115	paddr = saved;
116	piod.piod_op = PIOD_WRITE_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 write instruction at address 0x%" PRIuPTR,
122		    address);
123		return (-1);
124	}
125
126	return (0);
127}
128
129/*
130 * Decrement pc so that we delete the breakpoint at the correct
131 * address, i.e. at the BREAKPOINT_INSTR address.
132 */
133void
134proc_bkptregadj(unsigned long *pc)
135{
136	*pc = *pc - BREAKPOINT_INSTR_SZ;
137}
138
139/*
140 * Step over the breakpoint.
141 */
142int
143proc_bkptexec(struct proc_handle *phdl, unsigned long saved)
144{
145	unsigned long pc;
146	unsigned long samesaved;
147	int status;
148
149	if (proc_regget(phdl, REG_PC, &pc) < 0) {
150		warn("ERROR: couldn't get PC register");
151		return (-1);
152	}
153	proc_bkptregadj(&pc);
154	if (proc_bkptdel(phdl, pc, saved) < 0) {
155		warn("ERROR: couldn't delete breakpoint");
156		return (-1);
157	}
158	/*
159	 * Go back in time and step over the new instruction just
160	 * set up by proc_bkptdel().
161	 */
162	proc_regset(phdl, REG_PC, pc);
163	if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) {
164		warn("ERROR: ptrace step failed");
165		return (-1);
166	}
167	proc_wstatus(phdl);
168	status = proc_getwstat(phdl);
169	if (!WIFSTOPPED(status)) {
170		warn("ERROR: don't know why process stopped");
171		return (-1);
172	}
173	/*
174	 * Restore the breakpoint. The saved instruction should be
175	 * the same as the one that we were passed in.
176	 */
177	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
178		warn("ERROR: couldn't restore breakpoint");
179		return (-1);
180	}
181	assert(samesaved == saved);
182
183	return (0);
184}
185