xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_cmds.c (revision bbf21555)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 2012 by Delphix. All rights reserved.
29  * Copyright 2021 Joyent, Inc.
30  * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
31  * Copyright (c) 2015, 2017 by Delphix. All rights reserved.
32  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
33  */
34 
35 #include <sys/elf.h>
36 #include <sys/elf_SPARC.h>
37 
38 #include <libproc.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <alloca.h>
44 #include <libctf.h>
45 #include <ctype.h>
46 
47 #include <mdb/mdb_string.h>
48 #include <mdb/mdb_argvec.h>
49 #include <mdb/mdb_nv.h>
50 #include <mdb/mdb_fmt.h>
51 #include <mdb/mdb_target.h>
52 #include <mdb/mdb_err.h>
53 #include <mdb/mdb_debug.h>
54 #include <mdb/mdb_conf.h>
55 #include <mdb/mdb_module.h>
56 #include <mdb/mdb_modapi.h>
57 #include <mdb/mdb_stdlib.h>
58 #include <mdb/mdb_lex.h>
59 #include <mdb/mdb_io_impl.h>
60 #include <mdb/mdb_help.h>
61 #include <mdb/mdb_disasm.h>
62 #include <mdb/mdb_frame.h>
63 #include <mdb/mdb_evset.h>
64 #include <mdb/mdb_print.h>
65 #include <mdb/mdb_nm.h>
66 #include <mdb/mdb_set.h>
67 #include <mdb/mdb_demangle.h>
68 #include <mdb/mdb_ctf.h>
69 #include <mdb/mdb_whatis.h>
70 #include <mdb/mdb_whatis_impl.h>
71 #include <mdb/mdb_macalias.h>
72 #include <mdb/mdb_tab.h>
73 #include <mdb/mdb_typedef.h>
74 #include <mdb/mdb_linkerset.h>
75 #ifdef _KMDB
76 #include <kmdb/kmdb_kdi.h>
77 #endif
78 #include <mdb/mdb.h>
79 
80 #ifdef __sparc
81 #define	SETHI_MASK	0xc1c00000
82 #define	SETHI_VALUE	0x01000000
83 
84 #define	IS_SETHI(machcode)	(((machcode) & SETHI_MASK) == SETHI_VALUE)
85 
86 #define	OP(machcode)	((machcode) >> 30)
87 #define	OP3(machcode)	(((machcode) >> 19) & 0x3f)
88 #define	RD(machcode)	(((machcode) >> 25) & 0x1f)
89 #define	RS1(machcode)	(((machcode) >> 14) & 0x1f)
90 #define	I(machcode)	(((machcode) >> 13) & 0x01)
91 
92 #define	IMM13(machcode)	((machcode) & 0x1fff)
93 #define	IMM22(machcode)	((machcode) & 0x3fffff)
94 
95 #define	OP_ARITH_MEM_MASK	0x2
96 #define	OP_ARITH		0x2
97 #define	OP_MEM			0x3
98 
99 #define	OP3_CC_MASK		0x10
100 #define	OP3_COMPLEX_MASK	0x20
101 
102 #define	OP3_ADD			0x00
103 #define	OP3_OR			0x02
104 #define	OP3_XOR			0x03
105 
106 #ifndef	R_O7
107 #define	R_O7	0xf
108 #endif
109 #endif /* __sparc */
110 
111 static mdb_tgt_addr_t
write_uint8(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t ull,uint_t rdback)112 write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
113 {
114 	uint8_t o, n = (uint8_t)ull;
115 
116 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
117 	    addr) == -1)
118 		return (addr);
119 
120 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
121 		return (addr);
122 
123 	if (rdback) {
124 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
125 			return (addr);
126 
127 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
128 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
129 	}
130 
131 	return (addr + sizeof (n));
132 }
133 
134 static mdb_tgt_addr_t
write_uint16(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t ull,uint_t rdback)135 write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
136 {
137 	uint16_t o, n = (uint16_t)ull;
138 
139 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
140 	    addr) == -1)
141 		return (addr);
142 
143 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
144 		return (addr);
145 
146 	if (rdback) {
147 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
148 			return (addr);
149 
150 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
151 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
152 	}
153 
154 	return (addr + sizeof (n));
155 }
156 
157 static mdb_tgt_addr_t
write_uint32(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t ull,uint_t rdback)158 write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
159 {
160 	uint32_t o, n = (uint32_t)ull;
161 
162 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
163 	    addr) == -1)
164 		return (addr);
165 
166 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
167 		return (addr);
168 
169 	if (rdback) {
170 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
171 			return (addr);
172 
173 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
174 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
175 	}
176 
177 	return (addr + sizeof (n));
178 }
179 
180 static mdb_tgt_addr_t
write_uint64(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t n,uint_t rdback)181 write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
182 {
183 	uint64_t o;
184 
185 	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
186 	    addr) == -1)
187 		return (addr);
188 
189 	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
190 		return (addr);
191 
192 	if (rdback) {
193 		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
194 			return (addr);
195 
196 		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
197 		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
198 	}
199 
200 	return (addr + sizeof (n));
201 }
202 
203 /*
204  * Writes to objects of size 1, 2, 4, or 8 bytes. The function
205  * doesn't care if the object is a number or not (e.g. it could
206  * be a byte array, or a struct) as long as the size of the write
207  * is one of the aforementioned ones.
208  */
209 static mdb_tgt_addr_t
write_var_uint(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t val,size_t size,uint_t rdback)210 write_var_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, size_t size,
211     uint_t rdback)
212 {
213 	if (size < sizeof (uint64_t)) {
214 		uint64_t max_num = 1ULL << (size * NBBY);
215 
216 		if (val >= max_num) {
217 			uint64_t write_len = 0;
218 
219 			/* count bytes needed for val */
220 			while (val != 0) {
221 				write_len++;
222 				val >>= NBBY;
223 			}
224 
225 			mdb_warn("value too big for the length of the write: "
226 			    "supplied %llu bytes but maximum is %llu bytes\n",
227 			    (u_longlong_t)write_len, (u_longlong_t)size);
228 			return (addr);
229 		}
230 	}
231 
232 	switch (size) {
233 	case 1:
234 		return (write_uint8(as, addr, val, rdback));
235 	case 2:
236 		return (write_uint16(as, addr, val, rdback));
237 	case 4:
238 		return (write_uint32(as, addr, val, rdback));
239 	case 8:
240 		return (write_uint64(as, addr, val, rdback));
241 	default:
242 		mdb_warn("writes of size %u are not supported\n ", size);
243 		return (addr);
244 	}
245 }
246 
247 static mdb_tgt_addr_t
write_ctf_uint(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t n,uint_t rdback)248 write_ctf_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
249 {
250 	mdb_ctf_id_t mid;
251 	size_t size;
252 	ssize_t type_size;
253 	int kind;
254 
255 	if (mdb_ctf_lookup_by_addr(addr, &mid) != 0) {
256 		mdb_warn("no CTF data found at this address\n");
257 		return (addr);
258 	}
259 
260 	kind = mdb_ctf_type_kind(mid);
261 	if (kind == CTF_ERR) {
262 		mdb_warn("CTF data found but type kind could not be read");
263 		return (addr);
264 	}
265 
266 	if (kind == CTF_K_TYPEDEF) {
267 		mdb_ctf_id_t temp_id;
268 		if (mdb_ctf_type_resolve(mid, &temp_id) != 0) {
269 			mdb_warn("failed to resolve type");
270 			return (addr);
271 		}
272 		kind = mdb_ctf_type_kind(temp_id);
273 	}
274 
275 	if (kind != CTF_K_INTEGER && kind != CTF_K_POINTER &&
276 	    kind != CTF_K_ENUM) {
277 		mdb_warn("CTF type should be integer, pointer, or enum\n");
278 		return (addr);
279 	}
280 
281 	type_size = mdb_ctf_type_size(mid);
282 	if (type_size < 0) {
283 		mdb_warn("CTF data found but size could not be read");
284 		return (addr);
285 	}
286 	size = type_size;
287 
288 	return (write_var_uint(as, addr, n, size, rdback));
289 }
290 
291 static int
write_arglist(mdb_tgt_as_t as,mdb_tgt_addr_t addr,int argc,const mdb_arg_t * argv)292 write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
293     int argc, const mdb_arg_t *argv)
294 {
295 	mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
296 	    uint64_t, uint_t);
297 	mdb_tgt_addr_t naddr;
298 	uintmax_t value;
299 	int rdback = mdb.m_flags & MDB_FL_READBACK;
300 	size_t i;
301 
302 	if (argc == 1) {
303 		mdb_warn("expected value to write following %c\n",
304 		    argv->a_un.a_char);
305 		return (DCMD_ERR);
306 	}
307 
308 	switch (argv->a_un.a_char) {
309 	case 'v':
310 		write_value = write_uint8;
311 		break;
312 	case 'w':
313 		write_value = write_uint16;
314 		break;
315 	case 'z':
316 		write_value = write_ctf_uint;
317 		break;
318 	case 'W':
319 		write_value = write_uint32;
320 		break;
321 	case 'Z':
322 		write_value = write_uint64;
323 		break;
324 	default:
325 		write_value = NULL;
326 		break;
327 	}
328 
329 	for (argv++, i = 1; i < argc; i++, argv++) {
330 		if (argv->a_type == MDB_TYPE_CHAR) {
331 			mdb_warn("expected immediate value instead of '%c'\n",
332 			    argv->a_un.a_char);
333 			return (DCMD_ERR);
334 		}
335 
336 		if (argv->a_type == MDB_TYPE_STRING) {
337 			if (mdb_eval(argv->a_un.a_str) == -1) {
338 				mdb_warn("failed to write \"%s\"",
339 				    argv->a_un.a_str);
340 				return (DCMD_ERR);
341 			}
342 			value = mdb_nv_get_value(mdb.m_dot);
343 		} else
344 			value = argv->a_un.a_val;
345 
346 		mdb_nv_set_value(mdb.m_dot, addr);
347 
348 		if ((naddr = write_value(as, addr, value, rdback)) == addr) {
349 			mdb_warn("failed to write %llr at address 0x%llx",
350 			    value, addr);
351 			mdb.m_incr = 0;
352 			return (DCMD_ERR);
353 		}
354 
355 		mdb.m_incr = naddr - addr;
356 		addr = naddr;
357 	}
358 
359 	return (DCMD_OK);
360 }
361 
362 static mdb_tgt_addr_t
match_uint16(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t v64,uint64_t m64)363 match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
364 {
365 	uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
366 
367 	for (; mdb_tgt_aread(mdb.m_target, as, &x,
368 	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
369 
370 		if ((x & mask) == val) {
371 			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
372 			break;
373 		}
374 	}
375 	return (addr);
376 }
377 
378 static mdb_tgt_addr_t
match_uint32(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t v64,uint64_t m64)379 match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
380 {
381 	uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
382 
383 	for (; mdb_tgt_aread(mdb.m_target, as, &x,
384 	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
385 
386 		if ((x & mask) == val) {
387 			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
388 			break;
389 		}
390 	}
391 	return (addr);
392 }
393 
394 static mdb_tgt_addr_t
match_uint64(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t val,uint64_t mask)395 match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
396 {
397 	uint64_t x;
398 
399 	for (; mdb_tgt_aread(mdb.m_target, as, &x,
400 	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
401 
402 		if ((x & mask) == val) {
403 			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
404 			break;
405 		}
406 	}
407 	return (addr);
408 }
409 
410 static int
match_arglist(mdb_tgt_as_t as,uint_t flags,mdb_tgt_addr_t addr,int argc,const mdb_arg_t * argv)411 match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
412     int argc, const mdb_arg_t *argv)
413 {
414 	mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
415 	    uint64_t, uint64_t);
416 
417 	uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
418 	size_t i;
419 
420 	if (argc < 2) {
421 		mdb_warn("expected value following %c\n", argv->a_un.a_char);
422 		return (DCMD_ERR);
423 	}
424 
425 	if (argc > 3) {
426 		mdb_warn("only value and mask may follow %c\n",
427 		    argv->a_un.a_char);
428 		return (DCMD_ERR);
429 	}
430 
431 	switch (argv->a_un.a_char) {
432 	case 'l':
433 		match_value = match_uint16;
434 		break;
435 	case 'L':
436 		match_value = match_uint32;
437 		break;
438 	case 'M':
439 		match_value = match_uint64;
440 		break;
441 	default:
442 		mdb_warn("unknown match value %c\n",
443 		    argv->a_un.a_char);
444 		return (DCMD_ERR);
445 	}
446 
447 	for (argv++, i = 1; i < argc; i++, argv++) {
448 		if (argv->a_type == MDB_TYPE_CHAR) {
449 			mdb_warn("expected immediate value instead of '%c'\n",
450 			    argv->a_un.a_char);
451 			return (DCMD_ERR);
452 		}
453 
454 		if (argv->a_type == MDB_TYPE_STRING) {
455 			if (mdb_eval(argv->a_un.a_str) == -1) {
456 				mdb_warn("failed to evaluate \"%s\"",
457 				    argv->a_un.a_str);
458 				return (DCMD_ERR);
459 			}
460 			args[i - 1] = mdb_nv_get_value(mdb.m_dot);
461 		} else
462 			args[i - 1] = argv->a_un.a_val;
463 	}
464 
465 	addr = match_value(as, addr, args[0], args[1]);
466 	mdb_nv_set_value(mdb.m_dot, addr);
467 
468 	/*
469 	 * In adb(1), the match operators ignore any repeat count that has
470 	 * been applied to them.  We emulate this undocumented property
471 	 * by returning DCMD_ABORT if our input is not a pipeline.
472 	 */
473 	return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
474 }
475 
476 static int
argncmp(int argc,const mdb_arg_t * argv,const char * s)477 argncmp(int argc, const mdb_arg_t *argv, const char *s)
478 {
479 	for (; *s != '\0'; s++, argc--, argv++) {
480 		if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
481 			return (FALSE);
482 		if (argv->a_un.a_char != *s)
483 			return (FALSE);
484 	}
485 	return (TRUE);
486 }
487 
488 static int
print_arglist(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)489 print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
490     int argc, const mdb_arg_t *argv)
491 {
492 	char buf[MDB_TGT_SYM_NAMLEN];
493 	mdb_tgt_addr_t oaddr = addr;
494 	mdb_tgt_addr_t naddr;
495 	GElf_Sym sym;
496 	size_t i, n;
497 
498 	if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
499 		const char *fmt;
500 		int is_dis;
501 		/*
502 		 * This is nasty, but necessary for precise adb compatibility.
503 		 * Detect disassembly format by looking for "ai" or "ia":
504 		 */
505 		if (argncmp(argc, argv, "ai")) {
506 			fmt = "%-#*lla\n";
507 			is_dis = TRUE;
508 		} else if (argncmp(argc, argv, "ia")) {
509 			fmt = "%-#*lla";
510 			is_dis = TRUE;
511 		} else {
512 			fmt = "%-#*lla%16T";
513 			is_dis = FALSE;
514 		}
515 
516 		/*
517 		 * If symbolic decoding is on, disassembly is off, and the
518 		 * address exactly matches a symbol, print the symbol name:
519 		 */
520 		if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
521 		    (as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
522 		    mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
523 		    MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
524 			mdb_iob_printf(mdb.m_out, "%s:\n", buf);
525 
526 		/*
527 		 * If this is a virtual address, cast it so that it reflects
528 		 * only the valid component of the address.
529 		 */
530 		if (as == MDB_TGT_AS_VIRT)
531 			addr = (uintptr_t)addr;
532 
533 		mdb_iob_printf(mdb.m_out, fmt,
534 		    (uint_t)mdb_iob_getmargin(mdb.m_out), addr);
535 	}
536 
537 	if (argc == 0) {
538 		/*
539 		 * Yes, for you trivia buffs: if you use a format verb and give
540 		 * no format string, you get: X^"= "i ... note that in adb the
541 		 * the '=' verb once had 'z' as its default, but then 'z' was
542 		 * deleted (it was once an alias for 'i') and so =\n now calls
543 		 * scanform("z") and produces a 'bad modifier' message.
544 		 */
545 		static const mdb_arg_t def_argv[] = {
546 			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
547 			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
548 			{ MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
549 			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
550 		};
551 
552 		argc = sizeof (def_argv) / sizeof (mdb_arg_t);
553 		argv = def_argv;
554 	}
555 
556 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
557 
558 	for (i = 0, n = 1; i < argc; i++, argv++) {
559 		switch (argv->a_type) {
560 		case MDB_TYPE_CHAR:
561 			naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
562 			    argv->a_un.a_char);
563 			mdb.m_incr = naddr - addr;
564 			addr = naddr;
565 			n = 1;
566 			break;
567 
568 		case MDB_TYPE_IMMEDIATE:
569 			n = argv->a_un.a_val;
570 			break;
571 
572 		case MDB_TYPE_STRING:
573 			mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
574 			n = 1;
575 			break;
576 		}
577 	}
578 
579 	mdb.m_incr = addr - oaddr;
580 	mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
581 	return (DCMD_OK);
582 }
583 
584 static int
print_common(mdb_tgt_as_t as,uint_t flags,int argc,const mdb_arg_t * argv)585 print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
586 {
587 	mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
588 
589 	if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
590 		if (strchr("vwzWZ", argv->a_un.a_char))
591 			return (write_arglist(as, addr, argc, argv));
592 		if (strchr("lLM", argv->a_un.a_char))
593 			return (match_arglist(as, flags, addr, argc, argv));
594 	}
595 
596 	return (print_arglist(as, addr, flags, argc, argv));
597 }
598 
599 /*ARGSUSED*/
600 static int
cmd_print_core(uintptr_t x,uint_t flags,int argc,const mdb_arg_t * argv)601 cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
602 {
603 	return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
604 }
605 
606 #ifndef _KMDB
607 /*ARGSUSED*/
608 static int
cmd_print_object(uintptr_t x,uint_t flags,int argc,const mdb_arg_t * argv)609 cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
610 {
611 	return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
612 }
613 #endif
614 
615 /*ARGSUSED*/
616 static int
cmd_print_phys(uintptr_t x,uint_t flags,int argc,const mdb_arg_t * argv)617 cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
618 {
619 	return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
620 }
621 
622 /*ARGSUSED*/
623 static int
cmd_print_value(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)624 cmd_print_value(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
625 {
626 	uintmax_t ndot, dot = mdb_get_dot();
627 	const char *tgt_argv[1];
628 	mdb_tgt_t *t;
629 	size_t i, n;
630 
631 	if (argc == 0) {
632 		mdb_warn("expected one or more format characters "
633 		    "following '='\n");
634 		return (DCMD_ERR);
635 	}
636 
637 	tgt_argv[0] = (const char *)&dot;
638 	t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
639 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
640 
641 	for (i = 0, n = 1; i < argc; i++, argv++) {
642 		switch (argv->a_type) {
643 		case MDB_TYPE_CHAR:
644 			ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
645 			    dot, n, argv->a_un.a_char);
646 			if (argv->a_un.a_char == '+' ||
647 			    argv->a_un.a_char == '-')
648 				dot = ndot;
649 			n = 1;
650 			break;
651 
652 		case MDB_TYPE_IMMEDIATE:
653 			n = argv->a_un.a_val;
654 			break;
655 
656 		case MDB_TYPE_STRING:
657 			mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
658 			n = 1;
659 			break;
660 		}
661 	}
662 
663 	mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
664 	mdb_nv_set_value(mdb.m_dot, dot);
665 	mdb.m_incr = 0;
666 
667 	mdb_tgt_destroy(t);
668 	return (DCMD_OK);
669 }
670 
671 /*ARGSUSED*/
672 static int
cmd_assign_variable(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)673 cmd_assign_variable(uintptr_t addr, uint_t flags,
674     int argc, const mdb_arg_t *argv)
675 {
676 	uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
677 	const char *p;
678 	mdb_var_t *v;
679 
680 	if (argc == 2) {
681 		if (argv->a_type != MDB_TYPE_CHAR) {
682 			mdb_warn("improper arguments following '>' operator\n");
683 			return (DCMD_ERR);
684 		}
685 
686 		switch (argv->a_un.a_char) {
687 		case 'c':
688 			addr = *((uchar_t *)&addr);
689 			break;
690 		case 's':
691 			addr = *((ushort_t *)&addr);
692 			break;
693 		case 'i':
694 			addr = *((uint_t *)&addr);
695 			break;
696 		case 'l':
697 			addr = *((ulong_t *)&addr);
698 			break;
699 		default:
700 			mdb_warn("%c is not a valid // modifier\n",
701 			    argv->a_un.a_char);
702 			return (DCMD_ERR);
703 		}
704 
705 		dot = addr;
706 		argv++;
707 		argc--;
708 	}
709 
710 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
711 		mdb_warn("expected single variable name following '>'\n");
712 		return (DCMD_ERR);
713 	}
714 
715 	if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
716 		mdb_warn("variable names may not exceed %d characters\n",
717 		    MDB_NV_NAMELEN - 1);
718 		return (DCMD_ERR);
719 	}
720 
721 	if ((p = strbadid(argv->a_un.a_str)) != NULL) {
722 		mdb_warn("'%c' may not be used in a variable name\n", *p);
723 		return (DCMD_ERR);
724 	}
725 
726 	if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
727 		(void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
728 	else
729 		mdb_nv_set_value(v, dot);
730 
731 	mdb.m_incr = 0;
732 	return (DCMD_OK);
733 }
734 
735 static int
print_soutype(const char * sou,uintptr_t addr,uint_t flags)736 print_soutype(const char *sou, uintptr_t addr, uint_t flags)
737 {
738 	static const char *prefixes[] = { "struct ", "union " };
739 	size_t namesz = 7 + strlen(sou) + 1;
740 	char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
741 	mdb_ctf_id_t id;
742 	int i;
743 
744 	for (i = 0; i < 2; i++) {
745 		(void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
746 
747 		if (mdb_ctf_lookup_by_name(name, &id) == 0) {
748 			mdb_arg_t v;
749 			int rv;
750 
751 			v.a_type = MDB_TYPE_STRING;
752 			v.a_un.a_str = name;
753 
754 			rv = mdb_call_dcmd("print", addr, flags, 1, &v);
755 			return (rv);
756 		}
757 	}
758 
759 	return (DCMD_ERR);
760 }
761 
762 static int
print_type(const char * name,uintptr_t addr,uint_t flags)763 print_type(const char *name, uintptr_t addr, uint_t flags)
764 {
765 	mdb_ctf_id_t id;
766 	char *sname;
767 	size_t snamesz;
768 	int rv;
769 
770 	if (!(flags & DCMD_ADDRSPEC)) {
771 		addr = mdb_get_dot();
772 		flags |= DCMD_ADDRSPEC;
773 	}
774 
775 	if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
776 		return (rv);
777 
778 	snamesz = strlen(name) + 3;
779 	sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
780 	(void) mdb_snprintf(sname, snamesz, "%s_t", name);
781 
782 	if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
783 		mdb_arg_t v;
784 		int rv;
785 
786 		v.a_type = MDB_TYPE_STRING;
787 		v.a_un.a_str = sname;
788 
789 		rv = mdb_call_dcmd("print", addr, flags, 1, &v);
790 		return (rv);
791 	}
792 
793 	sname[snamesz - 2] = 's';
794 	rv = print_soutype(sname, addr, flags);
795 	return (rv);
796 }
797 
798 static int
exec_alias(const char * fname,uintptr_t addr,uint_t flags)799 exec_alias(const char *fname, uintptr_t addr, uint_t flags)
800 {
801 	const char *alias;
802 	int rv;
803 
804 	if ((alias = mdb_macalias_lookup(fname)) == NULL)
805 		return (DCMD_ERR);
806 
807 	if (flags & DCMD_ADDRSPEC) {
808 		size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
809 		char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
810 		(void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
811 		rv = mdb_eval(addralias);
812 	} else {
813 		rv = mdb_eval(alias);
814 	}
815 
816 	return (rv == -1 ? DCMD_ABORT : DCMD_OK);
817 }
818 
819 /*ARGSUSED*/
820 static int
cmd_src_file(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)821 cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
822 {
823 	const char *fname;
824 	mdb_io_t *fio;
825 	int rv;
826 
827 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
828 		return (DCMD_USAGE);
829 
830 	fname = argv->a_un.a_str;
831 
832 	if (flags & DCMD_PIPE_OUT) {
833 		mdb_warn("macro files cannot be used as input to a pipeline\n");
834 		return (DCMD_ABORT);
835 	}
836 
837 	if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
838 	    O_RDONLY, 0)) != NULL) {
839 		mdb_frame_t *fp = mdb.m_frame;
840 		int err;
841 
842 		mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
843 		mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
844 		err = mdb_run();
845 
846 		ASSERT(fp == mdb.m_frame);
847 		mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
848 		yylineno = mdb_iob_lineno(mdb.m_in);
849 
850 		if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
851 			longjmp(fp->f_pcb, err);
852 
853 		if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
854 		    err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
855 			longjmp(fp->f_pcb, err);
856 
857 		return (DCMD_OK);
858 	}
859 
860 	if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
861 	    (rv = print_type(fname, addr, flags)) != DCMD_ERR)
862 		return (rv);
863 
864 	mdb_warn("failed to open %s (see ::help '$<')\n", fname);
865 	return (DCMD_ABORT);
866 }
867 
868 static int
cmd_exec_file(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)869 cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
870 {
871 	const char *fname;
872 	mdb_io_t *fio;
873 	int rv;
874 
875 	/*
876 	 * The syntax [expr[,count]]$< with no trailing macro file name is
877 	 * magic in that if count is zero, this command won't be called and
878 	 * the expression is thus a no-op.  If count is non-zero, we get
879 	 * invoked with argc == 0, and this means abort the current macro.
880 	 * If our debugger stack depth is greater than one, we may be using
881 	 * $< from within a previous $<<, so in that case we set m_in to
882 	 * NULL to force this entire frame to be popped.
883 	 */
884 	if (argc == 0) {
885 		if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
886 			mdb_iob_destroy(mdb.m_in);
887 			mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
888 		} else if (mdb.m_depth > 1) {
889 			mdb_iob_destroy(mdb.m_in);
890 			mdb.m_in = NULL;
891 		} else
892 			mdb_warn("input stack is empty\n");
893 		return (DCMD_OK);
894 	}
895 
896 	if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
897 		return (cmd_src_file(addr, flags, argc, argv));
898 
899 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
900 		return (DCMD_USAGE);
901 
902 	fname = argv->a_un.a_str;
903 
904 	if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
905 	    O_RDONLY, 0)) != NULL) {
906 		mdb_iob_destroy(mdb.m_in);
907 		mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
908 		return (DCMD_OK);
909 	}
910 
911 	if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
912 	    (rv = print_type(fname, addr, flags)) != DCMD_ERR)
913 		return (rv);
914 
915 	mdb_warn("failed to open %s (see ::help '$<')\n", fname);
916 	return (DCMD_ABORT);
917 }
918 
919 #ifndef _KMDB
920 /*ARGSUSED*/
921 static int
cmd_cat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)922 cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
923 {
924 	int status = DCMD_OK;
925 	char buf[BUFSIZ];
926 	mdb_iob_t *iob;
927 	mdb_io_t *fio;
928 
929 	if (flags & DCMD_ADDRSPEC)
930 		return (DCMD_USAGE);
931 
932 	for (; argc-- != 0; argv++) {
933 		if (argv->a_type != MDB_TYPE_STRING) {
934 			mdb_warn("expected string argument\n");
935 			status = DCMD_ERR;
936 			continue;
937 		}
938 
939 		if ((fio = mdb_fdio_create_path(NULL,
940 		    argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
941 			mdb_warn("failed to open %s", argv->a_un.a_str);
942 			status = DCMD_ERR;
943 			continue;
944 		}
945 
946 		iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
947 
948 		while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
949 			ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
950 			if (len > 0) {
951 				if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
952 					if (errno != EPIPE)
953 						mdb_warn("write failed");
954 					status = DCMD_ERR;
955 					break;
956 				}
957 			}
958 		}
959 
960 		if (mdb_iob_err(iob))
961 			mdb_warn("error while reading %s", mdb_iob_name(iob));
962 
963 		mdb_iob_destroy(iob);
964 	}
965 
966 	return (status);
967 }
968 #endif
969 
970 /*ARGSUSED*/
971 static int
cmd_grep(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)972 cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
973 {
974 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
975 		return (DCMD_USAGE);
976 
977 	if (mdb_eval(argv->a_un.a_str) == -1)
978 		return (DCMD_ABORT);
979 
980 	if (mdb_get_dot() != 0)
981 		mdb_printf("%lr\n", addr);
982 
983 	return (DCMD_OK);
984 }
985 
986 /*ARGSUSED*/
987 static int
cmd_map(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)988 cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
989 {
990 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
991 		return (DCMD_USAGE);
992 
993 	if (mdb_eval(argv->a_un.a_str) == -1)
994 		return (DCMD_ABORT);
995 
996 	mdb_printf("%llr\n", mdb_get_dot());
997 	return (DCMD_OK);
998 }
999 
1000 /*ARGSUSED*/
1001 static int
cmd_notsup(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1002 cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1003 {
1004 	mdb_warn("command is not supported by current target\n");
1005 	return (DCMD_ERR);
1006 }
1007 
1008 /*ARGSUSED*/
1009 static int
cmd_quit(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1010 cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1011 {
1012 #ifdef _KMDB
1013 	uint_t opt_u = FALSE;
1014 
1015 	if (mdb_getopts(argc, argv,
1016 	    'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
1017 		return (DCMD_USAGE);
1018 
1019 	if (opt_u) {
1020 		if (mdb.m_flags & MDB_FL_NOUNLOAD) {
1021 			warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
1022 			return (DCMD_ERR);
1023 		}
1024 
1025 		kmdb_kdi_set_unload_request();
1026 	}
1027 #endif
1028 
1029 	longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
1030 	/*NOTREACHED*/
1031 	return (DCMD_ERR);
1032 }
1033 
1034 #ifdef _KMDB
1035 static void
quit_help(void)1036 quit_help(void)
1037 {
1038 	mdb_printf(
1039 	    "-u    unload the debugger (if not loaded at boot)\n");
1040 }
1041 #endif
1042 
1043 /*ARGSUSED*/
1044 static int
cmd_vars(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1045 cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1046 {
1047 	uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
1048 	mdb_var_t *v;
1049 
1050 	if (mdb_getopts(argc, argv,
1051 	    'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
1052 	    'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
1053 	    't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
1054 		return (DCMD_USAGE);
1055 
1056 	mdb_nv_rewind(&mdb.m_nv);
1057 
1058 	while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1059 		if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
1060 		    (opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
1061 			if (opt_prt) {
1062 				mdb_printf("%#llr>%s\n",
1063 				    mdb_nv_get_value(v), mdb_nv_get_name(v));
1064 			} else {
1065 				mdb_printf("%s = %llr\n",
1066 				    mdb_nv_get_name(v), mdb_nv_get_value(v));
1067 			}
1068 		}
1069 	}
1070 
1071 	return (DCMD_OK);
1072 }
1073 
1074 /*ARGSUSED*/
1075 static int
cmd_nzvars(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1076 cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1077 {
1078 	uintmax_t value;
1079 	mdb_var_t *v;
1080 
1081 	if (argc != 0)
1082 		return (DCMD_USAGE);
1083 
1084 	mdb_nv_rewind(&mdb.m_nv);
1085 
1086 	while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1087 		if ((value = mdb_nv_get_value(v)) != 0)
1088 			mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
1089 	}
1090 
1091 	return (DCMD_OK);
1092 }
1093 
1094 /*ARGSUSED*/
1095 static int
cmd_radix(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1096 cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1097 {
1098 	if (argc != 0)
1099 		return (DCMD_USAGE);
1100 
1101 	if (flags & DCMD_ADDRSPEC) {
1102 		if (addr < 2 || addr > 16) {
1103 			mdb_warn("expected radix from 2 to 16\n");
1104 			return (DCMD_ERR);
1105 		}
1106 		mdb.m_radix = (int)addr;
1107 	}
1108 
1109 	mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
1110 	return (DCMD_OK);
1111 }
1112 
1113 /*ARGSUSED*/
1114 static int
cmd_symdist(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1115 cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1116 {
1117 	if (argc != 0)
1118 		return (DCMD_USAGE);
1119 
1120 	if (flags & DCMD_ADDRSPEC)
1121 		mdb.m_symdist = addr;
1122 
1123 	mdb_printf("symbol matching distance = %lr (%s)\n",
1124 	    mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
1125 
1126 	return (DCMD_OK);
1127 }
1128 
1129 /*ARGSUSED*/
1130 static int
cmd_pgwidth(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1131 cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1132 {
1133 	if (argc != 0)
1134 		return (DCMD_USAGE);
1135 
1136 	if (flags & DCMD_ADDRSPEC)
1137 		mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
1138 
1139 	mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
1140 	return (DCMD_OK);
1141 }
1142 
1143 /*ARGSUSED*/
1144 static int
cmd_reopen(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1145 cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1146 {
1147 	if (argc != 0)
1148 		return (DCMD_USAGE);
1149 
1150 	if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
1151 		mdb_warn("failed to re-open target for writing");
1152 		return (DCMD_ERR);
1153 	}
1154 
1155 	return (DCMD_OK);
1156 }
1157 
1158 /*ARGSUSED*/
1159 static int
print_xdata(void * ignored,const char * name,const char * desc,size_t nbytes)1160 print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
1161 {
1162 	mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
1163 	return (0);
1164 }
1165 
1166 /*ARGSUSED*/
1167 static int
cmd_xdata(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1168 cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1169 {
1170 	if (argc != 0 || (flags & DCMD_ADDRSPEC))
1171 		return (DCMD_USAGE);
1172 
1173 	(void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
1174 	return (DCMD_OK);
1175 }
1176 
1177 /*ARGSUSED*/
1178 static int
cmd_unset(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1179 cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1180 {
1181 	mdb_var_t *v;
1182 	size_t i;
1183 
1184 	for (i = 0; i < argc; i++) {
1185 		if (argv[i].a_type != MDB_TYPE_STRING) {
1186 			mdb_warn("bad option: arg %lu is not a string\n",
1187 			    (ulong_t)i + 1);
1188 			return (DCMD_USAGE);
1189 		}
1190 	}
1191 
1192 	for (i = 0; i < argc; i++, argv++) {
1193 		if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
1194 			mdb_warn("variable '%s' not defined\n",
1195 			    argv->a_un.a_str);
1196 		else
1197 			mdb_nv_remove(&mdb.m_nv, v);
1198 	}
1199 
1200 	return (DCMD_OK);
1201 }
1202 
1203 #ifndef _KMDB
1204 /*ARGSUSED*/
1205 static int
cmd_log(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1206 cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1207 {
1208 	uint_t opt_e = FALSE, opt_d = FALSE;
1209 	const char *filename = NULL;
1210 	int i;
1211 
1212 	i = mdb_getopts(argc, argv,
1213 	    'd', MDB_OPT_SETBITS, TRUE, &opt_d,
1214 	    'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
1215 
1216 	if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
1217 	    (i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
1218 	    (i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
1219 		return (DCMD_USAGE);
1220 
1221 	if (mdb.m_depth != 1) {
1222 		mdb_warn("log may not be manipulated in this context\n");
1223 		return (DCMD_ABORT);
1224 	}
1225 
1226 	if (i != argc)
1227 		filename = argv[i].a_un.a_str;
1228 
1229 	/*
1230 	 * If no arguments were specified, print the log file name (if any)
1231 	 * and report whether the log is enabled or disabled.
1232 	 */
1233 	if (argc == 0) {
1234 		if (mdb.m_log) {
1235 			mdb_printf("%s: logging to \"%s\" is currently %s\n",
1236 			    mdb.m_pname, IOP_NAME(mdb.m_log),
1237 			    mdb.m_flags & MDB_FL_LOG ?  "enabled" : "disabled");
1238 		} else
1239 			mdb_printf("%s: no log is active\n", mdb.m_pname);
1240 		return (DCMD_OK);
1241 	}
1242 
1243 	/*
1244 	 * If the -d option was specified, pop the log i/o object off the
1245 	 * i/o stack of stdin, stdout, and stderr.
1246 	 */
1247 	if (opt_d) {
1248 		if (mdb.m_flags & MDB_FL_LOG) {
1249 			(void) mdb_iob_pop_io(mdb.m_in);
1250 			(void) mdb_iob_pop_io(mdb.m_out);
1251 			(void) mdb_iob_pop_io(mdb.m_err);
1252 			mdb.m_flags &= ~MDB_FL_LOG;
1253 		} else
1254 			mdb_warn("logging is already disabled\n");
1255 		return (DCMD_OK);
1256 	}
1257 
1258 	/*
1259 	 * The -e option is the default: (re-)enable logging by pushing
1260 	 * the log i/o object on to stdin, stdout, and stderr.  If we have
1261 	 * a previous log file, we need to pop it and close it.  If we have
1262 	 * no new log file, push the previous one back on.
1263 	 */
1264 	if (filename != NULL) {
1265 		if (mdb.m_log != NULL) {
1266 			if (mdb.m_flags & MDB_FL_LOG) {
1267 				(void) mdb_iob_pop_io(mdb.m_in);
1268 				(void) mdb_iob_pop_io(mdb.m_out);
1269 				(void) mdb_iob_pop_io(mdb.m_err);
1270 				mdb.m_flags &= ~MDB_FL_LOG;
1271 			}
1272 			mdb_io_rele(mdb.m_log);
1273 		}
1274 
1275 		mdb.m_log = mdb_fdio_create_path(NULL, filename,
1276 		    O_CREAT | O_APPEND | O_WRONLY, 0666);
1277 
1278 		if (mdb.m_log == NULL) {
1279 			mdb_warn("failed to open %s", filename);
1280 			return (DCMD_ERR);
1281 		}
1282 	}
1283 
1284 	if (mdb.m_log != NULL) {
1285 		mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
1286 		mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
1287 		mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
1288 
1289 		mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
1290 		mdb.m_log = mdb_io_hold(mdb.m_log);
1291 		mdb.m_flags |= MDB_FL_LOG;
1292 
1293 		return (DCMD_OK);
1294 	}
1295 
1296 	mdb_warn("no log file has been selected\n");
1297 	return (DCMD_ERR);
1298 }
1299 
1300 static int
cmd_old_log(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1301 cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1302 {
1303 	if (argc == 0) {
1304 		mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
1305 		return (cmd_log(addr, flags, 1, &arg));
1306 	}
1307 
1308 	return (cmd_log(addr, flags, argc, argv));
1309 }
1310 #endif
1311 
1312 /*ARGSUSED*/
1313 static int
cmd_load(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1314 cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1315 {
1316 	int i, mode = MDB_MOD_LOCAL;
1317 
1318 	i = mdb_getopts(argc, argv,
1319 #ifdef _KMDB
1320 	    'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1321 #endif
1322 	    'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
1323 	    'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
1324 	    's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
1325 	    NULL);
1326 
1327 	argc -= i;
1328 	argv += i;
1329 
1330 	if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
1331 	    argv->a_type != MDB_TYPE_STRING ||
1332 	    strchr("+-", argv->a_un.a_str[0]) != NULL)
1333 		return (DCMD_USAGE);
1334 
1335 	if (mdb_module_load(argv->a_un.a_str, mode) < 0)
1336 		return (DCMD_ERR);
1337 
1338 	return (DCMD_OK);
1339 }
1340 
1341 static void
load_help(void)1342 load_help(void)
1343 {
1344 	mdb_printf(
1345 #ifdef _KMDB
1346 	    "-d    defer load until next continue\n"
1347 #endif
1348 	    "-s    load module silently\n");
1349 }
1350 
1351 /*ARGSUSED*/
1352 static int
cmd_unload(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1353 cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1354 {
1355 	int mode = 0;
1356 	int i;
1357 
1358 	i = mdb_getopts(argc, argv,
1359 #ifdef _KMDB
1360 	    'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1361 #endif
1362 	    NULL);
1363 
1364 	argc -= i;
1365 	argv += i;
1366 
1367 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1368 		return (DCMD_USAGE);
1369 
1370 	if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
1371 		mdb_warn("failed to unload %s", argv->a_un.a_str);
1372 		return (DCMD_ERR);
1373 	}
1374 
1375 	return (DCMD_OK);
1376 }
1377 
1378 #ifdef _KMDB
1379 static void
unload_help(void)1380 unload_help(void)
1381 {
1382 	mdb_printf(
1383 	    "-d    defer unload until next continue\n");
1384 }
1385 #endif
1386 
1387 static int
cmd_dbmode(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1388 cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1389 {
1390 	if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1391 		return (DCMD_USAGE);
1392 
1393 	if (argc != 0) {
1394 		if (argv->a_type != MDB_TYPE_STRING)
1395 			return (DCMD_USAGE);
1396 		if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
1397 			mdb_dmode(addr);
1398 	} else if (flags & DCMD_ADDRSPEC)
1399 		mdb_dmode(addr);
1400 
1401 	mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
1402 	return (DCMD_OK);
1403 }
1404 
1405 /*ARGSUSED*/
1406 static int
cmd_version(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1407 cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1408 {
1409 #ifdef DEBUG
1410 	mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
1411 #else
1412 	mdb_printf("\r%s\n", mdb_conf_version());
1413 #endif
1414 	return (DCMD_OK);
1415 }
1416 
1417 /*ARGSUSED*/
1418 static int
cmd_algol(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1419 cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1420 {
1421 	if (mdb.m_flags & MDB_FL_ADB)
1422 		mdb_printf("No algol 68 here\n");
1423 	else
1424 		mdb_printf("No adb here\n");
1425 	return (DCMD_OK);
1426 }
1427 
1428 /*ARGSUSED*/
1429 static int
cmd_obey(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1430 cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1431 {
1432 	if (mdb.m_flags & MDB_FL_ADB)
1433 		mdb_printf("CHAPTER 1\n");
1434 	else
1435 		mdb_printf("No Language H here\n");
1436 	return (DCMD_OK);
1437 }
1438 
1439 /*ARGSUSED*/
1440 static int
print_global(void * data,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)1441 print_global(void *data, const GElf_Sym *sym, const char *name,
1442     const mdb_syminfo_t *sip, const char *obj)
1443 {
1444 	uintptr_t value;
1445 
1446 	if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
1447 	    (uintptr_t)sym->st_value) == sizeof (value))
1448 		mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
1449 	else
1450 		mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
1451 
1452 	return (0);
1453 }
1454 
1455 /*ARGSUSED*/
1456 static int
cmd_globals(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1457 cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1458 {
1459 	if (argc != 0)
1460 		return (DCMD_USAGE);
1461 
1462 	(void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1463 	    MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
1464 	    MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
1465 
1466 	return (0);
1467 }
1468 
1469 /*ARGSUSED*/
1470 static int
cmd_eval(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1471 cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1472 {
1473 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1474 		return (DCMD_USAGE);
1475 
1476 	if (mdb_eval(argv->a_un.a_str) == -1)
1477 		return (DCMD_ABORT);
1478 
1479 	return (DCMD_OK);
1480 }
1481 
1482 /*ARGSUSED*/
1483 static int
print_file(void * data,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)1484 print_file(void *data, const GElf_Sym *sym, const char *name,
1485     const mdb_syminfo_t *sip, const char *obj)
1486 {
1487 	int i = *((int *)data);
1488 
1489 	mdb_printf("%d\t%s\n", i++, name);
1490 	*((int *)data) = i;
1491 	return (0);
1492 }
1493 
1494 /*ARGSUSED*/
1495 static int
cmd_files(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1496 cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1497 {
1498 	int i = 1;
1499 	const char *obj = MDB_TGT_OBJ_EVERY;
1500 
1501 	if ((flags & DCMD_ADDRSPEC) || argc > 1)
1502 		return (DCMD_USAGE);
1503 
1504 	if (argc == 1) {
1505 		if (argv->a_type != MDB_TYPE_STRING)
1506 			return (DCMD_USAGE);
1507 
1508 		obj = argv->a_un.a_str;
1509 	}
1510 
1511 	(void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
1512 	    MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
1513 
1514 	return (DCMD_OK);
1515 }
1516 
1517 static const char *
map_name(const mdb_map_t * map,const char * name)1518 map_name(const mdb_map_t *map, const char *name)
1519 {
1520 	if (map->map_flags & MDB_TGT_MAP_HEAP)
1521 		return ("[ heap ]");
1522 	if (name != NULL && name[0] != 0)
1523 		return (name);
1524 
1525 	if (map->map_flags & MDB_TGT_MAP_SHMEM)
1526 		return ("[ shmem ]");
1527 	if (map->map_flags & MDB_TGT_MAP_STACK)
1528 		return ("[ stack ]");
1529 	if (map->map_flags & MDB_TGT_MAP_ANON)
1530 		return ("[ anon ]");
1531 	if (map->map_name[0] == '\0')
1532 		return ("[ unknown ]");
1533 	return (map->map_name);
1534 }
1535 
1536 /*ARGSUSED*/
1537 static int
print_map(void * ignored,const mdb_map_t * map,const char * name)1538 print_map(void *ignored, const mdb_map_t *map, const char *name)
1539 {
1540 	name = map_name(map, name);
1541 
1542 	mdb_printf("%?p %?p %?lx %s\n", map->map_base,
1543 	    map->map_base + map->map_size, map->map_size, name);
1544 	return (0);
1545 }
1546 
1547 static int
cmd_mappings(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1548 cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1549 {
1550 	const mdb_map_t *m;
1551 
1552 	if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1553 		return (DCMD_USAGE);
1554 
1555 	mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1556 	    "BASE", "LIMIT", "SIZE", "NAME");
1557 
1558 	if (flags & DCMD_ADDRSPEC) {
1559 		if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
1560 			mdb_warn("failed to obtain mapping");
1561 		else
1562 			(void) print_map(NULL, m, NULL);
1563 
1564 	} else if (argc != 0) {
1565 		if (argv->a_type == MDB_TYPE_STRING)
1566 			m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
1567 		else
1568 			m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
1569 
1570 		if (m == NULL)
1571 			mdb_warn("failed to obtain mapping");
1572 		else
1573 			(void) print_map(NULL, m, NULL);
1574 
1575 	} else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
1576 		mdb_warn("failed to iterate over mappings");
1577 
1578 	return (DCMD_OK);
1579 }
1580 
1581 static int
whatis_map_callback(void * wp,const mdb_map_t * map,const char * name)1582 whatis_map_callback(void *wp, const mdb_map_t *map, const char *name)
1583 {
1584 	mdb_whatis_t *w = wp;
1585 	uintptr_t cur;
1586 
1587 	name = map_name(map, name);
1588 
1589 	while (mdb_whatis_match(w, map->map_base, map->map_size, &cur))
1590 		mdb_whatis_report_address(w, cur, "in %s [%p,%p)\n",
1591 		    name, map->map_base, map->map_base + map->map_size);
1592 
1593 	return (0);
1594 }
1595 
1596 /*ARGSUSED*/
1597 int
whatis_run_mappings(mdb_whatis_t * w,void * ignored)1598 whatis_run_mappings(mdb_whatis_t *w, void *ignored)
1599 {
1600 	(void) mdb_tgt_mapping_iter(mdb.m_target, whatis_map_callback, w);
1601 	return (0);
1602 }
1603 
1604 /*ARGSUSED*/
1605 static int
objects_printversion(void * ignored,const mdb_map_t * map,const char * name)1606 objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
1607 {
1608 	ctf_file_t *ctfp;
1609 	const char *version;
1610 
1611 	ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
1612 	if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
1613 		version = "Unknown";
1614 
1615 	mdb_printf("%-28s %s\n", name, version);
1616 	return (0);
1617 }
1618 
1619 /*ARGSUSED*/
1620 static int
cmd_objects(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1621 cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1622 {
1623 	uint_t opt_v = FALSE;
1624 	mdb_tgt_map_f *cb;
1625 
1626 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1627 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1628 		return (DCMD_USAGE);
1629 
1630 	if (opt_v) {
1631 		cb = objects_printversion;
1632 		mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
1633 	} else {
1634 		cb = print_map;
1635 		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1636 		    "BASE", "LIMIT", "SIZE", "NAME");
1637 	}
1638 
1639 	if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
1640 		mdb_warn("failed to iterate over objects");
1641 		return (DCMD_ERR);
1642 	}
1643 
1644 	return (DCMD_OK);
1645 }
1646 
1647 /*ARGSUSED*/
1648 static int
showrev_addversion(void * vers_nv,const mdb_map_t * ignored,const char * object)1649 showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
1650 {
1651 	ctf_file_t *ctfp;
1652 	const char *version = NULL;
1653 	char *objname;
1654 
1655 	objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
1656 	(void) strcpy(objname, object);
1657 
1658 	if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
1659 		version = ctf_label_topmost(ctfp);
1660 
1661 	/*
1662 	 * Not all objects have CTF and label data, so set version to "Unknown".
1663 	 */
1664 	if (version == NULL)
1665 		version = "Unknown";
1666 
1667 	(void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
1668 	    MDB_NV_OVERLOAD);
1669 
1670 	return (0);
1671 }
1672 
1673 static int
showrev_ispatch(const char * s)1674 showrev_ispatch(const char *s)
1675 {
1676 	if (s == NULL)
1677 		return (0);
1678 
1679 	if (*s == 'T')
1680 		s++; /* skip T for T-patch */
1681 
1682 	for (; *s != '\0'; s++) {
1683 		if ((*s < '0' || *s > '9') && *s != '-')
1684 			return (0);
1685 	}
1686 
1687 	return (1);
1688 }
1689 
1690 /*ARGSUSED*/
1691 static int
showrev_printobject(mdb_var_t * v,void * ignored)1692 showrev_printobject(mdb_var_t *v, void *ignored)
1693 {
1694 	mdb_printf("%s ", MDB_NV_COOKIE(v));
1695 	return (0);
1696 }
1697 
1698 static int
showrev_printversion(mdb_var_t * v,void * showall)1699 showrev_printversion(mdb_var_t *v, void *showall)
1700 {
1701 	const char *version = mdb_nv_get_name(v);
1702 	int patch;
1703 
1704 	patch = showrev_ispatch(version);
1705 	if (patch || (uintptr_t)showall) {
1706 		mdb_printf("%s: %s  Objects: ",
1707 		    (patch ? "Patch" : "Version"), version);
1708 		(void) mdb_inc_indent(2);
1709 
1710 		mdb_nv_defn_iter(v, showrev_printobject, NULL);
1711 
1712 		(void) mdb_dec_indent(2);
1713 		mdb_printf("\n");
1714 	}
1715 
1716 	return (0);
1717 }
1718 
1719 /*
1720  * Display version information for each object in the system.
1721  * Print information about patches only, unless showall is TRUE.
1722  */
1723 static int
showrev_objectversions(int showall)1724 showrev_objectversions(int showall)
1725 {
1726 	mdb_nv_t vers_nv;
1727 
1728 	(void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
1729 	if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
1730 	    &vers_nv) == -1) {
1731 		mdb_warn("failed to iterate over objects");
1732 		return (DCMD_ERR);
1733 	}
1734 
1735 	mdb_nv_sort_iter(&vers_nv, showrev_printversion,
1736 	    (void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
1737 	return (DCMD_OK);
1738 }
1739 
1740 /*
1741  * Display information similar to what showrev(8) displays when invoked
1742  * with no arguments.
1743  */
1744 static int
showrev_sysinfo(void)1745 showrev_sysinfo(void)
1746 {
1747 	const char *s;
1748 	int rc;
1749 	struct utsname u;
1750 
1751 	if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
1752 		mdb_printf("Hostname: %s\n", u.nodename);
1753 		mdb_printf("Release: %s\n", u.release);
1754 		mdb_printf("Kernel architecture: %s\n", u.machine);
1755 	}
1756 
1757 	/*
1758 	 * Match the order of the showrev(8) output and put "Application
1759 	 * architecture" before "Kernel version"
1760 	 */
1761 	if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
1762 		mdb_printf("Application architecture: %s\n", s);
1763 
1764 	if (rc != -1)
1765 		mdb_printf("Kernel version: %s %s %s %s\n",
1766 		    u.sysname, u.release, u.machine, u.version);
1767 
1768 	if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
1769 		mdb_printf("Platform: %s\n", s);
1770 
1771 	return (DCMD_OK);
1772 }
1773 
1774 /*ARGSUSED*/
1775 static int
cmd_showrev(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1776 cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1777 {
1778 	uint_t opt_p = FALSE, opt_v = FALSE;
1779 
1780 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1781 	    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
1782 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1783 		return (DCMD_USAGE);
1784 
1785 	if (opt_p || opt_v)
1786 		return (showrev_objectversions(opt_v));
1787 	else
1788 		return (showrev_sysinfo());
1789 }
1790 
1791 #ifdef __sparc
1792 static void
findsym_output(uintptr_t * symlist,uintptr_t value,uintptr_t location)1793 findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
1794 {
1795 	uintptr_t	*symbolp;
1796 
1797 	for (symbolp = symlist; *symbolp; symbolp++)
1798 		if (value == *symbolp)
1799 			mdb_printf("found %a at %a\n", value, location);
1800 }
1801 
1802 /*ARGSUSED*/
1803 static int
findsym_cb(void * data,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)1804 findsym_cb(void *data, const GElf_Sym *sym, const char *name,
1805     const mdb_syminfo_t *sip, const char *obj)
1806 {
1807 	uint32_t	*text;
1808 	int		len;
1809 	int		i;
1810 	int		j;
1811 	uint8_t		rd;
1812 	uintptr_t	value;
1813 	int32_t		imm13;
1814 	uint8_t		op;
1815 	uint8_t		op3;
1816 	uintptr_t	*symlist = data;
1817 	size_t		size = sym->st_size;
1818 
1819 	/*
1820 	 * if the size of the symbol is 0, then this symbol must be for an
1821 	 * alternate entry point or just some global label. We will,
1822 	 * therefore, get back to the text that follows this symbol in
1823 	 * some other symbol
1824 	 */
1825 	if (size == 0)
1826 		return (0);
1827 
1828 	if (sym->st_shndx == SHN_UNDEF)
1829 		return (0);
1830 
1831 	text = alloca(size);
1832 
1833 	if (mdb_vread(text, size, sym->st_value) == -1) {
1834 		mdb_warn("failed to read text for %s", name);
1835 		return (0);
1836 	}
1837 
1838 	len = size / 4;
1839 	for (i = 0; i < len; i++) {
1840 		if (!IS_SETHI(text[i]))
1841 			continue;
1842 
1843 		rd = RD(text[i]);
1844 		value = IMM22(text[i]) << 10;
1845 
1846 		/*
1847 		 * see if we already have a match with just the sethi
1848 		 */
1849 		findsym_output(symlist, value, sym->st_value + i * 4);
1850 
1851 		/*
1852 		 * search from the sethi on until we hit a relevant instr
1853 		 */
1854 		for (j = i + 1; j < len; j++) {
1855 			if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
1856 				op3 = OP3(text[j]);
1857 
1858 				if (RS1(text[j]) != rd)
1859 					goto instr_end;
1860 
1861 				/*
1862 				 * This is a simple tool; we only deal
1863 				 * with operations which take immediates
1864 				 */
1865 				if (I(text[j]) == 0)
1866 					goto instr_end;
1867 
1868 				/*
1869 				 * sign extend the immediate value
1870 				 */
1871 				imm13 = IMM13(text[j]);
1872 				imm13 <<= 19;
1873 				imm13 >>= 19;
1874 
1875 				if (op == OP_ARITH) {
1876 					/* arithmetic operations */
1877 					if (op3 & OP3_COMPLEX_MASK)
1878 						goto instr_end;
1879 
1880 					switch (op3 & ~OP3_CC_MASK) {
1881 					case OP3_OR:
1882 						value |= imm13;
1883 						break;
1884 					case OP3_ADD:
1885 						value += imm13;
1886 						break;
1887 					case OP3_XOR:
1888 						value ^= imm13;
1889 						break;
1890 					default:
1891 						goto instr_end;
1892 					}
1893 				} else {
1894 					/* loads and stores */
1895 					/* op3 == OP_MEM */
1896 
1897 					value += imm13;
1898 				}
1899 
1900 				findsym_output(symlist, value,
1901 				    sym->st_value + j * 4);
1902 instr_end:
1903 				/*
1904 				 * if we're clobbering rd, break
1905 				 */
1906 				if (RD(text[j]) == rd)
1907 					break;
1908 			} else if (IS_SETHI(text[j])) {
1909 				if (RD(text[j]) == rd)
1910 					break;
1911 			} else if (OP(text[j]) == 1) {
1912 				/*
1913 				 * see if a call clobbers an %o or %g
1914 				 */
1915 				if (rd <= R_O7)
1916 					break;
1917 			}
1918 		}
1919 	}
1920 
1921 	return (0);
1922 }
1923 
1924 static int
cmd_findsym(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1925 cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1926 {
1927 	uintptr_t *symlist;
1928 	uint_t optg = FALSE;
1929 	uint_t type;
1930 	int len, i;
1931 
1932 	i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
1933 
1934 	argc -= i;
1935 	argv += i;
1936 
1937 	len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
1938 
1939 	if (len <= 1)
1940 		return (DCMD_USAGE);
1941 
1942 	/*
1943 	 * Set up a NULL-terminated symbol list, and then iterate over the
1944 	 * symbol table, scanning each function for references to these symbols.
1945 	 */
1946 	symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
1947 	len = 0;
1948 
1949 	for (i = 0; i < argc; i++, argv++) {
1950 		const char *str = argv->a_un.a_str;
1951 		uintptr_t value;
1952 		GElf_Sym sym;
1953 
1954 		if (argv->a_type == MDB_TYPE_STRING) {
1955 			if (strchr("+-", str[0]) != NULL)
1956 				return (DCMD_USAGE);
1957 			else if (str[0] >= '0' && str[0] <= '9')
1958 				value = mdb_strtoull(str);
1959 			else if (mdb_lookup_by_name(str, &sym) != 0) {
1960 				mdb_warn("symbol '%s' not found", str);
1961 				return (DCMD_USAGE);
1962 			} else
1963 				value = sym.st_value;
1964 		} else
1965 			value = argv[i].a_un.a_val;
1966 
1967 		if (value != (uintptr_t)NULL)
1968 			symlist[len++] = value;
1969 	}
1970 
1971 	if (flags & DCMD_ADDRSPEC)
1972 		symlist[len++] = addr;
1973 
1974 	symlist[len] = (uintptr_t)NULL;
1975 
1976 	if (optg)
1977 		type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
1978 	else
1979 		type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
1980 
1981 	if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1982 	    MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
1983 		mdb_warn("failed to iterate over symbol table");
1984 		return (DCMD_ERR);
1985 	}
1986 
1987 	return (DCMD_OK);
1988 }
1989 #endif /* __sparc */
1990 
1991 static int
dis_str2addr(const char * s,uintptr_t * addr)1992 dis_str2addr(const char *s, uintptr_t *addr)
1993 {
1994 	GElf_Sym sym;
1995 
1996 	if (s[0] >= '0' && s[0] <= '9') {
1997 		*addr = (uintptr_t)mdb_strtoull(s);
1998 		return (0);
1999 	}
2000 
2001 	if (mdb_tgt_lookup_by_name(mdb.m_target,
2002 	    MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
2003 		mdb_warn("symbol '%s' not found\n", s);
2004 		return (-1);
2005 	}
2006 
2007 	*addr = (uintptr_t)sym.st_value;
2008 	return (0);
2009 }
2010 
2011 static int
cmd_dis(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2012 cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2013 {
2014 	mdb_tgt_t *tgt = mdb.m_target;
2015 	mdb_disasm_t *dis = mdb.m_disasm;
2016 
2017 	uintptr_t oaddr, naddr;
2018 	mdb_tgt_as_t as;
2019 	mdb_tgt_status_t st;
2020 	char buf[BUFSIZ];
2021 	GElf_Sym sym;
2022 	int i;
2023 
2024 	uint_t opt_f = FALSE;		/* File-mode off by default */
2025 	uint_t opt_w = FALSE;		/* Window mode off by default */
2026 	uint_t opt_a = FALSE;		/* Raw-address mode off by default */
2027 	uint_t opt_b = FALSE;		/* Address & symbols off by default */
2028 	uintptr_t n = -1UL;		/* Length of window in instructions */
2029 	uintptr_t eaddr = 0;		/* Ending address; 0 if limited by n */
2030 
2031 	i = mdb_getopts(argc, argv,
2032 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f,
2033 	    'w', MDB_OPT_SETBITS, TRUE, &opt_w,
2034 	    'a', MDB_OPT_SETBITS, TRUE, &opt_a,
2035 	    'b', MDB_OPT_SETBITS, TRUE, &opt_b,
2036 	    'n', MDB_OPT_UINTPTR, &n, NULL);
2037 
2038 	/*
2039 	 * Disgusting argument post-processing ... basically the idea is to get
2040 	 * the target address into addr, which we do by using the specified
2041 	 * expression value, looking up a string as a symbol name, or by
2042 	 * using the address specified as dot.
2043 	 */
2044 	if (i != argc) {
2045 		if (argc != 0 && (argc - i) == 1) {
2046 			if (argv[i].a_type == MDB_TYPE_STRING) {
2047 				if (argv[i].a_un.a_str[0] == '-')
2048 					return (DCMD_USAGE);
2049 
2050 				if (dis_str2addr(argv[i].a_un.a_str, &addr))
2051 					return (DCMD_ERR);
2052 			} else
2053 				addr = argv[i].a_un.a_val;
2054 		} else
2055 			return (DCMD_USAGE);
2056 	}
2057 
2058 	/*
2059 	 * If we're not in window mode yet, and some type of arguments were
2060 	 * specified, see if the address corresponds nicely to a function.
2061 	 * If not, turn on window mode; otherwise disassemble the function.
2062 	 */
2063 	if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
2064 		if (mdb_tgt_lookup_by_addr(tgt, addr,
2065 		    MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
2066 		    GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
2067 			/*
2068 			 * If the symbol has a size then set our end address to
2069 			 * be the end of the function symbol we just located.
2070 			 */
2071 			if (sym.st_size != 0)
2072 				eaddr = addr + (uintptr_t)sym.st_size;
2073 		} else
2074 			opt_w = TRUE;
2075 	}
2076 
2077 	/*
2078 	 * Window-mode doesn't make sense in a loop.
2079 	 */
2080 	if (flags & DCMD_LOOP)
2081 		opt_w = FALSE;
2082 
2083 	/*
2084 	 * If -n was explicit, limit output to n instructions;
2085 	 * otherwise set n to some reasonable default
2086 	 */
2087 	if (n != -1UL)
2088 		eaddr = 0;
2089 	else
2090 		n = 10;
2091 
2092 	/*
2093 	 * If the state is IDLE (i.e. no address space), turn on -f.
2094 	 */
2095 	if (mdb_tgt_status(tgt, &st) == 0 && st.st_state == MDB_TGT_IDLE)
2096 		opt_f = TRUE;
2097 
2098 	if (opt_f)
2099 		as = MDB_TGT_AS_FILE;
2100 	else
2101 		as = MDB_TGT_AS_VIRT_I;
2102 
2103 	if (opt_w == FALSE) {
2104 		n++;
2105 		while ((eaddr == 0 && n-- != 0) || (addr < eaddr)) {
2106 			naddr = mdb_dis_ins2str(dis, tgt, as,
2107 			    buf, sizeof (buf), addr);
2108 			if (naddr == addr)
2109 				return (DCMD_ERR);
2110 			if (opt_a)
2111 				mdb_printf("%-#32p%8T%s\n", addr, buf);
2112 			else if (opt_b)
2113 				mdb_printf("%-#?p  %-#32a%8T%s\n",
2114 				    addr, addr, buf);
2115 			else
2116 				mdb_printf("%-#32a%8T%s\n", addr, buf);
2117 			addr = naddr;
2118 		}
2119 
2120 	} else {
2121 #ifdef __sparc
2122 		if (addr & 0x3) {
2123 			mdb_warn("address is not properly aligned\n");
2124 			return (DCMD_ERR);
2125 		}
2126 #endif
2127 
2128 		for (oaddr = mdb_dis_previns(dis, tgt, as, addr, n);
2129 		    oaddr < addr; oaddr = naddr) {
2130 			naddr = mdb_dis_ins2str(dis, tgt, as,
2131 			    buf, sizeof (buf), oaddr);
2132 			if (naddr == oaddr)
2133 				return (DCMD_ERR);
2134 			if (opt_a)
2135 				mdb_printf("%-#32p%8T%s\n", oaddr, buf);
2136 			else if (opt_b)
2137 				mdb_printf("%-#?p  %-#32a%8T%s\n",
2138 				    oaddr, oaddr, buf);
2139 			else
2140 				mdb_printf("%-#32a%8T%s\n", oaddr, buf);
2141 		}
2142 
2143 		if ((naddr = mdb_dis_ins2str(dis, tgt, as,
2144 		    buf, sizeof (buf), addr)) == addr)
2145 			return (DCMD_ERR);
2146 
2147 		mdb_printf("%<b>");
2148 		mdb_flush();
2149 		if (opt_a)
2150 			mdb_printf("%-#32p%8T%s%", addr, buf);
2151 		else if (opt_b)
2152 			mdb_printf("%-#?p  %-#32a%8T%s", addr, addr, buf);
2153 		else
2154 			mdb_printf("%-#32a%8T%s%", addr, buf);
2155 		mdb_printf("%</b>\n");
2156 
2157 		for (addr = naddr; n-- != 0; addr = naddr) {
2158 			naddr = mdb_dis_ins2str(dis, tgt, as,
2159 			    buf, sizeof (buf), addr);
2160 			if (naddr == addr)
2161 				return (DCMD_ERR);
2162 			if (opt_a)
2163 				mdb_printf("%-#32p%8T%s\n", addr, buf);
2164 			else if (opt_b)
2165 				mdb_printf("%-#?p  %-#32a%8T%s\n",
2166 				    addr, addr, buf);
2167 			else
2168 				mdb_printf("%-#32a%8T%s\n", addr, buf);
2169 		}
2170 	}
2171 
2172 	mdb_set_dot(addr);
2173 	return (DCMD_OK);
2174 }
2175 
2176 /*ARGSUSED*/
2177 static int
walk_step(uintptr_t addr,const void * data,void * private)2178 walk_step(uintptr_t addr, const void *data, void *private)
2179 {
2180 	mdb_printf("%#lr\n", addr);
2181 	return (WALK_NEXT);
2182 }
2183 
2184 static int
cmd_walk(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2185 cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2186 {
2187 	int status;
2188 
2189 	if (argc < 1 || argc > 2 || argv[0].a_type != MDB_TYPE_STRING ||
2190 	    argv[argc - 1].a_type != MDB_TYPE_STRING)
2191 		return (DCMD_USAGE);
2192 
2193 	if (argc > 1) {
2194 		const char *name = argv[1].a_un.a_str;
2195 		mdb_var_t *v = mdb_nv_lookup(&mdb.m_nv, name);
2196 		const char *p;
2197 
2198 		if (v != NULL && (v->v_flags & MDB_NV_RDONLY) != 0) {
2199 			mdb_warn("variable %s is read-only\n", name);
2200 			return (DCMD_ABORT);
2201 		}
2202 
2203 		if (v == NULL && (p = strbadid(name)) != NULL) {
2204 			mdb_warn("'%c' may not be used in a variable "
2205 			    "name\n", *p);
2206 			return (DCMD_ABORT);
2207 		}
2208 
2209 		if (v == NULL && (v = mdb_nv_insert(&mdb.m_nv,
2210 		    name, NULL, 0, 0)) == NULL)
2211 			return (DCMD_ERR);
2212 
2213 		/*
2214 		 * If there already exists a vcb for this variable, we may be
2215 		 * calling ::walk in a loop.  We only create a vcb for this
2216 		 * variable on the first invocation.
2217 		 */
2218 		if (mdb_vcb_find(v, mdb.m_frame) == NULL)
2219 			mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
2220 	}
2221 
2222 	if (flags & DCMD_ADDRSPEC)
2223 		status = mdb_pwalk(argv->a_un.a_str, walk_step, NULL, addr);
2224 	else
2225 		status = mdb_walk(argv->a_un.a_str, walk_step, NULL);
2226 
2227 	if (status == -1) {
2228 		mdb_warn("failed to perform walk");
2229 		return (DCMD_ERR);
2230 	}
2231 
2232 	return (DCMD_OK);
2233 }
2234 
2235 static int
cmd_walk_tab(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)2236 cmd_walk_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2237     const mdb_arg_t *argv)
2238 {
2239 	if (argc > 1)
2240 		return (1);
2241 
2242 	if (argc == 1) {
2243 		ASSERT(argv[0].a_type == MDB_TYPE_STRING);
2244 		return (mdb_tab_complete_walker(mcp, argv[0].a_un.a_str));
2245 	}
2246 
2247 	if (argc == 0 && flags & DCMD_TAB_SPACE)
2248 		return (mdb_tab_complete_walker(mcp, NULL));
2249 
2250 	return (1);
2251 }
2252 
2253 static ssize_t
mdb_partial_xread(void * buf,size_t nbytes,uintptr_t addr,void * arg)2254 mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
2255 {
2256 	ssize_t (*fp)(mdb_tgt_t *, const void *, size_t, uintptr_t) =
2257 	    (ssize_t (*)(mdb_tgt_t *, const void *, size_t, uintptr_t))arg;
2258 
2259 	return (fp(mdb.m_target, buf, nbytes, addr));
2260 }
2261 
2262 /* ARGSUSED3 */
2263 static ssize_t
mdb_partial_pread(void * buf,size_t nbytes,physaddr_t addr,void * arg)2264 mdb_partial_pread(void *buf, size_t nbytes, physaddr_t addr, void *arg)
2265 {
2266 	return (mdb_tgt_pread(mdb.m_target, buf, nbytes, addr));
2267 }
2268 
2269 
2270 static int
cmd_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2271 cmd_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2272 {
2273 	uint_t dflags =
2274 	    MDB_DUMP_ALIGN | MDB_DUMP_NEWDOT | MDB_DUMP_ASCII | MDB_DUMP_HEADER;
2275 	uint_t phys = FALSE;
2276 	uint_t file = FALSE;
2277 	uintptr_t group = 4;
2278 	uintptr_t length = 0;
2279 	uintptr_t width = 1;
2280 	mdb_tgt_status_t st;
2281 	int error;
2282 
2283 	if (mdb_getopts(argc, argv,
2284 	    'e', MDB_OPT_SETBITS, MDB_DUMP_ENDIAN, &dflags,
2285 	    'f', MDB_OPT_SETBITS, TRUE, &file,
2286 	    'g', MDB_OPT_UINTPTR, &group,
2287 	    'l', MDB_OPT_UINTPTR, &length,
2288 	    'p', MDB_OPT_SETBITS, TRUE, &phys,
2289 	    'q', MDB_OPT_CLRBITS, MDB_DUMP_ASCII, &dflags,
2290 	    'r', MDB_OPT_SETBITS, MDB_DUMP_RELATIVE, &dflags,
2291 	    's', MDB_OPT_SETBITS, MDB_DUMP_SQUISH, &dflags,
2292 	    't', MDB_OPT_SETBITS, MDB_DUMP_TRIM, &dflags,
2293 	    'u', MDB_OPT_CLRBITS, MDB_DUMP_ALIGN, &dflags,
2294 	    'v', MDB_OPT_SETBITS, MDB_DUMP_PEDANT, &dflags,
2295 	    'w', MDB_OPT_UINTPTR, &width, NULL) != argc)
2296 		return (DCMD_USAGE);
2297 
2298 	if ((phys && file) ||
2299 	    (width == 0) || (width > 0x10) ||
2300 	    (group == 0) || (group > 0x100) ||
2301 	    (mdb.m_dcount > 1 && length > 0))
2302 		return (DCMD_USAGE);
2303 	if (length == 0)
2304 		length = mdb.m_dcount;
2305 
2306 	/*
2307 	 * If neither -f nor -p were specified and the state is IDLE (i.e. no
2308 	 * address space), turn on -p.  This is so we can read large files.
2309 	 */
2310 	if (phys == FALSE && file == FALSE && mdb_tgt_status(mdb.m_target,
2311 	    &st) == 0 && st.st_state == MDB_TGT_IDLE)
2312 		phys = TRUE;
2313 
2314 	dflags |= MDB_DUMP_GROUP(group) | MDB_DUMP_WIDTH(width);
2315 	if (phys)
2316 		error = mdb_dump64(mdb_get_dot(), length, dflags,
2317 		    mdb_partial_pread, NULL);
2318 	else if (file)
2319 		error = mdb_dumpptr(addr, length, dflags,
2320 		    mdb_partial_xread, (void *)mdb_tgt_fread);
2321 	else
2322 		error = mdb_dumpptr(addr, length, dflags,
2323 		    mdb_partial_xread, (void *)mdb_tgt_vread);
2324 
2325 	return (((flags & DCMD_LOOP) || (error == -1)) ? DCMD_ABORT : DCMD_OK);
2326 }
2327 
2328 /*ARGSUSED*/
2329 static int
cmd_echo(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2330 cmd_echo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2331 {
2332 	if (flags & DCMD_ADDRSPEC)
2333 		return (DCMD_USAGE);
2334 
2335 	for (; argc-- != 0; argv++) {
2336 		if (argv->a_type == MDB_TYPE_STRING)
2337 			mdb_printf("%s ", argv->a_un.a_str);
2338 		else
2339 			mdb_printf("%llr ", argv->a_un.a_val);
2340 	}
2341 
2342 	mdb_printf("\n");
2343 	return (DCMD_OK);
2344 }
2345 
2346 /*ARGSUSED*/
2347 static int
cmd_head(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2348 cmd_head(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2349 {
2350 	uint64_t cnt = 10;
2351 	const char *c;
2352 	mdb_pipe_t p;
2353 
2354 	if (!(flags & DCMD_PIPE))
2355 		return (DCMD_USAGE);
2356 
2357 	if (argc == 1 || argc == 2) {
2358 		const char *num;
2359 
2360 		if (argc == 1) {
2361 			if (argv[0].a_type != MDB_TYPE_STRING ||
2362 			    *argv[0].a_un.a_str != '-')
2363 				return (DCMD_USAGE);
2364 
2365 			num = argv[0].a_un.a_str + 1;
2366 
2367 		} else {
2368 			if (argv[0].a_type != MDB_TYPE_STRING ||
2369 			    strcmp(argv[0].a_un.a_str, "-n") != 0)
2370 				return (DCMD_USAGE);
2371 
2372 			num = argv[1].a_un.a_str;
2373 		}
2374 
2375 		for (cnt = 0, c = num; *c != '\0' && isdigit(*c); c++)
2376 			cnt = cnt * 10 + (*c - '0');
2377 
2378 		if (*c != '\0')
2379 			return (DCMD_USAGE);
2380 
2381 	} else if (argc != 0) {
2382 		return (DCMD_USAGE);
2383 	}
2384 
2385 	mdb_get_pipe(&p);
2386 
2387 	if (p.pipe_data == NULL)
2388 		return (DCMD_OK);
2389 	p.pipe_len = MIN(p.pipe_len, cnt);
2390 
2391 	if (flags & DCMD_PIPE_OUT) {
2392 		mdb_set_pipe(&p);
2393 	} else {
2394 		while (p.pipe_len-- > 0)
2395 			mdb_printf("%lx\n", *p.pipe_data++);
2396 	}
2397 
2398 	return (DCMD_OK);
2399 }
2400 
2401 static void
head_help(void)2402 head_help(void)
2403 {
2404 	mdb_printf(
2405 	    "-n num\n or\n"
2406 	    "-num   pass only the first `num' elements in the pipe.\n"
2407 	    "\n%<b>Note:%</b> `num' is a decimal number.\n");
2408 }
2409 
2410 static int
cmd_typeset(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2411 cmd_typeset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2412 {
2413 	int add_tag = 0, del_tag = 0;
2414 	const char *p;
2415 	mdb_var_t *v;
2416 
2417 	if (argc == 0)
2418 		return (cmd_vars(addr, flags, argc, argv));
2419 
2420 	if (argv->a_type == MDB_TYPE_STRING && (argv->a_un.a_str[0] == '-' ||
2421 	    argv->a_un.a_str[0] == '+')) {
2422 		if (argv->a_un.a_str[1] != 't')
2423 			return (DCMD_USAGE);
2424 		if (argv->a_un.a_str[0] == '-')
2425 			add_tag++;
2426 		else
2427 			del_tag++;
2428 		argc--;
2429 		argv++;
2430 	}
2431 
2432 	if (!(flags & DCMD_ADDRSPEC))
2433 		addr = 0; /* set variables to zero unless explicit addr given */
2434 
2435 	for (; argc-- != 0; argv++) {
2436 		if (argv->a_type != MDB_TYPE_STRING)
2437 			continue;
2438 
2439 		if (argv->a_un.a_str[0] == '-' || argv->a_un.a_str[0] == '+') {
2440 			mdb_warn("ignored bad option -- %s\n",
2441 			    argv->a_un.a_str);
2442 			continue;
2443 		}
2444 
2445 		if ((p = strbadid(argv->a_un.a_str)) != NULL) {
2446 			mdb_warn("'%c' may not be used in a variable "
2447 			    "name\n", *p);
2448 			return (DCMD_ERR);
2449 		}
2450 
2451 		if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL) {
2452 			v = mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str,
2453 			    NULL, addr, 0);
2454 		} else if (flags & DCMD_ADDRSPEC)
2455 			mdb_nv_set_value(v, addr);
2456 
2457 		if (v != NULL) {
2458 			if (add_tag)
2459 				v->v_flags |= MDB_NV_TAGGED;
2460 			if (del_tag)
2461 				v->v_flags &= ~MDB_NV_TAGGED;
2462 		}
2463 	}
2464 
2465 	return (DCMD_OK);
2466 }
2467 
2468 #ifndef _KMDB
2469 /*ARGSUSED*/
2470 static int
cmd_context(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2471 cmd_context(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2472 {
2473 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
2474 		return (DCMD_USAGE);
2475 
2476 	if (mdb_tgt_setcontext(mdb.m_target, (void *)addr) == 0)
2477 		return (DCMD_OK);
2478 
2479 	return (DCMD_ERR);
2480 }
2481 #endif
2482 
2483 /*ARGSUSED*/
2484 static int
cmd_prompt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2485 cmd_prompt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2486 {
2487 	const char *p = "";
2488 
2489 	if (argc != 0) {
2490 		if (argc > 1 || argv->a_type != MDB_TYPE_STRING)
2491 			return (DCMD_USAGE);
2492 		p = argv->a_un.a_str;
2493 	}
2494 
2495 	(void) mdb_set_prompt(p);
2496 	return (DCMD_OK);
2497 }
2498 
2499 /*ARGSUSED*/
2500 static int
cmd_term(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2501 cmd_term(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2502 {
2503 	mdb_printf("%s\n", mdb.m_termtype);
2504 
2505 	return (DCMD_OK);
2506 }
2507 
2508 /*ARGSUSED*/
2509 static int
cmd_vtop(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2510 cmd_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2511 {
2512 	physaddr_t pa;
2513 	mdb_tgt_as_t as = MDB_TGT_AS_VIRT;
2514 
2515 	if (mdb_getopts(argc, argv, 'a', MDB_OPT_UINTPTR, (uintptr_t *)&as,
2516 	    NULL) != argc)
2517 		return (DCMD_USAGE);
2518 
2519 	if (mdb_tgt_vtop(mdb.m_target, as, addr, &pa) == -1) {
2520 		mdb_warn("failed to get physical mapping");
2521 		return (DCMD_ERR);
2522 	}
2523 
2524 	if (flags & DCMD_PIPE_OUT)
2525 		mdb_printf("%llr\n", pa);
2526 	else
2527 		mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
2528 	return (DCMD_OK);
2529 }
2530 
2531 #define	EVENTS_OPT_A	0x1	/* ::events -a (show all events) */
2532 #define	EVENTS_OPT_V	0x2	/* ::events -v (verbose display) */
2533 
2534 static const char *
event_action(const mdb_tgt_spec_desc_t * sp)2535 event_action(const mdb_tgt_spec_desc_t *sp)
2536 {
2537 	if (!(sp->spec_flags & MDB_TGT_SPEC_HIDDEN) && sp->spec_data != NULL)
2538 		return (sp->spec_data);
2539 
2540 	return ("-");
2541 }
2542 
2543 static void
print_evsep(void)2544 print_evsep(void)
2545 {
2546 	static const char dash20[] = "--------------------";
2547 	mdb_printf("----- - -- -- -- %s%s --%s\n", dash20, dash20, dash20);
2548 }
2549 
2550 /*ARGSUSED*/
2551 static int
print_event(mdb_tgt_t * t,void * private,int vid,void * data)2552 print_event(mdb_tgt_t *t, void *private, int vid, void *data)
2553 {
2554 	uint_t opts = (uint_t)(uintptr_t)private;
2555 	mdb_tgt_spec_desc_t sp;
2556 	char s1[41], s2[22];
2557 	const char *s2str;
2558 	int visible;
2559 
2560 	(void) mdb_tgt_vespec_info(t, vid, &sp, s1, sizeof (s1));
2561 	visible = !(sp.spec_flags & (MDB_TGT_SPEC_HIDDEN|MDB_TGT_SPEC_DELETED));
2562 
2563 	if ((opts & EVENTS_OPT_A) || visible) {
2564 		int encoding = (!(sp.spec_flags & MDB_TGT_SPEC_DISABLED)) |
2565 		    (!(sp.spec_flags & MDB_TGT_SPEC_MATCHED) << 1);
2566 
2567 		char ldelim = "<<(["[encoding];
2568 		char rdelim = ">>)]"[encoding];
2569 
2570 		char state = "0-+*!"[sp.spec_state];
2571 
2572 		char tflag = "T "[!(sp.spec_flags & MDB_TGT_SPEC_STICKY)];
2573 		char aflag = "d "[!(sp.spec_flags & MDB_TGT_SPEC_AUTODIS)];
2574 
2575 		if (sp.spec_flags & MDB_TGT_SPEC_TEMPORARY)
2576 			tflag = 't'; /* TEMP takes precedence over STICKY */
2577 		if (sp.spec_flags & MDB_TGT_SPEC_AUTODEL)
2578 			aflag = 'D'; /* AUTODEL takes precedence over AUTODIS */
2579 		if (sp.spec_flags & MDB_TGT_SPEC_AUTOSTOP)
2580 			aflag = 's'; /* AUTOSTOP takes precedence over both */
2581 
2582 		if (opts & EVENTS_OPT_V) {
2583 			if (sp.spec_state == MDB_TGT_SPEC_IDLE ||
2584 			    sp.spec_state == MDB_TGT_SPEC_ERROR)
2585 				s2str = mdb_strerror(sp.spec_errno);
2586 			else
2587 				s2str = "-";
2588 		} else
2589 			s2str = event_action(&sp);
2590 
2591 		if (mdb_snprintf(s2, sizeof (s2), "%s", s2str) >= sizeof (s2))
2592 			(void) strabbr(s2, sizeof (s2));
2593 
2594 		if (vid > -10 && vid < 10)
2595 			mdb_printf("%c%2d %c", ldelim, vid, rdelim);
2596 		else
2597 			mdb_printf("%c%3d%c", ldelim, vid, rdelim);
2598 
2599 		mdb_printf(" %c %c%c %2u %2u %-40s %-21s\n",
2600 		    state, tflag, aflag, sp.spec_hits, sp.spec_limit, s1, s2);
2601 
2602 		if (opts & EVENTS_OPT_V) {
2603 			mdb_printf("%-17s%s\n", "", event_action(&sp));
2604 			print_evsep();
2605 		}
2606 	}
2607 
2608 	return (0);
2609 }
2610 
2611 /*ARGSUSED*/
2612 static int
cmd_events(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2613 cmd_events(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2614 {
2615 	uint_t opts = 0;
2616 
2617 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
2618 	    'a', MDB_OPT_SETBITS, EVENTS_OPT_A, &opts,
2619 	    'v', MDB_OPT_SETBITS, EVENTS_OPT_V, &opts, NULL) != argc)
2620 		return (DCMD_USAGE);
2621 
2622 
2623 	if (opts & EVENTS_OPT_V) {
2624 		mdb_printf("   ID S TA HT LM %-40s %-21s\n%-17s%s\n",
2625 		    "Description", "Status", "", "Action");
2626 	} else {
2627 		mdb_printf("   ID S TA HT LM %-40s %-21s\n",
2628 		    "Description", "Action");
2629 	}
2630 
2631 	print_evsep();
2632 	return (mdb_tgt_vespec_iter(mdb.m_target, print_event,
2633 	    (void *)(uintptr_t)opts));
2634 }
2635 
2636 static int
tgt_status(const mdb_tgt_status_t * tsp)2637 tgt_status(const mdb_tgt_status_t *tsp)
2638 {
2639 	const char *format;
2640 	char buf[BUFSIZ];
2641 
2642 	if (tsp->st_flags & MDB_TGT_BUSY)
2643 		return (DCMD_OK);
2644 
2645 	if (tsp->st_pc != 0) {
2646 		if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target,
2647 		    MDB_TGT_AS_VIRT_I, buf, sizeof (buf), tsp->st_pc) !=
2648 		    tsp->st_pc)
2649 			format = "target stopped at:\n%-#16a%8T%s\n";
2650 		else
2651 			format = "target stopped at %a:\n";
2652 		mdb_warn(format, tsp->st_pc, buf);
2653 	}
2654 
2655 	switch (tsp->st_state) {
2656 	case MDB_TGT_IDLE:
2657 		mdb_warn("target is idle\n");
2658 		break;
2659 	case MDB_TGT_RUNNING:
2660 		if (tsp->st_flags & MDB_TGT_DSTOP)
2661 			mdb_warn("target is running, stop directive pending\n");
2662 		else
2663 			mdb_warn("target is running\n");
2664 		break;
2665 	case MDB_TGT_STOPPED:
2666 		if (tsp->st_pc == 0)
2667 			mdb_warn("target is stopped\n");
2668 		break;
2669 	case MDB_TGT_UNDEAD:
2670 		mdb_warn("target has terminated\n");
2671 		break;
2672 	case MDB_TGT_DEAD:
2673 		mdb_warn("target is a core dump\n");
2674 		break;
2675 	case MDB_TGT_LOST:
2676 		mdb_warn("target is no longer under debugger control\n");
2677 		break;
2678 	}
2679 
2680 	mdb_set_dot(tsp->st_pc);
2681 	return (DCMD_OK);
2682 }
2683 
2684 /*
2685  * mdb continue/step commands take an optional signal argument, but the
2686  * corresponding kmdb versions don't.
2687  */
2688 #ifdef _KMDB
2689 #define	CONT_MAXARGS	0	/* no optional SIG argument */
2690 #else
2691 #define	CONT_MAXARGS	1
2692 #endif
2693 
2694 /*ARGSUSED*/
2695 static int
cmd_cont_common(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv,int (* t_cont)(mdb_tgt_t *,mdb_tgt_status_t *),const char * name)2696 cmd_cont_common(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
2697     int (*t_cont)(mdb_tgt_t *, mdb_tgt_status_t *), const char *name)
2698 {
2699 	mdb_tgt_t *t = mdb.m_target;
2700 	mdb_tgt_status_t st;
2701 	int sig = 0;
2702 
2703 	if ((flags & DCMD_ADDRSPEC) || argc > CONT_MAXARGS)
2704 		return (DCMD_USAGE);
2705 
2706 	if (argc > 0) {
2707 		if (argv->a_type == MDB_TYPE_STRING) {
2708 			if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
2709 				mdb_warn("invalid signal name -- %s\n",
2710 				    argv->a_un.a_str);
2711 				return (DCMD_USAGE);
2712 			}
2713 		} else
2714 			sig = (int)(intmax_t)argv->a_un.a_val;
2715 	}
2716 
2717 	(void) mdb_tgt_status(t, &st);
2718 
2719 	if (st.st_state == MDB_TGT_IDLE && mdb_tgt_run(t, 0, NULL) == -1) {
2720 		if (errno != EMDB_TGT)
2721 			mdb_warn("failed to create new target");
2722 		return (DCMD_ERR);
2723 	}
2724 
2725 	if (sig != 0 && mdb_tgt_signal(t, sig) == -1) {
2726 		mdb_warn("failed to post signal %d", sig);
2727 		return (DCMD_ERR);
2728 	}
2729 
2730 	if (st.st_state == MDB_TGT_IDLE && t_cont == &mdb_tgt_step) {
2731 		(void) mdb_tgt_status(t, &st);
2732 		return (tgt_status(&st));
2733 	}
2734 
2735 	if (t_cont(t, &st) == -1) {
2736 		if (errno != EMDB_TGT)
2737 			mdb_warn("failed to %s target", name);
2738 		return (DCMD_ERR);
2739 	}
2740 
2741 	return (tgt_status(&st));
2742 }
2743 
2744 static int
cmd_step(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2745 cmd_step(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2746 {
2747 	int (*func)(mdb_tgt_t *, mdb_tgt_status_t *) = &mdb_tgt_step;
2748 	const char *name = "single-step";
2749 
2750 	if (argc > 0 && argv->a_type == MDB_TYPE_STRING) {
2751 		if (strcmp(argv->a_un.a_str, "out") == 0) {
2752 			func = &mdb_tgt_step_out;
2753 			name = "step (out)";
2754 			argv++;
2755 			argc--;
2756 		} else if (strcmp(argv->a_un.a_str, "over") == 0) {
2757 			func = &mdb_tgt_next;
2758 			name = "step (over)";
2759 			argv++;
2760 			argc--;
2761 		}
2762 	}
2763 
2764 	return (cmd_cont_common(addr, flags, argc, argv, func, name));
2765 }
2766 
2767 static int
cmd_step_out(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2768 cmd_step_out(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2769 {
2770 	return (cmd_cont_common(addr, flags, argc, argv,
2771 	    &mdb_tgt_step_out, "step (out)"));
2772 }
2773 
2774 static int
cmd_next(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2775 cmd_next(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2776 {
2777 	return (cmd_cont_common(addr, flags, argc, argv,
2778 	    &mdb_tgt_next, "step (over)"));
2779 }
2780 
2781 static int
cmd_cont(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2782 cmd_cont(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2783 {
2784 	return (cmd_cont_common(addr, flags, argc, argv,
2785 	    &mdb_tgt_continue, "continue"));
2786 }
2787 
2788 #ifndef _KMDB
2789 /*ARGSUSED*/
2790 static int
cmd_run(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2791 cmd_run(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2792 {
2793 	if (flags & DCMD_ADDRSPEC)
2794 		return (DCMD_USAGE);
2795 
2796 	if (mdb_tgt_run(mdb.m_target, argc, argv) == -1) {
2797 		if (errno != EMDB_TGT)
2798 			mdb_warn("failed to create new target");
2799 		return (DCMD_ERR);
2800 	}
2801 	return (cmd_cont(0, 0, 0, NULL));
2802 }
2803 #endif
2804 
2805 /*
2806  * To simplify the implementation of :d, :z, and ::delete, we use the sp
2807  * parameter to store the criteria for what to delete.  If spec_base is set,
2808  * we delete vespecs with a matching address.  If spec_id is set, we delete
2809  * vespecs with a matching id.  Otherwise, we delete all vespecs.  We bump
2810  * sp->spec_size so the caller can tell how many vespecs were deleted.
2811  */
2812 static int
ve_delete(mdb_tgt_t * t,mdb_tgt_spec_desc_t * sp,int vid,void * data)2813 ve_delete(mdb_tgt_t *t, mdb_tgt_spec_desc_t *sp, int vid, void *data)
2814 {
2815 	mdb_tgt_spec_desc_t spec;
2816 	int status = -1;
2817 
2818 	if (vid < 0)
2819 		return (0); /* skip over target implementation events */
2820 
2821 	if (sp->spec_base != 0) {
2822 		(void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2823 		if (sp->spec_base - spec.spec_base < spec.spec_size)
2824 			status = mdb_tgt_vespec_delete(t, vid);
2825 	} else if (sp->spec_id == 0) {
2826 		(void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2827 		if (!(spec.spec_flags & MDB_TGT_SPEC_STICKY))
2828 			status = mdb_tgt_vespec_delete(t, vid);
2829 	} else if (sp->spec_id == vid)
2830 		status = mdb_tgt_vespec_delete(t, vid);
2831 
2832 	if (status == 0) {
2833 		if (data != NULL)
2834 			strfree(data);
2835 		sp->spec_size++;
2836 	}
2837 
2838 	return (0);
2839 }
2840 
2841 static int
ve_delete_spec(mdb_tgt_spec_desc_t * sp)2842 ve_delete_spec(mdb_tgt_spec_desc_t *sp)
2843 {
2844 	(void) mdb_tgt_vespec_iter(mdb.m_target,
2845 	    (mdb_tgt_vespec_f *)ve_delete, sp);
2846 
2847 	if (sp->spec_size == 0) {
2848 		if (sp->spec_id != 0 || sp->spec_base != 0)
2849 			mdb_warn("no traced events matched description\n");
2850 	}
2851 
2852 	return (DCMD_OK);
2853 }
2854 
2855 /*ARGSUSED*/
2856 static int
cmd_zapall(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2857 cmd_zapall(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2858 {
2859 	mdb_tgt_spec_desc_t spec;
2860 
2861 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
2862 		return (DCMD_USAGE);
2863 
2864 	bzero(&spec, sizeof (spec));
2865 	return (ve_delete_spec(&spec));
2866 }
2867 
2868 static int
cmd_delete(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2869 cmd_delete(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2870 {
2871 	mdb_tgt_spec_desc_t spec;
2872 
2873 	if (((flags & DCMD_ADDRSPEC) && argc > 0) || argc > 1)
2874 		return (DCMD_USAGE);
2875 
2876 	bzero(&spec, sizeof (spec));
2877 
2878 	if (flags & DCMD_ADDRSPEC)
2879 		spec.spec_base = addr;
2880 	else if (argc == 0)
2881 		spec.spec_base = mdb_get_dot();
2882 	else if (argv->a_type == MDB_TYPE_STRING &&
2883 	    strcmp(argv->a_un.a_str, "all") != 0)
2884 		spec.spec_id = (int)(intmax_t)mdb_strtonum(argv->a_un.a_str,
2885 		    10);
2886 	else if (argv->a_type == MDB_TYPE_IMMEDIATE)
2887 		spec.spec_id = (int)(intmax_t)argv->a_un.a_val;
2888 
2889 	return (ve_delete_spec(&spec));
2890 }
2891 
2892 static int
cmd_write(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2893 cmd_write(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2894 {
2895 	mdb_tgt_as_t as;
2896 	int rdback = mdb.m_flags & MDB_FL_READBACK;
2897 	mdb_tgt_addr_t naddr;
2898 	size_t forced_size = 0;
2899 	boolean_t opt_p, opt_o, opt_l;
2900 	uint64_t val = 0;
2901 	int i;
2902 
2903 	opt_p = opt_o = opt_l = B_FALSE;
2904 
2905 	i = mdb_getopts(argc, argv,
2906 	    'p', MDB_OPT_SETBITS, B_TRUE, &opt_p,
2907 	    'o', MDB_OPT_SETBITS, B_TRUE, &opt_o,
2908 	    'l', MDB_OPT_UINTPTR_SET, &opt_l, (uintptr_t *)&forced_size, NULL);
2909 
2910 	if (!(flags & DCMD_ADDRSPEC))
2911 		return (DCMD_USAGE);
2912 
2913 	if (opt_p && opt_o) {
2914 		mdb_warn("-o and -p are incompatible\n");
2915 		return (DCMD_USAGE);
2916 	}
2917 
2918 	argc -= i;
2919 	argv += i;
2920 
2921 	if (argc == 0)
2922 		return (DCMD_USAGE);
2923 
2924 	switch (argv[0].a_type) {
2925 	case MDB_TYPE_STRING:
2926 		val = mdb_strtoull(argv[0].a_un.a_str);
2927 		break;
2928 	case MDB_TYPE_IMMEDIATE:
2929 		val = argv[0].a_un.a_val;
2930 		break;
2931 	default:
2932 		return (DCMD_USAGE);
2933 	}
2934 
2935 	if (opt_p)
2936 		as = MDB_TGT_AS_PHYS;
2937 	else if (opt_o)
2938 		as = MDB_TGT_AS_FILE;
2939 	else
2940 		as = MDB_TGT_AS_VIRT;
2941 
2942 	if (opt_l)
2943 		naddr = write_var_uint(as, addr, val, forced_size, rdback);
2944 	else
2945 		naddr = write_ctf_uint(as, addr, val, rdback);
2946 
2947 	if (addr == naddr) {
2948 		mdb_warn("failed to write %llr at address %#llx", val, addr);
2949 		return (DCMD_ERR);
2950 	}
2951 
2952 	return (DCMD_OK);
2953 }
2954 
2955 void
write_help(void)2956 write_help(void)
2957 {
2958 	mdb_printf(
2959 	    "-l length  force a write with the specified length in bytes\n"
2960 	    "-o         write data to the object file location specified\n"
2961 	    "-p         write data to the physical address specified\n"
2962 	    "\n"
2963 	    "Attempts to write the given value to the address provided.\n"
2964 	    "If -l is not specified, the address must be the position of a\n"
2965 	    "symbol that is either of integer, pointer, or enum type. The\n"
2966 	    "type and the size of the symbol are inferred by the CTF found\n"
2967 	    "in the provided address. The length of the write is guaranteed\n"
2968 	    "to be the inferred size of the symbol.\n"
2969 	    "\n"
2970 	    "If no CTF data exists, or the address provided is not a symbol\n"
2971 	    "of integer or pointer type, then the write fails. At that point\n"
2972 	    "the user can force the write by using the '-l' option and\n"
2973 	    "specifying its length.\n"
2974 	    "\n"
2975 	    "Note that forced writes with a length that are bigger than\n"
2976 	    "the size of the biggest data pointer supported are not allowed."
2977 	    "\n");
2978 }
2979 
2980 static void
srcexec_file_help(void)2981 srcexec_file_help(void)
2982 {
2983 	mdb_printf(
2984 "The library of macros delivered with previous versions of Solaris have been\n"
2985 "superseded by the dcmds and walkers provided by MDB.  See ::help for\n"
2986 "commands that can be used to list the available dcmds and walkers.\n"
2987 "\n"
2988 "Aliases have been created for several of the more popular macros.  To see\n"
2989 "the list of aliased macros, as well as their native MDB equivalents,\n"
2990 "type $M.\n");
2991 
2992 #ifdef _KMDB
2993 	mdb_printf(
2994 "When invoked, the $< and $<< dcmds will consult the macro alias list.  If an\n"
2995 "alias cannot be found, an attempt will be made to locate a data type whose\n"
2996 "name corresponds to the requested macro.  If such a type can be found, it\n"
2997 "will be displayed using the ::print dcmd.\n");
2998 #else
2999 	mdb_printf(
3000 "When invoked, the $< and $<< dcmds will first attempt to locate a macro with\n"
3001 "the indicated name.  If no macro can be found, and if no alias exists for\n"
3002 "this macro, an attempt will be made to locate a data type whose name\n"
3003 "corresponds to the requested macro.  If such a type can be found, it will be\n"
3004 "displayed using the ::print dcmd.\n");
3005 #endif
3006 }
3007 
3008 static void
events_help(void)3009 events_help(void)
3010 {
3011 	mdb_printf("Options:\n"
3012 	    "-a       show all events, including internal debugger events\n"
3013 	    "-v       show verbose display, including inactivity reason\n"
3014 	    "\nOutput Columns:\n"
3015 	    "ID       decimal event specifier id number:\n"
3016 	    "    [ ]  event tracing is enabled\n"
3017 	    "    ( )  event tracing is disabled\n"
3018 	    "    < >  target is currently stopped on this type of event\n\n"
3019 	    "S        event specifier state:\n"
3020 	    "     -   event specifier is idle (not applicable yet)\n"
3021 	    "     +   event specifier is active\n"
3022 	    "     *   event specifier is armed (target program running)\n"
3023 	    "     !   error occurred while attempting to arm event\n\n"
3024 	    "TA       event specifier flags:\n"
3025 	    "     t   event specifier is temporary (delete at next stop)\n"
3026 	    "     T   event specifier is sticky (::delete all has no effect)\n"
3027 	    "     d   event specifier will be disabled when HT = LM\n"
3028 	    "     D   event specifier will be deleted when HT = LM\n"
3029 	    "     s   target will automatically stop when HT = LM\n\n"
3030 	    "HT       hit count (number of times event has occurred)\n"
3031 	    "LM       hit limit (limit for autostop, disable, delete)\n");
3032 }
3033 
3034 static void
dump_help(void)3035 dump_help(void)
3036 {
3037 	mdb_printf(
3038 	    "-e    adjust for endianness\n"
3039 	    "      (assumes 4-byte words; use -g to change word size)\n"
3040 #ifdef _KMDB
3041 	    "-f    no effect\n"
3042 #else
3043 	    "-f    dump from object file\n"
3044 #endif
3045 	    "-g n  display bytes in groups of n\n"
3046 	    "      (default is 4; n must be a power of 2, divide line width)\n"
3047 	    "-l n  display n bytes\n"
3048 	    "      (default is 1; rounded up to multiple of line width)\n"
3049 	    "-p    dump from physical memory\n"
3050 	    "-q    don't print ASCII\n"
3051 	    "-r    use relative numbering (automatically sets -u)\n"
3052 	    "-s    elide repeated lines\n"
3053 	    "-t    only read from and display contents of specified addresses\n"
3054 	    "      (default is to read and print entire lines)\n"
3055 	    "-u    un-align output\n"
3056 	    "      (default is to align output at paragraph boundary)\n"
3057 	    "-w n  display n 16-byte paragraphs per line\n"
3058 	    "      (default is 1, maximum is 16)\n");
3059 }
3060 
3061 /*
3062  * Table of built-in dcmds associated with the root 'mdb' module.  Future
3063  * expansion of this program should be done here, or through the external
3064  * loadable module interface.
3065  */
3066 const mdb_dcmd_t mdb_dcmd_builtins[] = {
3067 
3068 	/*
3069 	 * dcmds common to both mdb and kmdb
3070 	 */
3071 	{ ">", "variable-name", "assign variable", cmd_assign_variable },
3072 	{ "/", "fmt-list", "format data from virtual as", cmd_print_core },
3073 	{ "\\", "fmt-list", "format data from physical as", cmd_print_phys },
3074 	{ "@", "fmt-list", "format data from physical as", cmd_print_phys },
3075 	{ "=", "fmt-list", "format immediate value", cmd_print_value },
3076 	{ "$<", "macro-name", "replace input with macro",
3077 	    cmd_exec_file, srcexec_file_help },
3078 	{ "$<<", "macro-name", "source macro",
3079 	    cmd_src_file, srcexec_file_help},
3080 	{ "$%", NULL, NULL, cmd_quit },
3081 	{ "$?", NULL, "print status and registers", cmd_notsup },
3082 	{ "$a", NULL, NULL, cmd_algol },
3083 	{ "$b", "[-av]", "list traced software events",
3084 	    cmd_events, events_help },
3085 	{ "$c", "?[cnt]", "print stack backtrace", cmd_notsup },
3086 	{ "$C", "?[cnt]", "print stack backtrace", cmd_notsup },
3087 	{ "$d", NULL, "get/set default output radix", cmd_radix },
3088 	{ "$D", "?[mode,...]", NULL, cmd_dbmode },
3089 	{ "$e", NULL, "print listing of global symbols", cmd_globals },
3090 	{ "$f", NULL, "print listing of source files", cmd_files },
3091 	{ "$m", "?[name]", "print address space mappings", cmd_mappings },
3092 	{ "$M", NULL, "list macro aliases", cmd_macalias_list },
3093 	{ "$P", "[prompt]", "set debugger prompt string", cmd_prompt },
3094 	{ "$q", NULL, "quit debugger", cmd_quit },
3095 	{ "$Q", NULL, "quit debugger", cmd_quit },
3096 	{ "$r", NULL, "print general-purpose registers", cmd_notsup },
3097 	{ "$s", NULL, "get/set symbol matching distance", cmd_symdist },
3098 	{ "$v", NULL, "print non-zero variables", cmd_nzvars },
3099 	{ "$V", "[mode]", "get/set disassembly mode", cmd_dismode },
3100 	{ "$w", NULL, "get/set output page width", cmd_pgwidth },
3101 	{ "$W", NULL, "re-open target in write mode", cmd_reopen },
3102 	{ ":a", ":[cmd...]", "set read access watchpoint", cmd_oldwpr },
3103 	{ ":b", ":[cmd...]", "breakpoint at the specified address", cmd_oldbp },
3104 	{ ":d", "?[id|all]", "delete traced software events", cmd_delete },
3105 	{ ":p", ":[cmd...]", "set execute access watchpoint", cmd_oldwpx },
3106 	{ ":S", NULL, NULL, cmd_step },
3107 	{ ":w", ":[cmd...]", "set write access watchpoint", cmd_oldwpw },
3108 	{ ":z", NULL, "delete all traced software events", cmd_zapall },
3109 	{ "array", ":[type count] [variable]", "print each array element's "
3110 	    "address", cmd_array },
3111 	{ "bp", "?[+/-dDestT] [-c cmd] [-n count] sym ...", "breakpoint at the "
3112 	    "specified addresses or symbols", cmd_bp, bp_help },
3113 	{ "dcmds", "[[-n] pattern]",
3114 	    "list available debugger commands", cmd_dcmds, cmd_dcmds_help },
3115 	{ "delete", "?[id|all]", "delete traced software events", cmd_delete },
3116 	{ "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis },
3117 	{ "disasms", NULL, "list available disassemblers", cmd_disasms },
3118 	{ "dismode", "[mode]", "get/set disassembly mode", cmd_dismode },
3119 	{ "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods },
3120 	{ "dump", "?[-eqrstu] [-f|-p] [-g bytes] [-l bytes] [-w paragraphs]",
3121 	    "dump memory from specified address", cmd_dump, dump_help },
3122 	{ "echo", "args ...", "echo arguments", cmd_echo },
3123 	{ "enum", "?[-ex] enum [name]", "print an enumeration", cmd_enum,
3124 	    enum_help },
3125 	{ "eval", "command", "evaluate the specified command", cmd_eval },
3126 	{ "events", "[-av]", "list traced software events",
3127 	    cmd_events, events_help },
3128 	{ "evset", "?[+/-dDestT] [-c cmd] [-n count] id ...",
3129 	    "set software event specifier attributes", cmd_evset, evset_help },
3130 	{ "files", "[object]", "print listing of source files", cmd_files },
3131 #ifdef __sparc
3132 	{ "findsym", "?[-g] [symbol|addr ...]", "search for symbol references "
3133 	    "in all known functions", cmd_findsym, NULL },
3134 #endif
3135 	{ "formats", NULL, "list format specifiers", cmd_formats },
3136 	{ "grep", "?expr", "print dot if expression is true", cmd_grep },
3137 	{ "head", "-num|-n num", "limit number of elements in pipe", cmd_head,
3138 	    head_help },
3139 	{ "help", "[cmd]", "list commands/command help", cmd_help, NULL,
3140 	    cmd_help_tab },
3141 	{ "linkerset", "[name]", "display linkersets", cmd_linkerset,
3142 	    linkerset_help, cmd_linkerset_tab },
3143 	{ "list", "?type member [variable]",
3144 	    "walk list using member as link pointer", cmd_list, NULL,
3145 	    mdb_tab_complete_mt },
3146 	{ "map", "?expr", "print dot after evaluating expression", cmd_map },
3147 	{ "mappings", "?[name]", "print address space mappings", cmd_mappings },
3148 	{ "nm", "?[-DPdghnopuvx] [-f format] [-t types] [object]",
3149 	    "print symbols", cmd_nm, nm_help },
3150 	{ "nmadd", ":[-fo] [-e end] [-s size] name",
3151 	    "add name to private symbol table", cmd_nmadd, nmadd_help },
3152 	{ "nmdel", "name", "remove name from private symbol table", cmd_nmdel },
3153 	{ "obey", NULL, NULL, cmd_obey },
3154 	{ "objects", "[-v]", "print load objects information", cmd_objects },
3155 	{ "offsetof", "type member", "print the offset of a given struct "
3156 	    "or union member", cmd_offsetof, NULL, mdb_tab_complete_mt },
3157 	{ "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
3158 	    "print the contents of a data structure", cmd_print, print_help,
3159 	    cmd_print_tab },
3160 	{ "printf", "?format type member ...", "print and format the "
3161 	    "member(s) of a data structure", cmd_printf, printf_help,
3162 	    cmd_printf_tab },
3163 	{ "regs", NULL, "print general purpose registers", cmd_notsup },
3164 	{ "set", "[-wF] [+/-o opt] [-s dist] [-I path] [-L path] [-P prompt]",
3165 	    "get/set debugger properties", cmd_set },
3166 	{ "showrev", "[-pv]", "print version information", cmd_showrev },
3167 	{ "sizeof", "type", "print the size of a type", cmd_sizeof, NULL,
3168 	    cmd_sizeof_tab },
3169 	{ "stack", "?[cnt]", "print stack backtrace", cmd_notsup },
3170 	{ "stackregs", "?", "print stack backtrace and registers",
3171 	    cmd_notsup },
3172 	{ "status", NULL, "print summary of current target", cmd_notsup },
3173 	{ "term", NULL, "display current terminal type", cmd_term },
3174 	{ "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset },
3175 	{ "typedef", "[-c model | -d | -l | -r file | -w file ] [type] [name]",
3176 		"create synthetic types", cmd_typedef, cmd_typedef_help },
3177 	{ "typelist", NULL, "list known types", cmd_typelist },
3178 	{ "unset", "[name ...]", "unset variables", cmd_unset },
3179 	{ "vars", "[-npt]", "print listing of variables", cmd_vars },
3180 	{ "version", NULL, "print debugger version string", cmd_version },
3181 	{ "vtop", ":[-a as]", "print physical mapping of virtual address",
3182 	    cmd_vtop },
3183 	{ "walk", "?name [variable]", "walk data structure", cmd_walk, NULL,
3184 	    cmd_walk_tab },
3185 	{ "walkers", "[[-n] pattern]", "list available walkers",
3186 	    cmd_walkers, cmd_walkers_help },
3187 	{ "whatis", ":[-aikqv]", "given an address, return information",
3188 	    cmd_whatis, whatis_help },
3189 	{ "whence", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3190 	{ "which", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3191 	{ "write", "?[-op] [-l len] value",
3192 	    "write value to the provided memory location", cmd_write,
3193 	    write_help },
3194 	{ "xdata", NULL, "print list of external data buffers", cmd_xdata },
3195 
3196 #ifdef _KMDB
3197 	/*
3198 	 * dcmds specific to kmdb, or which have kmdb-specific arguments
3199 	 */
3200 	{ "?", "fmt-list", "format data from virtual as", cmd_print_core },
3201 	{ ":c", NULL, "continue target execution", cmd_cont },
3202 	{ ":e", NULL, "step target over next instruction", cmd_next },
3203 	{ ":s", NULL, "single-step target to next instruction", cmd_step },
3204 	{ ":u", NULL, "step target out of current function", cmd_step_out },
3205 	{ "cont", NULL, "continue target execution", cmd_cont },
3206 	{ "load", "[-sd] module", "load debugger module", cmd_load, load_help },
3207 	{ "next", NULL, "step target over next instruction", cmd_next },
3208 	{ "quit", "[-u]", "quit debugger", cmd_quit, quit_help },
3209 	{ "step", "[ over | out ]",
3210 	    "single-step target to next instruction", cmd_step },
3211 	{ "unload", "[-d] module", "unload debugger module", cmd_unload,
3212 	    unload_help },
3213 	{ "wp", ":[+/-dDelstT] [-rwx] [-pi] [-c cmd] [-n count] [-L size]",
3214 	    "set a watchpoint at the specified address", cmd_wp, wp_help },
3215 
3216 #else
3217 	/*
3218 	 * dcmds specific to mdb, or which have mdb-specific arguments
3219 	 */
3220 	{ "?", "fmt-list", "format data from object file", cmd_print_object },
3221 	{ "$>", "[file]", "log session to a file", cmd_old_log },
3222 	{ "$g", "?", "get/set demangling options", cmd_demflags },
3223 	{ "$G", NULL, "enable/disable demangling support", cmd_demangle },
3224 	{ "$i", NULL, "print signals that are ignored", cmd_notsup },
3225 	{ "$l", NULL, "print the representative thread's lwp id", cmd_notsup },
3226 	{ "$p", ":", "change debugger target context", cmd_context },
3227 	{ "$x", NULL, "print floating point registers", cmd_notsup },
3228 	{ "$X", NULL, "print floating point registers", cmd_notsup },
3229 	{ "$y", NULL, "print floating point registers", cmd_notsup },
3230 	{ "$Y", NULL, "print floating point registers", cmd_notsup },
3231 	{ ":A", "?[core|pid]", "attach to process or core file", cmd_notsup },
3232 	{ ":c", "[SIG]", "continue target execution", cmd_cont },
3233 	{ ":e", "[SIG]", "step target over next instruction", cmd_next },
3234 	{ ":i", ":", "ignore signal (delete all matching events)", cmd_notsup },
3235 	{ ":k", NULL, "forcibly kill and release target", cmd_notsup },
3236 	{ ":t", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on delivery "
3237 	    "of the specified signals", cmd_sigbp, sigbp_help },
3238 	{ ":r", "[ args ... ]", "run a new target process", cmd_run },
3239 	{ ":R", NULL, "release the previously attached process", cmd_notsup },
3240 	{ ":s", "[SIG]", "single-step target to next instruction", cmd_step },
3241 	{ ":u", "[SIG]", "step target out of current function", cmd_step_out },
3242 	{ "attach", "?[core|pid]",
3243 	    "attach to process or core file", cmd_notsup },
3244 	{ "cat", "[file ...]", "concatenate and display files", cmd_cat },
3245 	{ "cont", "[SIG]", "continue target execution", cmd_cont },
3246 	{ "context", ":", "change debugger target context", cmd_context },
3247 	{ "dem", "name ...", "demangle C++ symbol names", cmd_demstr },
3248 	{ "fltbp", "?[+/-dDestT] [-c cmd] [-n count] fault ...",
3249 	    "stop on machine fault", cmd_fltbp, fltbp_help },
3250 	{ "fpregs", NULL, "print floating point registers", cmd_notsup },
3251 	{ "kill", NULL, "forcibly kill and release target", cmd_notsup },
3252 	{ "load", "[-s] module", "load debugger module", cmd_load, load_help },
3253 	{ "log", "[-d | [-e] file]", "log session to a file", cmd_log },
3254 	{ "next", "[SIG]", "step target over next instruction", cmd_next },
3255 	{ "quit", NULL, "quit debugger", cmd_quit },
3256 	{ "release", NULL,
3257 	    "release the previously attached process", cmd_notsup },
3258 	{ "run", "[ args ... ]", "run a new target process", cmd_run },
3259 	{ "sigbp", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on "
3260 	    "delivery of the specified signals", cmd_sigbp, sigbp_help },
3261 	{ "step", "[ over | out ] [SIG]",
3262 	    "single-step target to next instruction", cmd_step },
3263 	{ "sysbp", "?[+/-dDestT] [-io] [-c cmd] [-n count] syscall ...",
3264 	    "stop on entry or exit from system call", cmd_sysbp, sysbp_help },
3265 	{ "unload", "module", "unload debugger module", cmd_unload },
3266 	{ "wp", ":[+/-dDelstT] [-rwx] [-c cmd] [-n count] [-L size]",
3267 	    "set a watchpoint at the specified address", cmd_wp, wp_help },
3268 #endif
3269 
3270 	{ NULL }
3271 };
3272