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