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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  * Copyright 2020 Joyent, Inc.
  25  * Copyright (c) 2017 by Delphix. All rights reserved.
  26  */
  27 
  28 /*
  29  * Format String Decoder
  30  *
  31  * This file provides the core engine for converting strings of format
  32  * characters into formatted output.  The various format dcmds invoke the
  33  * mdb_fmt_print() function below with a target, address space identifier,
  34  * address, count, and format character, and it reads the required data from
  35  * the target and prints the formatted output to stdout.  Since nearly two
  36  * thirds of the format characters can be expressed as simple printf format
  37  * strings, we implement the engine using the lookup table below.  Each entry
  38  * provides either a pointer to a printf format string or a pointer to a
  39  * function to perform special processing.  For the printf case, the
  40  * corresponding data size in bytes is also supplied.  The printf processing
  41  * code handles 1, 2, 4, and 8-byte reads into an unsigned integer container
  42  * of the given size, and then simply calls mdb_iob_printf with the integer
  43  * and format string. This handles all printf cases, except when unsigned
  44  * promotion of an integer type in the varargs list does not perform the
  45  * conversion we require to get the proper result.  With the current set of
  46  * format characters, this case only occurs twice: we need a 4-byte float
  47  * to get promoted to 8-byte double for the 'f' format so it can be
  48  * correctly formatted by %f, and we need a 1-byte int8_t to get promoted
  49  * with sign extension to a 4-byte int32_t for the 'v' format so it can be
  50  * correctly formatted by %d.  We provide explicit functions to handle these
  51  * cases, as well as to handle special format characters such as 'i', etc.
  52  * We also provide a cmd_formats() dcmd function below which prints a table
  53  * of the output formats and their sizes.  Format characters that provide
  54  * custom functions provide their help description string explicitly.  All
  55  * the printf formats have their help strings generated automatically by
  56  * our printf "unparser" mdb_iob_format2str().
  57  */
  58 
  59 #include <mdb/mdb_types.h>
  60 #include <mdb/mdb_target.h>
  61 #include <mdb/mdb_io.h>
  62 #include <mdb/mdb_err.h>
  63 #include <mdb/mdb_string.h>
  64 #include <mdb/mdb_modapi.h>
  65 #include <mdb/mdb.h>
  66 
  67 #define FUNCP(p)        ((void *)(p))   /* Cast to f_ptr type */
  68 #define SZ_NONE         ((size_t)-1L)   /* Format does not change dot */
  69 
  70 typedef mdb_tgt_addr_t mdb_fmt_func_f(mdb_tgt_t *,
  71     mdb_tgt_as_t, mdb_tgt_addr_t, size_t);
  72 
  73 /*
  74  * There are several 'special' characters that are handled outside of
  75  * mdb_fmt_print().  These are characters that write (vwWZ) and characters that
  76  * match (lLM).  We include them here so that ::formats can display an
  77  * appropriate message, but they are handled specially by write_arglist() and
  78  * match_arglist() in mdb_cmds.c.
  79  */
  80 #define FMT_NONE        0x0     /* Format character is not supported */
  81 #define FMT_FUNC        0x1     /* f_ptr is a mdb_fmt_func_f to call */
  82 #define FMT_PRINTF      0x2     /* f_ptr is a const char * format string */
  83 #define FMT_MATCH       0x4     /* Match command (not supported here) */
  84 #define FMT_WRITE       0x8     /* Command writes to address space */
  85 #define FMT_NOAUTOWRAP  0x10    /* Autowrap should not be autoenabled */
  86 
  87 #define FMT_TYPE(x)     ((x) & 0x7) /* Excludes modifying flags */
  88 
  89 typedef struct mdb_fmt_desc {
  90         int f_type;             /* Type of format (see above) */
  91         void *f_ptr;            /* Data pointer (see above) */
  92         const char *f_help;     /* Additional help string */
  93         size_t f_size;          /* Size of type in bytes, or SZ_NONE */
  94         boolean_t f_float;      /* Is this a floating point type */
  95 } mdb_fmt_desc_t;
  96 
  97 static const char help_plus[] = "increment dot by the count";
  98 static const char help_minus[] = "decrement dot by the count";
  99 static const char help_escchr[] = "character using C character notation";
 100 static const char help_swapint[] = "swap bytes and shorts";
 101 static const char help_dotinstr[] = "address and disassembled instruction";
 102 static const char help_instr[] = "disassembled instruction";
 103 static const char help_escstr[] = "string using C string notation";
 104 static const char help_time32[] = "decoded time32_t";
 105 static const char help_carat[] = "decrement dot by increment * count";
 106 static const char help_dot[] = "dot as symbol+offset";
 107 #ifndef _KMDB
 108 static const char help_f[] = "float";
 109 #endif
 110 static const char help_swapshort[] = "swap bytes";
 111 static const char help_nl[] = "newline";
 112 static const char help_ws[] = "whitespace";
 113 static const char help_rawstr[] = "raw string";
 114 static const char help_tab[] = "horizontal tab";
 115 static const char help_sdbyte[] = "decimal signed int";
 116 static const char help_time64[] = "decoded time64_t";
 117 static const char help_binary[] = "binary unsigned long long";
 118 static const char help_hex64[] = "hexadecimal long long";
 119 static const char help_match32[] = "int";
 120 static const char help_match64[] = "long long";
 121 static const char help_match16[] = "short";
 122 static const char help_uintptr[] = "hexadecimal uintptr_t";
 123 static const char help_ctf[] = "whose size is inferred by CTF info";
 124 static const char help_jazzed[] = "jazzed-up binary unsigned long long";
 125 
 126 /*ARGSUSED*/
 127 static mdb_tgt_addr_t
 128 fmt_dot(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 129 {
 130         uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
 131         char buf[24];
 132 
 133         mdb_iob_clrflags(mdb.m_out, oflags);
 134 
 135         if (mdb.m_flags & MDB_FL_PSYM) {
 136                 while (cnt-- != 0)
 137                         mdb_iob_printf(mdb.m_out, "%-#16lla%16T", addr);
 138         } else {
 139                 (void) mdb_iob_snprintf(buf, sizeof (buf),
 140                     "%#llx:", (u_longlong_t)addr);
 141                 while (cnt-- != 0)
 142                         mdb_iob_printf(mdb.m_out, "%-16s%16T", buf);
 143         }
 144 
 145         mdb_iob_setflags(mdb.m_out, oflags);
 146         mdb_nv_set_value(mdb.m_rvalue, addr);
 147         return (addr);
 148 }
 149 
 150 #ifndef _KMDB
 151 static mdb_tgt_addr_t
 152 fmt_float(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 153 {
 154         float f;
 155         /*
 156          * We need to handle float as a special case because we need it to be
 157          * promoted to a double by virtue of appearing as a parameter, and all
 158          * our generic format handling below is based on integer types.
 159          */
 160         while (cnt-- != 0) {
 161                 if (mdb_tgt_aread(t, as, &f, sizeof (f), addr) != sizeof (f)) {
 162                         warn("failed to read data from target");
 163                         break;
 164                 }
 165                 mdb_iob_printf(mdb.m_out, "%e", f);
 166                 addr += sizeof (f);
 167         }
 168         return (addr);
 169 }
 170 #endif
 171 
 172 /*ARGSUSED*/
 173 static mdb_tgt_addr_t
 174 fmt_plus(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 175 {
 176         return (addr + cnt);
 177 }
 178 
 179 /*ARGSUSED*/
 180 static mdb_tgt_addr_t
 181 fmt_minus(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 182 {
 183         return (addr - cnt);
 184 }
 185 
 186 /*ARGSUSED*/
 187 static mdb_tgt_addr_t
 188 fmt_carat(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 189 {
 190         return (addr - (mdb.m_incr * cnt));
 191 }
 192 
 193 /*ARGSUSED*/
 194 static mdb_tgt_addr_t
 195 fmt_nl(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 196 {
 197         while (cnt-- != 0)
 198                 mdb_iob_nl(mdb.m_out);
 199 
 200         return (addr);
 201 }
 202 
 203 /*ARGSUSED*/
 204 static mdb_tgt_addr_t
 205 fmt_ws(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 206 {
 207         mdb_iob_ws(mdb.m_out, cnt);
 208         return (addr);
 209 }
 210 
 211 /*ARGSUSED*/
 212 static mdb_tgt_addr_t
 213 fmt_tab(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 214 {
 215         size_t ts = mdb_iob_gettabstop(mdb.m_out);
 216 
 217         mdb_iob_tabstop(mdb.m_out, cnt);
 218         mdb_iob_tab(mdb.m_out);
 219         mdb_iob_tabstop(mdb.m_out, ts);
 220 
 221         return (addr);
 222 }
 223 
 224 static mdb_tgt_addr_t
 225 fmt_rawstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 226 {
 227         uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
 228         char buf[BUFSIZ];
 229         ssize_t nbytes;
 230 
 231         mdb_iob_clrflags(mdb.m_out, oflags);
 232 
 233         for (; cnt-- != 0; addr++) {
 234                 do {
 235                         nbytes = mdb_tgt_readstr(t, as, buf, BUFSIZ, addr);
 236                         if (nbytes > 0) {
 237                                 mdb_iob_puts(mdb.m_out, buf);
 238                                 addr += MIN(nbytes, BUFSIZ - 1);
 239                         } else if (nbytes < 0) {
 240                                 warn("failed to read data from target");
 241                                 goto out;
 242                         }
 243                 } while (nbytes == BUFSIZ);
 244 
 245                 if (cnt != 0)
 246                         mdb_iob_puts(mdb.m_out, "\\0");
 247         }
 248 out:
 249         mdb_iob_setflags(mdb.m_out, oflags);
 250         return (addr);
 251 }
 252 
 253 static mdb_tgt_addr_t
 254 fmt_escstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 255 {
 256         uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
 257         char buf[BUFSIZ];
 258         ssize_t nbytes;
 259         char *s;
 260 
 261         mdb_iob_clrflags(mdb.m_out, oflags);
 262 
 263         for (; cnt-- != 0; addr++) {
 264                 do {
 265                         nbytes = mdb_tgt_readstr(t, as, buf, BUFSIZ, addr);
 266                         if (nbytes > 0) {
 267                                 s = strchr2esc(buf, strlen(buf));
 268                                 mdb_iob_puts(mdb.m_out, s);
 269                                 strfree(s);
 270                                 addr += MIN(nbytes, BUFSIZ - 1);
 271                         } else if (nbytes < 0) {
 272                                 warn("failed to read data from target");
 273                                 goto out;
 274                         }
 275                 } while (nbytes == BUFSIZ);
 276 
 277                 if (cnt != 0)
 278                         mdb_iob_puts(mdb.m_out, "\\0");
 279         }
 280 out:
 281         mdb_iob_setflags(mdb.m_out, oflags);
 282         return (addr);
 283 }
 284 
 285 static mdb_tgt_addr_t
 286 fmt_escchr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 287 {
 288         char *(*convert)(const char *, size_t);
 289         ssize_t nbytes;
 290         char *buf, *s;
 291 
 292         if (mdb.m_flags & MDB_FL_ADB)
 293                 convert = &strchr2adb;
 294         else
 295                 convert = &strchr2esc;
 296 
 297         buf = mdb_alloc(cnt + 1, UM_SLEEP);
 298         buf[cnt] = 0;
 299 
 300         if ((nbytes = mdb_tgt_aread(t, as, buf, cnt, addr)) > 0) {
 301                 s = convert(buf, nbytes);
 302                 mdb_iob_puts(mdb.m_out, s);
 303                 strfree(s);
 304                 addr += nbytes;
 305         }
 306 
 307         mdb_free(buf, cnt + 1);
 308         return (addr);
 309 }
 310 
 311 static mdb_tgt_addr_t
 312 fmt_swapshort(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 313 {
 314         ushort_t x;
 315 
 316         while (cnt-- != 0) {
 317                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 318                         x = (x << 8) | (x >> 8);
 319                         mdb_iob_printf(mdb.m_out, "%-8x", x);
 320                         mdb_nv_set_value(mdb.m_rvalue, x);
 321                         addr += sizeof (x);
 322                 } else {
 323                         warn("failed to read data from target");
 324                         break;
 325                 }
 326         }
 327         return (addr);
 328 }
 329 
 330 static mdb_tgt_addr_t
 331 fmt_swapint(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 332 {
 333         uint_t x;
 334 
 335         while (cnt-- != 0) {
 336                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 337                         x = ((x << 24) | ((x << 8) & 0xff0000) |
 338                             ((x >> 8) & 0xff00) | ((x >> 24) & 0xff));
 339                         mdb_iob_printf(mdb.m_out, "%-16x", x);
 340                         mdb_nv_set_value(mdb.m_rvalue, x);
 341                         addr += sizeof (x);
 342                 } else {
 343                         warn("failed to read data from target");
 344                         break;
 345                 }
 346         }
 347         return (addr);
 348 }
 349 
 350 static mdb_tgt_addr_t
 351 fmt_time32(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 352 {
 353         int32_t x;
 354 
 355         while (cnt-- != 0) {
 356                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 357                         mdb_iob_printf(mdb.m_out, "%-24Y", (time_t)x);
 358                         mdb_nv_set_value(mdb.m_rvalue, x);
 359                         addr += sizeof (x);
 360                 } else {
 361                         warn("failed to read data from target");
 362                         break;
 363                 }
 364         }
 365         return (addr);
 366 }
 367 
 368 static mdb_tgt_addr_t
 369 fmt_time64(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 370 {
 371         int64_t x;
 372 
 373         while (cnt-- != 0) {
 374                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 375                         if ((time_t)x == x)
 376                                 mdb_iob_printf(mdb.m_out, "%-24Y", (time_t)x);
 377                         else
 378                                 mdb_iob_printf(mdb.m_out, "%-24llR", x);
 379 
 380                         mdb_nv_set_value(mdb.m_rvalue, x);
 381                         addr += sizeof (x);
 382                 } else {
 383                         warn("failed to read data from target");
 384                         break;
 385                 }
 386         }
 387         return (addr);
 388 }
 389 
 390 static mdb_tgt_addr_t
 391 fmt_sdbyte(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 392 {
 393         int8_t x;
 394 
 395         while (cnt-- != 0) {
 396                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 397                         mdb_iob_printf(mdb.m_out, "%-8d", (int32_t)x);
 398                         mdb_nv_set_value(mdb.m_rvalue, (uint8_t)x);
 399                         addr += sizeof (x);
 400                 } else {
 401                         warn("failed to read data from target");
 402                         break;
 403                 }
 404         }
 405         return (addr);
 406 }
 407 
 408 static mdb_tgt_addr_t
 409 fmt_instr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 410 {
 411         char buf[BUFSIZ];
 412         uintptr_t naddr;
 413 
 414         while (cnt-- != 0) {
 415                 naddr = mdb_dis_ins2str(mdb.m_disasm, t, as,
 416                     buf, sizeof (buf), addr);
 417                 if (naddr == addr)
 418                         return (addr); /* If we didn't move, we failed */
 419                 mdb_iob_printf(mdb.m_out, "%s\n", buf);
 420                 addr = naddr;
 421         }
 422         return (addr);
 423 }
 424 
 425 static mdb_tgt_addr_t
 426 fmt_dotinstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 427 {
 428         uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
 429 
 430         char buf[BUFSIZ];
 431         uintptr_t naddr;
 432         uint32_t i;
 433 
 434         for (mdb_iob_clrflags(mdb.m_out, oflags); cnt-- != 0; addr = naddr) {
 435                 if (mdb_tgt_aread(t, as, &i, sizeof (i), addr) != sizeof (i)) {
 436                         warn("failed to read data from target");
 437                         break; /* Fail if we can't read instruction */
 438                 }
 439                 naddr = mdb_dis_ins2str(mdb.m_disasm, t, as,
 440                     buf, sizeof (buf), addr);
 441                 if (naddr == addr)
 442                         break; /* Fail if we didn't advance */
 443                 mdb_iob_printf(mdb.m_out, "%lx %x: %s\n", (long)addr, i, buf);
 444         }
 445 
 446         mdb_iob_setflags(mdb.m_out, oflags);
 447         return (addr);
 448 }
 449 
 450 static mdb_tgt_addr_t
 451 fmt_binary(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 452 {
 453         uint64_t x;
 454         const char *fmts[] = { "%-64s", "%-65s" };
 455         const uint64_t mask = 0x8000000000000000ull;
 456 
 457         while (cnt-- != 0) {
 458                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 459                         mdb_iob_printf(mdb.m_out, fmts[(x & mask) != 0],
 460                             numtostr(x, 2, NTOS_UNSIGNED));
 461                         mdb_nv_set_value(mdb.m_rvalue, x);
 462                         addr += sizeof (x);
 463                 } else {
 464                         warn("failed to read data from target");
 465                         break;
 466                 }
 467         }
 468         return (addr);
 469 }
 470 
 471 static mdb_tgt_addr_t
 472 fmt_jazzed(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 473 {
 474         uint64_t x;
 475         char buf[256];
 476 
 477         while (cnt-- != 0) {
 478                 boolean_t header = B_TRUE;
 479 
 480                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) != sizeof (x)) {
 481                         warn("failed to read data from target");
 482                         break;
 483                 }
 484 
 485                 mdb_nv_set_value(mdb.m_rvalue, x);
 486                 addr += sizeof (x);
 487 
 488                 mdb_iob_printf(mdb.m_out, "%s\n",
 489                     numtostr(x, 2, NTOS_UNSIGNED));
 490 
 491                 while (x != 0) {
 492                         int b = 63, forearm;
 493                         int i = 0, highbit;
 494 
 495                         /*
 496                          * Find the high bit...
 497                          */
 498                         while (!(x & (1ULL << b)))
 499                                 b--;
 500 
 501                         highbit = b;
 502 
 503                         /*
 504                          * ...and iterate over the remaining bits, putting
 505                          * the upper arm in our buffer for any set bit (and
 506                          * a space otherwise).
 507                          */
 508                         while (x & ((1ULL << b) - 1)) {
 509                                 buf[i++] = x & (1ULL << b) ? '|' : ' ';
 510                                 b--;
 511                         }
 512 
 513                         /*
 514                          * If this is the header line, print the upper arm
 515                          * for the lowest set bit and continue...
 516                          */
 517                         if (header) {
 518                                 header = B_FALSE;
 519                                 buf[i] = '\0';
 520                                 mdb_iob_printf(mdb.m_out, "%s|\n", buf);
 521                                 continue;
 522                         }
 523 
 524                         /*
 525                          * ...otherwise, put the elbow and forearm into our
 526                          * buffer, and print it.
 527                          */
 528                         buf[i++] = '+';
 529 
 530                         for (forearm = b; forearm > -2; forearm--)
 531                                 buf[i++] = '-';
 532 
 533                         buf[i] = '\0';
 534                         mdb_iob_printf(mdb.m_out, "%s bit %d %smask 0x%0*llx\n",
 535                             buf, b, b < 10 && highbit >= 10 ? " " : "",
 536                             (highbit / 4) + 1, 1ULL << b);
 537 
 538                         /*
 539                          * Finally, clear the lowest set bit and continue.
 540                          */
 541                         x &= ~(1ULL << b);
 542                 }
 543         }
 544 
 545         return (addr);
 546 }
 547 
 548 static mdb_tgt_addr_t
 549 fmt_hex64(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
 550 {
 551         const char *fmts[] = { "%-16llx", "%-17llx" };
 552         const uint64_t mask = 0xf000000000000000ull;
 553         uint64_t x;
 554 
 555         while (cnt-- != 0) {
 556                 if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
 557                         mdb_iob_printf(mdb.m_out, fmts[(x & mask) != 0], x);
 558                         mdb_nv_set_value(mdb.m_rvalue, x);
 559                         addr += sizeof (x);
 560                 } else {
 561                         warn("failed to read data from target");
 562                         break;
 563                 }
 564         }
 565         return (addr);
 566 }
 567 
 568 static const mdb_fmt_desc_t fmttab[] = {
 569         { FMT_NONE, NULL, NULL, 0 },                            /* 0 = NUL */
 570         { FMT_NONE, NULL, NULL, 0 },                            /* 1 = SOH */
 571         { FMT_NONE, NULL, NULL, 0 },                            /* 2 = STX */
 572         { FMT_NONE, NULL, NULL, 0 },                            /* 3 = ETX */
 573         { FMT_NONE, NULL, NULL, 0 },                            /* 4 = EOT */
 574         { FMT_NONE, NULL, NULL, 0 },                            /* 5 = ENQ */
 575         { FMT_NONE, NULL, NULL, 0 },                            /* 6 = ACK */
 576         { FMT_NONE, NULL, NULL, 0 },                            /* 7 = BEL */
 577         { FMT_NONE, NULL, NULL, 0 },                            /* 8 = BS */
 578         { FMT_NONE, NULL, NULL, 0 },                            /* 9 = \t */
 579         { FMT_NONE, NULL, NULL, 0 },                            /* 10 = \n */
 580         { FMT_NONE, NULL, NULL, 0 },                            /* 11 = VT */
 581         { FMT_NONE, NULL, NULL, 0 },                            /* 12 = FF */
 582         { FMT_NONE, NULL, NULL, 0 },                            /* 13 = \r */
 583         { FMT_NONE, NULL, NULL, 0 },                            /* 14 = SO */
 584         { FMT_NONE, NULL, NULL, 0 },                            /* 15 = SI */
 585         { FMT_NONE, NULL, NULL, 0 },                            /* 16 = DLE */
 586         { FMT_NONE, NULL, NULL, 0 },                            /* 17 = DC1 */
 587         { FMT_NONE, NULL, NULL, 0 },                            /* 18 = DC2 */
 588         { FMT_NONE, NULL, NULL, 0 },                            /* 19 = DC3 */
 589         { FMT_NONE, NULL, NULL, 0 },                            /* 20 = DC4 */
 590         { FMT_NONE, NULL, NULL, 0 },                            /* 21 = NAK */
 591         { FMT_NONE, NULL, NULL, 0 },                            /* 22 = EYC */
 592         { FMT_NONE, NULL, NULL, 0 },                            /* 23 = ETB */
 593         { FMT_NONE, NULL, NULL, 0 },                            /* 24 = CAN */
 594         { FMT_NONE, NULL, NULL, 0 },                            /* 25 = EM */
 595         { FMT_NONE, NULL, NULL, 0 },                            /* 26 = SUB */
 596         { FMT_NONE, NULL, NULL, 0 },                            /* 27 = ESC */
 597         { FMT_NONE, NULL, NULL, 0 },                            /* 28 = FS */
 598         { FMT_NONE, NULL, NULL, 0 },                            /* 29 = GS */
 599         { FMT_NONE, NULL, NULL, 0 },                            /* 30 = RS */
 600         { FMT_NONE, NULL, NULL, 0 },                            /* 31 = US */
 601         { FMT_NONE, NULL, NULL, 0 },                            /* 32 = SPACE */
 602         { FMT_NONE, NULL, NULL, 0 },                            /* 33 = ! */
 603         { FMT_NONE, NULL, NULL, 0 },                            /* 34 = " */
 604         { FMT_NONE, NULL, NULL, 0 },                            /* 35 = # */
 605         { FMT_NONE, NULL, NULL, 0 },                            /* 36 = $ */
 606         { FMT_NONE, NULL, NULL, 0 },                            /* 37 = % */
 607         { FMT_NONE, NULL, NULL, 0 },                            /* 38 = & */
 608         { FMT_NONE, NULL, NULL, 0 },                            /* 39 = ' */
 609         { FMT_NONE, NULL, NULL, 0 },                            /* 40 = ( */
 610         { FMT_NONE, NULL, NULL, 0 },                            /* 41 = ) */
 611         { FMT_NONE, NULL, NULL, 0 },                            /* 42 = * */
 612         { FMT_FUNC, FUNCP(fmt_plus), help_plus, 0 },            /* 43 = + */
 613         { FMT_NONE, NULL, NULL, 0 },                            /* 44 = , */
 614         { FMT_FUNC, FUNCP(fmt_minus), help_minus, 0 },          /* 45 = - */
 615         { FMT_NONE, NULL, NULL, 0 },                            /* 46 = . */
 616         { FMT_NONE, NULL, NULL, 0 },                            /* 47 = / */
 617         { FMT_NONE, NULL, NULL, 0 },                            /* 48 = 0 */
 618         { FMT_NONE, NULL, NULL, 0 },                            /* 49 = 1 */
 619         { FMT_NONE, NULL, NULL, 0 },                            /* 50 = 2 */
 620         { FMT_NONE, NULL, NULL, 0 },                            /* 51 = 3 */
 621         { FMT_NONE, NULL, NULL, 0 },                            /* 52 = 4 */
 622         { FMT_NONE, NULL, NULL, 0 },                            /* 53 = 5 */
 623         { FMT_NONE, NULL, NULL, 0 },                            /* 54 = 6 */
 624         { FMT_NONE, NULL, NULL, 0 },                            /* 55 = 7 */
 625         { FMT_NONE, NULL, NULL, 0 },                            /* 56 = 8 */
 626         { FMT_NONE, NULL, NULL, 0 },                            /* 57 = 9 */
 627         { FMT_NONE, NULL, NULL, 0 },                            /* 58 = : */
 628         { FMT_NONE, NULL, NULL, 0 },                            /* 59 = ; */
 629         { FMT_NONE, NULL, NULL, 0 },                            /* 60 = < */
 630         { FMT_NONE, NULL, NULL, 0 },                            /* 61 = = */
 631         { FMT_NONE, NULL, NULL, 0 },                            /* 62 = > */
 632         { FMT_NONE, NULL, NULL, 0 },                            /* 63 = ? */
 633         { FMT_NONE, NULL, NULL, 0 },                            /* 64 = @ */
 634         { FMT_NONE, NULL, NULL, 0 },                            /* 65 = A */
 635         { FMT_PRINTF, "%-8x", NULL, 1 },                        /* 66 = B */
 636         { FMT_FUNC, FUNCP(fmt_escchr), help_escchr, 1 },        /* 67 = C */
 637         { FMT_PRINTF, "%-16d", NULL, 4 },                       /* 68 = D */
 638         { FMT_PRINTF, "%-21llu", NULL, 8 },                     /* 69 = E */
 639 #ifdef _KMDB
 640         { FMT_NONE, NULL, NULL, 0 },                            /* 70 = F */
 641 #else
 642         { FMT_PRINTF, "%g", NULL, sizeof (double), B_TRUE },    /* 70 = F */
 643 #endif
 644         { FMT_PRINTF, "%-23llo", NULL, 8 },                     /* 71 = G */
 645         { FMT_FUNC, FUNCP(fmt_swapint), help_swapint, 4 },      /* 72 = H */
 646         { FMT_FUNC, FUNCP(fmt_dotinstr), help_dotinstr, 0 },    /* 73 = I */
 647         { FMT_FUNC, FUNCP(fmt_hex64), help_hex64, 8 },          /* 74 = J */
 648 #ifdef _LP64
 649         { FMT_FUNC, FUNCP(fmt_hex64), help_uintptr, 8 },        /* 75 = K (J) */
 650 #else
 651         { FMT_PRINTF, "%-16x", help_uintptr, 4 },               /* 75 = K (X) */
 652 #endif
 653         { FMT_MATCH, NULL, help_match32, 4 },                   /* 76 = L */
 654         { FMT_MATCH, NULL, help_match64, 8 },                   /* 77 = M */
 655         { FMT_FUNC, FUNCP(fmt_nl), help_nl, SZ_NONE },          /* 78 = N */
 656         { FMT_PRINTF, "%-#16o", NULL, 4 },                      /* 79 = O */
 657         { FMT_PRINTF, "%-19a", NULL, sizeof (uintptr_t) },      /* 80 = P */
 658         { FMT_PRINTF, "%-#16q", NULL, 4 },                      /* 81 = Q */
 659         { FMT_FUNC, FUNCP(fmt_binary), help_binary, 8 },        /* 82 = R */
 660         { FMT_FUNC, FUNCP(fmt_escstr), help_escstr, 0 },        /* 83 = S */
 661         { FMT_FUNC, FUNCP(fmt_tab), help_tab, SZ_NONE },        /* 84 = T */
 662         { FMT_PRINTF, "%-16u", NULL, 4 },                       /* 85 = U */
 663         { FMT_PRINTF, "%-8u", NULL, 1 },                        /* 86 = V */
 664         { FMT_PRINTF|FMT_WRITE, "%-16r", NULL, 4 },             /* 87 = W */
 665         { FMT_PRINTF, "%-16x", NULL, 4 },                       /* 88 = X */
 666         { FMT_FUNC, FUNCP(fmt_time32), help_time32, 4 },        /* 89 = Y */
 667         { FMT_FUNC|FMT_WRITE, FUNCP(fmt_hex64), help_hex64, 8 }, /* 90 = Z */
 668         { FMT_NONE, NULL, NULL, 0 },                            /* 91 = [ */
 669         { FMT_NONE, NULL, NULL, 0 },                            /* 92 = \ */
 670         { FMT_NONE, NULL, NULL, 0 },                            /* 93 = ] */
 671         { FMT_FUNC, FUNCP(fmt_carat), help_carat, 0 },          /* 94 = ^ */
 672         { FMT_NONE, NULL, NULL, 0 },                            /* 95 = _ */
 673         { FMT_NONE, NULL, NULL, 0 },                            /* 96 = ` */
 674         { FMT_FUNC, FUNCP(fmt_dot), help_dot, SZ_NONE },        /* 97 = a */
 675         { FMT_PRINTF, "%-#8o", NULL, 1 },                       /* 98 = b */
 676         { FMT_PRINTF, "%c", NULL, 1 },                          /* 99 = c */
 677         { FMT_PRINTF, "%-8hd", NULL, 2 },                       /* 100 = d */
 678         { FMT_PRINTF, "%-21lld", NULL, 8 },                     /* 101 = e */
 679 #ifdef _KMDB
 680         { FMT_NONE, NULL, NULL, 0 },                            /* 102 = f */
 681 #else
 682         { FMT_FUNC, FUNCP(fmt_float), help_f, sizeof (float),
 683             B_TRUE },                                           /* 102 = f */
 684 #endif
 685         { FMT_PRINTF, "%-24llq", NULL, 8 },                     /* 103 = g */
 686         { FMT_FUNC, FUNCP(fmt_swapshort), help_swapshort, 2 },  /* 104 = h */
 687         { FMT_FUNC, FUNCP(fmt_instr), help_instr, 0 },          /* 105 = i */
 688         { FMT_FUNC|FMT_NOAUTOWRAP,
 689             FUNCP(fmt_jazzed), help_jazzed, 8 },                /* 106 = j */
 690         { FMT_NONE, NULL, NULL, 0 },                            /* 107 = k */
 691         { FMT_MATCH, NULL, help_match16, 2 },                   /* 108 = l */
 692         { FMT_NONE, NULL, NULL, 0 },                            /* 109 = m */
 693         { FMT_FUNC, FUNCP(fmt_nl), help_nl, SZ_NONE },          /* 110 = n */
 694         { FMT_PRINTF, "%-#8ho", NULL, 2 },                      /* 111 = o */
 695         { FMT_PRINTF, "%-19a", NULL, sizeof (uintptr_t) },      /* 112 = p */
 696         { FMT_PRINTF, "%-#8hq", NULL, 2 },                      /* 113 = q */
 697         { FMT_FUNC, FUNCP(fmt_ws), help_ws, SZ_NONE },          /* 114 = r */
 698         { FMT_FUNC, FUNCP(fmt_rawstr), help_rawstr, 0 },        /* 115 = s */
 699         { FMT_FUNC, FUNCP(fmt_tab), help_tab, SZ_NONE },        /* 116 = t */
 700         { FMT_PRINTF, "%-8hu", NULL, 2 },                       /* 117 = u */
 701         { FMT_FUNC|FMT_WRITE, FUNCP(fmt_sdbyte), help_sdbyte, 1 }, /* 118 = v */
 702         { FMT_PRINTF|FMT_WRITE, "%-8hr", NULL, 2 },             /* 119 = w */
 703         { FMT_PRINTF, "%-8hx", NULL, 2 },                       /* 120 = x */
 704         { FMT_FUNC, FUNCP(fmt_time64), help_time64, 8 },        /* 121 = y */
 705         { FMT_WRITE, NULL, help_ctf, 0 },                       /* 122 = z */
 706 };
 707 
 708 mdb_tgt_addr_t
 709 mdb_fmt_print(mdb_tgt_t *t, mdb_tgt_as_t as,
 710     mdb_tgt_addr_t addr, size_t cnt, char fmt)
 711 {
 712         const mdb_fmt_desc_t *fp = &fmttab[fmt];
 713         mdb_fmt_func_f *funcp;
 714         uintmax_t rvalue;
 715         void *buf;
 716         uint_t oflags = mdb.m_flags;
 717 
 718         union {
 719                 uint64_t i8;
 720                 uint32_t i4;
 721                 uint16_t i2;
 722                 uint8_t i1;
 723                 double d;
 724         } u;
 725 
 726         if (fmt < 0 || fmt > (sizeof (fmttab) / sizeof (fmttab[0]))) {
 727                 warn("invalid format character -- '%c'\n", fmt);
 728                 return (addr);
 729         }
 730 
 731         if (!(fp->f_type & FMT_NOAUTOWRAP)) {
 732                 /*
 733                  * Unless a format has explicitly opted out, we force autowrap
 734                  * for the duration of mdb_fmt_print().
 735                  */
 736                 mdb_iob_set_autowrap(mdb.m_out);
 737         }
 738 
 739         switch (FMT_TYPE(fp->f_type)) {
 740         case FMT_FUNC:
 741                 funcp = (mdb_fmt_func_f *)fp->f_ptr;
 742                 addr = funcp(t, as, addr, cnt);
 743                 break;
 744 
 745         case FMT_PRINTF:
 746                 switch (fp->f_size) {
 747                 case 1:
 748                         buf = &u.i1;
 749                         break;
 750                 case 2:
 751                         buf = &u.i2;
 752                         break;
 753                 case 4:
 754                         buf = &u.i4;
 755                         break;
 756                 case 8:
 757                         buf = &u.i8;
 758                         break;
 759                 default:
 760                         fail("format %c is defined using illegal size\n", fmt);
 761                 }
 762 
 763                 if (fp->f_float == B_TRUE) {
 764                         if (fp->f_size != 8) {
 765                                 fail("format %c is using illegal fp size\n",
 766                                     fmt);
 767                         }
 768 
 769                         buf = &u.d;
 770                 }
 771 
 772                 while (cnt-- != 0) {
 773                         if (mdb_tgt_aread(t, as, buf, fp->f_size, addr) !=
 774                             fp->f_size) {
 775                                 warn("failed to read data from target");
 776                                 return (addr);
 777                         }
 778 
 779                         switch (fp->f_size) {
 780                         case 1:
 781                                 mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i1);
 782                                 rvalue = u.i1;
 783                                 break;
 784                         case 2:
 785                                 mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i2);
 786                                 rvalue = u.i2;
 787                                 break;
 788                         case 4:
 789                                 mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i4);
 790                                 rvalue = u.i4;
 791                                 break;
 792                         case 8:
 793                                 if (fp->f_float) {
 794                                         mdb_iob_printf(mdb.m_out, fp->f_ptr,
 795                                             u.d);
 796                                 } else {
 797                                         mdb_iob_printf(mdb.m_out, fp->f_ptr,
 798                                             u.i8);
 799                                 }
 800                                 rvalue = u.i8;
 801                                 break;
 802                         }
 803 
 804                         mdb_nv_set_value(mdb.m_rvalue, rvalue);
 805                         addr += fp->f_size;
 806                 }
 807                 break;
 808 
 809         default:
 810                 warn("invalid format character -- '%c'\n", fmt);
 811         }
 812 
 813         mdb.m_flags = oflags;
 814 
 815         return (addr);
 816 }
 817 
 818 /*ARGSUSED*/
 819 int
 820 cmd_formats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 821 {
 822         const mdb_fmt_desc_t *fp = &fmttab[0];
 823         int i;
 824         const char *write;
 825 
 826         if ((flags & DCMD_ADDRSPEC) || argc != 0)
 827                 return (DCMD_USAGE);
 828 
 829         for (i = 0; i < (sizeof (fmttab) / sizeof (fmttab[0])); i++, fp++) {
 830                 if (fp->f_type == FMT_NONE)
 831                         continue;
 832 
 833                 write = (fp->f_type & FMT_WRITE) ? "write " : "";
 834 
 835                 if (fp->f_type & FMT_FUNC)
 836                         mdb_printf("%c - %s%s", i, write, fp->f_help);
 837                 else if (fp->f_type & FMT_MATCH)
 838                         mdb_printf("%c - match %s", i, fp->f_help);
 839                 else if (fp->f_help != NULL)
 840                         mdb_printf("%c - %s%s", i, write, fp->f_help);
 841                 else
 842                         mdb_printf("%c - %s%s", i, write,
 843                             mdb_iob_format2str(fp->f_ptr));
 844 
 845                 switch (fp->f_size) {
 846                 case SZ_NONE:
 847                         mdb_printf("\n");
 848                         break;
 849                 case 0:
 850                         mdb_printf(" (variable size)\n");
 851                         break;
 852                 case 1:
 853                         mdb_printf(" (1 byte)\n");
 854                         break;
 855                 default:
 856                         mdb_printf(" (%lu bytes)\n", fp->f_size);
 857                 }
 858         }
 859 
 860         return (DCMD_OK);
 861 }