1316713Smarkj/*-
2316713Smarkj * Copyright (c) 2010 The FreeBSD Foundation
3316713Smarkj * All rights reserved.
4316713Smarkj *
5210688Srpaulo * This software was developed by Rui Paulo under sponsorship from the
6316713Smarkj * FreeBSD Foundation.
7316713Smarkj *
8316713Smarkj * Redistribution and use in source and binary forms, with or without
9316713Smarkj * modification, are permitted provided that the following conditions
10316713Smarkj * are met:
11316713Smarkj * 1. Redistributions of source code must retain the above copyright
12316713Smarkj *    notice, this list of conditions and the following disclaimer.
13316713Smarkj * 2. Redistributions in binary form must reproduce the above copyright
14316713Smarkj *    notice, this list of conditions and the following disclaimer in the
15316713Smarkj *    documentation and/or other materials provided with the distribution.
16316713Smarkj *
17316713Smarkj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18316713Smarkj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19316713Smarkj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20316713Smarkj * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21316713Smarkj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22316713Smarkj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23316713Smarkj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24316713Smarkj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25316713Smarkj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26316713Smarkj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27316713Smarkj * SUCH DAMAGE.
28316713Smarkj */
29210688Srpaulo
30210688Srpaulo#include <sys/cdefs.h>
31210688Srpaulo__FBSDID("$FreeBSD: stable/11/lib/libproc/proc_bkpt.c 316713 2017-04-11 17:36:19Z markj $");
32210688Srpaulo
33210688Srpaulo#include <sys/types.h>
34210688Srpaulo#include <sys/ptrace.h>
35210688Srpaulo#include <sys/wait.h>
36210688Srpaulo
37210688Srpaulo#include <assert.h>
38210688Srpaulo#include <err.h>
39257670Smarkj#include <errno.h>
40257670Smarkj#include <signal.h>
41210688Srpaulo#include <stdio.h>
42316713Smarkj
43210688Srpaulo#include "_libproc.h"
44210688Srpaulo
45285003Sbr#if defined(__aarch64__)
46285003Sbr#define	AARCH64_BRK		0xd4200000
47285003Sbr#define	AARCH64_BRK_IMM16_SHIFT	5
48285003Sbr#define	AARCH64_BRK_IMM16_VAL	(0xd << AARCH64_BRK_IMM16_SHIFT)
49285003Sbr#define	BREAKPOINT_INSTR	(AARCH64_BRK | AARCH64_BRK_IMM16_VAL)
50285003Sbr#define	BREAKPOINT_INSTR_SZ	4
51285003Sbr#elif defined(__amd64__) || defined(__i386__)
52285003Sbr#define	BREAKPOINT_INSTR	0xcc	/* int 0x3 */
53210688Srpaulo#define	BREAKPOINT_INSTR_SZ	1
54287106Sandrew#define	BREAKPOINT_ADJUST_SZ	BREAKPOINT_INSTR_SZ
55285003Sbr#elif defined(__arm__)
56285003Sbr#define	BREAKPOINT_INSTR	0xe7ffffff	/* bkpt */
57285003Sbr#define	BREAKPOINT_INSTR_SZ	4
58233402Sgonzo#elif defined(__mips__)
59285003Sbr#define	BREAKPOINT_INSTR	0xd	/* break */
60233402Sgonzo#define	BREAKPOINT_INSTR_SZ	4
61242723Sjhibbits#elif defined(__powerpc__)
62285003Sbr#define	BREAKPOINT_INSTR	0x7fe00008	/* trap */
63285003Sbr#define	BREAKPOINT_INSTR_SZ	4
64294662Sbr#elif defined(__riscv__)
65294662Sbr#define	BREAKPOINT_INSTR	0x00100073	/* sbreak */
66294662Sbr#define	BREAKPOINT_INSTR_SZ	4
67210688Srpaulo#else
68210688Srpaulo#error "Add support for your architecture"
69210688Srpaulo#endif
70210688Srpaulo
71257670Smarkjstatic int
72257670Smarkjproc_stop(struct proc_handle *phdl)
73257670Smarkj{
74257670Smarkj	int status;
75257670Smarkj
76257670Smarkj	if (kill(proc_getpid(phdl), SIGSTOP) == -1) {
77257670Smarkj		DPRINTF("kill %d", proc_getpid(phdl));
78257670Smarkj		return (-1);
79257670Smarkj	} else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) {
80257670Smarkj		DPRINTF("waitpid %d", proc_getpid(phdl));
81257670Smarkj		return (-1);
82257670Smarkj	} else if (!WIFSTOPPED(status)) {
83257670Smarkj		DPRINTFX("waitpid: unexpected status 0x%x", status);
84257670Smarkj		return (-1);
85257670Smarkj	}
86257670Smarkj
87257670Smarkj	return (0);
88257670Smarkj}
89257670Smarkj
90210688Srpauloint
91210688Srpauloproc_bkptset(struct proc_handle *phdl, uintptr_t address,
92210688Srpaulo    unsigned long *saved)
93210688Srpaulo{
94210688Srpaulo	struct ptrace_io_desc piod;
95210688Srpaulo	unsigned long paddr, caddr;
96265308Smarkj	int ret = 0, stopped;
97210688Srpaulo
98210688Srpaulo	*saved = 0;
99210688Srpaulo	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
100210688Srpaulo	    phdl->status == PS_IDLE) {
101210688Srpaulo		errno = ENOENT;
102210688Srpaulo		return (-1);
103210688Srpaulo	}
104210688Srpaulo
105257670Smarkj	DPRINTFX("adding breakpoint at 0x%lx", address);
106257670Smarkj
107265308Smarkj	stopped = 0;
108265308Smarkj	if (phdl->status != PS_STOP) {
109257670Smarkj		if (proc_stop(phdl) != 0)
110257670Smarkj			return (-1);
111265308Smarkj		stopped = 1;
112265308Smarkj	}
113257670Smarkj
114210688Srpaulo	/*
115210688Srpaulo	 * Read the original instruction.
116210688Srpaulo	 */
117210688Srpaulo	caddr = address;
118210688Srpaulo	paddr = 0;
119210688Srpaulo	piod.piod_op = PIOD_READ_I;
120210688Srpaulo	piod.piod_offs = (void *)caddr;
121210688Srpaulo	piod.piod_addr = &paddr;
122210688Srpaulo	piod.piod_len  = BREAKPOINT_INSTR_SZ;
123210688Srpaulo	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
124316713Smarkj		DPRINTF("ERROR: couldn't read instruction at address 0x%jx",
125316713Smarkj		    (uintmax_t)address);
126257670Smarkj		ret = -1;
127257670Smarkj		goto done;
128210688Srpaulo	}
129210688Srpaulo	*saved = paddr;
130210688Srpaulo	/*
131210688Srpaulo	 * Write a breakpoint instruction to that address.
132210688Srpaulo	 */
133210688Srpaulo	caddr = address;
134210688Srpaulo	paddr = BREAKPOINT_INSTR;
135210688Srpaulo	piod.piod_op = PIOD_WRITE_I;
136210688Srpaulo	piod.piod_offs = (void *)caddr;
137210688Srpaulo	piod.piod_addr = &paddr;
138210688Srpaulo	piod.piod_len  = BREAKPOINT_INSTR_SZ;
139210688Srpaulo	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
140316713Smarkj		DPRINTF("ERROR: couldn't write instruction at address 0x%jx",
141316713Smarkj		    (uintmax_t)address);
142257670Smarkj		ret = -1;
143257670Smarkj		goto done;
144210688Srpaulo	}
145210688Srpaulo
146257670Smarkjdone:
147265308Smarkj	if (stopped)
148257670Smarkj		/* Restart the process if we had to stop it. */
149265308Smarkj		proc_continue(phdl);
150257670Smarkj
151257670Smarkj	return (ret);
152210688Srpaulo}
153210688Srpaulo
154210688Srpauloint
155210688Srpauloproc_bkptdel(struct proc_handle *phdl, uintptr_t address,
156210688Srpaulo    unsigned long saved)
157210688Srpaulo{
158210688Srpaulo	struct ptrace_io_desc piod;
159210688Srpaulo	unsigned long paddr, caddr;
160265308Smarkj	int ret = 0, stopped;
161210688Srpaulo
162210688Srpaulo	if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
163210688Srpaulo	    phdl->status == PS_IDLE) {
164210688Srpaulo		errno = ENOENT;
165210688Srpaulo		return (-1);
166210688Srpaulo	}
167257670Smarkj
168257670Smarkj	DPRINTFX("removing breakpoint at 0x%lx", address);
169257670Smarkj
170265308Smarkj	stopped = 0;
171265308Smarkj	if (phdl->status != PS_STOP) {
172257670Smarkj		if (proc_stop(phdl) != 0)
173257670Smarkj			return (-1);
174265308Smarkj		stopped = 1;
175265308Smarkj	}
176257670Smarkj
177210688Srpaulo	/*
178210688Srpaulo	 * Overwrite the breakpoint instruction that we setup previously.
179210688Srpaulo	 */
180210688Srpaulo	caddr = address;
181210688Srpaulo	paddr = saved;
182210688Srpaulo	piod.piod_op = PIOD_WRITE_I;
183210688Srpaulo	piod.piod_offs = (void *)caddr;
184210688Srpaulo	piod.piod_addr = &paddr;
185210688Srpaulo	piod.piod_len  = BREAKPOINT_INSTR_SZ;
186210688Srpaulo	if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) {
187316713Smarkj		DPRINTF("ERROR: couldn't write instruction at address 0x%jx",
188316713Smarkj		    (uintmax_t)address);
189257670Smarkj		ret = -1;
190210688Srpaulo	}
191257670Smarkj
192265308Smarkj	if (stopped)
193257670Smarkj		/* Restart the process if we had to stop it. */
194265308Smarkj		proc_continue(phdl);
195316713Smarkj
196257670Smarkj	return (ret);
197210688Srpaulo}
198210688Srpaulo
199210688Srpaulo/*
200210688Srpaulo * Decrement pc so that we delete the breakpoint at the correct
201210688Srpaulo * address, i.e. at the BREAKPOINT_INSTR address.
202287106Sandrew *
203287106Sandrew * This is only needed on some architectures where the pc value
204287106Sandrew * when reading registers points at the instruction after the
205287106Sandrew * breakpoint, e.g. x86.
206210688Srpaulo */
207210688Srpaulovoid
208210688Srpauloproc_bkptregadj(unsigned long *pc)
209210688Srpaulo{
210287106Sandrew
211287106Sandrew	(void)pc;
212287106Sandrew#ifdef BREAKPOINT_ADJUST_SZ
213287106Sandrew	*pc = *pc - BREAKPOINT_ADJUST_SZ;
214287106Sandrew#endif
215210688Srpaulo}
216210688Srpaulo
217210688Srpaulo/*
218210688Srpaulo * Step over the breakpoint.
219210688Srpaulo */
220210688Srpauloint
221210688Srpauloproc_bkptexec(struct proc_handle *phdl, unsigned long saved)
222210688Srpaulo{
223210688Srpaulo	unsigned long pc;
224210688Srpaulo	unsigned long samesaved;
225210688Srpaulo	int status;
226210688Srpaulo
227210688Srpaulo	if (proc_regget(phdl, REG_PC, &pc) < 0) {
228257222Smarkj		DPRINTFX("ERROR: couldn't get PC register");
229210688Srpaulo		return (-1);
230210688Srpaulo	}
231210688Srpaulo	proc_bkptregadj(&pc);
232210688Srpaulo	if (proc_bkptdel(phdl, pc, saved) < 0) {
233257222Smarkj		DPRINTFX("ERROR: couldn't delete breakpoint");
234210688Srpaulo		return (-1);
235210688Srpaulo	}
236210688Srpaulo	/*
237210688Srpaulo	 * Go back in time and step over the new instruction just
238210688Srpaulo	 * set up by proc_bkptdel().
239210688Srpaulo	 */
240210688Srpaulo	proc_regset(phdl, REG_PC, pc);
241210688Srpaulo	if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) {
242257222Smarkj		DPRINTFX("ERROR: ptrace step failed");
243210688Srpaulo		return (-1);
244210688Srpaulo	}
245211184Srpaulo	proc_wstatus(phdl);
246211184Srpaulo	status = proc_getwstat(phdl);
247210688Srpaulo	if (!WIFSTOPPED(status)) {
248257222Smarkj		DPRINTFX("ERROR: don't know why process stopped");
249210688Srpaulo		return (-1);
250210688Srpaulo	}
251210688Srpaulo	/*
252210688Srpaulo	 * Restore the breakpoint. The saved instruction should be
253210688Srpaulo	 * the same as the one that we were passed in.
254210688Srpaulo	 */
255210688Srpaulo	if (proc_bkptset(phdl, pc, &samesaved) < 0) {
256257222Smarkj		DPRINTFX("ERROR: couldn't restore breakpoint");
257210688Srpaulo		return (-1);
258210688Srpaulo	}
259210688Srpaulo	assert(samesaved == saved);
260210688Srpaulo
261210688Srpaulo	return (0);
262210688Srpaulo}
263