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