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