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