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