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