Annotation of src/usr.bin/pmdb/break.c, Revision 1.4
1.4 ! art 1: /* $OpenBSD: break.c,v 1.3 2002/07/12 00:24:53 deraadt 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:
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
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)) {
111: free(bkpt);
112: return (-1);
113: }
114: }
115:
116: cb = emalloc(sizeof(*cb));
117: cb->cb_fun = fun;
118: cb->cb_arg = arg;
119: TAILQ_INSERT_TAIL(&bkpt->bkpt_cbs, cb, cb_list);
120:
121: return (0);
122: }
123:
124: /*
125: * Disable and delete a breakpoint.
126: */
127: void
128: bkpt_delete(struct pstate *ps, struct breakpoint *bkpt)
129: {
130: TAILQ_REMOVE(&ps->ps_bkpts, bkpt, bkpt_list);
131:
1.4 ! art 132: if (process_write(ps, bkpt->bkpt_pc, &bkpt->bkpt_old, BREAKPOINT_LEN))
1.1 art 133: warn("Breakpoint removal failed, process unstable");
134:
135: free(bkpt);
136: }
137:
138: /*
139: * Normal standard breakpoint. Keep it.
140: */
141: static int
142: bkpt_normal(struct pstate *ps, void *arg)
143: {
144: return (BKPT_KEEP_STOP);
145: }
146:
147: /*
148: * Single-step callback for "stepping over" a breakpoint (we restore the
149: * breakpoint instruction to what it was, single-step over it and then
150: * call this function).
151: */
152: static int
153: sstep_bkpt_readd(struct pstate *ps, void *arg)
154: {
155: reg pc = (reg)arg;
156:
157: bkpt_add_cb(ps, pc, bkpt_normal, NULL);
158:
159: return (0); /* let the process continue */
160: }
161:
162: /*
163: * Return 0 for stop, 1 for silent continue.
164: */
165: int
166: bkpt_check(struct pstate *ps)
167: {
168: struct breakpoint *bkpt;
169: struct callback *cb;
170: TAILQ_HEAD(,callback) sstep_cbs;
171: reg *rg, pc;
172: int ret;
173: int didsome = 0;
174: int stop = 0;
175:
176: /* Requeue all single-step callbacks because bkpts can add ssteps. */
177: TAILQ_INIT(&sstep_cbs);
178: while ((cb = TAILQ_FIRST(&ps->ps_sstep_cbs)) != NULL) {
179: TAILQ_REMOVE(&ps->ps_sstep_cbs, cb, cb_list);
180: TAILQ_INSERT_TAIL(&sstep_cbs, cb, cb_list);
181: }
182:
183: /*
184: * The default is to stop. Unless we do some processing and none
185: * of the callbacks require a stop.
186: */
187: rg = alloca(sizeof(*rg) * md_def.nregs);
1.3 deraadt 188: if (rg == NULL)
189: err(1, "bkpt_check: Can't allocate stack space.");
190:
1.1 art 191: if (md_getregs(ps, rg))
192: err(1, "bkpt_check: Can't get registers.");
193:
194: pc = rg[md_def.pcoff];
195: pc -= BREAKPOINT_DECR_PC;
196:
197: bkpt = bkpt_find_at_pc(ps, pc);
198: if (bkpt == NULL)
199: goto sstep;
200:
201: ps->ps_npc = pc;
202:
203: while ((cb = TAILQ_FIRST(&bkpt->bkpt_cbs)) != NULL) {
204: didsome = 1;
205: TAILQ_REMOVE(&bkpt->bkpt_cbs, cb, cb_list);
206: ret = (*cb->cb_fun)(ps, cb->cb_arg);
207: free(cb);
208: switch (ret) {
209: case BKPT_DEL_STOP:
210: stop = 1;
211: case BKPT_DEL_CONT:
212: break;
213: case BKPT_KEEP_STOP:
214: stop = 1;
215: case BKPT_KEEP_CONT:
216: sstep_set(ps, sstep_bkpt_readd, (void *)bkpt->bkpt_pc);
217: break;
218: default:
219: errx(1, "unkonwn bkpt_fun return, internal error");
220: }
221: }
222:
223: bkpt_delete(ps, bkpt);
224:
225: sstep:
226:
227: while ((cb = TAILQ_FIRST(&sstep_cbs)) != NULL) {
228: didsome = 1;
229: TAILQ_REMOVE(&sstep_cbs, cb, cb_list);
230: stop |= (*cb->cb_fun)(ps, cb->cb_arg);
231: free(cb);
232: }
233: ps->ps_flags &= ~PSF_STEP;
234:
235: return (didsome && !stop);
236: }
237:
238: int
239: cmd_bkpt_add(int argc, char **argv, void *arg)
240: {
241: struct pstate *ps = arg;
242: char *ep, *bkpt_name;
243: reg pc;
244:
245: if (ps->ps_state != STOPPED && ps->ps_state != LOADED) {
246: fprintf(stderr, "Process not loaded and stopped %d\n",
247: ps->ps_state);
248: return (0);
249: }
250:
251: bkpt_name = argv[1];
252: pc = strtol(bkpt_name, &ep, 0);
253: if (bkpt_name[0] == '\0' || *ep != '\0' || pc < 1) {
254: if (sym_lookup(ps, bkpt_name, &pc)) {
255: warnx("%s is not a valid pc", bkpt_name);
256: return (0);
257: }
258: }
259:
260: if (bkpt_add_cb(ps, pc, bkpt_normal, 0))
261: warn("Can't set break point");
262:
263: return (0);
264: }
265:
266: static int
267: sstep_normal(struct pstate *ps, void *arg)
268: {
269: return (1); /* stop the command line. */
270: }
271:
272: int
273: cmd_sstep(int argc, char **argv, void *arg)
274: {
275: struct pstate *ps = arg;
276:
277: if (ps->ps_state != STOPPED) {
278: fprintf(stderr, "Process not loaded and stopped %d\n",
279: ps->ps_state);
280: return 0;
281: }
282:
283: if (sstep_set(ps, sstep_normal, NULL))
284: warn("Can't set single step");
285:
286: return (cmd_process_cont(argc, argv, arg));
287: }
288:
289: int
290: sstep_set(struct pstate *ps, int (*fun)(struct pstate *, void *), void *arg)
291: {
292: struct callback *cb;
293:
294: cb = emalloc(sizeof(*cb));
295: cb->cb_fun = fun;
296: cb->cb_arg = arg;
297: TAILQ_INSERT_TAIL(&ps->ps_sstep_cbs, cb, cb_list);
298:
299: ps->ps_flags |= PSF_STEP;
300:
301: return (0);
302: }