1 /* $OpenBSD: pmdb.c,v 1.19 2003/08/18 17:55:57 jfb 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 #include <sys/endian.h>
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <paths.h>
40
41 #include "pmdb.h"
42 #include "symbol.h"
43 #include "clit.h"
44 #include "break.h"
45 #include "core.h"
46
47 static int cmd_show_registers(int, char **, void *);
48 static int cmd_show_backtrace(int, char **, void *);
49 static int cmd_examine(int, char **, void *);
50 static int cmd_quit(int, char **, void *);
51
52 struct clit cmds[] = {
53 /* debugging info commands. */
54 { "regs", "show registers", 0, 0, cmd_show_registers, (void *)-1 },
55 { "trace", "show backtrace", 0, 0, cmd_show_backtrace, (void *)-1 },
56 { "x", "examine memory", 1, 16, cmd_examine, (void *)-1 },
57
58 /* Process handling commands. */
59 { "run", "run process", 0, 0, cmd_process_run, (void *)-1 },
60 { "continue", "continue process", 0, 0, cmd_process_cont, (void *)-1 },
61 { "kill", "kill process", 0, 0, cmd_process_kill, (void *)-1 },
62 { "setenv", "set env variables", 2, 2, cmd_process_setenv, (void *)-1 },
63
64 /* signal handling commands. */
65 { "signal", "ignore signal", 2, 2, cmd_signal_ignore, (void *)-1 },
66 { "sigstate", "show signal state", 0, 0, cmd_signal_show, (void *)-1 },
67
68 /* breakpoints */
69 { "break", "set breakpoint", 1, 1, cmd_bkpt_add, (void *)-1 },
70 { "step", "single step one insn", 0, 0, cmd_sstep, (void *)-1 },
71
72 /* symbols */
73 { "sym_load", "load symbol table", 2, 2, cmd_sym_load, (void *)-1 },
74
75 /* misc commands. */
76 { "help", "print help", 0, 1, cmd_help, NULL },
77 { "quit", "quit", 0, 0, cmd_quit, (void *)-1 },
78 { "exit", "quit", 0, 0, cmd_quit, (void *)-1 },
79 };
80
81 #define NCMDS sizeof(cmds)/sizeof(cmds[0])
82
83 void
usage(void)84 usage(void)
85 {
86 extern char *__progname;
87
88 fprintf(stderr, "Usage: %s [-c core] [-p pid] <program> args\n",
89 __progname);
90 exit(1);
91 }
92
93 int
main(int argc,char ** argv)94 main(int argc, char **argv)
95 {
96 struct pstate ps;
97 int i, c;
98 int status;
99 void *cm;
100 char *pmenv, *core, *perr, execpath[MAXPATHLEN];
101 int level;
102 pid_t pid;
103
104 core = NULL;
105 pid = 0;
106
107 while ((c = getopt(argc, argv, "c:p:")) != -1) {
108 switch(c) {
109 case 'c':
110 core = optarg;
111 break;
112 case 'p':
113 pid = (pid_t) strtol(optarg, &perr, 10);
114 if (*perr != '\0')
115 errx(1, "invalid PID");
116 break;
117 case '?':
118 default:
119 usage();
120 /* NOTREACHED */
121 }
122 }
123 argc -= optind;
124 argv += optind;
125
126 if (argc == 0)
127 usage();
128
129 if ((pmenv = getenv("IN_PMDB")) != NULL) {
130 level = atoi(pmenv);
131 level++;
132 } else
133 level = 0;
134
135 if (level > 0)
136 asprintf(&prompt_add, "(%d)", level);
137 asprintf(&pmenv, "%d", level);
138 setenv("IN_PMDB", pmenv, 1);
139 if (pmenv)
140 free(pmenv);
141
142 if (getexecpath(argv[0], execpath, sizeof(execpath)) == -1) {
143 err(1, "cannot find `%s'", argv[0]);
144 }
145 argv[0] = execpath;
146
147 memset(&ps, 0, sizeof(ps));
148 process_setargv(&ps, argc, argv);
149
150 ps.ps_pid = pid;
151 ps.ps_state = NONE;
152 ps.ps_flags = 0;
153 ps.ps_signum = 0;
154 ps.ps_npc = 1;
155 TAILQ_INIT(&ps.ps_bkpts);
156 TAILQ_INIT(&ps.ps_sstep_cbs);
157
158 signal(SIGINT, SIG_IGN);
159
160 for (i = 0; i < NCMDS; i++)
161 if (cmds[i].arg == (void *)-1)
162 cmds[i].arg = &ps;
163
164 md_def_init();
165 init_sigstate(&ps);
166
167 if ((core != NULL) && (read_core(core, &ps) < 0))
168 warnx("failed to load core file");
169
170 if (process_load(&ps) < 0)
171 errx(1, "failed to load process");
172
173 cm = cmdinit(cmds, NCMDS);
174 while (ps.ps_state != TERMINATED) {
175 int signum;
176 int stopped;
177 int cont;
178
179 if (ps.ps_state == STOPPED) {
180 sym_update(&ps);
181 }
182
183 if (ps.ps_state != RUNNING && cmdloop(cm) == 0) {
184 cmd_quit(0, NULL, &ps);
185 }
186
187 if (ps.ps_state == TERMINATED)
188 break;
189
190 if (wait(&status) == 0)
191 err(1, "wait");
192 if (WIFEXITED(status)) {
193 if ((ps.ps_flags & PSF_KILL) == 0) {
194 ps.ps_state = NONE;
195 } else {
196 ps.ps_state = TERMINATED;
197 }
198 fprintf(stderr, "process exited with status %d\n",
199 WEXITSTATUS(status));
200 continue;
201 }
202 if (WIFSIGNALED(status)) {
203 signum = WTERMSIG(status);
204 stopped = 0;
205 } else {
206 signum = WSTOPSIG(status);
207 stopped = 1;
208 }
209 cont = 0;
210 if (stopped)
211 cont = bkpt_check(&ps);
212 process_signal(&ps, signum, stopped, cont);
213 }
214
215 cmdend(cm);
216
217 sym_destroy(&ps);
218
219 return (0);
220 }
221
222
223 static int
cmd_show_registers(int argc,char ** argv,void * arg)224 cmd_show_registers(int argc, char **argv, void *arg)
225 {
226 struct pstate *ps = arg;
227 char buf[256];
228 int i;
229 reg *rg;
230
231 if (ps->ps_state != STOPPED) {
232 if (ps->ps_flags & PSF_CORE) {
233 /* dump registers from core */
234 core_printregs(ps);
235 return (0);
236 }
237 fprintf(stderr, "process not stopped\n");
238 return (0);
239 }
240
241 rg = alloca(sizeof(*rg) * md_def.nregs);
242
243 if (md_getregs(ps, rg))
244 err(1, "can't get registers");
245 for (i = 0; i < md_def.nregs; i++)
246 printf("%s:\t0x%.*lx\t%s\n", md_def.md_reg_names[i],
247 (int)(sizeof(reg) * 2), (long)rg[i],
248 sym_print(ps, rg[i], buf, sizeof(buf)));
249 return (0);
250 }
251
252 static int
cmd_show_backtrace(int argc,char ** argv,void * arg)253 cmd_show_backtrace(int argc, char **argv, void *arg)
254 {
255 struct pstate *ps = arg;
256 int i;
257
258 if (ps->ps_state != STOPPED && !(ps->ps_flags & PSF_CORE)) {
259 fprintf(stderr, "process not stopped\n");
260 return (0);
261 }
262
263 /* no more than 100 frames */
264 for (i = 0; i < 100; i++) {
265 struct md_frame mfr;
266 char namebuf[1024], *name;
267 reg offs;
268 int j;
269
270 mfr.nargs = -1;
271
272 if (md_getframe(ps, i, &mfr))
273 break;
274
275 name = sym_name_and_offset(ps, mfr.pc, namebuf,
276 sizeof(namebuf), &offs);
277 if (name == NULL) {
278 snprintf(namebuf, sizeof(namebuf), "0x%lx", mfr.pc);
279 name = namebuf;
280 offs = 0;
281 }
282
283 printf("%s(", name);
284 for (j = 0; j < mfr.nargs; j++) {
285 printf("0x%lx", mfr.args[j]);
286 if (j < mfr.nargs - 1)
287 printf(", ");
288 }
289 if (offs == 0) {
290 printf(")\n");
291 } else {
292 printf(")+0x%lx\n", offs);
293 }
294 }
295
296 return (0);
297 }
298
299 static int
cmd_quit(int argc,char ** argv,void * arg)300 cmd_quit(int argc, char **argv, void *arg)
301 {
302 struct pstate *ps = arg;
303
304 if ((ps->ps_flags & PSF_ATCH)) {
305 if ((ps->ps_flags & PSF_ATCH) &&
306 ptrace(PT_DETACH, ps->ps_pid, NULL, 0) < 0)
307 err(1, "ptrace(PT_DETACH)");
308 } else {
309 ps->ps_flags |= PSF_KILL;
310
311 if (process_kill(ps))
312 return (1);
313 }
314
315 ps->ps_state = TERMINATED;
316 return (1);
317 }
318
319 static int
cmd_examine(int argc,char ** argv,void * arg)320 cmd_examine(int argc, char **argv, void *arg)
321 {
322 struct pstate *ps = arg;
323 char buf[256];
324 reg addr, val;
325 int i;
326
327 for (i = 1; argv[i]; i++) {
328
329 addr = strtoul(argv[i], NULL, 0);
330 if (!addr) { /* assume it's a symbol */
331 if (sym_lookup(ps, argv[i], &addr)) {
332 warn( "Can't find: %s", argv[i]);
333 return (0);
334 }
335 }
336
337 if (process_read(ps, addr, &val, sizeof(val)) < 0) {
338 warn("Can't read process contents at 0x%lx", addr);
339 return (0);
340 }
341
342 printf("%s:\t%s\n", argv[i],
343 sym_print(ps, val, buf, sizeof(buf)));
344 }
345
346 return (0);
347 }
348
349 /*
350 * Perform command completion.
351 * Pretty simple. if there are spaces in "buf", the last string is a symbol
352 * otherwise it's a command.
353 */
354 int
cmd_complt(char * buf,size_t buflen)355 cmd_complt(char *buf, size_t buflen)
356 {
357 struct clit *match;
358 char *start;
359 int command;
360 int i, j, len;
361 int onlymatch;
362
363 command = (strchr(buf, ' ') == NULL);
364
365 if (!command) {
366 /* XXX - can't handle symbols yet. */
367 return (-1);
368 }
369
370 start = buf;
371 len = strlen(buf);
372
373 match = NULL;
374 for (i = 0; i < sizeof(cmds) / sizeof(cmds[i]); i++) {
375 if (strncmp(start, cmds[i].cmd, len) == 0) {
376 struct clit *cmdp;
377
378 cmdp = &cmds[i];
379 if (match == NULL) {
380 onlymatch = 1;
381 match = cmdp;
382 strlcpy(buf, match->cmd, buflen);
383 continue;
384 }
385 onlymatch = 0;
386 for (j = len; j < buflen; j++) {
387 if (buf[j] != cmdp->cmd[j]) {
388 buf[j] = '\0';
389 break;
390 }
391 if (cmdp->cmd[j] == '\0')
392 break;
393 }
394 }
395 }
396
397 /*
398 * Be nice. If there could be arguments for this command and it's
399 * the only match append a space.
400 */
401 if (match && onlymatch /*&& match->maxargc > 0*/)
402 strlcat(buf, " ", buflen);
403
404 return (match && onlymatch) ? 0 : -1;
405 }
406
407 /*
408 * The "standard" wrapper
409 */
410 void *
emalloc(size_t sz)411 emalloc(size_t sz)
412 {
413 void *ret;
414 if ((ret = malloc(sz)) == NULL)
415 err(1, "malloc");
416 return (ret);
417 }
418
419
420 /*
421 * Find the first valid path to the executable whose name is <ename>.
422 * The resulting path is stored in <dst>, up to <dlen> - 1 bytes, and is
423 * NUL-terminated. If <dst> is too small, the result will be truncated to
424 * fit, but getexecpath() will return -1.
425 * Returns 0 on success, -1 on failure.
426 */
427
428 int
getexecpath(const char * ename,char * dst,size_t dlen)429 getexecpath(const char *ename, char *dst, size_t dlen)
430 {
431 char *envp, *pathenv, *pp, *pfp;
432 struct stat pstat;
433
434 if (stat(ename, &pstat) == 0) {
435 if (strlcpy(dst, ename, dlen) >= dlen)
436 return (-1);
437 return (0);
438 }
439
440 if (strchr(ename, '/') != NULL) {
441 /* don't bother looking in PATH */
442 return (-1);
443 }
444
445 envp = getenv("PATH");
446 if (envp == NULL)
447 envp = _PATH_DEFPATH;
448
449 pathenv = strdup(envp);
450 if (pathenv == NULL) {
451 warn("failed to allocate PATH buffer");
452 return (-1);
453 }
454
455 for (pp = pathenv; (pfp = strsep(&pp, ":")) != NULL; ) {
456 /* skip cwd, was already tested */
457 if (*pfp == '\0')
458 continue;
459
460 if (snprintf(dst, dlen, "%s/%s", pfp, ename) >= (int)dlen)
461 continue;
462
463 if (stat(dst, &pstat) != -1) {
464 free(pathenv);
465 return (0);
466 }
467 }
468
469 free(pathenv);
470 return (-1);
471 }
472