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