Annotation of src/usr.bin/pmdb/break.c, Revision 1.6
1.6 ! vincent 1: /* $OpenBSD: break.c,v 1.5 2002/07/31 02:34:30 art Exp $ */
1.1 art 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 *
63: bkpt_find_at_pc(struct pstate *ps, reg pc)
64: {
65: struct breakpoint *bkpt;
66:
1.6 ! vincent 67: TAILQ_FOREACH(bkpt, &ps->ps_bkpts, bkpt_list) {
1.1 art 68: if (bkpt->bkpt_pc == pc)
69: break;
1.6 ! vincent 70: }
1.1 art 71: return (bkpt);
72: }
73:
74: /*
75: * Enable this breakpoint.
76: */
77: static int
78: bkpt_enable(struct pstate *ps, struct breakpoint *bkpt)
79: {
80: reg pc = bkpt->bkpt_pc;
81:
1.4 art 82: if (process_read(ps, pc, &bkpt->bkpt_old, BREAKPOINT_LEN)) {
1.1 art 83: warn("Can't read process contents at 0x%lx", pc);
84: return (-1);
85: }
1.4 art 86: if (process_write(ps, pc, &bkpt_insn, BREAKPOINT_LEN)) {
1.1 art 87: warn("Can't write breakpoint at 0x%lx, attempting backout.", pc);
1.4 art 88: if (process_write(ps, pc, &bkpt->bkpt_old, BREAKPOINT_LEN))
1.1 art 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
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)) {
1.6 ! vincent 111: TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list);
1.1 art 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
129: bkpt_delete(struct pstate *ps, struct breakpoint *bkpt)
130: {
131: TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list);
132:
1.4 art 133: if (process_write(ps, bkpt->bkpt_pc, &bkpt->bkpt_old, BREAKPOINT_LEN))
1.1 art 134: warn("Breakpoint removal failed, process unstable");
135:
136: free(bkpt);
137: }
138:
139: /*
140: * Normal standard breakpoint. Keep it.
141: */
142: static int
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
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
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);
1.3 deraadt 189: if (rg == NULL)
190: err(1, "bkpt_check: Can't allocate stack space.");
191:
1.1 art 192: if (md_getregs(ps, rg))
1.5 art 193: err(1, "bkpt_check: Can't get registers");
1.1 art 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, "unkonwn 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
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
268: sstep_normal(struct pstate *ps, void *arg)
269: {
270: return (1); /* stop the command line. */
271: }
272:
273: int
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
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: }