1 /*	$OpenBSD: break.c,v 1.8 2003/08/02 20:38:38 mickey Exp $	*/
2 /*
3  * Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/ptrace.h>
29 #include <sys/wait.h>
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <string.h>
38 
39 #include "pmdb.h"
40 #include "symbol.h"
41 #include "pmdb_machdep.h"
42 #include "break.h"
43 
44 struct callback {
45 	TAILQ_ENTRY(callback) cb_list;
46 	int (*cb_fun)(struct pstate *, void *);
47 	void *cb_arg;
48 };
49 
50 struct breakpoint {
51 	TAILQ_ENTRY(breakpoint) bkpt_list;
52 	TAILQ_HEAD(,callback) bkpt_cbs;		/* list of all callbacks */
53 	char bkpt_old[BREAKPOINT_LEN];		/* old contents at bkpt */
54 	reg bkpt_pc;
55 };
56 
57 static char bkpt_insn[BREAKPOINT_LEN] = BREAKPOINT;
58 
59 /*
60  * Find a breakpoint at this address.
61  */
62 struct breakpoint *
bkpt_find_at_pc(struct pstate * ps,reg pc)63 bkpt_find_at_pc(struct pstate *ps, reg pc)
64 {
65 	struct breakpoint *bkpt;
66 
67 	TAILQ_FOREACH(bkpt, &ps->ps_bkpts, bkpt_list) {
68 		if (bkpt->bkpt_pc == pc)
69 			break;
70 	}
71 	return (bkpt);
72 }
73 
74 /*
75  * Enable this breakpoint.
76  */
77 static int
bkpt_enable(struct pstate * ps,struct breakpoint * bkpt)78 bkpt_enable(struct pstate *ps, struct breakpoint *bkpt)
79 {
80 	reg pc = bkpt->bkpt_pc;
81 
82 	if (process_read(ps, pc, &bkpt->bkpt_old, BREAKPOINT_LEN) < 0) {
83 		warn("Can't read process contents at 0x%lx", pc);
84 		return (-1);
85 	}
86 	if (process_write(ps, pc, &bkpt_insn, BREAKPOINT_LEN) < 0) {
87 		warn("Can't write breakpoint at 0x%lx, attempting backout.", pc);
88 		if (process_write(ps, pc, &bkpt->bkpt_old, BREAKPOINT_LEN) < 0)
89 			warn("Backout failed, process unstable");
90 		return (-1);
91 	}
92 	return (0);
93 }
94 
95 /*
96  * Create a new breakpoint and enable it.
97  */
98 int
bkpt_add_cb(struct pstate * ps,reg pc,int (* fun)(struct pstate *,void *),void * arg)99 bkpt_add_cb(struct pstate *ps, reg pc, int (*fun)(struct pstate *, void *),
100     void *arg)
101 {
102 	struct breakpoint *bkpt;
103 	struct callback *cb;
104 
105 	if ((bkpt = bkpt_find_at_pc(ps, pc)) == NULL) {
106 		bkpt = emalloc(sizeof(*bkpt));
107 		TAILQ_INIT(&bkpt->bkpt_cbs);
108 		TAILQ_INSERT_TAIL(&ps->ps_bkpts, bkpt, bkpt_list);
109 		bkpt->bkpt_pc = pc;
110 		if (bkpt_enable(ps, bkpt)) {
111 			TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list);
112 			free(bkpt);
113 			return (-1);
114 		}
115 	}
116 
117 	cb = emalloc(sizeof(*cb));
118 	cb->cb_fun = fun;
119 	cb->cb_arg = arg;
120 	TAILQ_INSERT_TAIL(&bkpt->bkpt_cbs, cb, cb_list);
121 
122 	return (0);
123 }
124 
125 /*
126  * Disable and delete a breakpoint.
127  */
128 void
bkpt_delete(struct pstate * ps,struct breakpoint * bkpt)129 bkpt_delete(struct pstate *ps, struct breakpoint *bkpt)
130 {
131 	TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list);
132 
133 	if (process_write(ps, bkpt->bkpt_pc, &bkpt->bkpt_old, BREAKPOINT_LEN))
134 		warn("Breakpoint removal failed, process unstable");
135 
136 	free(bkpt);
137 }
138 
139 /*
140  * Normal standard breakpoint. Keep it.
141  */
142 static int
bkpt_normal(struct pstate * ps,void * arg)143 bkpt_normal(struct pstate *ps, void *arg)
144 {
145 	return (BKPT_KEEP_STOP);
146 }
147 
148 /*
149  * Single-step callback for "stepping over" a breakpoint (we restore the
150  * breakpoint instruction to what it was, single-step over it and then
151  * call this function).
152  */
153 static int
sstep_bkpt_readd(struct pstate * ps,void * arg)154 sstep_bkpt_readd(struct pstate *ps, void *arg)
155 {
156 	reg pc = (reg)arg;
157 
158 	bkpt_add_cb(ps, pc, bkpt_normal, NULL);
159 
160 	return (0);	/* let the process continue */
161 }
162 
163 /*
164  * Return 0 for stop, 1 for silent continue.
165  */
166 int
bkpt_check(struct pstate * ps)167 bkpt_check(struct pstate *ps)
168 {
169 	struct breakpoint *bkpt;
170 	struct callback *cb;
171 	TAILQ_HEAD(,callback) sstep_cbs;
172 	reg *rg, pc;
173 	int ret;
174 	int didsome = 0;
175 	int stop = 0;
176 
177 	/* Requeue all single-step callbacks because bkpts can add ssteps. */
178 	TAILQ_INIT(&sstep_cbs);
179 	while ((cb = TAILQ_FIRST(&ps->ps_sstep_cbs)) != NULL) {
180 		TAILQ_REMOVE(&ps->ps_sstep_cbs, cb, cb_list);
181 		TAILQ_INSERT_TAIL(&sstep_cbs, cb, cb_list);
182 	}
183 
184 	/*
185 	 * The default is to stop. Unless we do some processing and none
186 	 * of the callbacks require a stop.
187 	 */
188 	rg = alloca(sizeof(*rg) * md_def.nregs);
189 	if (rg == NULL)
190 		err(1, "bkpt_check: Can't allocate stack space.");
191 
192 	if (md_getregs(ps, rg))
193 		err(1, "bkpt_check: Can't get registers");
194 
195 	pc = rg[md_def.pcoff];
196 	pc -= BREAKPOINT_DECR_PC;
197 
198 	bkpt = bkpt_find_at_pc(ps, pc);
199 	if (bkpt == NULL)
200 		goto sstep;
201 
202 	ps->ps_npc = pc;
203 
204 	while ((cb = TAILQ_FIRST(&bkpt->bkpt_cbs)) != NULL) {
205 		didsome = 1;
206 		TAILQ_REMOVE(&bkpt->bkpt_cbs, cb, cb_list);
207 		ret = (*cb->cb_fun)(ps, cb->cb_arg);
208 		free(cb);
209 		switch (ret) {
210 		case BKPT_DEL_STOP:
211 			stop = 1;
212 		case BKPT_DEL_CONT:
213 			break;
214 		case BKPT_KEEP_STOP:
215 			stop = 1;
216 		case BKPT_KEEP_CONT:
217 			sstep_set(ps, sstep_bkpt_readd, (void *)bkpt->bkpt_pc);
218 			break;
219 		default:
220 			errx(1, "unknown bkpt_fun return, internal error");
221 		}
222 	}
223 
224 	bkpt_delete(ps, bkpt);
225 
226 sstep:
227 
228 	while ((cb = TAILQ_FIRST(&sstep_cbs)) != NULL) {
229 		didsome = 1;
230 		TAILQ_REMOVE(&sstep_cbs, cb, cb_list);
231 		stop |= (*cb->cb_fun)(ps, cb->cb_arg);
232 		free(cb);
233 	}
234 	ps->ps_flags &= ~PSF_STEP;
235 
236 	return (didsome && !stop);
237 }
238 
239 int
cmd_bkpt_add(int argc,char ** argv,void * arg)240 cmd_bkpt_add(int argc, char **argv, void *arg)
241 {
242 	struct pstate *ps = arg;
243 	char *ep, *bkpt_name;
244 	reg pc;
245 
246         if (ps->ps_state != STOPPED && ps->ps_state != LOADED) {
247                 fprintf(stderr, "Process not loaded and stopped %d\n",
248                     ps->ps_state);
249                 return (0);
250         }
251 
252 	bkpt_name = argv[1];
253 	pc = strtol(bkpt_name, &ep, 0);
254 	if (bkpt_name[0] == '\0' || *ep != '\0' || pc < 1) {
255 		if (sym_lookup(ps, bkpt_name, &pc)) {
256 			warnx("%s is not a valid pc", bkpt_name);
257 			return (0);
258 		}
259 	}
260 
261 	if (bkpt_add_cb(ps, pc, bkpt_normal, 0))
262 		warn("Can't set break point");
263 
264 	return (0);
265 }
266 
267 static int
sstep_normal(struct pstate * ps,void * arg)268 sstep_normal(struct pstate *ps, void *arg)
269 {
270 	return (1);	/* stop the command line. */
271 }
272 
273 int
cmd_sstep(int argc,char ** argv,void * arg)274 cmd_sstep(int argc, char **argv, void *arg)
275 {
276 	struct pstate *ps = arg;
277 
278         if (ps->ps_state != STOPPED) {
279                 fprintf(stderr, "Process not loaded and stopped %d\n",
280                     ps->ps_state);
281                 return 0;
282         }
283 
284 	if (sstep_set(ps, sstep_normal, NULL))
285 		warn("Can't set single step");
286 
287 	return (cmd_process_cont(argc, argv, arg));
288 }
289 
290 int
sstep_set(struct pstate * ps,int (* fun)(struct pstate *,void *),void * arg)291 sstep_set(struct pstate *ps, int (*fun)(struct pstate *, void *), void *arg)
292 {
293 	struct callback *cb;
294 
295 	cb = emalloc(sizeof(*cb));
296 	cb->cb_fun = fun;
297 	cb->cb_arg = arg;
298 	TAILQ_INSERT_TAIL(&ps->ps_sstep_cbs, cb, cb_list);
299 
300 	ps->ps_flags |= PSF_STEP;
301 
302 	return (0);
303 }
304