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