OpenSolaris Launch
diff --git a/usr/src/cmd/mdb/common/mdb/Makefile b/usr/src/cmd/mdb/common/mdb/Makefile
new file mode 100644
index 0000000..6d772db
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/Makefile
@@ -0,0 +1,42 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1997-1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../../../Makefile.cmd
+
+HDRS = mdb_modapi.h
+ROOTHDRDIR = $(ROOT)/usr/include/sys
+ROOTHDRS = $(HDRS:%=$(ROOTHDRDIR)/%)
+
+$(ROOTHDRS) := FILEMODE= 0644
+
+$(ROOTHDRDIR)/%.h: %.h
+ $(INS.file)
+
+.KEEP_STATE:
+.SUFFIXES:
+
+install_h: $(ROOTHDRS)
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.c b/usr/src/cmd/mdb/common/mdb/mdb.c
new file mode 100644
index 0000000..755322c
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb.c
@@ -0,0 +1,1374 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Modular Debugger (MDB)
+ *
+ * Refer to the white paper "A Modular Debugger for Solaris" for information
+ * on the design, features, and goals of MDB. See /shared/sac/PSARC/1999/169
+ * for copies of the paper and related documentation.
+ *
+ * This file provides the basic construction and destruction of the debugger's
+ * global state, as well as the main execution loop, mdb_run(). MDB maintains
+ * a stack of execution frames (mdb_frame_t's) that keep track of its current
+ * state, including a stack of input and output buffers, walk and memory
+ * garbage collect lists, and a list of commands (mdb_cmd_t's). As the
+ * parser consumes input, it fills in a list of commands to execute, and then
+ * invokes mdb_call(), below. A command consists of a dcmd, telling us
+ * what function to execute, and a list of arguments and other invocation-
+ * specific data. Each frame may have more than one command, kept on a list,
+ * when multiple commands are separated by | operators. New frames may be
+ * stacked on old ones by nested calls to mdb_run: this occurs when, for
+ * example, in the middle of processing one input source (such as a file
+ * or the terminal), we invoke a dcmd that in turn calls mdb_eval(). mdb_eval
+ * will construct a new frame whose input source is the string passed to
+ * the eval function, and then execute this frame to completion.
+ */
+
+#include <sys/param.h>
+#include <stropts.h>
+
+#define _MDB_PRIVATE
+#include <mdb/mdb.h>
+
+#include <mdb/mdb_context.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_signal.h>
+#include <mdb/mdb_macalias.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_callb.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_io.h>
+#ifdef _KMDB
+#include <kmdb/kmdb_module.h>
+#endif
+
+/*
+ * Macro for testing if a dcmd's return status (x) indicates that we should
+ * abort the current loop or pipeline.
+ */
+#define DCMD_ABORTED(x) ((x) == DCMD_USAGE || (x) == DCMD_ABORT)
+
+extern const mdb_dcmd_t mdb_dcmd_builtins[];
+extern mdb_dis_ctor_f *const mdb_dis_builtins[];
+
+/*
+ * Variable discipline for toggling MDB_FL_PSYM based on the value of the
+ * undocumented '_' variable. Once adb(1) has been removed from the system,
+ * we should just remove this functionality and always disable PSYM for macros.
+ */
+static uintmax_t
+psym_disc_get(const mdb_var_t *v)
+{
+ int i = (mdb.m_flags & MDB_FL_PSYM) ? 1 : 0;
+ int j = (MDB_NV_VALUE(v) != 0) ? 1 : 0;
+
+ if ((i ^ j) == 0)
+ MDB_NV_VALUE((mdb_var_t *)v) = j ^ 1;
+
+ return (MDB_NV_VALUE(v));
+}
+
+static void
+psym_disc_set(mdb_var_t *v, uintmax_t value)
+{
+ if (value == 0)
+ mdb.m_flags |= MDB_FL_PSYM;
+ else
+ mdb.m_flags &= ~MDB_FL_PSYM;
+
+ MDB_NV_VALUE(v) = value;
+}
+
+/*
+ * Variable discipline for making <1 (most recent offset) behave properly.
+ */
+static uintmax_t
+roff_disc_get(const mdb_var_t *v)
+{
+ return (MDB_NV_VALUE(v));
+}
+
+static void
+roff_disc_set(mdb_var_t *v, uintmax_t value)
+{
+ mdb_nv_set_value(mdb.m_proffset, MDB_NV_VALUE(v));
+ MDB_NV_VALUE(v) = value;
+}
+
+/*
+ * Variable discipline for exporting the representative thread.
+ */
+static uintmax_t
+thr_disc_get(const mdb_var_t *v)
+{
+ mdb_tgt_status_t s;
+
+ if (mdb.m_target != NULL && mdb_tgt_status(mdb.m_target, &s) == 0)
+ return (s.st_tid);
+
+ return (MDB_NV_VALUE(v));
+}
+
+const char **
+mdb_path_alloc(const char *s, size_t *newlen)
+{
+ char *format = mdb_alloc(strlen(s) * 2 + 1, UM_NOSLEEP);
+ const char **path;
+ char *p, *q;
+
+ struct utsname uts;
+ size_t len;
+ int i;
+
+ mdb_arg_t arg_i, arg_m, arg_p, arg_r, arg_t, arg_R, arg_V;
+ mdb_argvec_t argv;
+
+ static const char *empty_path[] = { NULL };
+
+ if (format == NULL)
+ goto nomem;
+
+ while (*s == ':')
+ s++; /* strip leading delimiters */
+
+ if (*s == '\0') {
+ *newlen = 0;
+ return (empty_path);
+ }
+
+ (void) strcpy(format, s);
+ mdb_argvec_create(&argv);
+
+ /*
+ * %i embedded in path string expands to ISA.
+ */
+ arg_i.a_type = MDB_TYPE_STRING;
+ if (mdb.m_target != NULL)
+ arg_i.a_un.a_str = mdb_tgt_isa(mdb.m_target);
+ else
+ arg_i.a_un.a_str = mdb_conf_isa();
+
+ /*
+ * %p embedded in path string expands to the platform name.
+ */
+ arg_p.a_type = MDB_TYPE_STRING;
+ if (mdb.m_target != NULL)
+ arg_p.a_un.a_str = mdb_tgt_platform(mdb.m_target);
+ else
+ arg_p.a_un.a_str = mdb_conf_platform();
+
+ /*
+ * %r embedded in path string expands to root directory, or
+ * to the empty string if root is "/" (to avoid // in paths).
+ */
+ arg_r.a_type = MDB_TYPE_STRING;
+ arg_r.a_un.a_str = strcmp(mdb.m_root, "/") ? mdb.m_root : "";
+
+ /*
+ * %t embedded in path string expands to the target name.
+ */
+ arg_t.a_type = MDB_TYPE_STRING;
+ arg_t.a_un.a_str = mdb.m_target ? mdb_tgt_name(mdb.m_target) : "none";
+
+ /*
+ * %R and %V expand to uname -r (release) and uname -v (version).
+ */
+ if (mdb.m_target == NULL || mdb_tgt_uname(mdb.m_target, &uts) < 0)
+ mdb_conf_uname(&uts);
+
+ arg_m.a_type = MDB_TYPE_STRING;
+ arg_m.a_un.a_str = uts.machine;
+
+ arg_R.a_type = MDB_TYPE_STRING;
+ arg_R.a_un.a_str = uts.release;
+
+ arg_V.a_type = MDB_TYPE_STRING;
+ if (mdb.m_flags & MDB_FL_LATEST)
+ arg_V.a_un.a_str = "latest";
+ else
+ arg_V.a_un.a_str = uts.version;
+
+ /*
+ * In order to expand the buffer, we examine the format string for
+ * our % tokens and construct an argvec, replacing each % token
+ * with %s along the way. If we encounter an unknown token, we
+ * shift over the remaining format buffer and stick in %%.
+ */
+ for (q = format; (q = strchr(q, '%')) != NULL; q++) {
+ switch (q[1]) {
+ case 'i':
+ mdb_argvec_append(&argv, &arg_i);
+ *++q = 's';
+ break;
+ case 'm':
+ mdb_argvec_append(&argv, &arg_m);
+ *++q = 's';
+ break;
+ case 'p':
+ mdb_argvec_append(&argv, &arg_p);
+ *++q = 's';
+ break;
+ case 'r':
+ mdb_argvec_append(&argv, &arg_r);
+ *++q = 's';
+ break;
+ case 't':
+ mdb_argvec_append(&argv, &arg_t);
+ *++q = 's';
+ break;
+ case 'R':
+ mdb_argvec_append(&argv, &arg_R);
+ *++q = 's';
+ break;
+ case 'V':
+ mdb_argvec_append(&argv, &arg_V);
+ *++q = 's';
+ break;
+ default:
+ bcopy(q + 1, q + 2, strlen(q));
+ *++q = '%';
+ }
+ }
+
+ /*
+ * We're now ready to use our printf engine to format the final string.
+ * Take one lap with a NULL buffer to determine how long the final
+ * string will be, allocate it, and format it.
+ */
+ len = mdb_iob_asnprintf(NULL, 0, format, argv.a_data);
+ if ((p = mdb_alloc(len + 1, UM_NOSLEEP)) != NULL)
+ (void) mdb_iob_asnprintf(p, len + 1, format, argv.a_data);
+ else
+ goto nomem;
+
+ mdb_argvec_zero(&argv);
+ mdb_argvec_destroy(&argv);
+
+ mdb_free(format, strlen(s) * 2 + 1);
+ format = NULL;
+
+ /*
+ * Compress the string to exclude any leading delimiters.
+ */
+ for (q = p; *q == ':'; q++)
+ continue;
+ if (q != p)
+ bcopy(q, p, strlen(q) + 1);
+
+ /*
+ * Count up the number of delimited elements. A sequence of
+ * consecutive delimiters is only counted once.
+ */
+ for (i = 1, q = p; (q = strchr(q, ':')) != NULL; i++) {
+ while (*q == ':')
+ q++;
+ }
+
+ if ((path = mdb_alloc(sizeof (char *) * (i + 1), UM_NOSLEEP)) == NULL) {
+ mdb_free(p, len + 1);
+ goto nomem;
+ }
+
+ for (i = 0, q = strtok(p, ":"); q != NULL; q = strtok(NULL, ":"))
+ path[i++] = q;
+
+ path[i] = NULL;
+ *newlen = len + 1;
+ return (path);
+
+nomem:
+ warn("failed to allocate memory for path");
+ if (format != NULL)
+ mdb_free(format, strlen(s) * 2 + 1);
+ *newlen = 0;
+ return (empty_path);
+}
+
+const char **
+mdb_path_dup(const char *path[], size_t pathlen, size_t *npathlenp)
+{
+ char **npath;
+ int i, j;
+
+ for (i = 0; path[i] != NULL; i++)
+ continue; /* count the path elements */
+
+ npath = mdb_zalloc(sizeof (char *) * (i + 1), UM_SLEEP);
+ if (pathlen > 0) {
+ npath[0] = mdb_alloc(pathlen, UM_SLEEP);
+ bcopy(path[0], npath[0], pathlen);
+ }
+
+ for (j = 1; j < i; j++)
+ npath[j] = npath[0] + (path[j] - path[0]);
+ npath[i] = NULL;
+
+ *npathlenp = pathlen;
+ return ((const char **)npath);
+}
+
+void
+mdb_path_free(const char *path[], size_t pathlen)
+{
+ int i;
+
+ for (i = 0; path[i] != NULL; i++)
+ continue; /* count the path elements */
+
+ if (i > 0) {
+ mdb_free((void *)path[0], pathlen);
+ mdb_free(path, sizeof (char *) * (i + 1));
+ }
+}
+
+/*
+ * Convert path string "s" to canonical form, expanding any %o tokens that are
+ * found within the path. The old path string is specified by "path", a buffer
+ * of size MAXPATHLEN which is then overwritten with the new path string.
+ */
+static const char *
+path_canon(char *path, const char *s)
+{
+ char *p = path;
+ char *q = p + MAXPATHLEN - 1;
+
+ char old[MAXPATHLEN];
+ char c;
+
+ (void) strcpy(old, p);
+ *q = '\0';
+
+ while (p < q && (c = *s++) != '\0') {
+ if (c == '%') {
+ if ((c = *s++) == 'o') {
+ (void) strncpy(p, old, (size_t)(q - p));
+ p += strlen(p);
+ } else {
+ *p++ = '%';
+ if (p < q && c != '\0')
+ *p++ = c;
+ else
+ break;
+ }
+ } else
+ *p++ = c;
+ }
+
+ *p = '\0';
+ return (path);
+}
+
+void
+mdb_set_ipath(const char *path)
+{
+ if (mdb.m_ipath != NULL)
+ mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
+
+ path = path_canon(mdb.m_ipathstr, path);
+ mdb.m_ipath = mdb_path_alloc(path, &mdb.m_ipathlen);
+}
+
+void
+mdb_set_lpath(const char *path)
+{
+ if (mdb.m_lpath != NULL)
+ mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
+
+ path = path_canon(mdb.m_lpathstr, path);
+ mdb.m_lpath = mdb_path_alloc(path, &mdb.m_lpathlen);
+
+#ifdef _KMDB
+ kmdb_module_path_set(mdb.m_lpath, mdb.m_lpathlen);
+#endif
+}
+
+static void
+prompt_update(void)
+{
+ (void) mdb_snprintf(mdb.m_prompt, sizeof (mdb.m_prompt),
+ mdb.m_promptraw);
+ mdb.m_promptlen = strlen(mdb.m_prompt);
+}
+
+const char *
+mdb_get_prompt(void)
+{
+ if (mdb.m_promptlen == 0)
+ return (NULL);
+ else
+ return (mdb.m_prompt);
+}
+
+int
+mdb_set_prompt(const char *p)
+{
+ size_t len = strlen(p);
+
+ if (len > MDB_PROMPTLEN) {
+ warn("prompt may not exceed %d characters\n", MDB_PROMPTLEN);
+ return (0);
+ }
+
+ (void) strcpy(mdb.m_promptraw, p);
+ prompt_update();
+ return (1);
+}
+
+static mdb_frame_t frame0;
+
+void
+mdb_create(const char *execname, const char *arg0)
+{
+ static const mdb_nv_disc_t psym_disc = { psym_disc_set, psym_disc_get };
+ static const mdb_nv_disc_t roff_disc = { roff_disc_set, roff_disc_get };
+ static const mdb_nv_disc_t thr_disc = { NULL, thr_disc_get };
+
+ static char rootdir[MAXPATHLEN];
+
+ const mdb_dcmd_t *dcp;
+ int i;
+
+ bzero(&mdb, sizeof (mdb_t));
+
+ mdb.m_flags = MDB_FL_PSYM | MDB_FL_PAGER | MDB_FL_BPTNOSYMSTOP |
+ MDB_FL_READBACK;
+ mdb.m_radix = MDB_DEF_RADIX;
+ mdb.m_nargs = MDB_DEF_NARGS;
+ mdb.m_histlen = MDB_DEF_HISTLEN;
+ mdb.m_armemlim = MDB_DEF_ARRMEM;
+ mdb.m_arstrlim = MDB_DEF_ARRSTR;
+
+ mdb.m_pname = strbasename(arg0);
+ if (strcmp(mdb.m_pname, "adb") == 0) {
+ mdb.m_flags |= MDB_FL_NOMODS | MDB_FL_ADB | MDB_FL_REPLAST;
+ mdb.m_flags &= ~MDB_FL_PAGER;
+ }
+
+ mdb.m_ipathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
+ mdb.m_lpathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
+
+ (void) strncpy(rootdir, execname, sizeof (rootdir));
+ rootdir[sizeof (rootdir) - 1] = '\0';
+ (void) strdirname(rootdir);
+
+ if (strcmp(strbasename(rootdir), "sparcv9") == 0 ||
+ strcmp(strbasename(rootdir), "sparcv7") == 0 ||
+ strcmp(strbasename(rootdir), "amd64") == 0 ||
+ strcmp(strbasename(rootdir), "i86") == 0)
+ (void) strdirname(rootdir);
+
+ if (strcmp(strbasename(rootdir), "bin") == 0) {
+ (void) strdirname(rootdir);
+ if (strcmp(strbasename(rootdir), "usr") == 0)
+ (void) strdirname(rootdir);
+ } else
+ (void) strcpy(rootdir, "/");
+
+ mdb.m_root = rootdir;
+
+ mdb.m_rminfo.mi_dvers = MDB_API_VERSION;
+ mdb.m_rminfo.mi_dcmds = mdb_dcmd_builtins;
+ mdb.m_rminfo.mi_walkers = NULL;
+
+ (void) mdb_nv_create(&mdb.m_rmod.mod_walkers, UM_SLEEP);
+ (void) mdb_nv_create(&mdb.m_rmod.mod_dcmds, UM_SLEEP);
+
+ mdb.m_rmod.mod_name = mdb.m_pname;
+ mdb.m_rmod.mod_info = &mdb.m_rminfo;
+
+ (void) mdb_nv_create(&mdb.m_disasms, UM_SLEEP);
+ (void) mdb_nv_create(&mdb.m_modules, UM_SLEEP);
+ (void) mdb_nv_create(&mdb.m_dcmds, UM_SLEEP);
+ (void) mdb_nv_create(&mdb.m_walkers, UM_SLEEP);
+ (void) mdb_nv_create(&mdb.m_nv, UM_SLEEP);
+
+ mdb.m_dot = mdb_nv_insert(&mdb.m_nv, ".", NULL, 0, MDB_NV_PERSIST);
+ mdb.m_rvalue = mdb_nv_insert(&mdb.m_nv, "0", NULL, 0, MDB_NV_PERSIST);
+
+ mdb.m_roffset =
+ mdb_nv_insert(&mdb.m_nv, "1", &roff_disc, 0, MDB_NV_PERSIST);
+
+ mdb.m_proffset = mdb_nv_insert(&mdb.m_nv, "2", NULL, 0, MDB_NV_PERSIST);
+ mdb.m_rcount = mdb_nv_insert(&mdb.m_nv, "9", NULL, 0, MDB_NV_PERSIST);
+
+ (void) mdb_nv_insert(&mdb.m_nv, "b", NULL, 0, MDB_NV_PERSIST);
+ (void) mdb_nv_insert(&mdb.m_nv, "d", NULL, 0, MDB_NV_PERSIST);
+ (void) mdb_nv_insert(&mdb.m_nv, "e", NULL, 0, MDB_NV_PERSIST);
+ (void) mdb_nv_insert(&mdb.m_nv, "m", NULL, 0, MDB_NV_PERSIST);
+ (void) mdb_nv_insert(&mdb.m_nv, "t", NULL, 0, MDB_NV_PERSIST);
+ (void) mdb_nv_insert(&mdb.m_nv, "_", &psym_disc, 0, MDB_NV_PERSIST);
+ (void) mdb_nv_insert(&mdb.m_nv, "hits", NULL, 0, MDB_NV_PERSIST);
+
+ (void) mdb_nv_insert(&mdb.m_nv, "thread", &thr_disc, 0,
+ MDB_NV_PERSIST | MDB_NV_RDONLY);
+
+ mdb.m_prsym = mdb_gelf_symtab_create_mutable();
+
+ (void) mdb_nv_insert(&mdb.m_modules, mdb.m_pname, NULL,
+ (uintptr_t)&mdb.m_rmod, MDB_NV_RDONLY);
+
+ for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
+ (void) mdb_module_add_dcmd(&mdb.m_rmod, dcp, 0);
+
+ for (i = 0; mdb_dis_builtins[i] != NULL; i++)
+ (void) mdb_dis_create(mdb_dis_builtins[i]);
+
+ mdb_macalias_create();
+
+ mdb_create_builtin_tgts();
+
+ (void) mdb_callb_add(NULL, MDB_CALLB_PROMPT, (mdb_callb_f)prompt_update,
+ NULL);
+
+#ifdef _KMDB
+ (void) mdb_nv_create(&mdb.m_dmodctl, UM_SLEEP);
+#endif
+ mdb_lex_state_create(&frame0);
+
+ mdb_list_append(&mdb.m_flist, &frame0);
+ mdb.m_frame = &frame0;
+}
+
+void
+mdb_destroy(void)
+{
+ const mdb_dcmd_t *dcp;
+ mdb_var_t *v;
+ int unload_mode = MDB_MOD_SILENT;
+
+#ifdef _KMDB
+ unload_mode |= MDB_MOD_DEFER;
+#endif
+
+ mdb_intr_disable();
+
+ mdb_macalias_destroy();
+
+ /*
+ * Unload modules _before_ destroying the disassemblers since a
+ * module that installs a disassembler should try to clean up after
+ * itself.
+ */
+ mdb_module_unload_all(unload_mode);
+
+ mdb_nv_rewind(&mdb.m_disasms);
+ while ((v = mdb_nv_advance(&mdb.m_disasms)) != NULL)
+ mdb_dis_destroy(mdb_nv_get_cookie(v));
+
+ mdb_callb_remove_all();
+
+ if (mdb.m_defdisasm != NULL)
+ strfree(mdb.m_defdisasm);
+
+ if (mdb.m_target != NULL)
+ (void) mdb_tgt_destroy(mdb.m_target);
+
+ if (mdb.m_prsym != NULL)
+ mdb_gelf_symtab_destroy(mdb.m_prsym);
+
+ for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
+ (void) mdb_module_remove_dcmd(&mdb.m_rmod, dcp->dc_name);
+
+ mdb_nv_destroy(&mdb.m_nv);
+ mdb_nv_destroy(&mdb.m_walkers);
+ mdb_nv_destroy(&mdb.m_dcmds);
+ mdb_nv_destroy(&mdb.m_modules);
+ mdb_nv_destroy(&mdb.m_disasms);
+
+ mdb_free(mdb.m_ipathstr, MAXPATHLEN);
+ mdb_free(mdb.m_lpathstr, MAXPATHLEN);
+
+ if (mdb.m_ipath != NULL)
+ mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
+
+ if (mdb.m_lpath != NULL)
+ mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
+
+ if (mdb.m_in != NULL)
+ mdb_iob_destroy(mdb.m_in);
+
+ mdb_iob_destroy(mdb.m_out);
+ mdb.m_out = NULL;
+ mdb_iob_destroy(mdb.m_err);
+ mdb.m_err = NULL;
+
+ if (mdb.m_log != NULL)
+ mdb_io_rele(mdb.m_log);
+
+ mdb_lex_state_destroy(&frame0);
+}
+
+/*
+ * The real main loop of the debugger: create a new execution frame on the
+ * debugger stack, and while we have input available, call into the parser.
+ */
+int
+mdb_run(void)
+{
+ volatile int err;
+ mdb_frame_t f;
+
+ mdb_intr_disable();
+ mdb_frame_push(&f);
+
+ /*
+ * This is a fresh mdb context, so ignore any pipe command we may have
+ * inherited from the previous frame.
+ */
+ f.f_pcmd = NULL;
+
+ if ((err = setjmp(f.f_pcb)) != 0) {
+ int pop = (mdb.m_in != NULL &&
+ (mdb_iob_isapipe(mdb.m_in) || mdb_iob_isastr(mdb.m_in)));
+ int fromcmd = (f.f_cp != NULL);
+
+ mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught event %s\n",
+ f.f_id, mdb_err2str(err));
+
+ /*
+ * If a syntax error or other failure has occurred, pop all
+ * input buffers pushed by commands executed in this frame.
+ */
+ while (mdb_iob_stack_size(&f.f_istk) != 0) {
+ if (mdb.m_in != NULL)
+ mdb_iob_destroy(mdb.m_in);
+ mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
+ yylineno = mdb_iob_lineno(mdb.m_in);
+ }
+
+ /*
+ * Reset standard output and the current frame to a known,
+ * clean state, so we can continue execution.
+ */
+ mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
+ mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
+ mdb_iob_discard(mdb.m_out);
+ mdb_frame_reset(&f);
+
+ /*
+ * If there was an error writing to output, display a warning
+ * message if this is the topmost frame.
+ */
+ if (err == MDB_ERR_OUTPUT && mdb.m_depth == 1 && errno != EPIPE)
+ mdb_warn("write failed");
+
+ /*
+ * If an interrupt or quit signal is reported, we may have been
+ * in the middle of typing or processing the command line:
+ * print a newline and discard everything in the parser's iob.
+ * Note that we do this after m_out has been reset, otherwise
+ * we could trigger a pipe context switch or cause a write
+ * to a broken pipe (in the case of a shell command) when
+ * writing the newline.
+ */
+ if (err == MDB_ERR_SIGINT || err == MDB_ERR_QUIT) {
+ mdb_iob_nl(mdb.m_out);
+ yydiscard();
+ }
+
+ /*
+ * If we quit or abort using the output pager, reset the
+ * line count on standard output back to zero.
+ */
+ if (err == MDB_ERR_PAGER || MDB_ERR_IS_FATAL(err))
+ mdb_iob_clearlines(mdb.m_out);
+
+ /*
+ * If the user requested the debugger quit or abort back to
+ * the top, or if standard input is a pipe or mdb_eval("..."),
+ * then propagate the error up the debugger stack.
+ */
+ if (MDB_ERR_IS_FATAL(err) || pop != 0 ||
+ (err == MDB_ERR_PAGER && mdb.m_fmark != &f) ||
+ (err == MDB_ERR_NOMEM && !fromcmd)) {
+ mdb_frame_pop(&f, err);
+ return (err);
+ }
+
+ /*
+ * If we've returned here from a context where signals were
+ * blocked (e.g. a signal handler), we can now unblock them.
+ */
+ if (err == MDB_ERR_SIGINT)
+ (void) mdb_signal_unblock(SIGINT);
+ } else
+ mdb_intr_enable();
+
+ for (;;) {
+ while (mdb.m_in != NULL && (mdb_iob_getflags(mdb.m_in) &
+ (MDB_IOB_ERR | MDB_IOB_EOF)) == 0) {
+ if (mdb.m_depth == 1 &&
+ mdb_iob_stack_size(&f.f_istk) == 0) {
+ mdb_iob_clearlines(mdb.m_out);
+ mdb_tgt_periodic(mdb.m_target);
+ }
+
+ (void) yyparse();
+ }
+
+ if (mdb.m_in != NULL) {
+ if (mdb_iob_err(mdb.m_in)) {
+ warn("error reading input stream %s\n",
+ mdb_iob_name(mdb.m_in));
+ }
+ mdb_iob_destroy(mdb.m_in);
+ mdb.m_in = NULL;
+ }
+
+ if (mdb_iob_stack_size(&f.f_istk) == 0)
+ break; /* return when we're out of input */
+
+ mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
+ yylineno = mdb_iob_lineno(mdb.m_in);
+ }
+
+ mdb_frame_pop(&f, 0);
+
+ /*
+ * The value of '.' is a per-frame attribute, to preserve it properly
+ * when switching frames. But in the case of calling mdb_run()
+ * explicitly (such as through mdb_eval), we want to propagate the value
+ * of '.' to the parent.
+ */
+ mdb_nv_set_value(mdb.m_dot, f.f_dot);
+
+ return (0);
+}
+
+/*
+ * The read-side of the pipe executes this service routine. We simply call
+ * mdb_run to create a new frame on the execution stack and run the MDB parser,
+ * and then propagate any error code back to the previous frame.
+ */
+static int
+runsvc(void)
+{
+ int err = mdb_run();
+
+ if (err != 0) {
+ mdb_dprintf(MDB_DBG_DSTK, "forwarding error %s from pipeline\n",
+ mdb_err2str(err));
+ longjmp(mdb.m_frame->f_pcb, err);
+ }
+
+ return (err);
+}
+
+/*
+ * Read-side pipe service routine: if we longjmp here, just return to the read
+ * routine because now we have more data to consume. Otherwise:
+ * (1) if ctx_data is non-NULL, longjmp to the write-side to produce more data;
+ * (2) if wriob is NULL, there is no writer but this is the first read, so we
+ * can just execute mdb_run() to completion on the current stack;
+ * (3) if (1) and (2) are false, then there is a writer and this is the first
+ * read, so create a co-routine context to execute mdb_run().
+ */
+/*ARGSUSED*/
+static void
+rdsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
+{
+ if (setjmp(ctx->ctx_rpcb) == 0) {
+ /*
+ * Save the current standard input into the pipe context, and
+ * reset m_in to point to the pipe. We will restore it on
+ * the way back in wrsvc() below.
+ */
+ ctx->ctx_iob = mdb.m_in;
+ mdb.m_in = rdiob;
+
+ ctx->ctx_rptr = mdb.m_frame;
+ if (ctx->ctx_wptr != NULL)
+ mdb_frame_switch(ctx->ctx_wptr);
+
+ if (ctx->ctx_data != NULL)
+ longjmp(ctx->ctx_wpcb, 1);
+ else if (wriob == NULL)
+ (void) runsvc();
+ else if ((ctx->ctx_data = mdb_context_create(runsvc)) != NULL)
+ mdb_context_switch(ctx->ctx_data);
+ else
+ mdb_warn("failed to create pipe context");
+ }
+}
+
+/*
+ * Write-side pipe service routine: if we longjmp here, just return to the
+ * write routine because now we have free space in the pipe buffer for writing;
+ * otherwise longjmp to the read-side to consume data and create space for us.
+ */
+/*ARGSUSED*/
+static void
+wrsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
+{
+ if (setjmp(ctx->ctx_wpcb) == 0) {
+ ctx->ctx_wptr = mdb.m_frame;
+ if (ctx->ctx_rptr != NULL)
+ mdb_frame_switch(ctx->ctx_rptr);
+
+ mdb.m_in = ctx->ctx_iob;
+ longjmp(ctx->ctx_rpcb, 1);
+ }
+}
+
+/*
+ * Call the current frame's mdb command. This entry point is used by the
+ * MDB parser to actually execute a command once it has successfully parsed
+ * a line of input. The command is waiting for us in the current frame.
+ * We loop through each command on the list, executing its dcmd with the
+ * appropriate argument. If the command has a successor, we know it had
+ * a | operator after it, and so we need to create a pipe and replace
+ * stdout with the pipe's output buffer.
+ */
+int
+mdb_call(uintmax_t addr, uintmax_t count, uint_t flags)
+{
+ mdb_frame_t *fp = mdb.m_frame;
+ mdb_cmd_t *cp, *ncp;
+ mdb_iob_t *iobs[2];
+ int status, err = 0;
+ jmp_buf pcb;
+
+ if (mdb_iob_isapipe(mdb.m_in))
+ yyerror("syntax error");
+
+ mdb_intr_disable();
+ fp->f_cp = mdb_list_next(&fp->f_cmds);
+
+ if (flags & DCMD_LOOP)
+ flags |= DCMD_LOOPFIRST; /* set LOOPFIRST if this is a loop */
+
+ for (cp = mdb_list_next(&fp->f_cmds); cp; cp = mdb_list_next(cp)) {
+ if (mdb_list_next(cp) != NULL) {
+ mdb_iob_pipe(iobs, rdsvc, wrsvc);
+
+ mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
+ mdb.m_in = iobs[MDB_IOB_RDIOB];
+
+ mdb_iob_stack_push(&fp->f_ostk, mdb.m_out, 0);
+ mdb.m_out = iobs[MDB_IOB_WRIOB];
+
+ ncp = mdb_list_next(cp);
+ mdb_vcb_inherit(cp, ncp);
+
+ bcopy(fp->f_pcb, pcb, sizeof (jmp_buf));
+ ASSERT(fp->f_pcmd == NULL);
+ fp->f_pcmd = ncp;
+
+ if ((err = setjmp(fp->f_pcb)) == 0) {
+ status = mdb_call_idcmd(cp->c_dcmd, addr, count,
+ flags | DCMD_PIPE_OUT, &cp->c_argv,
+ &cp->c_addrv, cp->c_vcbs);
+
+ ASSERT(mdb.m_in == iobs[MDB_IOB_RDIOB]);
+ ASSERT(mdb.m_out == iobs[MDB_IOB_WRIOB]);
+ } else {
+ mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught "
+ "error %s from pipeline\n", fp->f_id,
+ mdb_err2str(err));
+ }
+
+ if (err != 0 || DCMD_ABORTED(status)) {
+ mdb_iob_setflags(mdb.m_in, MDB_IOB_ERR);
+ mdb_iob_setflags(mdb.m_out, MDB_IOB_ERR);
+ } else {
+ mdb_iob_flush(mdb.m_out);
+ (void) mdb_iob_ctl(mdb.m_out, I_FLUSH,
+ (void *)FLUSHW);
+ }
+
+ mdb_iob_destroy(mdb.m_out);
+ mdb.m_out = mdb_iob_stack_pop(&fp->f_ostk);
+
+ if (mdb.m_in != NULL)
+ mdb_iob_destroy(mdb.m_in);
+
+ mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
+ yylineno = mdb_iob_lineno(mdb.m_in);
+
+ fp->f_pcmd = NULL;
+ bcopy(pcb, fp->f_pcb, sizeof (jmp_buf));
+
+ if (MDB_ERR_IS_FATAL(err))
+ longjmp(fp->f_pcb, err);
+
+ if (err != 0 || DCMD_ABORTED(status) ||
+ mdb_addrvec_length(&ncp->c_addrv) == 0)
+ break;
+
+ addr = mdb_nv_get_value(mdb.m_dot);
+ count = 1;
+ flags = 0;
+
+ } else {
+ mdb_intr_enable();
+ (void) mdb_call_idcmd(cp->c_dcmd, addr, count, flags,
+ &cp->c_argv, &cp->c_addrv, cp->c_vcbs);
+ mdb_intr_disable();
+ }
+
+ fp->f_cp = mdb_list_next(cp);
+ mdb_cmd_reset(cp);
+ }
+
+ /*
+ * If our last-command list is non-empty, destroy it. Then copy the
+ * current frame's cmd list to the m_lastc list and reset the frame.
+ */
+ while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
+ mdb_list_delete(&mdb.m_lastc, cp);
+ mdb_cmd_destroy(cp);
+ }
+
+ mdb_list_move(&fp->f_cmds, &mdb.m_lastc);
+ mdb_frame_reset(fp);
+ mdb_intr_enable();
+ return (err == 0);
+}
+
+uintmax_t
+mdb_dot_incr(const char *op)
+{
+ uintmax_t odot, ndot;
+
+ odot = mdb_nv_get_value(mdb.m_dot);
+ ndot = odot + mdb.m_incr;
+
+ if ((odot ^ ndot) & 0x8000000000000000ull)
+ yyerror("'%s' would cause '.' to overflow\n", op);
+
+ return (ndot);
+}
+
+uintmax_t
+mdb_dot_decr(const char *op)
+{
+ uintmax_t odot, ndot;
+
+ odot = mdb_nv_get_value(mdb.m_dot);
+ ndot = odot - mdb.m_incr;
+
+ if (ndot > odot)
+ yyerror("'%s' would cause '.' to underflow\n", op);
+
+ return (ndot);
+}
+
+mdb_iwalker_t *
+mdb_walker_lookup(const char *s)
+{
+ const char *p = strchr(s, '`');
+ mdb_var_t *v;
+
+ if (p != NULL) {
+ size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
+ char mname[MDB_NV_NAMELEN];
+ mdb_module_t *mod;
+
+ (void) strncpy(mname, s, nbytes);
+ mname[nbytes] = '\0';
+
+ if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
+ (void) set_errno(EMDB_NOMOD);
+ return (NULL);
+ }
+
+ mod = mdb_nv_get_cookie(v);
+
+ if ((v = mdb_nv_lookup(&mod->mod_walkers, ++p)) != NULL)
+ return (mdb_nv_get_cookie(v));
+
+ } else if ((v = mdb_nv_lookup(&mdb.m_walkers, s)) != NULL)
+ return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
+
+ (void) set_errno(EMDB_NOWALK);
+ return (NULL);
+}
+
+mdb_idcmd_t *
+mdb_dcmd_lookup(const char *s)
+{
+ const char *p = strchr(s, '`');
+ mdb_var_t *v;
+
+ if (p != NULL) {
+ size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
+ char mname[MDB_NV_NAMELEN];
+ mdb_module_t *mod;
+
+ (void) strncpy(mname, s, nbytes);
+ mname[nbytes] = '\0';
+
+ if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
+ (void) set_errno(EMDB_NOMOD);
+ return (NULL);
+ }
+
+ mod = mdb_nv_get_cookie(v);
+
+ if ((v = mdb_nv_lookup(&mod->mod_dcmds, ++p)) != NULL)
+ return (mdb_nv_get_cookie(v));
+
+ } else if ((v = mdb_nv_lookup(&mdb.m_dcmds, s)) != NULL)
+ return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
+
+ (void) set_errno(EMDB_NODCMD);
+ return (NULL);
+}
+
+void
+mdb_dcmd_usage(const mdb_idcmd_t *idcp, mdb_iob_t *iob)
+{
+ const char *prefix = "", *usage = "";
+ char name0 = idcp->idc_name[0];
+
+ if (idcp->idc_usage != NULL) {
+ if (idcp->idc_usage[0] == ':') {
+ if (name0 != ':' && name0 != '$')
+ prefix = "address::";
+ else
+ prefix = "address";
+ usage = &idcp->idc_usage[1];
+
+ } else if (idcp->idc_usage[0] == '?') {
+ if (name0 != ':' && name0 != '$')
+ prefix = "[address]::";
+ else
+ prefix = "[address]";
+ usage = &idcp->idc_usage[1];
+
+ } else
+ usage = idcp->idc_usage;
+ }
+
+ mdb_iob_printf(iob, "Usage: %s%s %s\n", prefix, idcp->idc_name, usage);
+
+ if (idcp->idc_help != NULL) {
+ mdb_iob_printf(iob, "%s: try '::help %s' for more "
+ "information\n", mdb.m_pname, idcp->idc_name);
+ }
+}
+
+static mdb_idcmd_t *
+dcmd_ndef(const mdb_idcmd_t *idcp)
+{
+ mdb_var_t *v = mdb_nv_get_ndef(idcp->idc_var);
+
+ if (v != NULL)
+ return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
+
+ return (NULL);
+}
+
+static int
+dcmd_invoke(mdb_idcmd_t *idcp, uintptr_t addr, uint_t flags,
+ int argc, const mdb_arg_t *argv, const mdb_vcb_t *vcbs)
+{
+ int status;
+
+ mdb_dprintf(MDB_DBG_DCMD, "dcmd %s`%s dot = %lr incr = %llr\n",
+ idcp->idc_modp->mod_name, idcp->idc_name, addr, mdb.m_incr);
+
+ if ((status = idcp->idc_funcp(addr, flags, argc, argv)) == DCMD_USAGE) {
+ mdb_dcmd_usage(idcp, mdb.m_err);
+ goto done;
+ }
+
+ while (status == DCMD_NEXT && (idcp = dcmd_ndef(idcp)) != NULL)
+ status = idcp->idc_funcp(addr, flags, argc, argv);
+
+ if (status == DCMD_USAGE)
+ mdb_dcmd_usage(idcp, mdb.m_err);
+
+ if (status == DCMD_NEXT)
+ status = DCMD_OK;
+done:
+ /*
+ * If standard output is a pipe and there are vcbs active, we need to
+ * flush standard out and the write-side of the pipe. The reasons for
+ * this are explained in more detail in mdb_vcb.c.
+ */
+ if ((flags & DCMD_PIPE_OUT) && (vcbs != NULL)) {
+ mdb_iob_flush(mdb.m_out);
+ (void) mdb_iob_ctl(mdb.m_out, I_FLUSH, (void *)FLUSHW);
+ }
+
+ return (status);
+}
+
+/*
+ * Call an internal dcmd directly: this code is used by module API functions
+ * that need to execute dcmds, and by mdb_call() above.
+ */
+int
+mdb_call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
+ uint_t flags, mdb_argvec_t *avp, mdb_addrvec_t *adp, mdb_vcb_t *vcbs)
+{
+ int is_exec = (strcmp(idcp->idc_name, "$<") == 0);
+ mdb_arg_t *argv;
+ int argc;
+ uintmax_t i;
+ int status;
+
+ /*
+ * Update the values of dot and the most recent address and count
+ * to the values of our input parameters.
+ */
+ mdb_nv_set_value(mdb.m_dot, addr);
+ mdb.m_raddr = addr;
+ mdb.m_dcount = count;
+
+ /*
+ * Here the adb(1) man page lies: '9' is only set to count
+ * when the command is $<, not when it's $<<.
+ */
+ if (is_exec)
+ mdb_nv_set_value(mdb.m_rcount, count);
+
+ /*
+ * We can now return if the repeat count is zero.
+ */
+ if (count == 0)
+ return (DCMD_OK);
+
+ /*
+ * To guard against bad dcmds, we avoid passing the actual argv that
+ * we will use to free argument strings directly to the dcmd. Instead,
+ * we pass a copy that will be garbage collected automatically.
+ */
+ argc = avp->a_nelems;
+ argv = mdb_alloc(sizeof (mdb_arg_t) * argc, UM_SLEEP | UM_GC);
+ bcopy(avp->a_data, argv, sizeof (mdb_arg_t) * argc);
+
+ if (mdb_addrvec_length(adp) != 0) {
+ flags |= DCMD_PIPE | DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
+ addr = mdb_addrvec_shift(adp);
+ mdb_nv_set_value(mdb.m_dot, addr);
+ mdb_vcb_propagate(vcbs);
+ count = 1;
+ }
+
+ status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
+ if (DCMD_ABORTED(status))
+ goto done;
+
+ /*
+ * If the command is $< and we're not receiving input from a pipe, we
+ * ignore the repeat count and just return since the macro file is now
+ * pushed on to the input stack.
+ */
+ if (is_exec && mdb_addrvec_length(adp) == 0)
+ goto done;
+
+ /*
+ * If we're going to loop, we've already executed the dcmd once,
+ * so clear the LOOPFIRST flag before proceeding.
+ */
+ if (flags & DCMD_LOOP)
+ flags &= ~DCMD_LOOPFIRST;
+
+ for (i = 1; i < count; i++) {
+ addr = mdb_dot_incr(",");
+ mdb_nv_set_value(mdb.m_dot, addr);
+ status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
+ if (DCMD_ABORTED(status))
+ goto done;
+ }
+
+ while (mdb_addrvec_length(adp) != 0) {
+ addr = mdb_addrvec_shift(adp);
+ mdb_nv_set_value(mdb.m_dot, addr);
+ mdb_vcb_propagate(vcbs);
+ status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
+ if (DCMD_ABORTED(status))
+ goto done;
+ }
+done:
+ mdb_iob_nlflush(mdb.m_out);
+ return (status);
+}
+
+void
+mdb_intr_enable(void)
+{
+ ASSERT(mdb.m_intr >= 1);
+ if (mdb.m_intr == 1 && mdb.m_pend != 0) {
+ (void) mdb_signal_block(SIGINT);
+ mdb.m_intr = mdb.m_pend = 0;
+ mdb_dprintf(MDB_DBG_DSTK, "delivering pending INT\n");
+ longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
+ } else
+ mdb.m_intr--;
+}
+
+void
+mdb_intr_disable(void)
+{
+ mdb.m_intr++;
+ ASSERT(mdb.m_intr >= 1);
+}
+
+/*
+ * Create an encoded string representing the internal user-modifiable
+ * configuration of the debugger and return a pointer to it. The string can be
+ * used to initialize another instance of the debugger with the same
+ * configuration as this one.
+ */
+char *
+mdb_get_config(void)
+{
+ size_t r, n = 0;
+ char *s = NULL;
+
+ while ((r = mdb_snprintf(s, n,
+ "%x;%x;%x;%x;%x;%x;%lx;%x;%x;%s;%s;%s;%s;%s",
+ mdb.m_tgtflags, mdb.m_flags, mdb.m_debug, mdb.m_radix, mdb.m_nargs,
+ mdb.m_histlen, (ulong_t)mdb.m_symdist, mdb.m_execmode,
+ mdb.m_forkmode, mdb.m_root, mdb.m_termtype, mdb.m_ipathstr,
+ mdb.m_lpathstr, mdb.m_prompt)) > n) {
+
+ mdb_free(s, n);
+ n = r + 1;
+ s = mdb_alloc(r + 1, UM_SLEEP);
+ }
+
+ return (s);
+}
+
+/*
+ * Decode a configuration string created with mdb_get_config() and reset the
+ * appropriate parts of the global mdb_t accordingly.
+ */
+void
+mdb_set_config(const char *s)
+{
+ const char *p;
+ size_t len;
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_tgtflags = strntoul(s, (size_t)(p - s), 16);
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_flags = strntoul(s, (size_t)(p - s), 16);
+ mdb.m_flags &= ~(MDB_FL_LOG | MDB_FL_LATEST);
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_debug = strntoul(s, (size_t)(p - s), 16);
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_radix = (int)strntoul(s, (size_t)(p - s), 16);
+ if (mdb.m_radix < 2 || mdb.m_radix > 16)
+ mdb.m_radix = MDB_DEF_RADIX;
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_nargs = (int)strntoul(s, (size_t)(p - s), 16);
+ mdb.m_nargs = MAX(mdb.m_nargs, 0);
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_histlen = (int)strntoul(s, (size_t)(p - s), 16);
+ mdb.m_histlen = MAX(mdb.m_histlen, 1);
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_symdist = strntoul(s, (size_t)(p - s), 16);
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_execmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
+ if (mdb.m_execmode > MDB_EM_FOLLOW)
+ mdb.m_execmode = MDB_EM_ASK;
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_forkmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
+ if (mdb.m_forkmode > MDB_FM_CHILD)
+ mdb.m_forkmode = MDB_FM_ASK;
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_root = strndup(s, (size_t)(p - s));
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ mdb.m_termtype = strndup(s, (size_t)(p - s));
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ size_t len = MIN(sizeof (mdb.m_ipathstr) - 1, p - s);
+ strncpy(mdb.m_ipathstr, s, len);
+ mdb.m_ipathstr[len] = '\0';
+ s = p + 1;
+ }
+
+ if ((p = strchr(s, ';')) != NULL) {
+ size_t len = MIN(sizeof (mdb.m_lpathstr) - 1, p - s);
+ strncpy(mdb.m_lpathstr, s, len);
+ mdb.m_lpathstr[len] = '\0';
+ s = p + 1;
+ }
+
+ p = s + strlen(s);
+ len = MIN(MDB_PROMPTLEN, (size_t)(p - s));
+ (void) strncpy(mdb.m_prompt, s, len);
+ mdb.m_prompt[len] = '\0';
+ mdb.m_promptlen = len;
+}
+
+mdb_module_t *
+mdb_get_module(void)
+{
+ if (mdb.m_lmod)
+ return (mdb.m_lmod);
+
+ if (mdb.m_frame && mdb.m_frame->f_cp && mdb.m_frame->f_cp->c_dcmd)
+ return (mdb.m_frame->f_cp->c_dcmd->idc_modp);
+
+ return (NULL);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.h b/usr/src/cmd/mdb/common/mdb/mdb.h
new file mode 100644
index 0000000..9af8354
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb.h
@@ -0,0 +1,247 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_H
+#define _MDB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_addrvec.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_disasm.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_vcb.h>
+#ifdef _KMDB
+#include <kmdb/kmdb_wr.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MDB_ERR_PARSE 1 /* Error occurred in lexer or parser */
+#define MDB_ERR_NOMEM 2 /* Failed to allocate needed memory */
+#define MDB_ERR_PAGER 3 /* User quit current command from pager */
+#define MDB_ERR_SIGINT 4 /* User interrupt: abort current command */
+#define MDB_ERR_QUIT 5 /* User request: quit debugger */
+#define MDB_ERR_ASSERT 6 /* Assertion failure: abort current command */
+#define MDB_ERR_API 7 /* API function error: abort current command */
+#define MDB_ERR_ABORT 8 /* User abort or resume: abort to top level */
+#define MDB_ERR_OUTPUT 9 /* Write to m_out failed: abort to top level */
+
+#define MDB_ERR_IS_FATAL(err) \
+ ((err) == MDB_ERR_QUIT || (err) == MDB_ERR_ABORT || \
+ (err) == MDB_ERR_OUTPUT)
+
+#define MDB_DEF_RADIX 16 /* Default output radix */
+#define MDB_DEF_NARGS 6 /* Default # of arguments in stack trace */
+#define MDB_DEF_HISTLEN 128 /* Default length of command history */
+#define MDB_DEF_SYMDIST 0x8000 /* Default symbol distance for addresses */
+#define MDB_DEF_ARRMEM 32 /* Default number of array members to print */
+#define MDB_DEF_ARRSTR 1024 /* Default number of array chars to print */
+
+#define MDB_ARR_NOLIMIT -1UL /* No limit on number of array elements */
+
+#define MDB_FL_PSYM 0x00001 /* Print dot as symbol + offset when possible */
+#define MDB_FL_LOG 0x00002 /* Logging is enabled */
+#define MDB_FL_NOMODS 0x00004 /* Skip automatic mdb module loading */
+#define MDB_FL_USECUP 0x00008 /* Use terminal cup initialization sequences */
+#define MDB_FL_ADB 0x00010 /* Enable stricter adb(1) compatibility */
+#define MDB_FL_SHOWLMID 0x00020 /* Always show link map id with symbol names */
+#define MDB_FL_IGNEOF 0x00040 /* Ignore EOF as a synonym for ::quit */
+#define MDB_FL_REPLAST 0x00080 /* Naked newline repeats previous command */
+#define MDB_FL_PAGER 0x00100 /* Enable pager by default */
+#define MDB_FL_LATEST 0x00200 /* Replace version string with "latest" */
+#define MDB_FL_VCREATE 0x00400 /* Victim process was created by debugger */
+#define MDB_FL_JOBCTL 0x00800 /* Victim process jobctl stopped on same tty */
+#define MDB_FL_DEMANGLE 0x01000 /* Demangle symbols as part of %a processing */
+#define MDB_FL_EXEC 0x02000 /* Debugger exec'd by a previous instance */
+#define MDB_FL_NOCTF 0x04000 /* Skip automatic CTF data loading */
+#define MDB_FL_BPTNOSYMSTOP 0x08000 /* Stop on deferred bkpts for unk symbols */
+#define MDB_FL_TERMGUESS 0x10000 /* m_termtype derived from userland */
+#define MDB_FL_READBACK 0x20000 /* Read value back after write */
+#ifdef _KMDB
+#define MDB_FL_NOUNLOAD 0x40000 /* Don't allow debugger unload */
+#endif
+
+#define MDB_FL_VOLATILE 0x0001 /* Mask of all volatile flags to save/restore */
+
+#define MDB_EM_ASK 0 /* Ask what to do on an exec */
+#define MDB_EM_STOP 1 /* Stop after an exec */
+#define MDB_EM_FOLLOW 2 /* Follow an exec */
+
+#define MDB_FM_ASK 0 /* Ask what to do on a fork */
+#define MDB_FM_PARENT 1 /* Follow parent process on a fork */
+#define MDB_FM_CHILD 2 /* Follow child process on a fork */
+
+#define MDB_PROMPTLEN 35 /* Maximum prompt length */
+
+struct kmdb_promif;
+
+typedef struct mdb {
+ uint_t m_tgtflags; /* Target open flags (see mdb_target.h) */
+ uint_t m_flags; /* Miscellaneous flags (see above) */
+ uint_t m_debug; /* Debugging flags (see mdb_debug.h) */
+ int m_radix; /* Default radix for output formatting */
+ int m_nargs; /* Default number of arguments in stack trace */
+ int m_histlen; /* Length of command history */
+ size_t m_symdist; /* Distance from sym for addr match (0=smart) */
+ const char *m_pname; /* Program basename from argv[0] */
+ char m_promptraw[MDB_PROMPTLEN + 1]; /* Un-expanded prompt */
+ char m_prompt[MDB_PROMPTLEN + 1]; /* Prompt for interactive mode */
+ size_t m_promptlen; /* Length of prompt in bytes */
+ const char *m_shell; /* Shell for ! commands and pipelines */
+ char *m_root; /* Root for path construction */
+ char *m_ipathstr; /* Path string for include path */
+ char *m_lpathstr; /* Path string for library path */
+ const char **m_ipath; /* Path for $< and $<< macro files */
+ size_t m_ipathlen; /* Length of underlying ipath buffer */
+ const char **m_lpath; /* Path for :: loadable modules */
+ size_t m_lpathlen; /* Length of underlying lpath buffer */
+ mdb_modinfo_t m_rminfo; /* Root debugger module information */
+ mdb_module_t m_rmod; /* Root debugger module (builtins) */
+ mdb_module_t *m_mhead; /* Head of module list (in load order) */
+ mdb_module_t *m_mtail; /* Tail of module list (in load order) */
+ mdb_list_t m_tgtlist; /* List of active target backends */
+ mdb_tgt_t *m_target; /* Current debugger target backend */
+ mdb_nv_t m_disasms; /* Hash of available disassemblers */
+ mdb_disasm_t *m_disasm; /* Current disassembler backend */
+ char *m_defdisasm; /* Deferred diassembler selection */
+ mdb_nv_t m_modules; /* Name/value hash for loadable modules */
+ mdb_nv_t m_dcmds; /* Name/value hash for extended commands */
+ mdb_nv_t m_walkers; /* Name/value hash for walk operations */
+ mdb_nv_t m_nv; /* Name/value hash for named variables */
+ mdb_var_t *m_dot; /* Variable reference for '.' */
+ uintmax_t m_incr; /* Current increment */
+ uintmax_t m_raddr; /* Most recent address specified to a dcmd */
+ uintmax_t m_dcount; /* Most recent count specified to a dcmd */
+ mdb_var_t *m_rvalue; /* Most recent value printed */
+ mdb_var_t *m_roffset; /* Most recent offset from an instruction */
+ mdb_var_t *m_proffset; /* Previous value of m_roffset */
+ mdb_var_t *m_rcount; /* Most recent count on $< dcmd */
+ mdb_iob_t *m_in; /* Input stream */
+ mdb_iob_t *m_out; /* Output stream */
+ mdb_iob_t *m_err; /* Error stream */
+ mdb_iob_t *m_null; /* Null stream */
+ char *m_termtype; /* Interactive mode terminal type */
+ mdb_io_t *m_term; /* Terminal for interactive mode */
+ mdb_io_t *m_log; /* Log file i/o backend (NULL if not logging) */
+ mdb_module_t *m_lmod; /* Pointer to loading module, if in load */
+ mdb_list_t m_lastc; /* Last executed command list */
+ mdb_gelf_symtab_t *m_prsym; /* Private symbol table */
+ mdb_demangler_t *m_demangler; /* Demangler (see <mdb/mdb_demangle.h>) */
+ mdb_list_t m_flist; /* Stack of execution frames */
+ struct mdb_frame *volatile m_frame; /* Current stack frame */
+ struct mdb_frame *volatile m_fmark; /* Stack marker for pager */
+ uint_t m_fid; /* Next frame identifier number to assign */
+ uint_t m_depth; /* Depth of m_frame stack */
+ volatile uint_t m_intr; /* Don't allow SIGINT if set */
+ volatile uint_t m_pend; /* Pending SIGINT count */
+ pid_t m_pgid; /* Debugger process group id */
+ uint_t m_rdvers; /* Librtld_db version number */
+ uint_t m_ctfvers; /* Libctf version number */
+ ulong_t m_armemlim; /* Limit on number of array members to print */
+ ulong_t m_arstrlim; /* Limit on number of array chars to print */
+ uchar_t m_execmode; /* Follow exec behavior */
+ uchar_t m_forkmode; /* Follow fork behavior */
+ char **m_env; /* Current environment */
+ mdb_list_t m_cblist; /* List of callbacks */
+ mdb_nv_t m_macaliases; /* Name/value hash of ADB macro aliases */
+#ifdef _KMDB
+ struct dpi_ops *m_dpi; /* DPI ops vector */
+ struct kdi *m_kdi; /* KDI ops vector */
+ size_t m_pagesize; /* Base page size for this machine */
+ caddr_t m_dseg; /* Debugger segment address */
+ size_t m_dsegsz; /* Debugger segment size */
+ mdb_nv_t m_dmodctl; /* dmod name -> kmdb_modctl hash */
+ kmdb_wr_t *m_drvwrhead; /* Driver work request queue */
+ kmdb_wr_t *m_drvwrtail; /* Driver work request queue */
+ kmdb_wr_t *m_dbgwrhead; /* Debugger request queue */
+ kmdb_wr_t *m_dbgwrtail; /* Debugger request queue */
+ struct cons_polledio *m_pio; /* Polled I/O struct from kernel */
+ struct kmdb_promif *m_promif; /* Debugger/PROM interface state */
+#endif
+} mdb_t;
+
+#ifdef _MDB_PRIVATE
+mdb_t mdb;
+#else
+extern mdb_t mdb;
+#endif
+
+#ifdef _MDB
+
+#define MDB_CONFIG_ENV_VAR "_MDB_CONFIG"
+
+extern void mdb_create(const char *, const char *);
+extern void mdb_destroy(void);
+
+extern int mdb_call_idcmd(mdb_idcmd_t *, uintmax_t, uintmax_t, uint_t,
+ mdb_argvec_t *, mdb_addrvec_t *, mdb_vcb_t *);
+
+extern int mdb_call(uintmax_t, uintmax_t, uint_t);
+extern int mdb_run(void);
+
+extern const char *mdb_get_prompt(void);
+extern int mdb_set_prompt(const char *);
+extern void mdb_set_ipath(const char *);
+extern void mdb_set_lpath(const char *);
+
+extern const char **mdb_path_alloc(const char *, size_t *);
+extern const char **mdb_path_dup(const char *[], size_t, size_t *);
+extern void mdb_path_free(const char *[], size_t);
+
+extern uintmax_t mdb_dot_incr(const char *);
+extern uintmax_t mdb_dot_decr(const char *);
+
+extern mdb_iwalker_t *mdb_walker_lookup(const char *);
+extern mdb_idcmd_t *mdb_dcmd_lookup(const char *);
+extern void mdb_dcmd_usage(const mdb_idcmd_t *, mdb_iob_t *);
+
+extern void mdb_pservice_init(void);
+
+extern void mdb_intr_enable(void);
+extern void mdb_intr_disable(void);
+
+extern char *mdb_get_config(void);
+extern void mdb_set_config(const char *);
+
+extern mdb_module_t *mdb_get_module(void);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_addrvec.c b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.c
new file mode 100644
index 0000000..052aa3d
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.c
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_addrvec.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_modapi.h>
+
+#include <strings.h>
+
+#define AD_INIT 16 /* initial size of addrvec array */
+#define AD_GROW 2 /* array growth multiplier */
+
+void
+mdb_addrvec_create(mdb_addrvec_t *adp)
+{
+ bzero(adp, sizeof (mdb_addrvec_t));
+}
+
+void
+mdb_addrvec_destroy(mdb_addrvec_t *adp)
+{
+ mdb_free(adp->ad_data, sizeof (uintptr_t) * adp->ad_size);
+ bzero(adp, sizeof (mdb_addrvec_t));
+}
+
+void
+mdb_addrvec_unshift(mdb_addrvec_t *adp, uintptr_t value)
+{
+ if (adp->ad_nelems >= adp->ad_size) {
+ size_t size = adp->ad_size ? adp->ad_size * AD_GROW : AD_INIT;
+ void *data = mdb_alloc(sizeof (uintptr_t) * size, UM_SLEEP);
+
+ bcopy(adp->ad_data, data, sizeof (uintptr_t) * adp->ad_size);
+ mdb_free(adp->ad_data, sizeof (uintptr_t) * adp->ad_size);
+
+ adp->ad_data = data;
+ adp->ad_size = size;
+ }
+
+ adp->ad_data[adp->ad_nelems++] = value;
+}
+
+uintptr_t
+mdb_addrvec_shift(mdb_addrvec_t *adp)
+{
+ if (adp->ad_ndx < adp->ad_nelems)
+ return (adp->ad_data[adp->ad_ndx++]);
+
+ return ((uintptr_t)-1L);
+}
+
+size_t
+mdb_addrvec_length(mdb_addrvec_t *adp)
+{
+ if (adp != NULL) {
+ ASSERT(adp->ad_nelems >= adp->ad_ndx);
+ return (adp->ad_nelems - adp->ad_ndx);
+ }
+
+ return (0); /* convenience for callers */
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_addrvec.h b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.h
new file mode 100644
index 0000000..936177f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.h
@@ -0,0 +1,60 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_ADDRVEC_H
+#define _MDB_ADDRVEC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_addrvec {
+ uintptr_t *ad_data; /* Array of addresses */
+ size_t ad_nelems; /* Number of valid elements */
+ size_t ad_size; /* Array size */
+ size_t ad_ndx; /* Array index */
+} mdb_addrvec_t;
+
+#ifdef _MDB
+
+extern void mdb_addrvec_create(mdb_addrvec_t *);
+extern void mdb_addrvec_destroy(mdb_addrvec_t *);
+
+extern uintptr_t mdb_addrvec_shift(mdb_addrvec_t *);
+extern void mdb_addrvec_unshift(mdb_addrvec_t *, uintptr_t);
+extern size_t mdb_addrvec_length(mdb_addrvec_t *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_ADDRVEC_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_argvec.c b/usr/src/cmd/mdb/common/mdb/mdb_argvec.c
new file mode 100644
index 0000000..3505906
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_argvec.c
@@ -0,0 +1,415 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_types.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_stdlib.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb.h>
+
+#include <alloca.h>
+
+#define AV_DEFSZ 16 /* Initial size of argument vector */
+#define AV_GROW 2 /* Multiplier for growing argument vector */
+
+void
+mdb_argvec_create(mdb_argvec_t *vec)
+{
+ vec->a_data = NULL;
+ vec->a_nelems = 0;
+ vec->a_size = 0;
+}
+
+void
+mdb_argvec_destroy(mdb_argvec_t *vec)
+{
+ if (vec->a_data != NULL) {
+ mdb_argvec_reset(vec);
+ mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
+ }
+}
+
+void
+mdb_argvec_append(mdb_argvec_t *vec, const mdb_arg_t *arg)
+{
+ if (vec->a_nelems >= vec->a_size) {
+ size_t size = vec->a_size ? vec->a_size * AV_GROW : AV_DEFSZ;
+ void *data = mdb_alloc(sizeof (mdb_arg_t) * size, UM_NOSLEEP);
+
+ if (data == NULL) {
+ warn("failed to grow argument vector");
+ longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
+ }
+
+ bcopy(vec->a_data, data, sizeof (mdb_arg_t) * vec->a_size);
+ mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
+
+ vec->a_data = data;
+ vec->a_size = size;
+ }
+
+ bcopy(arg, &vec->a_data[vec->a_nelems++], sizeof (mdb_arg_t));
+}
+
+void
+mdb_argvec_reset(mdb_argvec_t *vec)
+{
+ size_t nelems = vec->a_nelems;
+ mdb_arg_t *arg;
+
+ for (arg = vec->a_data; nelems != 0; nelems--, arg++) {
+ if (arg->a_type == MDB_TYPE_STRING && arg->a_un.a_str != NULL)
+ strfree((char *)arg->a_un.a_str);
+ }
+
+ vec->a_nelems = 0;
+}
+
+void
+mdb_argvec_zero(mdb_argvec_t *vec)
+{
+#ifdef DEBUG
+ size_t i;
+
+ for (i = 0; i < vec->a_size; i++) {
+ vec->a_data[i].a_type = UMEM_UNINITIALIZED_PATTERN;
+ vec->a_data[i].a_un.a_val =
+ ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN << 32) |
+ ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN);
+ }
+#endif
+ vec->a_nelems = 0;
+}
+
+void
+mdb_argvec_copy(mdb_argvec_t *dst, const mdb_argvec_t *src)
+{
+ if (src->a_nelems > dst->a_size) {
+ mdb_arg_t *data =
+ mdb_alloc(sizeof (mdb_arg_t) * src->a_nelems, UM_NOSLEEP);
+
+ if (data == NULL) {
+ warn("failed to grow argument vector");
+ longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
+ }
+
+ if (dst->a_data != NULL)
+ mdb_free(dst->a_data, sizeof (mdb_arg_t) * dst->a_size);
+
+ dst->a_data = data;
+ dst->a_size = src->a_nelems;
+ }
+
+ bcopy(src->a_data, dst->a_data, sizeof (mdb_arg_t) * src->a_nelems);
+ dst->a_nelems = src->a_nelems;
+}
+
+static int
+argvec_process_subopt(const mdb_opt_t *opt, const mdb_arg_t *arg)
+{
+ mdb_subopt_t *sop;
+ const char *start;
+ const char *next;
+ char error[32];
+ size_t len;
+ uint_t value = 0;
+ uint_t i;
+
+ start = arg->a_un.a_str;
+
+ for (i = 0; ; i++) {
+ next = strchr(start, ',');
+
+ if (next == NULL)
+ len = strlen(start);
+ else
+ len = next - start;
+
+ /*
+ * Record the index of the subopt if a match if found.
+ */
+ for (sop = opt->opt_subopts; sop->sop_flag; sop++) {
+ if (strlen(sop->sop_str) == len &&
+ strncmp(sop->sop_str, start, len) == 0) {
+ value |= sop->sop_flag;
+ sop->sop_index = i;
+ goto found;
+ }
+ }
+ (void) mdb_snprintf(error, len + 1, "%s", start);
+ warn("invalid option for -%c: \"%s\"\n", opt->opt_char, error);
+
+ return (-1);
+
+found:
+ if (next == NULL)
+ break;
+ start = next + 1;
+ }
+
+ *((uint_t *)opt->opt_valp) = value;
+
+ return (0);
+}
+
+
+static int
+argvec_process_opt(const mdb_opt_t *opt, const mdb_arg_t *arg)
+{
+ uint64_t ui64;
+ uintptr_t uip;
+
+ switch (opt->opt_type) {
+ case MDB_OPT_SETBITS:
+ *((uint_t *)opt->opt_valp) |= opt->opt_bits;
+ break;
+
+ case MDB_OPT_CLRBITS:
+ *((uint_t *)opt->opt_valp) &= ~opt->opt_bits;
+ break;
+
+ case MDB_OPT_STR:
+ if (arg->a_type != MDB_TYPE_STRING) {
+ warn("string argument required for -%c\n",
+ opt->opt_char);
+ return (-1);
+ }
+ *((const char **)opt->opt_valp) = arg->a_un.a_str;
+ break;
+
+ case MDB_OPT_UINTPTR_SET:
+ *opt->opt_flag = TRUE;
+ /* FALLTHROUGH */
+ case MDB_OPT_UINTPTR:
+ if (arg->a_type == MDB_TYPE_STRING)
+ uip = (uintptr_t)mdb_strtoull(arg->a_un.a_str);
+ else
+ uip = (uintptr_t)arg->a_un.a_val;
+ *((uintptr_t *)opt->opt_valp) = uip;
+ break;
+
+ case MDB_OPT_UINT64:
+ if (arg->a_type == MDB_TYPE_STRING)
+ ui64 = mdb_strtoull(arg->a_un.a_str);
+ else
+ ui64 = arg->a_un.a_val;
+ *((uint64_t *)opt->opt_valp) = ui64;
+ break;
+
+ case MDB_OPT_SUBOPTS:
+ if (arg->a_type != MDB_TYPE_STRING) {
+ warn("string argument required for -%c\n",
+ opt->opt_char);
+ return (-1);
+ }
+ return (argvec_process_subopt(opt, arg));
+
+ default:
+ warn("internal: bad opt=%p type=%hx\n",
+ (void *)opt, opt->opt_type);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static const mdb_opt_t *
+argvec_findopt(const mdb_opt_t *opts, char c)
+{
+ const mdb_opt_t *optp;
+
+ for (optp = opts; optp->opt_char != 0; optp++) {
+ if (optp->opt_char == c)
+ return (optp);
+ }
+
+ return (NULL);
+}
+
+static int
+argvec_getopts(const mdb_opt_t *opts, const mdb_arg_t *argv, int argc)
+{
+ const mdb_opt_t *optp;
+ const mdb_arg_t *argp;
+
+ mdb_arg_t arg;
+
+ const char *p;
+ int i;
+ int nargs; /* Number of arguments consumed in an iteration */
+
+ for (i = 0; i < argc; i++, argv++) {
+ /*
+ * Each option must begin with a string argument whose first
+ * character is '-' and has additional characters afterward.
+ */
+ if (argv->a_type != MDB_TYPE_STRING ||
+ argv->a_un.a_str[0] != '-' || argv->a_un.a_str[1] == '\0')
+ return (i);
+
+ /*
+ * The special prefix '--' ends option processing.
+ */
+ if (strncmp(argv->a_un.a_str, "--", 2) == 0)
+ return (i);
+
+ for (p = &argv->a_un.a_str[1]; *p != '\0'; p++) {
+ /*
+ * Locate an option struct whose opt_char field
+ * matches the current option letter.
+ */
+ if ((optp = argvec_findopt(opts, *p)) == NULL) {
+ warn("illegal option -- %c\n", *p);
+ return (i);
+ }
+
+ /*
+ * Require an argument for strings, immediate
+ * values, subopt-lists and callback functions
+ * which require arguments.
+ */
+ if (optp->opt_type == MDB_OPT_STR ||
+ optp->opt_type == MDB_OPT_UINTPTR ||
+ optp->opt_type == MDB_OPT_UINTPTR_SET ||
+ optp->opt_type == MDB_OPT_SUBOPTS ||
+ optp->opt_type == MDB_OPT_UINT64) {
+ /*
+ * More text after the option letter:
+ * forge a string argument from remainder.
+ */
+ if (p[1] != '\0') {
+ arg.a_type = MDB_TYPE_STRING;
+ arg.a_un.a_str = ++p;
+ argp = &arg;
+ p += strlen(p) - 1;
+
+ nargs = 0;
+ /*
+ * Otherwise use the next argv element as
+ * the argument if there is one.
+ */
+ } else if (++i == argc) {
+ warn("option requires an "
+ "argument -- %c\n", *p);
+ return (i - 1);
+ } else {
+ argp = ++argv;
+ nargs = 1;
+ }
+ } else {
+ argp = NULL;
+ nargs = 0;
+ }
+
+ /*
+ * Perform type-specific handling for this option.
+ */
+ if (argvec_process_opt(optp, argp) == -1)
+ return (i - nargs);
+ }
+ }
+
+ return (i);
+}
+
+int
+mdb_getopts(int argc, const mdb_arg_t *argv, ...)
+{
+ /*
+ * For simplicity just declare enough options on the stack to handle
+ * a-z and A-Z and an extra terminator.
+ */
+ mdb_opt_t opts[53], *op = &opts[0];
+ va_list alist;
+ int c, i = 0;
+ mdb_subopt_t *sop;
+
+ va_start(alist, argv);
+
+ for (i = 0; i < (sizeof (opts) / sizeof (opts[0]) - 1); i++, op++) {
+ if ((c = va_arg(alist, int)) == 0)
+ break; /* end of options */
+
+ op->opt_char = (char)c;
+ op->opt_type = va_arg(alist, uint_t);
+
+ if (op->opt_type == MDB_OPT_SETBITS ||
+ op->opt_type == MDB_OPT_CLRBITS) {
+ op->opt_bits = va_arg(alist, uint_t);
+ } else if (op->opt_type == MDB_OPT_UINTPTR_SET) {
+ op->opt_flag = va_arg(alist, boolean_t *);
+ } else if (op->opt_type == MDB_OPT_SUBOPTS) {
+ op->opt_subopts = va_arg(alist, mdb_subopt_t *);
+
+ for (sop = op->opt_subopts; sop->sop_flag; sop++)
+ sop->sop_index = -1;
+ }
+
+ op->opt_valp = va_arg(alist, void *);
+ }
+
+ bzero(&opts[i], sizeof (mdb_opt_t));
+ va_end(alist);
+
+ return (argvec_getopts(opts, argv, argc));
+}
+
+/*
+ * The old adb breakpoint and watchpoint routines did not accept any arguments;
+ * all characters after the verb were concatenated to form the string callback.
+ * This utility function concatenates all arguments in argv[] into a single
+ * string to simplify the implementation of these legacy routines.
+ */
+char *
+mdb_argv_to_str(int argc, const mdb_arg_t *argv)
+{
+ char *s = NULL;
+ size_t n = 0;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (argv[i].a_type == MDB_TYPE_STRING)
+ n += strlen(argv[i].a_un.a_str);
+ }
+
+ if (n != 0) {
+ s = mdb_zalloc(n + argc, UM_SLEEP);
+
+ for (i = 0; i < argc - 1; i++, argv++) {
+ (void) strcat(s, argv->a_un.a_str);
+ (void) strcat(s, " ");
+ }
+
+ (void) strcat(s, argv->a_un.a_str);
+ }
+
+ return (s);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_argvec.h b/usr/src/cmd/mdb/common/mdb/mdb_argvec.h
new file mode 100644
index 0000000..d76280d
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_argvec.h
@@ -0,0 +1,93 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_ARGVEC_H
+#define _MDB_ARGVEC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mdb_arg;
+
+typedef struct mdb_argvec {
+ struct mdb_arg *a_data; /* Array of arguments */
+ size_t a_nelems; /* Number of valid elements */
+ size_t a_size; /* Array size */
+} mdb_argvec_t;
+
+/* see mdb_modapi.h for 1-6 */
+#define MDB_OPT_SUBOPTS 7 /* Option requires a mdb_subopt_t */
+ /* list and a value argument */
+
+typedef struct mdb_subopt {
+ uint_t sop_flag; /* Option flag */
+ const char *sop_str; /* Sub-option name */
+ int sop_index; /* Index of subopt in argument */
+} mdb_subopt_t;
+
+typedef struct mdb_opt {
+ char opt_char; /* Option name */
+ void *opt_valp; /* Value storage pointer */
+ uint_t opt_bits; /* Bits to set or clear for booleans */
+ boolean_t *opt_flag; /* pointer to flag (uintptr_set) */
+ mdb_subopt_t *opt_subopts; /* List of mdb_subopt_t */
+ uint_t opt_type; /* Option type (see above) */
+} mdb_opt_t;
+
+#ifdef _MDB
+
+#ifdef _BIG_ENDIAN
+#ifdef _LP64
+#define MDB_INIT_CHAR(x) ((const char *)((uintptr_t)(uchar_t)(x) << 56))
+#else /* _LP64 */
+#define MDB_INIT_CHAR(x) ((const char *)((uintptr_t)(uchar_t)(x) << 24))
+#endif /* _LP64 */
+#else /* _BIG_ENDIAN */
+#define MDB_INIT_CHAR(x) ((const char *)(uchar_t)(x))
+#endif /* _BIG_ENDIAN */
+#define MDB_INIT_STRING(x) ((const char *)(x))
+
+extern void mdb_argvec_create(mdb_argvec_t *);
+extern void mdb_argvec_destroy(mdb_argvec_t *);
+extern void mdb_argvec_append(mdb_argvec_t *, const struct mdb_arg *);
+extern void mdb_argvec_reset(mdb_argvec_t *);
+extern void mdb_argvec_zero(mdb_argvec_t *);
+extern void mdb_argvec_copy(mdb_argvec_t *, const mdb_argvec_t *);
+
+extern char *mdb_argv_to_str(int, const struct mdb_arg *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_ARGVEC_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_callb.c b/usr/src/cmd/mdb/common/mdb/mdb_callb.c
new file mode 100644
index 0000000..308eb48
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_callb.c
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Callback facility designed to allow interested parties (dmods, targets, or
+ * even the core debugger framework) to register for notification when certain
+ * "interesting" events occur.
+ */
+
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_callb.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb.h>
+
+mdb_callb_t *
+mdb_callb_add(mdb_module_t *m, int class, mdb_callb_f fp, void *arg)
+{
+ mdb_callb_t *new = mdb_zalloc(sizeof (mdb_callb_t), UM_SLEEP);
+
+ ASSERT(class == MDB_CALLB_STCHG || class == MDB_CALLB_PROMPT);
+
+ new->cb_mod = m;
+ new->cb_class = class;
+ new->cb_func = fp;
+ new->cb_arg = arg;
+
+ if (m == NULL) {
+ mdb_list_prepend(&mdb.m_cblist, new);
+ } else {
+ mdb_list_insert(&mdb.m_cblist, m->mod_cb, new);
+ if (m->mod_cb == NULL)
+ m->mod_cb = new;
+ }
+
+ return (new);
+}
+
+void
+mdb_callb_remove(mdb_callb_t *cb)
+{
+ if (cb->cb_mod != NULL) {
+ mdb_callb_t *next = mdb_list_next(cb);
+ mdb_module_t *mod = cb->cb_mod;
+
+ if (mod->mod_cb == cb) {
+ if (next == NULL || next->cb_mod != mod)
+ mod->mod_cb = NULL;
+ else
+ mod->mod_cb = next;
+ }
+ }
+
+ mdb_list_delete(&mdb.m_cblist, cb);
+
+ mdb_free(cb, sizeof (mdb_callb_t));
+}
+
+void
+mdb_callb_remove_by_mod(mdb_module_t *m)
+{
+ while (m->mod_cb != NULL)
+ mdb_callb_remove(m->mod_cb);
+}
+
+void
+mdb_callb_remove_all(void)
+{
+ mdb_callb_t *cb;
+
+ while ((cb = mdb_list_next(&mdb.m_cblist)) != NULL)
+ mdb_callb_remove(cb);
+}
+
+void
+mdb_callb_fire(int class)
+{
+ mdb_callb_t *cb, *next;
+
+ ASSERT(class == MDB_CALLB_STCHG || class == MDB_CALLB_PROMPT);
+
+ mdb_dprintf(MDB_DBG_CALLB, "invoking %s callbacks\n",
+ (class == MDB_CALLB_STCHG ? "state change" : "prompt"));
+
+ for (cb = mdb_list_next(&mdb.m_cblist); cb != NULL; cb = next) {
+ next = mdb_list_next(cb);
+ if (cb->cb_class == class)
+ cb->cb_func(cb->cb_arg);
+ }
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_callb.h b/usr/src/cmd/mdb/common/mdb/mdb_callb.h
new file mode 100644
index 0000000..1c12e4c
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_callb.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_CALLB_H
+#define _MDB_CALLB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_module.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Callback facility designed to allow interested parties (dmods, targets, or
+ * even the core debugger framework) to register for notification when certain
+ * "interesting" events occur.
+ */
+
+/*
+ * Callback classes:
+ * (MDB_CALLBACK_* definitions in the module API need to be in sync with these)
+ */
+#define MDB_CALLB_STCHG 1 /* System execution state change */
+#define MDB_CALLB_PROMPT 2 /* Before printing the prompt */
+
+typedef void (*mdb_callb_f)(void *);
+
+typedef struct mdb_callb {
+ mdb_list_t cb_list; /* List of callbacks */
+ mdb_module_t *cb_mod; /* Requesting module (if any) */
+ int cb_class; /* When to notify */
+ mdb_callb_f cb_func; /* Function to invoke */
+ void *cb_arg; /* Argument for cb_func */
+} mdb_callb_t;
+
+extern mdb_callb_t *mdb_callb_add(mdb_module_t *, int, mdb_callb_f, void *);
+extern void mdb_callb_remove(mdb_callb_t *);
+extern void mdb_callb_remove_by_mod(mdb_module_t *);
+extern void mdb_callb_remove_all(void);
+extern void mdb_callb_fire(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CALLB_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.c b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.c
new file mode 100644
index 0000000..b317462
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.c
@@ -0,0 +1,484 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The MDB command buffer is a simple structure that keeps track of the
+ * command history list, and provides operations to manipulate the current
+ * buffer according to the various emacs editing options. The terminal
+ * code uses this code to keep track of the actual contents of the command
+ * line, and then uses this content to perform redraw operations.
+ */
+
+#include <strings.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_cmdbuf.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb.h>
+
+#define CMDBUF_LINELEN BUFSIZ /* Length of each buffer line */
+#define CMDBUF_TABLEN 8 /* Length of a tab in spaces */
+
+static void
+cmdbuf_shiftr(mdb_cmdbuf_t *cmd, size_t nbytes)
+{
+ bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
+ &cmd->cmd_buf[cmd->cmd_bufidx + nbytes],
+ cmd->cmd_buflen - cmd->cmd_bufidx);
+}
+
+void
+mdb_cmdbuf_create(mdb_cmdbuf_t *cmd)
+{
+ size_t i;
+
+ /*
+ * This is pretty weak, but good enough for the moment: just allocate
+ * BUFSIZ-sized chunks in advance for every history element. Later
+ * it would be nice to replace this with either code that allocates
+ * space for the history list on-the-fly so as not to waste so much
+ * memory, or that keeps a mapped history file like the shell.
+ */
+ cmd->cmd_history = mdb_alloc(mdb.m_histlen * sizeof (char *), UM_SLEEP);
+ cmd->cmd_linebuf = mdb_alloc(CMDBUF_LINELEN, UM_SLEEP);
+
+ for (i = 0; i < mdb.m_histlen; i++)
+ cmd->cmd_history[i] = mdb_alloc(CMDBUF_LINELEN, UM_SLEEP);
+
+ cmd->cmd_buf = cmd->cmd_history[0];
+ cmd->cmd_linelen = CMDBUF_LINELEN;
+ cmd->cmd_histlen = mdb.m_histlen;
+ cmd->cmd_buflen = 0;
+ cmd->cmd_bufidx = 0;
+ cmd->cmd_hold = 0;
+ cmd->cmd_hnew = 0;
+ cmd->cmd_hcur = 0;
+ cmd->cmd_hlen = 0;
+}
+
+void
+mdb_cmdbuf_destroy(mdb_cmdbuf_t *cmd)
+{
+ size_t i;
+
+ for (i = 0; i < cmd->cmd_histlen; i++)
+ mdb_free(cmd->cmd_history[i], CMDBUF_LINELEN);
+
+ mdb_free(cmd->cmd_linebuf, CMDBUF_LINELEN);
+ mdb_free(cmd->cmd_history, cmd->cmd_histlen * sizeof (char *));
+}
+
+int
+mdb_cmdbuf_caninsert(mdb_cmdbuf_t *cmd, size_t nbytes)
+{
+ return (cmd->cmd_buflen + nbytes < cmd->cmd_linelen);
+}
+
+int
+mdb_cmdbuf_atstart(mdb_cmdbuf_t *cmd)
+{
+ return (cmd->cmd_bufidx == 0);
+}
+
+int
+mdb_cmdbuf_atend(mdb_cmdbuf_t *cmd)
+{
+ return (cmd->cmd_bufidx == cmd->cmd_buflen);
+}
+
+int
+mdb_cmdbuf_insert(mdb_cmdbuf_t *cmd, int c)
+{
+ if (c == '\t') {
+ if (cmd->cmd_buflen + CMDBUF_TABLEN < cmd->cmd_linelen) {
+ int i;
+
+ if (cmd->cmd_buflen != cmd->cmd_bufidx)
+ cmdbuf_shiftr(cmd, CMDBUF_TABLEN);
+
+ for (i = 0; i < CMDBUF_TABLEN; i++)
+ cmd->cmd_buf[cmd->cmd_bufidx++] = ' ';
+
+ cmd->cmd_buflen += CMDBUF_TABLEN;
+ return (0);
+ }
+
+ return (-1);
+ }
+
+ if (c < ' ' || c > '~')
+ return (-1);
+
+ if (cmd->cmd_buflen < cmd->cmd_linelen) {
+ if (cmd->cmd_buflen != cmd->cmd_bufidx)
+ cmdbuf_shiftr(cmd, 1);
+
+ cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
+ cmd->cmd_buflen++;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+const char *
+mdb_cmdbuf_accept(mdb_cmdbuf_t *cmd)
+{
+ if (cmd->cmd_bufidx < cmd->cmd_linelen) {
+ cmd->cmd_buf[cmd->cmd_buflen++] = '\0';
+ (void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
+
+ /*
+ * Don't bother inserting empty buffers into the history ring.
+ */
+ if (cmd->cmd_buflen > 1) {
+ cmd->cmd_hnew = (cmd->cmd_hnew + 1) % cmd->cmd_histlen;
+ cmd->cmd_buf = cmd->cmd_history[cmd->cmd_hnew];
+ cmd->cmd_hcur = cmd->cmd_hnew;
+
+ if (cmd->cmd_hlen + 1 == cmd->cmd_histlen)
+ cmd->cmd_hold =
+ (cmd->cmd_hold + 1) % cmd->cmd_histlen;
+ else
+ cmd->cmd_hlen++;
+ }
+
+ cmd->cmd_bufidx = 0;
+ cmd->cmd_buflen = 0;
+
+ return ((const char *)cmd->cmd_linebuf);
+ }
+
+ return (NULL);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_backspace(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx > 0) {
+ if (cmd->cmd_buflen != cmd->cmd_bufidx) {
+ bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
+ &cmd->cmd_buf[cmd->cmd_bufidx - 1],
+ cmd->cmd_buflen - cmd->cmd_bufidx);
+ }
+
+ cmd->cmd_bufidx--;
+ cmd->cmd_buflen--;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_delchar(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx < cmd->cmd_buflen) {
+ if (cmd->cmd_bufidx < --cmd->cmd_buflen) {
+ bcopy(&cmd->cmd_buf[cmd->cmd_bufidx + 1],
+ &cmd->cmd_buf[cmd->cmd_bufidx],
+ cmd->cmd_buflen - cmd->cmd_bufidx);
+ }
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_fwdchar(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx < cmd->cmd_buflen) {
+ cmd->cmd_bufidx++;
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_backchar(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx > 0) {
+ cmd->cmd_bufidx--;
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+mdb_cmdbuf_transpose(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx > 0 && cmd->cmd_buflen > 1) {
+ c = cmd->cmd_buf[cmd->cmd_bufidx - 1];
+
+ if (cmd->cmd_bufidx == cmd->cmd_buflen) {
+ cmd->cmd_buf[cmd->cmd_bufidx - 1] =
+ cmd->cmd_buf[cmd->cmd_bufidx - 2];
+ cmd->cmd_buf[cmd->cmd_bufidx - 2] = (char)c;
+ } else {
+ cmd->cmd_buf[cmd->cmd_bufidx - 1] =
+ cmd->cmd_buf[cmd->cmd_bufidx];
+ cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
+ }
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_home(mdb_cmdbuf_t *cmd, int c)
+{
+ cmd->cmd_bufidx = 0;
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_end(mdb_cmdbuf_t *cmd, int c)
+{
+ cmd->cmd_bufidx = cmd->cmd_buflen;
+ return (0);
+}
+
+static size_t
+fwdword_index(mdb_cmdbuf_t *cmd)
+{
+ size_t i = cmd->cmd_bufidx + 1;
+
+ ASSERT(cmd->cmd_bufidx < cmd->cmd_buflen);
+
+ while (i < cmd->cmd_buflen && isspace(cmd->cmd_buf[i]))
+ i++;
+
+ while (i < cmd->cmd_buflen && !isspace(cmd->cmd_buf[i]) &&
+ !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
+ i++;
+
+ while (i < cmd->cmd_buflen &&
+ (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
+ i++;
+
+ return (i);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_fwdword(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx == cmd->cmd_buflen)
+ return (-1);
+
+ cmd->cmd_bufidx = fwdword_index(cmd);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_killfwdword(mdb_cmdbuf_t *cmd, int c)
+{
+ size_t i;
+
+ if (cmd->cmd_bufidx == cmd->cmd_buflen)
+ return (-1);
+
+ i = fwdword_index(cmd);
+
+ bcopy(&cmd->cmd_buf[i], &cmd->cmd_buf[cmd->cmd_bufidx],
+ cmd->cmd_buflen - i);
+
+ cmd->cmd_buflen -= i - cmd->cmd_bufidx;
+
+ return (0);
+}
+
+static size_t
+backword_index(mdb_cmdbuf_t *cmd)
+{
+ size_t i = cmd->cmd_bufidx - 1;
+
+ ASSERT(cmd->cmd_bufidx != 0);
+
+ while (i != 0 && isspace(cmd->cmd_buf[i]))
+ i--;
+
+ while (i != 0 && !isspace(cmd->cmd_buf[i]) &&
+ !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
+ i--;
+
+ while (i != 0 && (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
+ i--;
+
+ if (i != 0)
+ i++;
+
+ return (i);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_backword(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_bufidx == 0)
+ return (-1);
+
+ cmd->cmd_bufidx = backword_index(cmd);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_killbackword(mdb_cmdbuf_t *cmd, int c)
+{
+ size_t i;
+
+ if (cmd->cmd_bufidx == 0)
+ return (-1);
+
+ i = backword_index(cmd);
+
+ bcopy(&cmd->cmd_buf[cmd->cmd_bufidx], &cmd->cmd_buf[i],
+ cmd->cmd_buflen - cmd->cmd_bufidx);
+
+ cmd->cmd_buflen -= cmd->cmd_bufidx - i;
+ cmd->cmd_bufidx = i;
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_kill(mdb_cmdbuf_t *cmd, int c)
+{
+ cmd->cmd_buflen = cmd->cmd_bufidx;
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_reset(mdb_cmdbuf_t *cmd, int c)
+{
+ cmd->cmd_buflen = 0;
+ cmd->cmd_bufidx = 0;
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_prevhist(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_hcur != cmd->cmd_hold) {
+ if (cmd->cmd_hcur-- == cmd->cmd_hnew) {
+ cmd->cmd_buf[cmd->cmd_buflen] = 0;
+ (void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
+ }
+
+ if (cmd->cmd_hcur < 0)
+ cmd->cmd_hcur = cmd->cmd_histlen - 1;
+
+ (void) strcpy(cmd->cmd_buf, cmd->cmd_history[cmd->cmd_hcur]);
+ cmd->cmd_bufidx = strlen(cmd->cmd_buf);
+ cmd->cmd_buflen = cmd->cmd_bufidx;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_nexthist(mdb_cmdbuf_t *cmd, int c)
+{
+ if (cmd->cmd_hcur != cmd->cmd_hnew) {
+ cmd->cmd_hcur = (cmd->cmd_hcur + 1) % cmd->cmd_histlen;
+
+ if (cmd->cmd_hcur == cmd->cmd_hnew) {
+ (void) strcpy(cmd->cmd_buf, cmd->cmd_linebuf);
+ } else {
+ (void) strcpy(cmd->cmd_buf,
+ cmd->cmd_history[cmd->cmd_hcur]);
+ }
+
+ cmd->cmd_bufidx = strlen(cmd->cmd_buf);
+ cmd->cmd_buflen = cmd->cmd_bufidx;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_findhist(mdb_cmdbuf_t *cmd, int c)
+{
+ ssize_t i, n;
+
+ if (cmd->cmd_buflen != 0) {
+ cmd->cmd_hcur = cmd->cmd_hnew;
+ cmd->cmd_buf[cmd->cmd_buflen] = 0;
+ (void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
+ }
+
+ for (i = cmd->cmd_hcur, n = 0; n < cmd->cmd_hlen; n++) {
+ if (--i < 0)
+ i = cmd->cmd_histlen - 1;
+
+ if (strstr(cmd->cmd_history[i], cmd->cmd_linebuf) != NULL) {
+ (void) strcpy(cmd->cmd_buf, cmd->cmd_history[i]);
+ cmd->cmd_bufidx = strlen(cmd->cmd_buf);
+ cmd->cmd_buflen = cmd->cmd_bufidx;
+ cmd->cmd_hcur = i;
+
+ return (0);
+ }
+ }
+
+ cmd->cmd_hcur = cmd->cmd_hnew;
+
+ cmd->cmd_bufidx = 0;
+ cmd->cmd_buflen = 0;
+
+ return (-1);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.h b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.h
new file mode 100644
index 0000000..ae49042
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.h
@@ -0,0 +1,87 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1997-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_CMDBUF_H
+#define _MDB_CMDBUF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_cmdbuf {
+ char **cmd_history; /* Circular array of history buffers */
+ char *cmd_linebuf; /* Temporary history for current buffer */
+ char *cmd_buf; /* Current line buffer */
+ size_t cmd_linelen; /* Maximum line length */
+ size_t cmd_histlen; /* Maximum history entries */
+ size_t cmd_buflen; /* Number of bytes in current line buffer */
+ size_t cmd_bufidx; /* Byte position in current line buffer */
+ ssize_t cmd_hold; /* Oldest history entry index */
+ ssize_t cmd_hnew; /* Newest history entry index */
+ ssize_t cmd_hcur; /* Current history entry index */
+ ssize_t cmd_hlen; /* Number of valid history buffers */
+} mdb_cmdbuf_t;
+
+#ifdef _MDB
+
+extern void mdb_cmdbuf_create(mdb_cmdbuf_t *);
+extern void mdb_cmdbuf_destroy(mdb_cmdbuf_t *);
+
+extern const char *mdb_cmdbuf_accept(mdb_cmdbuf_t *);
+
+extern int mdb_cmdbuf_caninsert(mdb_cmdbuf_t *, size_t);
+extern int mdb_cmdbuf_atstart(mdb_cmdbuf_t *);
+extern int mdb_cmdbuf_atend(mdb_cmdbuf_t *);
+
+extern int mdb_cmdbuf_insert(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_backspace(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_delchar(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_fwdchar(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_backchar(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_transpose(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_home(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_end(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_fwdword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_backword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_killfwdword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_killbackword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_kill(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_reset(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_prevhist(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_nexthist(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_findhist(mdb_cmdbuf_t *, int);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CMDBUF_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
new file mode 100644
index 0000000..46a1c6f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
@@ -0,0 +1,2991 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/elf.h>
+#include <sys/elf_SPARC.h>
+
+#include <libproc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <alloca.h>
+#include <libctf.h>
+#include <ctype.h>
+
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_fmt.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_stdlib.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb_help.h>
+#include <mdb/mdb_disasm.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_evset.h>
+#include <mdb/mdb_print.h>
+#include <mdb/mdb_nm.h>
+#include <mdb/mdb_set.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_macalias.h>
+#ifdef _KMDB
+#include <kmdb/kmdb_kdi.h>
+#endif
+#include <mdb/mdb.h>
+
+#ifdef __sparc
+#define SETHI_MASK 0xc1c00000
+#define SETHI_VALUE 0x01000000
+
+#define IS_SETHI(machcode) (((machcode) & SETHI_MASK) == SETHI_VALUE)
+
+#define OP(machcode) ((machcode) >> 30)
+#define OP3(machcode) (((machcode) >> 19) & 0x3f)
+#define RD(machcode) (((machcode) >> 25) & 0x1f)
+#define RS1(machcode) (((machcode) >> 14) & 0x1f)
+#define I(machcode) (((machcode) >> 13) & 0x01)
+
+#define IMM13(machcode) ((machcode) & 0x1fff)
+#define IMM22(machcode) ((machcode) & 0x3fffff)
+
+#define OP_ARITH_MEM_MASK 0x2
+#define OP_ARITH 0x2
+#define OP_MEM 0x3
+
+#define OP3_CC_MASK 0x10
+#define OP3_COMPLEX_MASK 0x20
+
+#define OP3_ADD 0x00
+#define OP3_OR 0x02
+#define OP3_XOR 0x03
+
+#ifndef R_O7
+#define R_O7 0xf
+#endif
+#endif /* __sparc */
+
+static mdb_tgt_addr_t
+write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
+{
+ uint8_t o, n = (uint8_t)ull;
+
+ if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+ addr) == -1)
+ return (addr);
+
+ if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ if (rdback) {
+ if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
+ mdb_iob_getmargin(mdb.m_out), addr, o, n);
+ }
+
+ return (addr + sizeof (n));
+}
+
+static mdb_tgt_addr_t
+write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
+{
+ uint16_t o, n = (uint16_t)ull;
+
+ if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+ addr) == -1)
+ return (addr);
+
+ if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ if (rdback) {
+ if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
+ mdb_iob_getmargin(mdb.m_out), addr, o, n);
+ }
+
+ return (addr + sizeof (n));
+}
+
+static mdb_tgt_addr_t
+write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
+{
+ uint32_t o, n = (uint32_t)ull;
+
+ if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+ addr) == -1)
+ return (addr);
+
+ if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ if (rdback) {
+ if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
+ mdb_iob_getmargin(mdb.m_out), addr, o, n);
+ }
+
+ return (addr + sizeof (n));
+}
+
+static mdb_tgt_addr_t
+write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
+{
+ uint64_t o;
+
+ if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+ addr) == -1)
+ return (addr);
+
+ if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ if (rdback) {
+ if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+ return (addr);
+
+ mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
+ mdb_iob_getmargin(mdb.m_out), addr, o, n);
+ }
+
+ return (addr + sizeof (n));
+}
+
+static int
+write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
+ int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
+ uint64_t, uint_t);
+ mdb_tgt_addr_t naddr;
+ uintmax_t value;
+ int rdback = mdb.m_flags & MDB_FL_READBACK;
+ size_t i;
+
+ if (argc == 1) {
+ mdb_warn("expected value to write following %c\n",
+ argv->a_un.a_char);
+ return (DCMD_ERR);
+ }
+
+ switch (argv->a_un.a_char) {
+ case 'v':
+ write_value = write_uint8;
+ break;
+ case 'w':
+ write_value = write_uint16;
+ break;
+ case 'W':
+ write_value = write_uint32;
+ break;
+ case 'Z':
+ write_value = write_uint64;
+ break;
+ }
+
+ for (argv++, i = 1; i < argc; i++, argv++) {
+ if (argv->a_type == MDB_TYPE_CHAR) {
+ mdb_warn("expected immediate value instead of '%c'\n",
+ argv->a_un.a_char);
+ return (DCMD_ERR);
+ }
+
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (mdb_eval(argv->a_un.a_str) == -1) {
+ mdb_warn("failed to write \"%s\"",
+ argv->a_un.a_str);
+ return (DCMD_ERR);
+ }
+ value = mdb_nv_get_value(mdb.m_dot);
+ } else
+ value = argv->a_un.a_val;
+
+ mdb_nv_set_value(mdb.m_dot, addr);
+
+ if ((naddr = write_value(as, addr, value, rdback)) == addr) {
+ mdb_warn("failed to write %llr at address 0x%llx",
+ value, addr);
+ mdb.m_incr = 0;
+ break;
+ }
+
+ mdb.m_incr = naddr - addr;
+ addr = naddr;
+ }
+
+ return (DCMD_OK);
+}
+
+static mdb_tgt_addr_t
+match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
+{
+ uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
+
+ for (; mdb_tgt_aread(mdb.m_target, as, &x,
+ sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
+
+ if ((x & mask) == val) {
+ mdb_iob_printf(mdb.m_out, "%lla\n", addr);
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
+{
+ uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
+
+ for (; mdb_tgt_aread(mdb.m_target, as, &x,
+ sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
+
+ if ((x & mask) == val) {
+ mdb_iob_printf(mdb.m_out, "%lla\n", addr);
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
+{
+ uint64_t x;
+
+ for (; mdb_tgt_aread(mdb.m_target, as, &x,
+ sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
+
+ if ((x & mask) == val) {
+ mdb_iob_printf(mdb.m_out, "%lla\n", addr);
+ break;
+ }
+ }
+ return (addr);
+}
+
+static int
+match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
+ int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
+ uint64_t, uint64_t);
+
+ uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
+ size_t i;
+
+ if (argc < 2) {
+ mdb_warn("expected value following %c\n", argv->a_un.a_char);
+ return (DCMD_ERR);
+ }
+
+ if (argc > 3) {
+ mdb_warn("only value and mask may follow %c\n",
+ argv->a_un.a_char);
+ return (DCMD_ERR);
+ }
+
+ switch (argv->a_un.a_char) {
+ case 'l':
+ match_value = match_uint16;
+ break;
+ case 'L':
+ match_value = match_uint32;
+ break;
+ case 'M':
+ match_value = match_uint64;
+ break;
+ }
+
+ for (argv++, i = 1; i < argc; i++, argv++) {
+ if (argv->a_type == MDB_TYPE_CHAR) {
+ mdb_warn("expected immediate value instead of '%c'\n",
+ argv->a_un.a_char);
+ return (DCMD_ERR);
+ }
+
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (mdb_eval(argv->a_un.a_str) == -1) {
+ mdb_warn("failed to evaluate \"%s\"",
+ argv->a_un.a_str);
+ return (DCMD_ERR);
+ }
+ args[i - 1] = mdb_nv_get_value(mdb.m_dot);
+ } else
+ args[i - 1] = argv->a_un.a_val;
+ }
+
+ addr = match_value(as, addr, args[0], args[1]);
+ mdb_nv_set_value(mdb.m_dot, addr);
+
+ /*
+ * In adb(1), the match operators ignore any repeat count that has
+ * been applied to them. We emulate this undocumented property
+ * by returning DCMD_ABORT if our input is not a pipeline.
+ */
+ return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
+}
+
+static int
+argncmp(int argc, const mdb_arg_t *argv, const char *s)
+{
+ for (; *s != '\0'; s++, argc--, argv++) {
+ if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
+ return (FALSE);
+ if (argv->a_un.a_char != *s)
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+static int
+print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
+ int argc, const mdb_arg_t *argv)
+{
+ char buf[MDB_TGT_SYM_NAMLEN];
+ mdb_tgt_addr_t oaddr = addr;
+ mdb_tgt_addr_t naddr;
+ GElf_Sym sym;
+ size_t i, n;
+
+ if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
+ const char *fmt;
+ int is_dis;
+ /*
+ * This is nasty, but necessary for precise adb compatibility.
+ * Detect disassembly format by looking for "ai" or "ia":
+ */
+ if (argncmp(argc, argv, "ai")) {
+ fmt = "%-#*lla\n";
+ is_dis = TRUE;
+ } else if (argncmp(argc, argv, "ia")) {
+ fmt = "%-#*lla";
+ is_dis = TRUE;
+ } else {
+ fmt = "%-#*lla%16T";
+ is_dis = FALSE;
+ }
+
+ /*
+ * If symbolic decoding is on, disassembly is off, and the
+ * address exactly matches a symbol, print the symbol name:
+ */
+ if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
+ (as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
+ mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
+ MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
+ mdb_iob_printf(mdb.m_out, "%s:\n", buf);
+
+ /*
+ * If this is a virtual address, cast it so that it reflects
+ * only the valid component of the address.
+ */
+ if (as == MDB_TGT_AS_VIRT)
+ addr = (uintptr_t)addr;
+
+ mdb_iob_printf(mdb.m_out, fmt,
+ (uint_t)mdb_iob_getmargin(mdb.m_out), addr);
+ }
+
+ if (argc == 0) {
+ /*
+ * Yes, for you trivia buffs: if you use a format verb and give
+ * no format string, you get: X^"= "i ... note that in adb the
+ * the '=' verb once had 'z' as its default, but then 'z' was
+ * deleted (it was once an alias for 'i') and so =\n now calls
+ * scanform("z") and produces a 'bad modifier' message.
+ */
+ static const mdb_arg_t def_argv[] = {
+ { MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
+ { MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
+ { MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
+ { MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
+ };
+
+ argc = sizeof (def_argv) / sizeof (mdb_arg_t);
+ argv = def_argv;
+ }
+
+ mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
+
+ for (i = 0, n = 1; i < argc; i++, argv++) {
+ switch (argv->a_type) {
+ case MDB_TYPE_CHAR:
+ naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
+ argv->a_un.a_char);
+ mdb.m_incr = naddr - addr;
+ addr = naddr;
+ n = 1;
+ break;
+
+ case MDB_TYPE_IMMEDIATE:
+ n = argv->a_un.a_val;
+ break;
+
+ case MDB_TYPE_STRING:
+ mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
+ n = 1;
+ break;
+ }
+ }
+
+ mdb.m_incr = addr - oaddr;
+ mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
+ return (DCMD_OK);
+}
+
+static int
+print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
+
+ if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
+ if (strchr("vwWZ", argv->a_un.a_char))
+ return (write_arglist(as, addr, argc, argv));
+ if (strchr("lLM", argv->a_un.a_char))
+ return (match_arglist(as, flags, addr, argc, argv));
+ }
+
+ return (print_arglist(as, addr, flags, argc, argv));
+}
+
+/*ARGSUSED*/
+static int
+cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
+}
+
+/*ARGSUSED*/
+static int
+cmd_print_value(uintptr_t addr, uint_t flags,
+ int argc, const mdb_arg_t *argv)
+{
+ uintmax_t ndot, dot = mdb_get_dot();
+ const char *tgt_argv[1];
+ mdb_tgt_t *t;
+ size_t i, n;
+
+ if (argc == 0) {
+ mdb_warn("expected one or more format characters "
+ "following '='\n");
+ return (DCMD_ERR);
+ }
+
+ tgt_argv[0] = (const char *)˙
+ t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
+ mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
+
+ for (i = 0, n = 1; i < argc; i++, argv++) {
+ switch (argv->a_type) {
+ case MDB_TYPE_CHAR:
+ ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
+ dot, n, argv->a_un.a_char);
+ if (argv->a_un.a_char == '+' ||
+ argv->a_un.a_char == '-')
+ dot = ndot;
+ n = 1;
+ break;
+
+ case MDB_TYPE_IMMEDIATE:
+ n = argv->a_un.a_val;
+ break;
+
+ case MDB_TYPE_STRING:
+ mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
+ n = 1;
+ break;
+ }
+ }
+
+ mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
+ mdb_nv_set_value(mdb.m_dot, dot);
+ mdb.m_incr = 0;
+
+ mdb_tgt_destroy(t);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_assign_variable(uintptr_t addr, uint_t flags,
+ int argc, const mdb_arg_t *argv)
+{
+ uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
+ const char *p;
+ mdb_var_t *v;
+
+ if (argc == 2) {
+ if (argv->a_type != MDB_TYPE_CHAR) {
+ mdb_warn("improper arguments following '>' operator\n");
+ return (DCMD_ERR);
+ }
+
+ switch (argv->a_un.a_char) {
+ case 'c':
+ addr = *((uchar_t *)&addr);
+ break;
+ case 's':
+ addr = *((ushort_t *)&addr);
+ break;
+ case 'i':
+ addr = *((uint_t *)&addr);
+ break;
+ case 'l':
+ addr = *((ulong_t *)&addr);
+ break;
+ default:
+ mdb_warn("%c is not a valid // modifier\n",
+ argv->a_un.a_char);
+ return (DCMD_ERR);
+ }
+
+ dot = addr;
+ argv++;
+ argc--;
+ }
+
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
+ mdb_warn("expected single variable name following '>'\n");
+ return (DCMD_ERR);
+ }
+
+ if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
+ mdb_warn("variable names may not exceed %d characters\n",
+ MDB_NV_NAMELEN - 1);
+ return (DCMD_ERR);
+ }
+
+ if ((p = strbadid(argv->a_un.a_str)) != NULL) {
+ mdb_warn("'%c' may not be used in a variable name\n", *p);
+ return (DCMD_ERR);
+ }
+
+ if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
+ (void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
+ else
+ mdb_nv_set_value(v, dot);
+
+ mdb.m_incr = 0;
+ return (DCMD_OK);
+}
+
+static int
+print_soutype(const char *sou, uintptr_t addr, uint_t flags)
+{
+ static const char *prefixes[] = { "struct ", "union " };
+ size_t namesz = 7 + strlen(sou) + 1;
+ char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
+ mdb_ctf_id_t id;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ (void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
+
+ if (mdb_ctf_lookup_by_name(name, &id) == 0) {
+ mdb_arg_t v;
+ int rv;
+
+ v.a_type = MDB_TYPE_STRING;
+ v.a_un.a_str = name;
+
+ rv = mdb_call_dcmd("print", addr, flags, 1, &v);
+ return (rv);
+ }
+ }
+
+ return (DCMD_ERR);
+}
+
+static int
+print_type(const char *name, uintptr_t addr, uint_t flags)
+{
+ mdb_ctf_id_t id;
+ char *sname;
+ size_t snamesz;
+ int rv;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ addr = mdb_get_dot();
+ flags |= DCMD_ADDRSPEC;
+ }
+
+ if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
+ return (rv);
+
+ snamesz = strlen(name) + 3;
+ sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
+ (void) mdb_snprintf(sname, snamesz, "%s_t", name);
+
+ if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
+ mdb_arg_t v;
+ int rv;
+
+ v.a_type = MDB_TYPE_STRING;
+ v.a_un.a_str = sname;
+
+ rv = mdb_call_dcmd("print", addr, flags, 1, &v);
+ return (rv);
+ }
+
+ sname[snamesz - 2] = 's';
+ rv = print_soutype(sname, addr, flags);
+ return (rv);
+}
+
+static int
+exec_alias(const char *fname, uintptr_t addr, uint_t flags)
+{
+ const char *alias;
+ int rv;
+
+ if ((alias = mdb_macalias_lookup(fname)) == NULL)
+ return (DCMD_ERR);
+
+ if (flags & DCMD_ADDRSPEC) {
+ size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
+ char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
+ (void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
+ rv = mdb_eval(addralias);
+ } else {
+ rv = mdb_eval(alias);
+ }
+
+ return (rv == -1 ? DCMD_ABORT : DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char *fname;
+ mdb_io_t *fio;
+ int rv;
+
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ fname = argv->a_un.a_str;
+
+ if (flags & DCMD_PIPE_OUT) {
+ mdb_warn("macro files cannot be used as input to a pipeline\n");
+ return (DCMD_ABORT);
+ }
+
+ if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
+ O_RDONLY, 0)) != NULL) {
+ mdb_frame_t *fp = mdb.m_frame;
+ int err;
+
+ mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
+ mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
+ err = mdb_run();
+
+ ASSERT(fp == mdb.m_frame);
+ mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
+ yylineno = mdb_iob_lineno(mdb.m_in);
+
+ if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
+ longjmp(fp->f_pcb, err);
+
+ if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
+ err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
+ longjmp(fp->f_pcb, err);
+
+ return (DCMD_OK);
+ }
+
+ if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
+ (rv = print_type(fname, addr, flags)) != DCMD_ERR)
+ return (rv);
+
+ mdb_warn("failed to open %s (see ::help '$<')\n", fname);
+ return (DCMD_ABORT);
+}
+
+static int
+cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char *fname;
+ mdb_io_t *fio;
+ int rv;
+
+ /*
+ * The syntax [expr[,count]]$< with no trailing macro file name is
+ * magic in that if count is zero, this command won't be called and
+ * the expression is thus a no-op. If count is non-zero, we get
+ * invoked with argc == 0, and this means abort the current macro.
+ * If our debugger stack depth is greater than one, we may be using
+ * $< from within a previous $<<, so in that case we set m_in to
+ * NULL to force this entire frame to be popped.
+ */
+ if (argc == 0) {
+ if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
+ mdb_iob_destroy(mdb.m_in);
+ mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
+ } else if (mdb.m_depth > 1) {
+ mdb_iob_destroy(mdb.m_in);
+ mdb.m_in = NULL;
+ } else
+ mdb_warn("input stack is empty\n");
+ return (DCMD_OK);
+ }
+
+ if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
+ return (cmd_src_file(addr, flags, argc, argv));
+
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ fname = argv->a_un.a_str;
+
+ if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
+ O_RDONLY, 0)) != NULL) {
+ mdb_iob_destroy(mdb.m_in);
+ mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
+ return (DCMD_OK);
+ }
+
+ if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
+ (rv = print_type(fname, addr, flags)) != DCMD_ERR)
+ return (rv);
+
+ mdb_warn("failed to open %s (see ::help '$<')\n", fname);
+ return (DCMD_ABORT);
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int status = DCMD_OK;
+ char buf[BUFSIZ];
+ mdb_iob_t *iob;
+ mdb_io_t *fio;
+
+ if (flags & DCMD_ADDRSPEC)
+ return (DCMD_USAGE);
+
+ for (; argc-- != 0; argv++) {
+ if (argv->a_type != MDB_TYPE_STRING) {
+ mdb_warn("expected string argument\n");
+ status = DCMD_ERR;
+ continue;
+ }
+
+ if ((fio = mdb_fdio_create_path(NULL,
+ argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
+ mdb_warn("failed to open %s", argv->a_un.a_str);
+ status = DCMD_ERR;
+ continue;
+ }
+
+ iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
+
+ while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
+ ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
+ if (len > 0) {
+ if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
+ if (errno != EPIPE)
+ mdb_warn("write failed");
+ status = DCMD_ERR;
+ break;
+ }
+ }
+ }
+
+ if (mdb_iob_err(iob))
+ mdb_warn("error while reading %s", mdb_iob_name(iob));
+
+ mdb_iob_destroy(iob);
+ }
+
+ return (status);
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ if (mdb_eval(argv->a_un.a_str) == -1)
+ return (DCMD_ABORT);
+
+ if (mdb_get_dot() != 0)
+ mdb_printf("%lr\n", addr);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ if (mdb_eval(argv->a_un.a_str) == -1)
+ return (DCMD_ABORT);
+
+ mdb_printf("%llr\n", mdb_get_dot());
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_warn("command is not supported by current target\n");
+ return (DCMD_ERR);
+}
+
+/*ARGSUSED*/
+static int
+cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+#ifdef _KMDB
+ uint_t opt_u = FALSE;
+
+ if (mdb_getopts(argc, argv,
+ 'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (opt_u) {
+ if (mdb.m_flags & MDB_FL_NOUNLOAD) {
+ warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
+ return (DCMD_ERR);
+ }
+
+ kmdb_kdi_set_unload_request();
+ }
+#endif
+
+ longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
+ /*NOTREACHED*/
+ return (DCMD_ERR);
+}
+
+#ifdef _KMDB
+static void
+quit_help(void)
+{
+ mdb_printf(
+ "-u unload the debugger (if not loaded at boot)\n");
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
+ mdb_var_t *v;
+
+ if (mdb_getopts(argc, argv,
+ 'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
+ 'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
+ 't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
+ return (DCMD_USAGE);
+
+ mdb_nv_rewind(&mdb.m_nv);
+
+ while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
+ if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
+ (opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
+ if (opt_prt) {
+ mdb_printf("%#llr>%s\n",
+ mdb_nv_get_value(v), mdb_nv_get_name(v));
+ } else {
+ mdb_printf("%s = %llr\n",
+ mdb_nv_get_name(v), mdb_nv_get_value(v));
+ }
+ }
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uintmax_t value;
+ mdb_var_t *v;
+
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ mdb_nv_rewind(&mdb.m_nv);
+
+ while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
+ if ((value = mdb_nv_get_value(v)) != 0)
+ mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ if (flags & DCMD_ADDRSPEC) {
+ if (addr < 2 || addr > 16) {
+ mdb_warn("expected radix from 2 to 16\n");
+ return (DCMD_ERR);
+ }
+ mdb.m_radix = (int)addr;
+ }
+
+ mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ if (flags & DCMD_ADDRSPEC)
+ mdb.m_symdist = addr;
+
+ mdb_printf("symbol matching distance = %lr (%s)\n",
+ mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ if (flags & DCMD_ADDRSPEC)
+ mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
+
+ mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
+ mdb_warn("failed to re-open target for writing");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
+{
+ mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0 || (flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ (void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_var_t *v;
+ size_t i;
+
+ for (i = 0; i < argc; i++) {
+ if (argv[i].a_type != MDB_TYPE_STRING) {
+ mdb_warn("bad option: arg %lu is not a string\n",
+ (ulong_t)i + 1);
+ return (DCMD_USAGE);
+ }
+ }
+
+ for (i = 0; i < argc; i++, argv++) {
+ if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
+ mdb_warn("variable '%s' not defined\n",
+ argv->a_un.a_str);
+ else
+ mdb_nv_remove(&mdb.m_nv, v);
+ }
+
+ return (DCMD_OK);
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t opt_e = FALSE, opt_d = FALSE;
+ const char *filename = NULL;
+ int i;
+
+ i = mdb_getopts(argc, argv,
+ 'd', MDB_OPT_SETBITS, TRUE, &opt_d,
+ 'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
+
+ if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
+ (i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
+ (i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ if (mdb.m_depth != 1) {
+ mdb_warn("log may not be manipulated in this context\n");
+ return (DCMD_ABORT);
+ }
+
+ if (i != argc)
+ filename = argv[i].a_un.a_str;
+
+ /*
+ * If no arguments were specified, print the log file name (if any)
+ * and report whether the log is enabled or disabled.
+ */
+ if (argc == 0) {
+ if (mdb.m_log) {
+ mdb_printf("%s: logging to \"%s\" is currently %s\n",
+ mdb.m_pname, IOP_NAME(mdb.m_log),
+ mdb.m_flags & MDB_FL_LOG ? "enabled" : "disabled");
+ } else
+ mdb_printf("%s: no log is active\n", mdb.m_pname);
+ return (DCMD_OK);
+ }
+
+ /*
+ * If the -d option was specified, pop the log i/o object off the
+ * i/o stack of stdin, stdout, and stderr.
+ */
+ if (opt_d) {
+ if (mdb.m_flags & MDB_FL_LOG) {
+ (void) mdb_iob_pop_io(mdb.m_in);
+ (void) mdb_iob_pop_io(mdb.m_out);
+ (void) mdb_iob_pop_io(mdb.m_err);
+ mdb.m_flags &= ~MDB_FL_LOG;
+ } else
+ mdb_warn("logging is already disabled\n");
+ return (DCMD_OK);
+ }
+
+ /*
+ * The -e option is the default: (re-)enable logging by pushing
+ * the log i/o object on to stdin, stdout, and stderr. If we have
+ * a previous log file, we need to pop it and close it. If we have
+ * no new log file, push the previous one back on.
+ */
+ if (filename != NULL) {
+ if (mdb.m_log != NULL) {
+ if (mdb.m_flags & MDB_FL_LOG) {
+ (void) mdb_iob_pop_io(mdb.m_in);
+ (void) mdb_iob_pop_io(mdb.m_out);
+ (void) mdb_iob_pop_io(mdb.m_err);
+ mdb.m_flags &= ~MDB_FL_LOG;
+ }
+ mdb_io_rele(mdb.m_log);
+ }
+
+ mdb.m_log = mdb_fdio_create_path(NULL, filename,
+ O_CREAT | O_APPEND | O_WRONLY, 0666);
+
+ if (mdb.m_log == NULL) {
+ mdb_warn("failed to open %s", filename);
+ return (DCMD_ERR);
+ }
+ }
+
+ if (mdb.m_log != NULL) {
+ mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
+ mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
+ mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
+
+ mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
+ mdb.m_log = mdb_io_hold(mdb.m_log);
+ mdb.m_flags |= MDB_FL_LOG;
+
+ return (DCMD_OK);
+ }
+
+ mdb_warn("no log file has been selected\n");
+ return (DCMD_ERR);
+}
+
+static int
+cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc == 0) {
+ mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
+ return (cmd_log(addr, flags, 1, &arg));
+ }
+
+ return (cmd_log(addr, flags, argc, argv));
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int i, mode = MDB_MOD_LOCAL;
+
+ i = mdb_getopts(argc, argv,
+#ifdef _KMDB
+ 'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
+#endif
+ 'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
+ 'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
+ 's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
+ NULL);
+
+ argc -= i;
+ argv += i;
+
+ if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
+ argv->a_type != MDB_TYPE_STRING ||
+ strchr("+-", argv->a_un.a_str[0]) != NULL)
+ return (DCMD_USAGE);
+
+ if (mdb_module_load(argv->a_un.a_str, mode) < 0)
+ return (DCMD_ERR);
+
+ return (DCMD_OK);
+}
+
+static void
+load_help(void)
+{
+ mdb_printf(
+#ifdef _KMDB
+ "-d defer load until next continue\n"
+#endif
+ "-s load module silently\n");
+}
+
+/*ARGSUSED*/
+static int
+cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int mode = 0;
+ int i;
+
+ i = mdb_getopts(argc, argv,
+#ifdef _KMDB
+ 'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
+#endif
+ NULL);
+
+ argc -= i;
+ argv += i;
+
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
+ mdb_warn("failed to unload %s", argv->a_un.a_str);
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+#ifdef _KMDB
+static void
+unload_help(void)
+{
+ mdb_printf(
+ "-d defer unload until next continue\n");
+}
+#endif
+
+static int
+cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
+ return (DCMD_USAGE);
+
+ if (argc != 0) {
+ if (argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+ if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
+ mdb_dmode(addr);
+ } else if (flags & DCMD_ADDRSPEC)
+ mdb_dmode(addr);
+
+ mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+#ifdef DEBUG
+ mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
+#else
+ mdb_printf("\r%s\n", mdb_conf_version());
+#endif
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (mdb.m_flags & MDB_FL_ADB)
+ mdb_printf("No algol 68 here\n");
+ else
+ mdb_printf("No adb here\n");
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (mdb.m_flags & MDB_FL_ADB)
+ mdb_printf("CHAPTER 1\n");
+ else
+ mdb_printf("No Language H here\n");
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_global(void *data, const GElf_Sym *sym, const char *name,
+ const mdb_syminfo_t *sip, const char *obj)
+{
+ uintptr_t value;
+
+ if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
+ (uintptr_t)sym->st_value) == sizeof (value))
+ mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
+ else
+ mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ (void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
+ MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
+ MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ if (mdb_eval(argv->a_un.a_str) == -1)
+ return (DCMD_ABORT);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_file(void *data, const GElf_Sym *sym, const char *name,
+ const mdb_syminfo_t *sip, const char *obj)
+{
+ int i = *((int *)data);
+
+ mdb_printf("%d\t%s\n", i++, name);
+ *((int *)data) = i;
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int i = 1;
+ const char *obj = MDB_TGT_OBJ_EVERY;
+
+ if ((flags & DCMD_ADDRSPEC) || argc > 1)
+ return (DCMD_USAGE);
+
+ if (argc == 1) {
+ if (argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ obj = argv->a_un.a_str;
+ }
+
+ (void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
+ MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_map(void *ignored, const mdb_map_t *map, const char *name)
+{
+ if (name == NULL || *name == '\0') {
+ if (map->map_flags & MDB_TGT_MAP_SHMEM)
+ name = "[ shmem ]";
+ else if (map->map_flags & MDB_TGT_MAP_STACK)
+ name = "[ stack ]";
+ else if (map->map_flags & MDB_TGT_MAP_HEAP)
+ name = "[ heap ]";
+ else if (map->map_flags & MDB_TGT_MAP_ANON)
+ name = "[ anon ]";
+ }
+
+ mdb_printf("%?p %?p %?lx %s\n", map->map_base, map->map_base +
+ map->map_size, map->map_size, name ? name : map->map_name);
+ return (0);
+}
+
+static int
+cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const mdb_map_t *m;
+
+ if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
+ return (DCMD_USAGE);
+
+ mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
+ "BASE", "LIMIT", "SIZE", "NAME");
+
+ if (flags & DCMD_ADDRSPEC) {
+ if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
+ mdb_warn("failed to obtain mapping");
+ else
+ (void) print_map(NULL, m, NULL);
+
+ } else if (argc != 0) {
+ if (argv->a_type == MDB_TYPE_STRING)
+ m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
+ else
+ m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
+
+ if (m == NULL)
+ mdb_warn("failed to obtain mapping");
+ else
+ (void) print_map(NULL, m, NULL);
+
+ } else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
+ mdb_warn("failed to iterate over mappings");
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
+{
+ ctf_file_t *ctfp;
+ const char *version;
+
+ ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
+ if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
+ version = "Unknown";
+
+ mdb_printf("%-28s %s\n", name, version);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t opt_v = FALSE;
+ mdb_tgt_map_f *cb;
+
+ if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
+ 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (opt_v) {
+ cb = objects_printversion;
+ mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
+ } else {
+ cb = print_map;
+ mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
+ "BASE", "LIMIT", "SIZE", "NAME");
+ }
+
+ if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
+ mdb_warn("failed to iterate over objects");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
+{
+ ctf_file_t *ctfp;
+ const char *version = NULL;
+ char *objname;
+
+ objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
+ (void) strcpy(objname, object);
+
+ if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
+ version = ctf_label_topmost(ctfp);
+
+ /*
+ * Not all objects have CTF and label data, so set version to "Unknown".
+ */
+ if (version == NULL)
+ version = "Unknown";
+
+ /*
+ * The hash table implementation in OVERLOAD mode limits the version
+ * name to 31 characters because we cannot specify an external name.
+ * The full version name is available via the ::objects dcmd if needed.
+ */
+ (void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
+ MDB_NV_OVERLOAD);
+
+ return (0);
+}
+
+static int
+showrev_ispatch(const char *s)
+{
+ if (s == NULL)
+ return (0);
+
+ if (*s == 'T')
+ s++; /* skip T for T-patch */
+
+ for (; *s != '\0'; s++) {
+ if ((*s < '0' || *s > '9') && *s != '-')
+ return (0);
+ }
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+showrev_printobject(mdb_var_t *v, void *ignored)
+{
+ mdb_printf("%s ", MDB_NV_COOKIE(v));
+ return (0);
+}
+
+static int
+showrev_printversion(mdb_var_t *v, void *showall)
+{
+ const char *version = mdb_nv_get_name(v);
+ int patch;
+
+ patch = showrev_ispatch(version);
+ if (patch || (uintptr_t)showall) {
+ mdb_printf("%s: %s Objects: ",
+ (patch ? "Patch" : "Version"), version);
+ (void) mdb_inc_indent(2);
+
+ mdb_nv_defn_iter(v, showrev_printobject, NULL);
+
+ (void) mdb_dec_indent(2);
+ mdb_printf("\n");
+ }
+
+ return (0);
+}
+
+/*
+ * Display version information for each object in the system.
+ * Print information about patches only, unless showall is TRUE.
+ */
+static int
+showrev_objectversions(int showall)
+{
+ mdb_nv_t vers_nv;
+
+ (void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
+ if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
+ &vers_nv) == -1) {
+ mdb_warn("failed to iterate over objects");
+ return (DCMD_ERR);
+ }
+
+ mdb_nv_sort_iter(&vers_nv, showrev_printversion,
+ (void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
+ return (DCMD_OK);
+}
+
+/*
+ * Display information similar to what showrev(1M) displays when invoked
+ * with no arguments.
+ */
+static int
+showrev_sysinfo(void)
+{
+ const char *s;
+ int rc;
+ struct utsname u;
+
+ if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
+ mdb_printf("Hostname: %s\n", u.nodename);
+ mdb_printf("Release: %s\n", u.release);
+ mdb_printf("Kernel architecture: %s\n", u.machine);
+ }
+
+ /*
+ * Match the order of the showrev(1M) output and put "Application
+ * architecture" before "Kernel version"
+ */
+ if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
+ mdb_printf("Application architecture: %s\n", s);
+
+ if (rc != -1)
+ mdb_printf("Kernel version: %s %s %s %s\n",
+ u.sysname, u.release, u.machine, u.version);
+
+ if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
+ mdb_printf("Platform: %s\n", s);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t opt_p = FALSE, opt_v = FALSE;
+
+ if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
+ 'p', MDB_OPT_SETBITS, TRUE, &opt_p,
+ 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (opt_p || opt_v)
+ return (showrev_objectversions(opt_v));
+ else
+ return (showrev_sysinfo());
+}
+
+#ifdef __sparc
+static void
+findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
+{
+ uintptr_t *symbolp;
+
+ for (symbolp = symlist; *symbolp; symbolp++)
+ if (value == *symbolp)
+ mdb_printf("found %a at %a\n", value, location);
+}
+
+/*ARGSUSED*/
+static int
+findsym_cb(void *data, const GElf_Sym *sym, const char *name,
+ const mdb_syminfo_t *sip, const char *obj)
+{
+ uint32_t *text;
+ int len;
+ int i;
+ int j;
+ uint8_t rd;
+ uintptr_t value;
+ int32_t imm13;
+ uint8_t op;
+ uint8_t op3;
+ uintptr_t *symlist = data;
+ size_t size = sym->st_size;
+
+ /*
+ * if the size of the symbol is 0, then this symbol must be for an
+ * alternate entry point or just some global label. We will,
+ * therefore, get back to the text that follows this symbol in
+ * some other symbol
+ */
+ if (size == 0)
+ return (0);
+
+ if (sym->st_shndx == SHN_UNDEF)
+ return (0);
+
+ text = alloca(size);
+
+ if (mdb_vread(text, size, sym->st_value) == -1) {
+ mdb_warn("failed to read text for %s", name);
+ return (0);
+ }
+
+ len = size / 4;
+ for (i = 0; i < len; i++) {
+ if (!IS_SETHI(text[i]))
+ continue;
+
+ rd = RD(text[i]);
+ value = IMM22(text[i]) << 10;
+
+ /*
+ * see if we already have a match with just the sethi
+ */
+ findsym_output(symlist, value, sym->st_value + i * 4);
+
+ /*
+ * search from the sethi on until we hit a relevent instr
+ */
+ for (j = i + 1; j < len; j++) {
+ if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
+ op3 = OP3(text[j]);
+
+ if (RS1(text[j]) != rd)
+ goto instr_end;
+
+ /*
+ * This is a simple tool; we only deal
+ * with operations which take immediates
+ */
+ if (I(text[j]) == 0)
+ goto instr_end;
+
+ /*
+ * sign extend the immediate value
+ */
+ imm13 = IMM13(text[j]);
+ imm13 <<= 19;
+ imm13 >>= 19;
+
+ if (op == OP_ARITH) {
+ /* arithmetic operations */
+ if (op3 & OP3_COMPLEX_MASK)
+ goto instr_end;
+
+ switch (op3 & ~OP3_CC_MASK) {
+ case OP3_OR:
+ value |= imm13;
+ break;
+ case OP3_ADD:
+ value += imm13;
+ break;
+ case OP3_XOR:
+ value ^= imm13;
+ break;
+ default:
+ goto instr_end;
+ }
+ } else {
+ /* loads and stores */
+ /* op3 == OP_MEM */
+
+ value += imm13;
+ }
+
+ findsym_output(symlist, value,
+ sym->st_value + j * 4);
+instr_end:
+ /*
+ * if we're clobbering rd, break
+ */
+ if (RD(text[j]) == rd)
+ break;
+ } else if (IS_SETHI(text[j])) {
+ if (RD(text[j]) == rd)
+ break;
+ } else if (OP(text[j]) == 1) {
+ /*
+ * see if a call clobbers an %o or %g
+ */
+ if (rd <= R_O7)
+ break;
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uintptr_t *symlist;
+ uint_t optg = FALSE;
+ uint_t type;
+ int len, i;
+
+ i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
+
+ argc -= i;
+ argv += i;
+
+ len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
+
+ if (len <= 1)
+ return (DCMD_USAGE);
+
+ /*
+ * Set up a NULL-terminated symbol list, and then iterate over the
+ * symbol table, scanning each function for references to these symbols.
+ */
+ symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
+ len = 0;
+
+ for (i = 0; i < argc; i++, argv++) {
+ const char *str = argv->a_un.a_str;
+ uintptr_t value;
+ GElf_Sym sym;
+
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (strchr("+-", str[0]) != NULL)
+ return (DCMD_USAGE);
+ else if (str[0] >= '0' && str[0] <= '9')
+ value = mdb_strtoull(str);
+ else if (mdb_lookup_by_name(str, &sym) != 0) {
+ mdb_warn("symbol '%s' not found", str);
+ return (DCMD_USAGE);
+ } else
+ value = sym.st_value;
+ } else
+ value = argv[i].a_un.a_val;
+
+ if (value != NULL)
+ symlist[len++] = value;
+ }
+
+ if (flags & DCMD_ADDRSPEC)
+ symlist[len++] = addr;
+
+ symlist[len] = NULL;
+
+ if (optg)
+ type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
+ else
+ type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
+
+ if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
+ MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
+ mdb_warn("failed to iterate over symbol table");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+#endif /* __sparc */
+
+static int
+dis_str2addr(const char *s, uintptr_t *addr)
+{
+ GElf_Sym sym;
+
+ if (s[0] >= '0' && s[0] <= '9') {
+ *addr = (uintptr_t)mdb_strtoull(s);
+ return (0);
+ }
+
+ if (mdb_tgt_lookup_by_name(mdb.m_target,
+ MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
+ mdb_warn("symbol '%s' not found\n", s);
+ return (-1);
+ }
+
+ *addr = (uintptr_t)sym.st_value;
+ return (0);
+}
+
+static int
+cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_t *tgt = mdb.m_target;
+ mdb_disasm_t *dis = mdb.m_disasm;
+
+ uintptr_t oaddr, naddr;
+ mdb_tgt_as_t as;
+ mdb_tgt_status_t st;
+ char buf[BUFSIZ];
+ GElf_Sym sym;
+ int i;
+
+ uint_t opt_f = FALSE; /* File-mode off by default */
+ uint_t opt_w = FALSE; /* Window mode off by default */
+ uint_t opt_a = FALSE; /* Raw-address mode off by default */
+ uintptr_t n = -1UL; /* Length of window in instructions */
+ uintptr_t eaddr = 0; /* Ending address; 0 if limited by n */
+
+ i = mdb_getopts(argc, argv,
+ 'f', MDB_OPT_SETBITS, TRUE, &opt_f,
+ 'w', MDB_OPT_SETBITS, TRUE, &opt_w,
+ 'a', MDB_OPT_SETBITS, TRUE, &opt_a,
+ 'n', MDB_OPT_UINTPTR, &n, NULL);
+
+ /*
+ * Disgusting argument post-processing ... basically the idea is to get
+ * the target address into addr, which we do by using the specified
+ * expression value, looking up a string as a symbol name, or by
+ * using the address specified as dot.
+ */
+ if (i != argc) {
+ if (argc != 0 && (argc - i) == 1) {
+ if (argv[i].a_type == MDB_TYPE_STRING) {
+ if (argv[i].a_un.a_str[0] == '-')
+ return (DCMD_USAGE);
+
+ if (dis_str2addr(argv[i].a_un.a_str, &addr))
+ return (DCMD_ERR);
+ } else
+ addr = argv[i].a_un.a_val;
+ } else
+ return (DCMD_USAGE);
+ }
+
+ /*
+ * If we're not in window mode yet, and some type of arguments were
+ * specified, see if the address corresponds nicely to a function.
+ * If not, turn on window mode; otherwise disassemble the function.
+ */
+ if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
+ if (mdb_tgt_lookup_by_addr(tgt, addr,
+ MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
+ GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
+ /*
+ * If the symbol has a size then set our end address to
+ * be the end of the function symbol we just located.
+ */
+ if (sym.st_size != 0)
+ eaddr = addr + (uintptr_t)sym.st_size;
+ } else
+ opt_w = TRUE;
+ }
+
+ /*
+ * Window-mode doesn't make sense in a loop.
+ */
+ if (flags & DCMD_LOOP)
+ opt_w = FALSE;
+
+ /*
+ * If -n was explicit, limit output to n instructions;
+ * otherwise set n to some reasonable default
+ */
+ if (n != -1UL)
+ eaddr = 0;
+ else
+ n = 10;
+
+ /*
+ * If the state is IDLE (i.e. no address space), turn on -f.
+ */
+ if (mdb_tgt_status(tgt, &st) == 0 && st.st_state == MDB_TGT_IDLE)
+ opt_f = TRUE;
+
+ if (opt_f)
+ as = MDB_TGT_AS_FILE;
+ else
+ as = MDB_TGT_AS_VIRT;
+
+ if (opt_w == FALSE) {
+ n++;
+ while ((eaddr == 0 && n-- != 0) || (addr < eaddr)) {
+ naddr = mdb_dis_ins2str(dis, tgt, as,
+ buf, sizeof (buf), addr);
+ if (naddr == addr)
+ return (DCMD_ERR);
+ if (opt_a)
+ mdb_printf("%-#32p%8T%s\n", addr, buf);
+ else
+ mdb_printf("%-#32a%8T%s\n", addr, buf);
+ addr = naddr;
+ }
+
+ } else {
+#ifdef __sparc
+ if (addr & 0x3) {
+ mdb_warn("address is not properly aligned\n");
+ return (DCMD_ERR);
+ }
+#endif
+
+ for (oaddr = mdb_dis_previns(dis, tgt, as, addr, n);
+ oaddr < addr; oaddr = naddr) {
+ naddr = mdb_dis_ins2str(dis, tgt, as,
+ buf, sizeof (buf), oaddr);
+ if (naddr == oaddr)
+ return (DCMD_ERR);
+ if (opt_a)
+ mdb_printf("%-#32p%8T%s\n", oaddr, buf);
+ else
+ mdb_printf("%-#32a%8T%s\n", oaddr, buf);
+ }
+
+ if ((naddr = mdb_dis_ins2str(dis, tgt, as,
+ buf, sizeof (buf), addr)) == addr)
+ return (DCMD_ERR);
+
+ mdb_printf("%<b>");
+ mdb_flush();
+ if (opt_a)
+ mdb_printf("%-#32p%8T%s%", addr, buf);
+ else
+ mdb_printf("%-#32a%8T%s%", addr, buf);
+ mdb_printf("%</b>\n");
+
+ for (addr = naddr; n-- != 0; addr = naddr) {
+ naddr = mdb_dis_ins2str(dis, tgt, as,
+ buf, sizeof (buf), addr);
+ if (naddr == addr)
+ return (DCMD_ERR);
+ if (opt_a)
+ mdb_printf("%-#32p%8T%s\n", addr, buf);
+ else
+ mdb_printf("%-#32a%8T%s\n", addr, buf);
+ }
+ }
+
+ mdb_set_dot(addr);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+walk_step(uintptr_t addr, const void *data, void *private)
+{
+ mdb_printf("%lr\n", addr);
+ return (WALK_NEXT);
+}
+
+static int
+cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int status;
+
+ if (argc < 1 || argc > 2 || argv[0].a_type != MDB_TYPE_STRING ||
+ argv[argc - 1].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ if (argc > 1) {
+ const char *name = argv[1].a_un.a_str;
+ mdb_var_t *v = mdb_nv_lookup(&mdb.m_nv, name);
+ const char *p;
+
+ if (v != NULL && (v->v_flags & MDB_NV_RDONLY) != 0) {
+ mdb_warn("variable %s is read-only\n", name);
+ return (DCMD_ABORT);
+ }
+
+ if (v == NULL && (p = strbadid(name)) != NULL) {
+ mdb_warn("'%c' may not be used in a variable "
+ "name\n", *p);
+ return (DCMD_ABORT);
+ }
+
+ if (v == NULL && (v = mdb_nv_insert(&mdb.m_nv,
+ name, NULL, 0, 0)) == NULL)
+ return (DCMD_ERR);
+
+ /*
+ * If there already exists a vcb for this variable, we may be
+ * calling ::walk in a loop. We only create a vcb for this
+ * variable on the first invocation.
+ */
+ if (mdb_vcb_find(v, mdb.m_frame) == NULL)
+ mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
+ }
+
+ if (flags & DCMD_ADDRSPEC)
+ status = mdb_pwalk(argv->a_un.a_str, walk_step, NULL, addr);
+ else
+ status = mdb_walk(argv->a_un.a_str, walk_step, NULL);
+
+ if (status == -1) {
+ mdb_warn("failed to perform walk");
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+static ssize_t
+mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
+{
+ ssize_t (*fp)(mdb_tgt_t *, const void *, size_t, uintptr_t) =
+ (ssize_t (*)(mdb_tgt_t *, const void *, size_t, uintptr_t))arg;
+
+ return (fp(mdb.m_target, buf, nbytes, addr));
+}
+
+/* ARGSUSED3 */
+static ssize_t
+mdb_partial_pread(void *buf, size_t nbytes, physaddr_t addr, void *arg)
+{
+ return (mdb_tgt_pread(mdb.m_target, buf, nbytes, addr));
+}
+
+
+static int
+cmd_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t dflags =
+ MDB_DUMP_ALIGN | MDB_DUMP_NEWDOT | MDB_DUMP_ASCII | MDB_DUMP_HEADER;
+ uint_t phys = FALSE;
+ uint_t file = FALSE;
+ uintptr_t group = 4;
+ uintptr_t width = 1;
+ mdb_tgt_status_t st;
+ int error;
+
+ if (mdb_getopts(argc, argv,
+ 'e', MDB_OPT_SETBITS, MDB_DUMP_ENDIAN, &dflags,
+ 'f', MDB_OPT_SETBITS, TRUE, &file,
+ 'g', MDB_OPT_UINTPTR, &group,
+ 'p', MDB_OPT_SETBITS, TRUE, &phys,
+ 'q', MDB_OPT_CLRBITS, MDB_DUMP_ASCII, &dflags,
+ 'r', MDB_OPT_SETBITS, MDB_DUMP_RELATIVE, &dflags,
+ 's', MDB_OPT_SETBITS, MDB_DUMP_SQUISH, &dflags,
+ 't', MDB_OPT_SETBITS, MDB_DUMP_TRIM, &dflags,
+ 'u', MDB_OPT_CLRBITS, MDB_DUMP_ALIGN, &dflags,
+ 'v', MDB_OPT_SETBITS, MDB_DUMP_PEDANT, &dflags,
+ 'w', MDB_OPT_UINTPTR, &width, NULL) != argc)
+ return (DCMD_USAGE);
+
+ if ((phys && file) ||
+ (width == 0) || (width > 0x10) ||
+ (group == 0) || (group > 0x100))
+ return (DCMD_USAGE);
+
+ /*
+ * If neither -f nor -p were specified and the state is IDLE (i.e. no
+ * address space), turn on -p. This is so we can read large files.
+ */
+ if (phys == FALSE && file == FALSE && mdb_tgt_status(mdb.m_target,
+ &st) == 0 && st.st_state == MDB_TGT_IDLE)
+ phys = TRUE;
+
+ dflags |= MDB_DUMP_GROUP(group) | MDB_DUMP_WIDTH(width);
+ if (phys)
+ error = mdb_dump64(mdb_get_dot(), mdb.m_dcount, dflags,
+ mdb_partial_pread, NULL);
+ else if (file)
+ error = mdb_dumpptr(addr, mdb.m_dcount, dflags,
+ mdb_partial_xread, (void *)mdb_tgt_fread);
+ else
+ error = mdb_dumpptr(addr, mdb.m_dcount, dflags,
+ mdb_partial_xread, (void *)mdb_tgt_vread);
+
+ return (((flags & DCMD_LOOP) || (error == -1)) ? DCMD_ABORT : DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_echo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (flags & DCMD_ADDRSPEC)
+ return (DCMD_USAGE);
+
+ for (; argc-- != 0; argv++) {
+ if (argv->a_type == MDB_TYPE_STRING)
+ mdb_printf("%s ", argv->a_un.a_str);
+ else
+ mdb_printf("%llr ", argv->a_un.a_val);
+ }
+
+ mdb_printf("\n");
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_head(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint64_t cnt = 10;
+ const char *c;
+ mdb_pipe_t p;
+
+ if (!flags & DCMD_PIPE)
+ return (DCMD_USAGE);
+
+ if (argc == 1 || argc == 2) {
+ const char *num;
+
+ if (argc == 1) {
+ if (argv[0].a_type != MDB_TYPE_STRING ||
+ *argv[0].a_un.a_str != '-')
+ return (DCMD_USAGE);
+
+ num = argv[0].a_un.a_str + 1;
+
+ } else {
+ if (argv[0].a_type != MDB_TYPE_STRING ||
+ strcmp(argv[0].a_un.a_str, "-n") != 0)
+ return (DCMD_USAGE);
+
+ num = argv[1].a_un.a_str;
+ }
+
+ for (cnt = 0, c = num; *c != '\0' && isdigit(*c); c++)
+ cnt = cnt * 10 + (*c - '0');
+
+ if (*c != '\0')
+ return (DCMD_USAGE);
+
+ } else if (argc != 0) {
+ return (DCMD_USAGE);
+ }
+
+ mdb_get_pipe(&p);
+
+ if (p.pipe_data == NULL)
+ return (DCMD_OK);
+ p.pipe_len = MIN(p.pipe_len, cnt);
+
+ if (flags & DCMD_PIPE_OUT) {
+ mdb_set_pipe(&p);
+ } else {
+ while (p.pipe_len-- > 0)
+ mdb_printf("%lx\n", *p.pipe_data++);
+ }
+
+ return (DCMD_OK);
+}
+
+static void
+head_help(void)
+{
+ mdb_printf(
+ "-n num\n or\n"
+ "-num pass only the first `num' elements in the pipe.\n"
+ "\n%<b>Note:%</b> `num' is a decimal number.\n");
+}
+
+static int
+cmd_typeset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int add_tag = 0, del_tag = 0;
+ const char *p;
+ mdb_var_t *v;
+
+ if (argc == 0)
+ return (cmd_vars(addr, flags, argc, argv));
+
+ if (argv->a_type == MDB_TYPE_STRING && (argv->a_un.a_str[0] == '-' ||
+ argv->a_un.a_str[0] == '+')) {
+ if (argv->a_un.a_str[1] != 't')
+ return (DCMD_USAGE);
+ if (argv->a_un.a_str[0] == '-')
+ add_tag++;
+ else
+ del_tag++;
+ argc--;
+ argv++;
+ }
+
+ if (!(flags & DCMD_ADDRSPEC))
+ addr = 0; /* set variables to zero unless explicit addr given */
+
+ for (; argc-- != 0; argv++) {
+ if (argv->a_type != MDB_TYPE_STRING)
+ continue;
+
+ if (argv->a_un.a_str[0] == '-' || argv->a_un.a_str[0] == '+') {
+ mdb_warn("ignored bad option -- %s\n",
+ argv->a_un.a_str);
+ continue;
+ }
+
+ if ((p = strbadid(argv->a_un.a_str)) != NULL) {
+ mdb_warn("'%c' may not be used in a variable "
+ "name\n", *p);
+ return (DCMD_ERR);
+ }
+
+ if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL) {
+ v = mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str,
+ NULL, addr, 0);
+ } else if (flags & DCMD_ADDRSPEC)
+ mdb_nv_set_value(v, addr);
+
+ if (v != NULL) {
+ if (add_tag)
+ v->v_flags |= MDB_NV_TAGGED;
+ if (del_tag)
+ v->v_flags &= ~MDB_NV_TAGGED;
+ }
+ }
+
+ return (DCMD_OK);
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_context(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (argc != 0 || !(flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ if (mdb_tgt_setcontext(mdb.m_target, (void *)addr) == 0)
+ return (DCMD_OK);
+
+ return (DCMD_ERR);
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_prompt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char *p = "";
+
+ if (argc != 0) {
+ if (argc > 1 || argv->a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+ p = argv->a_un.a_str;
+ }
+
+ (void) mdb_set_prompt(p);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_term(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_printf("%s\n", mdb.m_termtype);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ physaddr_t pa;
+ mdb_tgt_as_t as = MDB_TGT_AS_VIRT;
+
+ if (mdb_getopts(argc, argv, 'a', MDB_OPT_UINTPTR, (uintptr_t *)&as,
+ NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (mdb_tgt_vtop(mdb.m_target, as, addr, &pa) == -1) {
+ mdb_warn("failed to get physical mapping");
+ return (DCMD_ERR);
+ }
+
+ if (flags & DCMD_PIPE_OUT)
+ mdb_printf("%llr\n", pa);
+ else
+ mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
+ return (DCMD_OK);
+}
+
+#define EVENTS_OPT_A 0x1 /* ::events -a (show all events) */
+#define EVENTS_OPT_V 0x2 /* ::events -v (verbose display) */
+
+static const char *
+event_action(const mdb_tgt_spec_desc_t *sp)
+{
+ if (!(sp->spec_flags & MDB_TGT_SPEC_HIDDEN) && sp->spec_data != NULL)
+ return (sp->spec_data);
+
+ return ("-");
+}
+
+static void
+print_evsep(void)
+{
+ static const char dash20[] = "--------------------";
+ mdb_printf("----- - -- -- -- %s%s --%s\n", dash20, dash20, dash20);
+}
+
+/*ARGSUSED*/
+static int
+print_event(mdb_tgt_t *t, void *private, int vid, void *data)
+{
+ uint_t opts = (uint_t)(uintptr_t)private;
+ mdb_tgt_spec_desc_t sp;
+ char s1[41], s2[22];
+ const char *s2str;
+ int visible;
+
+ (void) mdb_tgt_vespec_info(t, vid, &sp, s1, sizeof (s1));
+ visible = !(sp.spec_flags & (MDB_TGT_SPEC_HIDDEN|MDB_TGT_SPEC_DELETED));
+
+ if ((opts & EVENTS_OPT_A) || visible) {
+ int encoding = (!(sp.spec_flags & MDB_TGT_SPEC_DISABLED)) |
+ (!(sp.spec_flags & MDB_TGT_SPEC_MATCHED) << 1);
+
+ char ldelim = "<<(["[encoding];
+ char rdelim = ">>)]"[encoding];
+
+ char state = "0-+*!"[sp.spec_state];
+
+ char tflag = "T "[!(sp.spec_flags & MDB_TGT_SPEC_STICKY)];
+ char aflag = "d "[!(sp.spec_flags & MDB_TGT_SPEC_AUTODIS)];
+
+ if (sp.spec_flags & MDB_TGT_SPEC_TEMPORARY)
+ tflag = 't'; /* TEMP takes precedence over STICKY */
+ if (sp.spec_flags & MDB_TGT_SPEC_AUTODEL)
+ aflag = 'D'; /* AUTODEL takes precedence over AUTODIS */
+ if (sp.spec_flags & MDB_TGT_SPEC_AUTOSTOP)
+ aflag = 's'; /* AUTOSTOP takes precedence over both */
+
+ if (opts & EVENTS_OPT_V) {
+ if (sp.spec_state == MDB_TGT_SPEC_IDLE ||
+ sp.spec_state == MDB_TGT_SPEC_ERROR)
+ s2str = mdb_strerror(sp.spec_errno);
+ else
+ s2str = "-";
+ } else
+ s2str = event_action(&sp);
+
+ if (mdb_snprintf(s2, sizeof (s2), "%s", s2str) >= sizeof (s2))
+ (void) strabbr(s2, sizeof (s2));
+
+ if (vid > -10 && vid < 10)
+ mdb_printf("%c%2d %c", ldelim, vid, rdelim);
+ else
+ mdb_printf("%c%3d%c", ldelim, vid, rdelim);
+
+ mdb_printf(" %c %c%c %2u %2u %-40s %-21s\n",
+ state, tflag, aflag, sp.spec_hits, sp.spec_limit, s1, s2);
+
+ if (opts & EVENTS_OPT_V) {
+ mdb_printf("%-17s%s\n", "", event_action(&sp));
+ print_evsep();
+ }
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_events(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t opts = 0;
+
+ if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
+ 'a', MDB_OPT_SETBITS, EVENTS_OPT_A, &opts,
+ 'v', MDB_OPT_SETBITS, EVENTS_OPT_V, &opts, NULL) != argc)
+ return (DCMD_USAGE);
+
+
+ if (opts & EVENTS_OPT_V) {
+ mdb_printf(" ID S TA HT LM %-40s %-21s\n%-17s%s\n",
+ "Description", "Status", "", "Action");
+ } else {
+ mdb_printf(" ID S TA HT LM %-40s %-21s\n",
+ "Description", "Action");
+ }
+
+ print_evsep();
+ return (mdb_tgt_vespec_iter(mdb.m_target, print_event,
+ (void *)(uintptr_t)opts));
+}
+
+static int
+tgt_status(const mdb_tgt_status_t *tsp)
+{
+ const char *format;
+ char buf[BUFSIZ];
+
+ if (tsp->st_flags & MDB_TGT_BUSY)
+ return (DCMD_OK);
+
+ if (tsp->st_pc != 0) {
+ if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target, MDB_TGT_AS_VIRT,
+ buf, sizeof (buf), tsp->st_pc) != tsp->st_pc)
+ format = "target stopped at:\n%-#16a%8T%s\n";
+ else
+ format = "target stopped at %a:\n";
+ mdb_warn(format, tsp->st_pc, buf);
+ }
+
+ switch (tsp->st_state) {
+ case MDB_TGT_IDLE:
+ mdb_warn("target is idle\n");
+ break;
+ case MDB_TGT_RUNNING:
+ if (tsp->st_flags & MDB_TGT_DSTOP)
+ mdb_warn("target is running, stop directive pending\n");
+ else
+ mdb_warn("target is running\n");
+ break;
+ case MDB_TGT_STOPPED:
+ if (tsp->st_pc == 0)
+ mdb_warn("target is stopped\n");
+ break;
+ case MDB_TGT_UNDEAD:
+ mdb_warn("target has terminated\n");
+ break;
+ case MDB_TGT_DEAD:
+ mdb_warn("target is a core dump\n");
+ break;
+ case MDB_TGT_LOST:
+ mdb_warn("target is no longer under debugger control\n");
+ break;
+ }
+
+ mdb_set_dot(tsp->st_pc);
+ return (DCMD_OK);
+}
+
+/*
+ * mdb continue/step commands take an optional signal argument, but the
+ * corresponding kmdb versions don't.
+ */
+#ifdef _KMDB
+#define CONT_MAXARGS 0 /* no optional SIG argument */
+#else
+#define CONT_MAXARGS 1
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_cont_common(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
+ int (*t_cont)(mdb_tgt_t *, mdb_tgt_status_t *), const char *name)
+{
+ mdb_tgt_t *t = mdb.m_target;
+ mdb_tgt_status_t st;
+ int sig = 0;
+
+ if ((flags & DCMD_ADDRSPEC) || argc > CONT_MAXARGS)
+ return (DCMD_USAGE);
+
+ if (argc > 0) {
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
+ mdb_warn("invalid signal name -- %s\n",
+ argv->a_un.a_str);
+ return (DCMD_USAGE);
+ }
+ } else
+ sig = (int)(intmax_t)argv->a_un.a_val;
+ }
+
+ (void) mdb_tgt_status(t, &st);
+
+ if (st.st_state == MDB_TGT_IDLE && mdb_tgt_run(t, 0, NULL) == -1) {
+ if (errno != EMDB_TGT)
+ mdb_warn("failed to create new target");
+ return (DCMD_ERR);
+ }
+
+ if (sig != 0 && mdb_tgt_signal(t, sig) == -1) {
+ mdb_warn("failed to post signal %d", sig);
+ return (DCMD_ERR);
+ }
+
+ if (st.st_state == MDB_TGT_IDLE && t_cont == &mdb_tgt_step) {
+ (void) mdb_tgt_status(t, &st);
+ return (tgt_status(&st));
+ }
+
+ if (t_cont(t, &st) == -1) {
+ if (errno != EMDB_TGT)
+ mdb_warn("failed to %s target", name);
+ return (DCMD_ERR);
+ }
+
+ return (tgt_status(&st));
+}
+
+static int
+cmd_step(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int (*func)(mdb_tgt_t *, mdb_tgt_status_t *) = &mdb_tgt_step;
+ const char *name = "single-step";
+
+ if (argc > 0 && argv->a_type == MDB_TYPE_STRING) {
+ if (strcmp(argv->a_un.a_str, "out") == 0) {
+ func = &mdb_tgt_step_out;
+ name = "step (out)";
+ argv++;
+ argc--;
+ } else if (strcmp(argv->a_un.a_str, "branch") == 0) {
+ func = &mdb_tgt_step_branch;
+ name = "step (branch)";
+ argv++;
+ argc--;
+ } else if (strcmp(argv->a_un.a_str, "over") == 0) {
+ func = &mdb_tgt_next;
+ name = "step (over)";
+ argv++;
+ argc--;
+ }
+ }
+
+ return (cmd_cont_common(addr, flags, argc, argv, func, name));
+}
+
+static int
+cmd_step_out(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (cmd_cont_common(addr, flags, argc, argv,
+ &mdb_tgt_step_out, "step (out)"));
+}
+
+static int
+cmd_next(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (cmd_cont_common(addr, flags, argc, argv,
+ &mdb_tgt_next, "step (over)"));
+}
+
+static int
+cmd_cont(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (cmd_cont_common(addr, flags, argc, argv,
+ &mdb_tgt_continue, "continue"));
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_run(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if (flags & DCMD_ADDRSPEC)
+ return (DCMD_USAGE);
+
+ if (mdb_tgt_run(mdb.m_target, argc, argv) == -1) {
+ if (errno != EMDB_TGT)
+ mdb_warn("failed to create new target");
+ return (DCMD_ERR);
+ }
+ return (cmd_cont(NULL, 0, 0, NULL));
+}
+#endif
+
+/*
+ * To simplify the implementation of :d, :z, and ::delete, we use the sp
+ * parameter to store the criteria for what to delete. If spec_base is set,
+ * we delete vespecs with a matching address. If spec_id is set, we delete
+ * vespecs with a matching id. Otherwise, we delete all vespecs. We bump
+ * sp->spec_size so the caller can tell how many vespecs were deleted.
+ */
+static int
+ve_delete(mdb_tgt_t *t, mdb_tgt_spec_desc_t *sp, int vid, void *data)
+{
+ mdb_tgt_spec_desc_t spec;
+ int status = -1;
+
+ if (vid < 0)
+ return (0); /* skip over target implementation events */
+
+ if (sp->spec_base != NULL) {
+ (void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
+ if (sp->spec_base - spec.spec_base < spec.spec_size)
+ status = mdb_tgt_vespec_delete(t, vid);
+ } else if (sp->spec_id == 0) {
+ (void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
+ if (!(spec.spec_flags & MDB_TGT_SPEC_STICKY))
+ status = mdb_tgt_vespec_delete(t, vid);
+ } else if (sp->spec_id == vid)
+ status = mdb_tgt_vespec_delete(t, vid);
+
+ if (status == 0) {
+ if (data != NULL)
+ strfree(data);
+ sp->spec_size++;
+ }
+
+ return (0);
+}
+
+static int
+ve_delete_spec(mdb_tgt_spec_desc_t *sp)
+{
+ (void) mdb_tgt_vespec_iter(mdb.m_target,
+ (mdb_tgt_vespec_f *)ve_delete, sp);
+
+ if (sp->spec_size == 0) {
+ if (sp->spec_id != 0 || sp->spec_base != NULL)
+ mdb_warn("no traced events matched description\n");
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_zapall(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_spec_desc_t spec;
+
+ if ((flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ bzero(&spec, sizeof (spec));
+ return (ve_delete_spec(&spec));
+}
+
+static int
+cmd_delete(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_spec_desc_t spec;
+
+ if (((flags & DCMD_ADDRSPEC) && argc > 0) || argc > 1)
+ return (DCMD_USAGE);
+
+ bzero(&spec, sizeof (spec));
+
+ if (flags & DCMD_ADDRSPEC)
+ spec.spec_base = addr;
+ else if (argc == 0)
+ spec.spec_base = mdb_get_dot();
+ else if (argv->a_type == MDB_TYPE_STRING &&
+ strcmp(argv->a_un.a_str, "all") != 0)
+ spec.spec_id = (int)(intmax_t)strtonum(argv->a_un.a_str, 10);
+ else if (argv->a_type == MDB_TYPE_IMMEDIATE)
+ spec.spec_id = (int)(intmax_t)argv->a_un.a_val;
+
+ return (ve_delete_spec(&spec));
+}
+
+static void
+srcexec_file_help(void)
+{
+ mdb_printf(
+"The library of macros delivered with previous versions of Solaris have been\n"
+"superseded by the dcmds and walkers provided by MDB. See ::help for\n"
+"commands that can be used to list the available dcmds and walkers.\n"
+"\n"
+"Aliases have been created for several of the more popular macros. To see\n"
+"the list of aliased macros, as well as their native MDB equivalents,\n"
+"type $M.\n");
+
+#ifdef _KMDB
+ mdb_printf(
+"When invoked, the $< and $<< dcmds will consult the macro alias list. If an\n"
+"alias cannot be found, an attempt will be made to locate a data type whose\n"
+"name corresponds to the requested macro. If such a type can be found, it\n"
+"will be displayed using the ::print dcmd.\n");
+#else
+ mdb_printf(
+"When invoked, the $< and $<< dcmds will first attempt to locate a macro with\n"
+"the indicated name. If no macro can be found, and if no alias exists for\n"
+"this macro, an attempt will be made to locate a data type whose name\n"
+"corresponds to the requested macro. If such a type can be found, it will be\n"
+"displayed using the ::print dcmd.\n");
+#endif
+}
+
+static void
+events_help(void)
+{
+ mdb_printf("Options:\n"
+ "-a show all events, including internal debugger events\n"
+ "-v show verbose display, including inactivity reason\n"
+ "\nOutput Columns:\n"
+ "ID decimal event specifier id number:\n"
+ " [ ] event tracing is enabled\n"
+ " ( ) event tracing is disabled\n"
+ " < > target is currently stopped on this type of event\n\n"
+ "S event specifier state:\n"
+ " - event specifier is idle (not applicable yet)\n"
+ " + event specifier is active\n"
+ " * event specifier is armed (target program running)\n"
+ " ! error occurred while attempting to arm event\n\n"
+ "TA event specifier flags:\n"
+ " t event specifier is temporary (delete at next stop)\n"
+ " T event specifier is sticky (::delete all has no effect)\n"
+ " d event specifier will be disabled when HT = LM\n"
+ " D event specifier will be deleted when HT = LM\n"
+ " s target will automatically stop when HT = LM\n\n"
+ "HT hit count (number of times event has occurred)\n"
+ "LM hit limit (limit for autostop, disable, delete)\n");
+}
+
+static void
+dump_help(void)
+{
+ mdb_printf(
+ "-e adjust for endianness\n"
+ " (assumes 4-byte words; use -g to change word size)\n"
+#ifdef _KMDB
+ "-f no effect\n"
+#else
+ "-f dump from object file\n"
+#endif
+ "-g n display bytes in groups of n\n"
+ " (default is 4; n must be a power of 2, divide line width)\n"
+ "-p dump from physical memory\n"
+ "-q don't print ASCII\n"
+ "-r use relative numbering (automatically sets -u)\n"
+ "-s elide repeated lines\n"
+ "-t only read from and display contents of specified addresses\n"
+ " (default is to read and print entire lines)\n"
+ "-u un-align output\n"
+ " (default is to align output at paragraph boundary)\n"
+ "-w n display n 16-byte paragraphs per line\n"
+ " (default is 1, maximum is 16)\n");
+}
+
+/*
+ * Table of built-in dcmds associated with the root 'mdb' module. Future
+ * expansion of this program should be done here, or through the external
+ * loadable module interface.
+ */
+const mdb_dcmd_t mdb_dcmd_builtins[] = {
+
+ /*
+ * dcmds common to both mdb and kmdb
+ */
+ { ">", "variable-name", "assign variable", cmd_assign_variable },
+ { "/", "fmt-list", "format data from virtual as", cmd_print_core },
+ { "\\", "fmt-list", "format data from physical as", cmd_print_phys },
+ { "@", "fmt-list", "format data from physical as", cmd_print_phys },
+ { "=", "fmt-list", "format immediate value", cmd_print_value },
+ { "$<", "macro-name", "replace input with macro",
+ cmd_exec_file, srcexec_file_help },
+ { "$<<", "macro-name", "source macro",
+ cmd_src_file, srcexec_file_help},
+ { "$%", NULL, NULL, cmd_quit },
+ { "$?", NULL, "print status and registers", cmd_notsup },
+ { "$a", NULL, NULL, cmd_algol },
+ { "$b", "[-av]", "list traced software events",
+ cmd_events, events_help },
+ { "$c", "?[cnt]", "print stack backtrace", cmd_notsup },
+ { "$C", "?[cnt]", "print stack backtrace", cmd_notsup },
+ { "$d", NULL, "get/set default output radix", cmd_radix },
+ { "$D", "?[mode,...]", NULL, cmd_dbmode },
+ { "$e", NULL, "print listing of global symbols", cmd_globals },
+ { "$f", NULL, "print listing of source files", cmd_files },
+ { "$m", "?[name]", "print address space mappings", cmd_mappings },
+ { "$M", NULL, "list macro aliases", cmd_macalias_list },
+ { "$P", "[prompt]", "set debugger prompt string", cmd_prompt },
+ { "$q", NULL, "quit debugger", cmd_quit },
+ { "$Q", NULL, "quit debugger", cmd_quit },
+ { "$r", NULL, "print general-purpose registers", cmd_notsup },
+ { "$s", NULL, "get/set symbol matching distance", cmd_symdist },
+ { "$v", NULL, "print non-zero variables", cmd_nzvars },
+ { "$V", "[mode]", "get/set disassembly mode", cmd_dismode },
+ { "$w", NULL, "get/set output page width", cmd_pgwidth },
+ { "$W", NULL, "re-open target in write mode", cmd_reopen },
+ { ":a", ":[cmd...]", "set read access watchpoint", cmd_oldwpr },
+ { ":b", ":[cmd...]", "breakpoint at the specified address", cmd_oldbp },
+ { ":d", "?[id|all]", "delete traced software events", cmd_delete },
+ { ":p", ":[cmd...]", "set execute access watchpoint", cmd_oldwpx },
+ { ":S", NULL, NULL, cmd_step },
+ { ":w", ":[cmd...]", "set write access watchpoint", cmd_oldwpw },
+ { ":z", NULL, "delete all traced software events", cmd_zapall },
+ { "array", ":[type count] [variable]", "print each array element's "
+ "address", cmd_array },
+ { "bp", "?[+/-dDestT] [-c cmd] [-n count] sym ...", "breakpoint at the "
+ "specified addresses or symbols", cmd_bp, bp_help },
+ { "dcmds", NULL, "list available debugger commands", cmd_dcmds },
+ { "delete", "?[id|all]", "delete traced software events", cmd_delete },
+ { "dis", "?[-afw] [-n cnt] [addr]", "disassemble near addr", cmd_dis },
+ { "disasms", NULL, "list available disassemblers", cmd_disasms },
+ { "dismode", "[mode]", "get/set disassembly mode", cmd_dismode },
+ { "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods },
+ { "dump", "?[-eqrstu] [-f|-p] [-g bytes] [-w paragraphs]",
+ "dump memory from specified address", cmd_dump, dump_help },
+ { "echo", "args ...", "echo arguments", cmd_echo },
+ { "enum", "?[-x] enum [name]", "print an enumeration", cmd_enum },
+ { "eval", "command", "evaluate the specified command", cmd_eval },
+ { "events", "[-av]", "list traced software events",
+ cmd_events, events_help },
+ { "evset", "?[+/-dDestT] [-c cmd] [-n count] id ...",
+ "set software event specifier attributes", cmd_evset, evset_help },
+ { "files", "[object]", "print listing of source files", cmd_files },
+#ifdef __sparc
+ { "findsym", "?[-g] [symbol|addr ...]", "search for symbol references "
+ "in all known functions", cmd_findsym, NULL },
+#endif
+ { "formats", NULL, "list format specifiers", cmd_formats },
+ { "grep", "?expr", "print dot if expression is true", cmd_grep },
+ { "head", "-num|-n num", "limit number of elements in pipe", cmd_head,
+ head_help },
+ { "help", "[cmd]", "list commands/command help", cmd_help },
+ { "list", "?type member [variable]",
+ "walk list using member as link pointer", cmd_list },
+ { "map", "?expr", "print dot after evaluating expression", cmd_map },
+ { "mappings", "?[name]", "print address space mappings", cmd_mappings },
+ { "nm", "?[-DPdghnopuvx] [-f format] [-t types] [object]",
+ "print symbols", cmd_nm, nm_help },
+ { "nmadd", ":[-fo] [-e end] [-s size] name",
+ "add name to private symbol table", cmd_nmadd, nmadd_help },
+ { "nmdel", "name", "remove name from private symbol table", cmd_nmdel },
+ { "obey", NULL, NULL, cmd_obey },
+ { "objects", "[-v]", "print load objects information", cmd_objects },
+ { "offsetof", "type member", "print the offset of a given struct "
+ "or union member", cmd_offsetof },
+ { "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
+ "print the contents of a data structure", cmd_print, print_help },
+ { "regs", NULL, "print general purpose registers", cmd_notsup },
+ { "set", "[-wF] [+/-o opt] [-s dist] [-I path] [-L path] [-P prompt]",
+ "get/set debugger properties", cmd_set },
+ { "showrev", "[-pv]", "print version information", cmd_showrev },
+ { "sizeof", "type", "print the size of a type", cmd_sizeof },
+ { "stack", "?[cnt]", "print stack backtrace", cmd_notsup },
+ { "stackregs", "?", "print stack backtrace and registers",
+ cmd_notsup },
+ { "status", NULL, "print summary of current target", cmd_notsup },
+ { "term", NULL, "display current terminal type", cmd_term },
+ { "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset },
+ { "unset", "[name ...]", "unset variables", cmd_unset },
+ { "vars", "[-npt]", "print listing of variables", cmd_vars },
+ { "version", NULL, "print debugger version string", cmd_version },
+ { "vtop", ":[-a as]", "print physical mapping of virtual address",
+ cmd_vtop },
+ { "walk", "?name [variable]", "walk data structure", cmd_walk },
+ { "walkers", NULL, "list available walkers", cmd_walkers },
+ { "whence", "[-v] name ...", "show source of walk or dcmd", cmd_which },
+ { "which", "[-v] name ...", "show source of walk or dcmd", cmd_which },
+ { "xdata", NULL, "print list of external data buffers", cmd_xdata },
+
+#ifdef _KMDB
+ /*
+ * dcmds specific to kmdb, or which have kmdb-specific arguments
+ */
+ { "?", "fmt-list", "format data from virtual as", cmd_print_core },
+ { ":c", NULL, "continue target execution", cmd_cont },
+ { ":e", NULL, "step target over next instruction", cmd_next },
+ { ":s", NULL, "single-step target to next instruction", cmd_step },
+ { ":u", NULL, "step target out of current function", cmd_step_out },
+ { "cont", NULL, "continue target execution", cmd_cont },
+ { "load", "[-sd] module", "load debugger module", cmd_load, load_help },
+ { "next", NULL, "step target over next instruction", cmd_next },
+ { "quit", "[-u]", "quit debugger", cmd_quit, quit_help },
+ { "step", "[ over | out ]",
+ "single-step target to next instruction", cmd_step },
+ { "unload", "[-d] module", "unload debugger module", cmd_unload,
+ unload_help },
+ { "wp", ":[+/-dDelstT] [-rwx] [-pi] [-c cmd] [-n count] [-L size]",
+ "set a watchpoint at the specified address", cmd_wp, wp_help },
+
+#else
+ /*
+ * dcmds specific to mdb, or which have mdb-specific arguments
+ */
+ { "?", "fmt-list", "format data from object file", cmd_print_object },
+ { "$>", "[file]", "log session to a file", cmd_old_log },
+ { "$g", "?", "get/set C++ demangling options", cmd_demflags },
+ { "$G", NULL, "enable/disable C++ demangling support", cmd_demangle },
+ { "$i", NULL, "print signals that are ignored", cmd_notsup },
+ { "$l", NULL, "print the representative thread's lwp id", cmd_notsup },
+ { "$p", ":", "change debugger target context", cmd_context },
+ { "$x", NULL, "print floating point registers", cmd_notsup },
+ { "$X", NULL, "print floating point registers", cmd_notsup },
+ { "$y", NULL, "print floating point registers", cmd_notsup },
+ { "$Y", NULL, "print floating point registers", cmd_notsup },
+ { ":A", "?[core|pid]", "attach to process or core file", cmd_notsup },
+ { ":c", "[SIG]", "continue target execution", cmd_cont },
+ { ":e", "[SIG]", "step target over next instruction", cmd_next },
+ { ":i", ":", "ignore signal (delete all matching events)", cmd_notsup },
+ { ":k", NULL, "forcibly kill and release target", cmd_notsup },
+ { ":t", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on delivery "
+ "of the specified signals", cmd_sigbp, sigbp_help },
+ { ":r", "[ args ... ]", "run a new target process", cmd_run },
+ { ":R", NULL, "release the previously attached process", cmd_notsup },
+ { ":s", "[SIG]", "single-step target to next instruction", cmd_step },
+ { ":u", "[SIG]", "step target out of current function", cmd_step_out },
+ { "attach", "?[core|pid]",
+ "attach to process or core file", cmd_notsup },
+ { "cat", "[file ...]", "concatenate and display files", cmd_cat },
+ { "cont", "[SIG]", "continue target execution", cmd_cont },
+ { "context", ":", "change debugger target context", cmd_context },
+ { "dem", "name ...", "demangle C++ symbol names", cmd_demstr },
+ { "fltbp", "?[+/-dDestT] [-c cmd] [-n count] fault ...",
+ "stop on machine fault", cmd_fltbp, fltbp_help },
+ { "fpregs", NULL, "print floating point registers", cmd_notsup },
+ { "kill", NULL, "forcibly kill and release target", cmd_notsup },
+ { "load", "[-s] module", "load debugger module", cmd_load, load_help },
+ { "log", "[-d | [-e] file]", "log session to a file", cmd_log },
+ { "next", "[SIG]", "step target over next instruction", cmd_next },
+ { "quit", NULL, "quit debugger", cmd_quit },
+ { "release", NULL,
+ "release the previously attached process", cmd_notsup },
+ { "run", "[ args ... ]", "run a new target process", cmd_run },
+ { "sigbp", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on "
+ "delivery of the specified signals", cmd_sigbp, sigbp_help },
+ { "step", "[ over | out ] [SIG]",
+ "single-step target to next instruction", cmd_step },
+ { "sysbp", "?[+/-dDestT] [-io] [-c cmd] [-n count] syscall ...",
+ "stop on entry or exit from system call", cmd_sysbp, sysbp_help },
+ { "unload", "module", "unload debugger module", cmd_unload },
+ { "wp", ":[+/-dDelstT] [-rwx] [-c cmd] [-n count] [-L size]",
+ "set a watchpoint at the specified address", cmd_wp, wp_help },
+#endif
+
+ { NULL }
+};
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_conf.c b/usr/src/cmd/mdb/common/mdb/mdb_conf.c
new file mode 100644
index 0000000..4132090
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_conf.c
@@ -0,0 +1,77 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/param.h>
+#include <sys/systeminfo.h>
+#include <sys/isa_defs.h>
+#include <sys/utsname.h>
+#include <strings.h>
+
+static const char _mdb_version[] = "mdb 1.1";
+
+const char *
+mdb_conf_version(void)
+{
+ return (_mdb_version);
+}
+
+const char *
+mdb_conf_platform(void)
+{
+ static char platbuf[MAXNAMELEN];
+
+ if (sysinfo(SI_PLATFORM, platbuf, MAXNAMELEN) != -1)
+ return (platbuf);
+
+ return ("unknown");
+}
+
+const char *
+mdb_conf_isa(void)
+{
+#if defined(__sparc)
+#if defined(__sparcv9)
+ return ("sparcv9");
+#else /* __sparcv9 */
+ return ("sparc");
+#endif /* __sparcv9 */
+#elif defined(__amd64)
+ return ("amd64");
+#elif defined(__i386)
+ return ("i386");
+#else
+#error "unknown ISA"
+#endif
+}
+
+void
+mdb_conf_uname(struct utsname *utsp)
+{
+ bzero(utsp, sizeof (struct utsname));
+ (void) uname(utsp);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_conf.h b/usr/src/cmd/mdb/common/mdb/mdb_conf.h
new file mode 100644
index 0000000..6d4d664
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_conf.h
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_CONF_H
+#define _MDB_CONF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/utsname.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+extern const char *mdb_conf_version(void);
+extern const char *mdb_conf_platform(void);
+extern const char *mdb_conf_isa(void);
+extern void mdb_conf_uname(struct utsname *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CONF_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_context.c b/usr/src/cmd/mdb/common/mdb/mdb_context.c
new file mode 100644
index 0000000..ff4483b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_context.c
@@ -0,0 +1,133 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Debugger co-routine context support: In order to implement the context-
+ * switching necessary for MDB pipes, we need the ability to establish a
+ * co-routine context that has a separate stack. We use this stack to execute
+ * the MDB parser, and then switch back and forth between this code and the
+ * dcmd which is producing output to be consumed. We implement a context by
+ * mapping a few pages of anonymous memory, and then using setcontext(2) to
+ * switch to this stack and begin execution of a new function.
+ */
+
+#include <mdb/mdb_context_impl.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ucontext.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static void
+context_init(mdb_context_t *volatile c)
+{
+ c->ctx_status = c->ctx_func();
+ ASSERT(c->ctx_resumes > 0);
+ longjmp(c->ctx_pcb, 1);
+}
+
+mdb_context_t *
+mdb_context_create(int (*func)(void))
+{
+ mdb_context_t *c = mdb_zalloc(sizeof (mdb_context_t), UM_NOSLEEP);
+ size_t pagesize = sysconf(_SC_PAGESIZE);
+ int prot = sysconf(_SC_STACK_PROT);
+ static int zfd = -1;
+
+ if (c == NULL)
+ return (NULL);
+
+ if (prot == -1)
+ prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+
+ c->ctx_func = func;
+ c->ctx_stacksize = pagesize * 4;
+ c->ctx_stack = mmap(NULL, c->ctx_stacksize, prot,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ /*
+ * If the mmap failed with EBADFD, this kernel doesn't have MAP_ANON
+ * support; fall back to opening /dev/zero, caching the fd, and using
+ * that to mmap chunks of anonymous memory.
+ */
+ if (c->ctx_stack == MAP_FAILED && errno == EBADF) {
+ if (zfd == -1 && (zfd = open("/dev/zero", O_RDWR)) >= 0)
+ (void) fcntl(zfd, F_SETFD, FD_CLOEXEC);
+
+ if (zfd >= 0) {
+ c->ctx_stack = mmap(NULL, c->ctx_stacksize, prot,
+ MAP_PRIVATE, zfd, 0);
+ }
+ }
+
+ c->ctx_uc.uc_flags = UC_ALL;
+ if (c->ctx_stack == MAP_FAILED || getcontext(&c->ctx_uc) != 0) {
+ mdb_free(c, sizeof (mdb_context_t));
+ return (NULL);
+ }
+
+ c->ctx_uc.uc_stack.ss_sp = c->ctx_stack;
+ c->ctx_uc.uc_stack.ss_size = c->ctx_stacksize;
+ c->ctx_uc.uc_stack.ss_flags = 0;
+ c->ctx_uc.uc_link = NULL;
+ makecontext(&c->ctx_uc, context_init, 1, c);
+
+ return (c);
+}
+
+void
+mdb_context_destroy(mdb_context_t *c)
+{
+ if (munmap(c->ctx_stack, c->ctx_stacksize) == -1)
+ fail("failed to unmap stack %p", c->ctx_stack);
+
+ mdb_free(c, sizeof (mdb_context_t));
+}
+
+void
+mdb_context_switch(mdb_context_t *c)
+{
+ if (setjmp(c->ctx_pcb) == 0 && setcontext(&c->ctx_uc) == -1)
+ fail("failed to change context to %p", (void *)c);
+ else
+ fail("unexpectedly returned from context %p", (void *)c);
+}
+
+jmp_buf *
+mdb_context_getpcb(mdb_context_t *c)
+{
+ c->ctx_resumes++;
+ return (&c->ctx_pcb);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_context.h b/usr/src/cmd/mdb/common/mdb/mdb_context.h
new file mode 100644
index 0000000..8022fbc
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_context.h
@@ -0,0 +1,58 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_CONTEXT_H
+#define _MDB_CONTEXT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <setjmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+/*
+ * We hide the details of the context from the rest of MDB using the opaque
+ * mdb_context tag. This will facilitate later porting activities.
+ */
+typedef struct mdb_context mdb_context_t;
+
+extern mdb_context_t *mdb_context_create(int (*)(void));
+extern void mdb_context_destroy(mdb_context_t *);
+extern void mdb_context_switch(mdb_context_t *);
+extern jmp_buf *mdb_context_getpcb(mdb_context_t *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CONTEXT_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_context_impl.h b/usr/src/cmd/mdb/common/mdb/mdb_context_impl.h
new file mode 100644
index 0000000..fdae589
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_context_impl.h
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_CONTEXT_IMPL_H
+#define _MDB_CONTEXT_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_context.h>
+
+#include <sys/types.h>
+
+#include <ucontext.h>
+#include <setjmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mdb_context {
+ int (*ctx_func)(void); /* pointer to start function */
+ int ctx_status; /* return status of ctx_func */
+ int ctx_resumes; /* count of context resume calls */
+ size_t ctx_stacksize; /* size of stack in bytes */
+ void *ctx_stack; /* stack base address */
+ ucontext_t ctx_uc; /* user context structure */
+ jmp_buf ctx_pcb; /* control block for resume */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CONTEXT_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_create.c b/usr/src/cmd/mdb/common/mdb/mdb_create.c
new file mode 100644
index 0000000..ea30457
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_create.c
@@ -0,0 +1,89 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_module.h>
+
+#include <sys/types.h>
+#include <limits.h>
+
+#include <dirent.h>
+
+void
+mdb_create_builtin_tgts(void)
+{
+ mdb_module_t *mp;
+
+ if ((mp = mdb_module_load_builtin("mdb_kvm")) != NULL)
+ mp->mod_tgt_ctor = mdb_kvm_tgt_create;
+
+ if ((mp = mdb_module_load_builtin("mdb_proc")) != NULL)
+ mp->mod_tgt_ctor = mdb_proc_tgt_create;
+
+ if ((mp = mdb_module_load_builtin("mdb_kproc")) != NULL)
+ mp->mod_tgt_ctor = mdb_kproc_tgt_create;
+
+ if ((mp = mdb_module_load_builtin("mdb_raw")) != NULL)
+ mp->mod_tgt_ctor = mdb_rawfile_tgt_create;
+}
+
+void
+mdb_create_loadable_disasms(void)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char buf[PATH_MAX], *p, *q;
+ size_t len;
+
+#ifdef _LP64
+ len = mdb_snprintf(buf, sizeof (buf), "%s/usr/lib/mdb/disasm/%s",
+ mdb.m_root, mdb_conf_isa());
+#else
+ len = mdb_snprintf(buf, sizeof (buf), "%s/usr/lib/mdb/disasm",
+ mdb.m_root);
+#endif
+ p = &buf[len];
+
+ if ((dir = opendir(buf)) == NULL)
+ return;
+
+ while ((dp = readdir(dir)) != NULL) {
+ if (dp->d_name[0] == '.')
+ continue; /* skip "." and ".." */
+ if ((q = strrchr(dp->d_name, '.')) == NULL ||
+ strcmp(q, ".so") != 0)
+ continue;
+
+ (void) mdb_snprintf(p, sizeof (buf) - len, "/%s", dp->d_name);
+
+ (void) mdb_module_load(buf, MDB_MOD_SILENT);
+ }
+
+ (void) closedir(dir);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
new file mode 100644
index 0000000..2de57f5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
@@ -0,0 +1,1166 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_ctf_impl.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb.h>
+#include <mdb/mdb_debug.h>
+
+#include <libctf.h>
+#include <string.h>
+
+typedef struct tnarg {
+ mdb_tgt_t *tn_tgt; /* target to use for lookup */
+ const char *tn_name; /* query string to lookup */
+ ctf_file_t *tn_fp; /* CTF container from match */
+ ctf_id_t tn_id; /* CTF type ID from match */
+} tnarg_t;
+
+typedef struct type_iter {
+ mdb_ctf_type_f *ti_cb;
+ void *ti_arg;
+ ctf_file_t *ti_fp;
+} type_iter_t;
+
+typedef struct member_iter {
+ mdb_ctf_member_f *mi_cb;
+ void *mi_arg;
+ ctf_file_t *mi_fp;
+} member_iter_t;
+
+typedef struct type_visit {
+ mdb_ctf_visit_f *tv_cb;
+ void *tv_arg;
+ ctf_file_t *tv_fp;
+} type_visit_t;
+
+typedef struct mbr_info {
+ const char *mbr_member;
+ ulong_t *mbr_offp;
+ mdb_ctf_id_t *mbr_typep;
+} mbr_info_t;
+
+static void
+set_ctf_id(mdb_ctf_id_t *p, ctf_file_t *fp, ctf_id_t id)
+{
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
+
+ mcip->mci_fp = fp;
+ mcip->mci_id = id;
+}
+
+/*
+ * Callback function for mdb_tgt_object_iter used from name_to_type, below,
+ * to search the CTF namespace for a given object file.
+ */
+/*ARGSUSED*/
+static int
+obj_lookup(void *data, const mdb_map_t *mp, const char *name)
+{
+ tnarg_t *tnp = data;
+ ctf_file_t *fp;
+ ctf_id_t id;
+
+ if ((fp = mdb_tgt_name_to_ctf(tnp->tn_tgt, name)) != NULL &&
+ (id = ctf_lookup_by_name(fp, tnp->tn_name)) != CTF_ERR) {
+ tnp->tn_fp = fp;
+ tnp->tn_id = id;
+
+ /*
+ * We may have found a forward declaration. If we did, we'll
+ * note the ID and file pointer, but we'll keep searching in
+ * an attempt to find the real thing. If we found something
+ * real (i.e. not a forward), we stop the iteration.
+ */
+ return (ctf_type_kind(fp, id) == CTF_K_FORWARD ? 0 : -1);
+ }
+
+ return (0);
+}
+
+/*
+ * Convert a string type name with an optional leading object specifier into
+ * the corresponding CTF file container and type ID. If an error occurs, we
+ * print an appropriate message and return NULL.
+ */
+static ctf_file_t *
+name_to_type(mdb_tgt_t *t, const char *cname, ctf_id_t *idp)
+{
+ const char *object = MDB_TGT_OBJ_EXEC;
+ ctf_file_t *fp = NULL;
+ ctf_id_t id;
+ tnarg_t arg;
+ char *p, *s;
+ char buf[MDB_SYM_NAMLEN];
+ char *name = &buf[0];
+
+ (void) mdb_snprintf(buf, sizeof (buf), "%s", cname);
+
+ if ((p = strrsplit(name, '`')) != NULL) {
+ /*
+ * We need to shuffle things around a little to support
+ * type names of the form "struct module`name".
+ */
+ if ((s = strsplit(name, ' ')) != NULL) {
+ bcopy(cname + (s - name), name, (p - s) - 1);
+ name[(p - s) - 1] = '\0';
+ bcopy(cname, name + (p - s), s - name);
+ p = name + (p - s);
+ }
+ if (*name != '\0')
+ object = name;
+ name = p;
+ }
+
+ /*
+ * Attempt to look up the name in the primary object file. If this
+ * fails and the name was unscoped, search all remaining object files.
+ */
+ if (((fp = mdb_tgt_name_to_ctf(t, object)) == NULL ||
+ (id = ctf_lookup_by_name(fp, name)) == CTF_ERR ||
+ ctf_type_kind(fp, id) == CTF_K_FORWARD) &&
+ object == MDB_TGT_OBJ_EXEC) {
+
+ arg.tn_tgt = t;
+ arg.tn_name = name;
+ arg.tn_fp = NULL;
+ arg.tn_id = CTF_ERR;
+
+ (void) mdb_tgt_object_iter(t, obj_lookup, &arg);
+
+ if (arg.tn_id != CTF_ERR) {
+ fp = arg.tn_fp;
+ id = arg.tn_id;
+ }
+ }
+
+ if (fp == NULL)
+ return (NULL); /* errno is set for us */
+
+ if (id == CTF_ERR) {
+ (void) set_errno(ctf_to_errno(ctf_errno(fp)));
+ return (NULL);
+ }
+
+ *idp = id;
+ return (fp);
+}
+
+/*
+ * Check to see if there is ctf data in the given object. This is useful
+ * so that we don't enter some loop where every call to lookup fails.
+ */
+int
+mdb_ctf_enabled_by_object(const char *object)
+{
+ mdb_tgt_t *t = mdb.m_target;
+
+ return (mdb_tgt_name_to_ctf(t, object) != NULL);
+}
+
+int
+mdb_ctf_lookup_by_name(const char *name, mdb_ctf_id_t *p)
+{
+ ctf_file_t *fp = NULL;
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
+ mdb_tgt_t *t = mdb.m_target;
+
+ if (mcip == NULL)
+ return (set_errno(EINVAL));
+
+ if ((fp = name_to_type(t, name, &mcip->mci_id)) == NULL) {
+ mdb_ctf_type_invalidate(p);
+ return (-1); /* errno is set for us */
+ }
+
+ mcip->mci_fp = fp;
+
+ return (0);
+}
+
+int
+mdb_ctf_lookup_by_symbol(const GElf_Sym *symp, const mdb_syminfo_t *sip,
+ mdb_ctf_id_t *p)
+{
+ ctf_file_t *fp = NULL;
+ mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
+ mdb_tgt_t *t = mdb.m_target;
+
+ if (mcip == NULL)
+ return (set_errno(EINVAL));
+
+ if (symp == NULL || sip == NULL) {
+ mdb_ctf_type_invalidate(p);
+ return (set_errno(EINVAL));
+ }
+
+ if ((fp = mdb_tgt_addr_to_ctf(t, symp->st_value)) == NULL) {
+ mdb_ctf_type_invalidate(p);
+ return (-1); /* errno is set for us */
+ }
+
+ if ((mcip->mci_id = ctf_lookup_by_symbol(fp, sip->sym_id)) == CTF_ERR) {
+ mdb_ctf_type_invalidate(p);
+ return (set_errno(ctf_to_errno(ctf_errno(fp))));
+ }
+
+ mcip->mci_fp = fp;
+
+ return (0);
+}
+
+int
+mdb_ctf_lookup_by_addr(uintptr_t addr, mdb_ctf_id_t *p)
+{
+ GElf_Sym sym;
+ mdb_syminfo_t si;
+ char name[MDB_SYM_NAMLEN];
+ const mdb_map_t *mp;
+ mdb_tgt_t *t = mdb.m_target;
+ const char *obj, *c;
+
+ if (p == NULL)
+ return (set_errno(EINVAL));
+
+ if (mdb_tgt_lookup_by_addr(t, addr, MDB_TGT_SYM_EXACT, name,
+ sizeof (name), NULL, NULL) == -1) {
+ mdb_ctf_type_invalidate(p);
+ return (-1); /* errno is set for us */
+ }
+
+ if ((c = strrsplit(name, '`')) != NULL) {
+ obj = name;
+ } else {
+ if ((mp = mdb_tgt_addr_to_map(t, addr)) == NULL) {
+ mdb_ctf_type_invalidate(p);
+ return (-1); /* errno is set for us */
+ }
+
+ obj = mp->map_name;
+ c = name;
+ }
+
+ if (mdb_tgt_lookup_by_name(t, obj, c, &sym, &si) == -1) {
+ mdb_ctf_type_invalidate(p);
+ return (-1); /* errno is set for us */
+ }
+
+ return (mdb_ctf_lookup_by_symbol(&sym, &si, p));
+}
+
+int
+mdb_ctf_module_lookup(const char *name, mdb_ctf_id_t *p)
+{
+ ctf_file_t *fp;
+ ctf_id_t id;
+ mdb_module_t *mod;
+
+ if ((mod = mdb_get_module()) == NULL)
+ return (set_errno(EMDB_CTX));
+
+ if ((fp = mod->mod_ctfp) == NULL)
+ return (set_errno(EMDB_NOCTF));
+
+ if ((id = ctf_lookup_by_name(fp, name)) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(fp))));
+
+ set_ctf_id(p, fp, id);
+
+ return (0);
+}
+
+int
+mdb_ctf_func_info(const GElf_Sym *symp, const mdb_syminfo_t *sip,
+ mdb_ctf_funcinfo_t *mfp)
+{
+ ctf_file_t *fp = NULL;
+ ctf_funcinfo_t f;
+ mdb_tgt_t *t = mdb.m_target;
+
+ if (symp == NULL || sip == NULL || mfp == NULL)
+ return (set_errno(EINVAL));
+
+ if ((fp = mdb_tgt_addr_to_ctf(t, symp->st_value)) == NULL)
+ return (-1); /* errno is set for us */
+
+ if (ctf_func_info(fp, sip->sym_id, &f) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(fp))));
+
+ set_ctf_id(&mfp->mtf_return, fp, f.ctc_return);
+ mfp->mtf_argc = f.ctc_argc;
+ mfp->mtf_flags = f.ctc_flags;
+ mfp->mtf_symidx = sip->sym_id;
+
+ return (0);
+}
+
+int
+mdb_ctf_func_args(const mdb_ctf_funcinfo_t *funcp, uint_t len,
+ mdb_ctf_id_t *argv)
+{
+ ctf_file_t *fp;
+ ctf_id_t cargv[32];
+ int i;
+
+ if (len > (sizeof (cargv) / sizeof (cargv[0])))
+ return (set_errno(EINVAL));
+
+ if (funcp == NULL || argv == NULL)
+ return (set_errno(EINVAL));
+
+ fp = mdb_ctf_type_file(funcp->mtf_return);
+
+ if (ctf_func_args(fp, funcp->mtf_symidx, len, cargv) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(fp))));
+
+ for (i = MIN(len, funcp->mtf_argc) - 1; i >= 0; i--) {
+ set_ctf_id(&argv[i], fp, cargv[i]);
+ }
+
+ return (0);
+}
+
+void
+mdb_ctf_type_invalidate(mdb_ctf_id_t *idp)
+{
+ set_ctf_id(idp, NULL, CTF_ERR);
+}
+
+int
+mdb_ctf_type_valid(mdb_ctf_id_t id)
+{
+ return (((mdb_ctf_impl_t *)&id)->mci_id != CTF_ERR);
+}
+
+int
+mdb_ctf_type_cmp(mdb_ctf_id_t aid, mdb_ctf_id_t bid)
+{
+ mdb_ctf_impl_t *aidp = (mdb_ctf_impl_t *)&aid;
+ mdb_ctf_impl_t *bidp = (mdb_ctf_impl_t *)&bid;
+
+ return (ctf_type_cmp(aidp->mci_fp, aidp->mci_id,
+ bidp->mci_fp, bidp->mci_id));
+}
+
+int
+mdb_ctf_type_resolve(mdb_ctf_id_t mid, mdb_ctf_id_t *outp)
+{
+ ctf_id_t id;
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)∣
+
+ if ((id = ctf_type_resolve(idp->mci_fp, idp->mci_id)) == CTF_ERR) {
+ if (outp)
+ mdb_ctf_type_invalidate(outp);
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+ }
+
+ if (outp != NULL)
+ set_ctf_id(outp, idp->mci_fp, id);
+
+ return (0);
+}
+
+char *
+mdb_ctf_type_name(mdb_ctf_id_t id, char *buf, size_t len)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ char *ret;
+
+ if (!mdb_ctf_type_valid(id)) {
+ (void) set_errno(EINVAL);
+ return (NULL);
+ }
+
+ ret = ctf_type_name(idp->mci_fp, idp->mci_id, buf, len);
+ if (ret == NULL)
+ (void) set_errno(ctf_to_errno(ctf_errno(idp->mci_fp)));
+
+ return (ret);
+}
+
+ssize_t
+mdb_ctf_type_size(mdb_ctf_id_t id)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ ssize_t ret;
+
+ if ((ret = ctf_type_size(idp->mci_fp, idp->mci_id)) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+
+ return (ret);
+}
+
+int
+mdb_ctf_type_kind(mdb_ctf_id_t id)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ int ret;
+
+ if ((ret = ctf_type_kind(idp->mci_fp, idp->mci_id)) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+
+ return (ret);
+}
+
+int
+mdb_ctf_type_reference(mdb_ctf_id_t mid, mdb_ctf_id_t *outp)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)∣
+ ctf_id_t id;
+
+ if ((id = ctf_type_reference(idp->mci_fp, idp->mci_id)) == CTF_ERR) {
+ if (outp)
+ mdb_ctf_type_invalidate(outp);
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+ }
+
+ if (outp != NULL)
+ set_ctf_id(outp, idp->mci_fp, id);
+
+ return (0);
+}
+
+
+int
+mdb_ctf_type_encoding(mdb_ctf_id_t id, ctf_encoding_t *ep)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+
+ if (ctf_type_encoding(idp->mci_fp, idp->mci_id, ep) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+
+ return (0);
+}
+
+/*
+ * callback proxy for mdb_ctf_type_visit
+ */
+static int
+type_cb(const char *name, ctf_id_t type, ulong_t off, int depth, void *arg)
+{
+ type_visit_t *tvp = arg;
+ mdb_ctf_id_t id;
+
+ set_ctf_id(&id, tvp->tv_fp, type);
+
+ return (tvp->tv_cb(name, id, off, depth, tvp->tv_arg));
+}
+
+int
+mdb_ctf_type_visit(mdb_ctf_id_t id, mdb_ctf_visit_f *func, void *arg)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ type_visit_t tv;
+ int ret;
+
+ tv.tv_cb = func;
+ tv.tv_arg = arg;
+ tv.tv_fp = idp->mci_fp;
+
+ ret = ctf_type_visit(idp->mci_fp, idp->mci_id, type_cb, &tv);
+
+ if (ret == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+
+ return (ret);
+}
+
+int
+mdb_ctf_array_info(mdb_ctf_id_t id, mdb_ctf_arinfo_t *arp)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ ctf_arinfo_t car;
+
+ if (ctf_array_info(idp->mci_fp, idp->mci_id, &car) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+
+ set_ctf_id(&arp->mta_contents, idp->mci_fp, car.ctr_contents);
+ set_ctf_id(&arp->mta_index, idp->mci_fp, car.ctr_index);
+
+ arp->mta_nelems = car.ctr_nelems;
+
+ return (0);
+}
+
+const char *
+mdb_ctf_enum_name(mdb_ctf_id_t id, int value)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ const char *ret;
+
+ if ((ret = ctf_enum_name(idp->mci_fp, idp->mci_id, value)) == NULL)
+ (void) set_errno(ctf_to_errno(ctf_errno(idp->mci_fp)));
+
+ return (ret);
+}
+
+/*
+ * callback proxy for mdb_ctf_member_iter
+ */
+static int
+member_iter_cb(const char *name, ctf_id_t type, ulong_t off, void *data)
+{
+ member_iter_t *mip = data;
+ mdb_ctf_id_t id;
+
+ set_ctf_id(&id, mip->mi_fp, type);
+
+ return (mip->mi_cb(name, id, off, mip->mi_arg));
+}
+
+int
+mdb_ctf_member_iter(mdb_ctf_id_t id, mdb_ctf_member_f *cb, void *data)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+ member_iter_t mi;
+ int ret;
+
+ mi.mi_cb = cb;
+ mi.mi_arg = data;
+ mi.mi_fp = idp->mci_fp;
+
+ ret = ctf_member_iter(idp->mci_fp, idp->mci_id, member_iter_cb, &mi);
+
+ if (ret == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
+
+ return (ret);
+}
+
+int
+mdb_ctf_enum_iter(mdb_ctf_id_t id, mdb_ctf_enum_f *cb, void *data)
+{
+ mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
+
+ return (ctf_enum_iter(idp->mci_fp, idp->mci_id, cb, data));
+}
+
+/*
+ * callback proxy for mdb_ctf_type_iter
+ */
+static int
+type_iter_cb(ctf_id_t type, void *data)
+{
+ type_iter_t *tip = data;
+ mdb_ctf_id_t id;
+
+ set_ctf_id(&id, tip->ti_fp, type);
+
+ return (tip->ti_cb(id, tip->ti_arg));
+}
+
+int
+mdb_ctf_type_iter(const char *object, mdb_ctf_type_f *cb, void *data)
+{
+ ctf_file_t *fp;
+ mdb_tgt_t *t = mdb.m_target;
+ int ret;
+ type_iter_t ti;
+
+ if ((fp = mdb_tgt_name_to_ctf(t, object)) == NULL)
+ return (-1);
+
+ ti.ti_cb = cb;
+ ti.ti_arg = data;
+ ti.ti_fp = fp;
+
+ if ((ret = ctf_type_iter(fp, type_iter_cb, &ti)) == CTF_ERR)
+ return (set_errno(ctf_to_errno(ctf_errno(fp))));
+
+ return (ret);
+}
+
+/* utility functions */
+
+ctf_id_t
+mdb_ctf_type_id(mdb_ctf_id_t id)
+{
+ return (((mdb_ctf_impl_t *)&id)->mci_id);
+}
+
+ctf_file_t *
+mdb_ctf_type_file(mdb_ctf_id_t id)
+{
+ return (((mdb_ctf_impl_t *)&id)->mci_fp);
+}
+
+static int
+member_info_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
+{
+ mbr_info_t *mbrp = data;
+
+ if (strcmp(name, mbrp->mbr_member) == 0) {
+ if (mbrp->mbr_offp != NULL)
+ *(mbrp->mbr_offp) = off;
+ if (mbrp->mbr_typep != NULL)
+ *(mbrp->mbr_typep) = id;
+
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+mdb_ctf_member_info(mdb_ctf_id_t id, const char *member, ulong_t *offp,
+ mdb_ctf_id_t *typep)
+{
+ mbr_info_t mbr;
+ int rc;
+
+ mbr.mbr_member = member;
+ mbr.mbr_offp = offp;
+ mbr.mbr_typep = typep;
+
+ rc = mdb_ctf_member_iter(id, member_info_cb, &mbr);
+
+ /* couldn't get member list */
+ if (rc == -1)
+ return (-1); /* errno is set for us */
+
+ /* not a member */
+ if (rc == 0)
+ return (set_errno(EMDB_CTFNOMEMB));
+
+ return (0);
+}
+
+int
+mdb_ctf_offsetof(mdb_ctf_id_t id, const char *member, ulong_t *retp)
+{
+ return (mdb_ctf_member_info(id, member, retp, NULL));
+}
+
+/*ARGSUSED*/
+static int
+num_members_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
+{
+ int *count = data;
+ *count = *count + 1;
+ return (0);
+}
+
+int
+mdb_ctf_num_members(mdb_ctf_id_t id)
+{
+ int count = 0;
+
+ if (mdb_ctf_member_iter(id, num_members_cb, &count) != 0)
+ return (-1); /* errno is set for us */
+
+ return (count);
+}
+
+typedef struct mbr_contains {
+ char **mbc_bufp;
+ size_t *mbc_lenp;
+ ulong_t *mbc_offp;
+ mdb_ctf_id_t *mbc_idp;
+ ssize_t mbc_total;
+} mbr_contains_t;
+
+static int
+offset_to_name_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
+{
+ mbr_contains_t *mbc = data;
+ ulong_t size;
+ ctf_encoding_t e;
+ size_t n;
+
+ if (*mbc->mbc_offp < off)
+ return (0);
+
+ if (mdb_ctf_type_encoding(id, &e) == -1)
+ size = mdb_ctf_type_size(id) * NBBY;
+ else
+ size = e.cte_bits;
+
+ if (off + size <= *mbc->mbc_offp)
+ return (0);
+
+ n = mdb_snprintf(*mbc->mbc_bufp, *mbc->mbc_lenp, "%s", name);
+ mbc->mbc_total += n;
+ if (n > *mbc->mbc_lenp)
+ n = *mbc->mbc_lenp;
+
+ *mbc->mbc_lenp -= n;
+ *mbc->mbc_bufp += n;
+
+ *mbc->mbc_offp -= off;
+ *mbc->mbc_idp = id;
+
+ return (1);
+}
+
+ssize_t
+mdb_ctf_offset_to_name(mdb_ctf_id_t id, ulong_t off, char *buf, size_t len,
+ int dot, mdb_ctf_id_t *midp, ulong_t *moffp)
+{
+ size_t size;
+ size_t n;
+ mbr_contains_t mbc;
+
+ if (!mdb_ctf_type_valid(id))
+ return (set_errno(EINVAL));
+
+ /*
+ * Quick sanity check to make sure the given offset is within
+ * this scope of this type.
+ */
+ if (mdb_ctf_type_size(id) * NBBY <= off)
+ return (set_errno(EINVAL));
+
+ mbc.mbc_bufp = &buf;
+ mbc.mbc_lenp = &len;
+ mbc.mbc_offp = &off;
+ mbc.mbc_idp = &id;
+ mbc.mbc_total = 0;
+
+ *buf = '\0';
+
+ for (;;) {
+ /*
+ * Check for an exact match.
+ */
+ if (off == 0)
+ break;
+
+ (void) mdb_ctf_type_resolve(id, &id);
+
+ /*
+ * Find the member that contains this offset.
+ */
+ switch (mdb_ctf_type_kind(id)) {
+ case CTF_K_ARRAY: {
+ mdb_ctf_arinfo_t ar;
+ uint_t index;
+
+ (void) mdb_ctf_array_info(id, &ar);
+ size = mdb_ctf_type_size(ar.mta_contents) * NBBY;
+ index = off / size;
+
+ id = ar.mta_contents;
+ off %= size;
+
+ n = mdb_snprintf(buf, len, "[%u]", index);
+ mbc.mbc_total += n;
+ if (n > len)
+ n = len;
+
+ buf += n;
+ len -= n;
+ break;
+ }
+
+ case CTF_K_STRUCT: {
+ int ret;
+
+ /*
+ * Find the member that contains this offset
+ * and continue.
+ */
+
+ if (dot) {
+ mbc.mbc_total++;
+ if (len != 0) {
+ *buf++ = '.';
+ *buf = '\0';
+ len--;
+ }
+ }
+
+ ret = mdb_ctf_member_iter(id, offset_to_name_cb, &mbc);
+ if (ret == -1)
+ return (-1); /* errno is set for us */
+
+ /*
+ * If we did not find a member containing this offset
+ * (due to holes in the structure), return EINVAL.
+ */
+ if (ret == 0)
+ return (set_errno(EINVAL));
+
+ break;
+ }
+
+ case CTF_K_UNION:
+ /*
+ * Treat unions like atomic entities since we can't
+ * do more than guess which member of the union
+ * might be the intended one.
+ */
+ goto done;
+
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_POINTER:
+ case CTF_K_ENUM:
+ goto done;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ dot = 1;
+ }
+done:
+ if (midp != NULL)
+ *midp = id;
+ if (moffp != NULL)
+ *moffp = off;
+
+ return (mbc.mbc_total);
+}
+
+/*
+ * Check if two types are structurally the same rather than logically
+ * the same. That is to say that two types are equal if they have the
+ * same logical structure rather than having the same ids in CTF-land.
+ */
+static int type_equals(mdb_ctf_id_t, mdb_ctf_id_t);
+
+static int
+type_equals_cb(const char *name, mdb_ctf_id_t amem, ulong_t aoff, void *data)
+{
+ mdb_ctf_id_t b = *(mdb_ctf_id_t *)data;
+ ulong_t boff;
+ mdb_ctf_id_t bmem;
+
+ /*
+ * Look up the corresponding member in the other composite type.
+ */
+ if (mdb_ctf_member_info(b, name, &boff, &bmem) != 0)
+ return (1);
+
+ /*
+ * We don't allow members to be shuffled around.
+ */
+ if (aoff != boff)
+ return (1);
+
+ return (type_equals(amem, bmem) ? 0 : 1);
+}
+
+static int
+type_equals(mdb_ctf_id_t a, mdb_ctf_id_t b)
+{
+ size_t asz, bsz;
+ int akind, bkind;
+ mdb_ctf_arinfo_t aar, bar;
+
+ /*
+ * Resolve both types down to their fundamental types, and make
+ * sure their sizes and kinds match.
+ */
+ if (mdb_ctf_type_resolve(a, &a) != 0 ||
+ mdb_ctf_type_resolve(b, &b) != 0 ||
+ (asz = mdb_ctf_type_size(a)) == -1UL ||
+ (bsz = mdb_ctf_type_size(b)) == -1UL ||
+ (akind = mdb_ctf_type_kind(a)) == -1 ||
+ (bkind = mdb_ctf_type_kind(b)) == -1 ||
+ asz != bsz || akind != bkind) {
+ return (0);
+ }
+
+ switch (akind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_POINTER:
+ /*
+ * For pointers we could be a little stricter and require
+ * both pointers to reference types which look vaguely
+ * similar (for example, we could insist that the two types
+ * have the same name). However, all we really care about
+ * here is that the structure of the two types are the same,
+ * and, in that regard, one pointer is as good as another.
+ */
+ return (1);
+
+ case CTF_K_UNION:
+ case CTF_K_STRUCT:
+ /*
+ * The test for the number of members is only strictly
+ * necessary for unions since we'll find other problems with
+ * structs. However, the extra check will do no harm.
+ */
+ return (mdb_ctf_num_members(a) == mdb_ctf_num_members(b) &&
+ mdb_ctf_member_iter(a, type_equals_cb, &b) == 0);
+
+ case CTF_K_ARRAY:
+ return (mdb_ctf_array_info(a, &aar) == 0 &&
+ mdb_ctf_array_info(b, &bar) == 0 &&
+ aar.mta_nelems == bar.mta_nelems &&
+ type_equals(aar.mta_index, bar.mta_index) &&
+ type_equals(aar.mta_contents, bar.mta_contents));
+ }
+
+ return (0);
+}
+
+
+typedef struct member {
+ char *m_modbuf;
+ char *m_tgtbuf;
+ mdb_ctf_id_t m_tgtid;
+ uint_t m_flags;
+} member_t;
+
+static int vread_helper(mdb_ctf_id_t, char *, mdb_ctf_id_t, char *, uint_t);
+
+static int
+member_cb(const char *name, mdb_ctf_id_t modmid, ulong_t modoff, void *data)
+{
+ member_t *mp = data;
+ char *modbuf = mp->m_modbuf;
+ mdb_ctf_id_t tgtmid;
+ char *tgtbuf = mp->m_tgtbuf;
+ ulong_t tgtoff;
+
+ if (mdb_ctf_member_info(mp->m_tgtid, name, &tgtoff, &tgtmid) != 0) {
+ if (mp->m_flags & MDB_CTF_VREAD_IGNORE_ABSENT)
+ return (0);
+ else
+ return (set_errno(EMDB_CTFNOMEMB));
+ }
+
+ return (vread_helper(modmid, modbuf + modoff / NBBY,
+ tgtmid, tgtbuf + tgtoff / NBBY, mp->m_flags));
+}
+
+
+static int
+vread_helper(mdb_ctf_id_t modid, char *modbuf,
+ mdb_ctf_id_t tgtid, char *tgtbuf, uint_t flags)
+{
+ size_t modsz, tgtsz;
+ int modkind, tgtkind;
+ member_t mbr;
+ int ret;
+ mdb_ctf_arinfo_t tar, mar;
+ int i;
+
+ /*
+ * Resolve the types to their canonical form.
+ */
+ (void) mdb_ctf_type_resolve(modid, &modid);
+ (void) mdb_ctf_type_resolve(tgtid, &tgtid);
+
+ if ((modkind = mdb_ctf_type_kind(modid)) == -1)
+ return (-1); /* errno is set for us */
+ if ((tgtkind = mdb_ctf_type_kind(tgtid)) == -1)
+ return (-1); /* errno is set for us */
+
+ if (tgtkind != modkind)
+ return (set_errno(EMDB_INCOMPAT));
+
+ switch (modkind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_POINTER:
+ if ((modsz = mdb_ctf_type_size(modid)) == -1UL)
+ return (-1); /* errno is set for us */
+
+ if ((tgtsz = mdb_ctf_type_size(tgtid)) == -1UL)
+ return (-1); /* errno is set for us */
+
+ /*
+ * If the sizes don't match we need to be tricky to make
+ * sure that the caller gets the correct data.
+ */
+ if (modsz < tgtsz) {
+ if (!(flags & MDB_CTF_VREAD_IGNORE_GROW))
+ return (set_errno(EMDB_INCOMPAT));
+#ifdef _BIG_ENDIAN
+ bcopy(tgtbuf + tgtsz - modsz, modbuf, modsz);
+#else
+ bcopy(tgtbuf, modbuf, modsz);
+#endif
+ } else if (modsz > tgtsz) {
+ bzero(modbuf, modsz);
+#ifdef _BIG_ENDIAN
+ bcopy(tgtbuf, modbuf + modsz - tgtsz, tgtsz);
+#else
+ bcopy(tgtbuf, modbuf, tgtsz);
+#endif
+ } else {
+ bcopy(tgtbuf, modbuf, modsz);
+ }
+
+ return (0);
+
+ case CTF_K_STRUCT:
+ mbr.m_modbuf = modbuf;
+ mbr.m_tgtbuf = tgtbuf;
+ mbr.m_tgtid = tgtid;
+ mbr.m_flags = flags;
+
+ return (mdb_ctf_member_iter(modid, member_cb, &mbr));
+
+ case CTF_K_UNION:
+
+ /*
+ * Unions are a little tricky. The only time it's truly
+ * safe to read in a union is if no part of the union or
+ * any of its component types have changed. We allow the
+ * consumer to ignore unions. The correct use of this
+ * feature is to read the containing structure, figure
+ * out which component of the union is valid, compute
+ * the location of that in the target and then read in
+ * that part of the structure.
+ */
+ if (flags & MDB_CTF_VREAD_IGNORE_UNIONS)
+ return (0);
+
+ if (!type_equals(modid, tgtid))
+ return (set_errno(EMDB_INCOMPAT));
+
+ modsz = mdb_ctf_type_size(modid);
+ tgtsz = mdb_ctf_type_size(tgtid);
+
+ ASSERT(modsz == tgtsz);
+
+ bcopy(tgtbuf, modbuf, modsz);
+
+ return (0);
+
+ case CTF_K_ARRAY:
+ if (mdb_ctf_array_info(tgtid, &tar) != 0)
+ return (-1); /* errno is set for us */
+ if (mdb_ctf_array_info(modid, &mar) != 0)
+ return (-1); /* errno is set for us */
+
+ if (tar.mta_nelems != mar.mta_nelems)
+ return (set_errno(EMDB_INCOMPAT));
+
+ if ((modsz = mdb_ctf_type_size(mar.mta_contents)) == -1UL)
+ return (-1); /* errno is set for us */
+
+ if ((tgtsz = mdb_ctf_type_size(tar.mta_contents)) == -1UL)
+ return (-1); /* errno is set for us */
+
+ for (i = 0; i < tar.mta_nelems; i++) {
+ ret = vread_helper(mar.mta_contents, modbuf + i * modsz,
+ tar.mta_contents, tgtbuf + i * tgtsz, flags);
+
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (0);
+ }
+
+ return (set_errno(EMDB_INCOMPAT));
+}
+
+
+int
+mdb_ctf_vread(void *modbuf, const char *typename, uintptr_t addr, uint_t flags)
+{
+ ctf_file_t *mfp;
+ ctf_id_t mid;
+ void *tgtbuf;
+ size_t size;
+ mdb_ctf_id_t tgtid;
+ mdb_ctf_id_t modid;
+ mdb_module_t *mod;
+
+ if ((mod = mdb_get_module()) == NULL || (mfp = mod->mod_ctfp) == NULL)
+ return (set_errno(EMDB_NOCTF));
+
+ if ((mid = ctf_lookup_by_name(mfp, typename)) == CTF_ERR) {
+ mdb_dprintf(MDB_DBG_CTF, "couldn't find module's ctf data\n");
+ return (set_errno(ctf_to_errno(ctf_errno(mfp))));
+ }
+
+ set_ctf_id(&modid, mfp, mid);
+
+ if (mdb_ctf_lookup_by_name(typename, &tgtid) != 0) {
+ mdb_dprintf(MDB_DBG_CTF, "couldn't find target's ctf data\n");
+ return (set_errno(EMDB_NOCTF));
+ }
+
+ /*
+ * Read the data out of the target's address space.
+ */
+ if ((size = mdb_ctf_type_size(tgtid)) == -1UL)
+ return (-1); /* errno is set for us */
+
+ tgtbuf = mdb_alloc(size, UM_SLEEP | UM_GC);
+
+ if (mdb_vread(tgtbuf, size, addr) < 0)
+ return (-1); /* errno is set for us */
+
+ return (vread_helper(modid, modbuf, tgtid, tgtbuf, flags));
+}
+
+int
+mdb_ctf_readsym(void *buf, const char *typename, const char *name, uint_t flags)
+{
+ GElf_Sym sym;
+
+ if (mdb_lookup_by_name(name, &sym) != 0)
+ return (-1); /* errno is set for us */
+
+ return (mdb_ctf_vread(buf, typename, sym.st_value, flags));
+}
+
+ctf_file_t *
+mdb_ctf_bufopen(const void *ctf_va, size_t ctf_size, const void *sym_va,
+ Shdr *symhdr, const void *str_va, Shdr *strhdr, int *errp)
+{
+ ctf_sect_t ctdata, symtab, strtab;
+
+ ctdata.cts_name = ".SUNW_ctf";
+ ctdata.cts_type = SHT_PROGBITS;
+ ctdata.cts_flags = 0;
+ ctdata.cts_data = ctf_va;
+ ctdata.cts_size = ctf_size;
+ ctdata.cts_entsize = 1;
+ ctdata.cts_offset = 0;
+
+ symtab.cts_name = ".symtab";
+ symtab.cts_type = symhdr->sh_type;
+ symtab.cts_flags = symhdr->sh_flags;
+ symtab.cts_data = sym_va;
+ symtab.cts_size = symhdr->sh_size;
+ symtab.cts_entsize = symhdr->sh_entsize;
+ symtab.cts_offset = symhdr->sh_offset;
+
+ strtab.cts_name = ".strtab";
+ strtab.cts_type = strhdr->sh_type;
+ strtab.cts_flags = strhdr->sh_flags;
+ strtab.cts_data = str_va;
+ strtab.cts_size = strhdr->sh_size;
+ strtab.cts_entsize = strhdr->sh_entsize;
+ strtab.cts_offset = strhdr->sh_offset;
+
+ return (ctf_bufopen(&ctdata, &symtab, &strtab, errp));
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.h b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h
new file mode 100644
index 0000000..e786ebf
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h
@@ -0,0 +1,139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_CTF_H
+#define _MDB_CTF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_target.h>
+#include <libctf.h>
+
+#ifdef _MDB
+#include <sys/machelf.h>
+#endif
+
+/*
+ * The following directive tells the mapfile generator that prototypes and
+ * declarations ending with an "Internal" comment should be excluded from the
+ * mapfile.
+ *
+ * MAPFILE: exclude "Internal"
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_ctf_id {
+ void *_opaque[2];
+} mdb_ctf_id_t;
+
+typedef struct mdb_ctf_funcinfo {
+ mdb_ctf_id_t mtf_return; /* function return type */
+ uint_t mtf_argc; /* number of arguments */
+ uint_t mtf_flags; /* function attributes (see libctf.h) */
+ uint_t mtf_symidx; /* for ctf_func_args */
+} mdb_ctf_funcinfo_t;
+
+typedef struct mdb_ctf_arinfo {
+ mdb_ctf_id_t mta_contents; /* type of array conents */
+ mdb_ctf_id_t mta_index; /* type of array index */
+ uint_t mta_nelems; /* number of elements */
+} mdb_ctf_arinfo_t;
+
+typedef int mdb_ctf_visit_f(const char *, mdb_ctf_id_t, ulong_t, int, void *);
+typedef int mdb_ctf_member_f(const char *, mdb_ctf_id_t, ulong_t, void *);
+typedef int mdb_ctf_enum_f(const char *, int, void *);
+typedef int mdb_ctf_type_f(mdb_ctf_id_t, void *);
+
+extern int mdb_ctf_enabled_by_object(const char *);
+
+extern int mdb_ctf_lookup_by_name(const char *, mdb_ctf_id_t *);
+extern int mdb_ctf_lookup_by_addr(uintptr_t, mdb_ctf_id_t *);
+
+extern int mdb_ctf_module_lookup(const char *, mdb_ctf_id_t *);
+
+extern int mdb_ctf_func_info(const GElf_Sym *, const mdb_syminfo_t *,
+ mdb_ctf_funcinfo_t *);
+extern int mdb_ctf_func_args(const mdb_ctf_funcinfo_t *, uint_t,
+ mdb_ctf_id_t *);
+
+extern void mdb_ctf_type_invalidate(mdb_ctf_id_t *);
+extern int mdb_ctf_type_valid(mdb_ctf_id_t);
+extern int mdb_ctf_type_cmp(mdb_ctf_id_t, mdb_ctf_id_t);
+
+extern int mdb_ctf_type_resolve(mdb_ctf_id_t, mdb_ctf_id_t *);
+extern char *mdb_ctf_type_name(mdb_ctf_id_t, char *, size_t);
+extern ssize_t mdb_ctf_type_size(mdb_ctf_id_t);
+extern int mdb_ctf_type_kind(mdb_ctf_id_t);
+extern int mdb_ctf_type_reference(const mdb_ctf_id_t, mdb_ctf_id_t *);
+extern int mdb_ctf_type_encoding(mdb_ctf_id_t, ctf_encoding_t *);
+extern int mdb_ctf_type_visit(mdb_ctf_id_t, mdb_ctf_visit_f *, void *);
+
+extern int mdb_ctf_array_info(mdb_ctf_id_t, mdb_ctf_arinfo_t *);
+extern const char *mdb_ctf_enum_name(mdb_ctf_id_t, int);
+
+extern int mdb_ctf_member_iter(mdb_ctf_id_t, mdb_ctf_member_f *, void *);
+extern int mdb_ctf_enum_iter(mdb_ctf_id_t, mdb_ctf_enum_f *, void *);
+extern int mdb_ctf_type_iter(const char *, mdb_ctf_type_f *, void *);
+
+/* utility stuff */
+extern ctf_id_t mdb_ctf_type_id(mdb_ctf_id_t);
+extern ctf_file_t *mdb_ctf_type_file(mdb_ctf_id_t);
+
+
+extern int mdb_ctf_member_info(mdb_ctf_id_t, const char *,
+ ulong_t *, mdb_ctf_id_t *);
+extern int mdb_ctf_offsetof(mdb_ctf_id_t, const char *, ulong_t *);
+extern int mdb_ctf_num_members(mdb_ctf_id_t);
+
+extern ssize_t mdb_ctf_offset_to_name(mdb_ctf_id_t, ulong_t, char *, size_t,
+ int, mdb_ctf_id_t *, ulong_t *);
+
+#define MDB_CTF_VREAD_IGNORE_GROW 0x01
+#define MDB_CTF_VREAD_IGNORE_ABSENT 0x02
+#define MDB_CTF_VREAD_IGNORE_UNIONS 0x04
+
+#define MDB_CTF_VREAD_IGNORE_ALL 0x07
+
+extern int mdb_ctf_vread(void *, const char *, uintptr_t, uint_t);
+extern int mdb_ctf_readsym(void *, const char *, const char *, uint_t);
+
+#ifdef _MDB
+
+extern ctf_file_t *mdb_ctf_open(const char *, int *); /* Internal */
+extern ctf_file_t *mdb_ctf_bufopen(const void *, size_t, /* Internal */
+ const void *, Shdr *, const void *, Shdr *, int *);
+extern void mdb_ctf_close(ctf_file_t *fp); /* Internal */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CTF_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf_impl.h b/usr/src/cmd/mdb/common/mdb/mdb_ctf_impl.h
new file mode 100644
index 0000000..c809d5b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf_impl.h
@@ -0,0 +1,48 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_CTF_IMPL_H
+#define _MDB_CTF_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_ctf_impl {
+ ctf_file_t *mci_fp;
+ ctf_id_t mci_id;
+} mdb_ctf_impl_t;
+
+extern int mdb_ctf_lookup_by_symbol(const GElf_Sym *, const mdb_syminfo_t *,
+ mdb_ctf_id_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CTF_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c
new file mode 100644
index 0000000..730a9a4
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * libctf open/close interposition layer
+ *
+ * The mdb flavor of the interposition layer serves only to make ctf_bufopen
+ * calls easier. The kmdb flavor (the real reason for the layer) has more
+ * intelligence behind mdb_ctf_open() than does this one.
+ */
+
+#include <mdb/mdb_ctf.h>
+#include <libctf.h>
+
+ctf_file_t *
+mdb_ctf_open(const char *filename, int *errp)
+{
+ return (ctf_open(filename, errp));
+}
+
+void
+mdb_ctf_close(ctf_file_t *fp)
+{
+ ctf_close(fp);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.c b/usr/src/cmd/mdb/common/mdb/mdb_debug.c
new file mode 100644
index 0000000..d373d37
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.c
@@ -0,0 +1,189 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb.h>
+
+#include <libproc.h>
+#include <libctf.h>
+#include <rtld_db.h>
+#include <strings.h>
+#include <stdarg.h>
+
+typedef struct dbg_mode {
+ const char *m_name;
+ const char *m_desc;
+ uint_t m_bits;
+} dbg_mode_t;
+
+static const dbg_mode_t dbg_modetab[] = {
+ { "cmdbuf", "debug command editing buffer", MDB_DBG_CMDBUF },
+#ifdef YYDEBUG
+ { "parser", "debug parser internals", MDB_DBG_PARSER },
+#endif
+ { "help", "display this listing", MDB_DBG_HELP },
+ { "module", "debug module processing", MDB_DBG_MODULE },
+ { "dcmd", "debug dcmd processing", MDB_DBG_DCMD },
+ { "elf", "debug ELF file processing", MDB_DBG_ELF },
+ { "mach", "debug machine-dependent code", MDB_DBG_MACH },
+ { "shell", "debug shell escapes", MDB_DBG_SHELL },
+ { "kmod", "debug kernel module processing", MDB_DBG_KMOD },
+ { "walk", "debug walk callback processing", MDB_DBG_WALK },
+ { "umem", "debug memory management", MDB_DBG_UMEM },
+ { "dstk", "debug execution stack", MDB_DBG_DSTK },
+ { "tgt", "debug target backends", MDB_DBG_TGT },
+ { "psvc", "debug proc_service clients", MDB_DBG_PSVC },
+ { "proc", "debug libproc internals", MDB_DBG_PROC },
+ { "ctf", "debug libctf internals", MDB_DBG_CTF },
+ { "dpi", "debugger/PROM interface (kmdb only)", MDB_DBG_DPI },
+ { "kdi", "kernel/debugger interface (kmdb only)", MDB_DBG_KDI },
+ { "callb", "debug callback invocations", MDB_DBG_CALLB },
+ { "all", "set all debug modes", (uint_t)~MDB_DBG_HELP },
+ { "none", "unset all debug modes", 0 },
+ { NULL, 0 }
+};
+
+static const char dbg_prefix[] = "mdb DEBUG: ";
+
+/*PRINTFLIKE2*/
+void
+mdb_dprintf(uint_t mode, const char *format, ...)
+{
+ if ((mdb.m_debug & mode) == mode && mdb.m_err != NULL) {
+ va_list alist;
+
+ mdb_iob_puts(mdb.m_err, dbg_prefix);
+ va_start(alist, format);
+ mdb_iob_vprintf(mdb.m_err, format, alist);
+ va_end(alist);
+ }
+}
+
+void
+mdb_dvprintf(uint_t mode, const char *format, va_list alist)
+{
+ if ((mdb.m_debug & mode) == mode && format != NULL && *format != '\0' &&
+ mdb.m_err != NULL) {
+ mdb_iob_puts(mdb.m_err, dbg_prefix);
+ mdb_iob_vprintf(mdb.m_err, format, alist);
+ if (format[strlen(format) - 1] != '\n')
+ mdb_iob_nl(mdb.m_err);
+ }
+}
+
+uint_t
+mdb_dstr2mode(const char *s)
+{
+ const dbg_mode_t *mp;
+ const char *name;
+ char dstr[256];
+
+ uint_t bits = 0;
+
+ if (s == NULL)
+ return (0);
+
+ (void) strncpy(dstr, s, sizeof (dstr));
+ dstr[sizeof (dstr) - 1] = '\0';
+
+ for (name = strtok(dstr, ","); name; name = strtok(NULL, ",")) {
+ for (mp = dbg_modetab; mp->m_name != NULL; mp++) {
+ if (strcmp(name, mp->m_name) == 0) {
+ if (mp->m_bits != 0)
+ bits |= mp->m_bits;
+ else
+ bits = 0;
+ break;
+ }
+ }
+
+ if (mp->m_name == NULL)
+ warn("unknown debug option \"%s\"\n", name);
+ }
+
+ if (bits & MDB_DBG_HELP) {
+ warn("Debugging tokens:\n");
+ for (mp = dbg_modetab; mp->m_name != NULL; mp++)
+ warn("\t%s: %s\n", mp->m_name, mp->m_desc);
+ }
+
+ return (bits);
+}
+
+void
+mdb_dmode(uint_t bits)
+{
+ int *libproc_debugp, *libctf_debugp;
+ void (*rd_logp)(const int);
+
+ if ((libproc_debugp = dlsym(RTLD_SELF, "_libproc_debug")) != NULL)
+ *libproc_debugp = (bits & MDB_DBG_PROC) != 0;
+
+ if ((libctf_debugp = dlsym(RTLD_SELF, "_libctf_debug")) != NULL)
+ *libctf_debugp = (bits & MDB_DBG_CTF) != 0;
+
+ if ((rd_logp = (void (*)())dlsym(RTLD_SELF, "rd_log")) != NULL)
+ rd_logp((bits & MDB_DBG_PSVC) != 0);
+
+ mdb_lex_debug(bits & MDB_DBG_PARSER);
+ mdb.m_debug = bits;
+}
+
+#ifdef DEBUG
+int
+mdb_dassert(const char *expr, const char *file, int line)
+{
+ fail("\"%s\", line %d: assertion failed: %s\n", file, line, expr);
+ /*NOTREACHED*/
+ return (0);
+}
+#endif
+
+/*
+ * Function to convert mdb longjmp codes (see <mdb/mdb.h>) into a string for
+ * debugging routines.
+ */
+const char *
+mdb_err2str(int err)
+{
+ static const char *const errtab[] = {
+ "0", "PARSE", "NOMEM", "PAGER", "SIGINT",
+ "QUIT", "ASSERT", "API", "ABORT", "OUTPUT"
+ };
+
+ static char buf[32];
+
+ if (err >= 0 && err < sizeof (errtab) / sizeof (errtab[0]))
+ return (errtab[err]);
+
+ (void) mdb_iob_snprintf(buf, sizeof (buf), "ERR#%d", err);
+ return (buf);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.h b/usr/src/cmd/mdb/common/mdb/mdb_debug.h
new file mode 100644
index 0000000..cf3b97b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.h
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_DEBUG_H
+#define _MDB_DEBUG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <stdarg.h>
+
+#define MDB_DBG_CMDBUF 0x00000001
+#define MDB_DBG_PARSER 0x00000002
+#define MDB_DBG_HELP 0x00000004
+#define MDB_DBG_MODULE 0x00000008
+#define MDB_DBG_DCMD 0x00000010
+#define MDB_DBG_ELF 0x00000020
+#define MDB_DBG_MACH 0x00000040
+#define MDB_DBG_SHELL 0x00000080
+#define MDB_DBG_KMOD 0x00000100
+#define MDB_DBG_WALK 0x00000200
+#define MDB_DBG_UMEM 0x00000400
+#define MDB_DBG_DSTK 0x00000800
+#define MDB_DBG_TGT 0x00001000
+#define MDB_DBG_PSVC 0x00002000
+#define MDB_DBG_PROC 0x00004000
+#define MDB_DBG_CTF 0x00008000
+#define MDB_DBG_DPI 0x00010000
+#define MDB_DBG_KDI 0x00020000
+#define MDB_DBG_CALLB 0x00040000
+
+#ifdef _MDB
+
+extern void mdb_dprintf(uint_t, const char *, ...);
+extern void mdb_dvprintf(uint_t, const char *, va_list);
+
+extern uint_t mdb_dstr2mode(const char *);
+extern void mdb_dmode(uint_t);
+
+extern const char *mdb_err2str(int);
+
+#ifdef DEBUG
+extern int mdb_dassert(const char *, const char *, int);
+#define ASSERT(x) ((void)((x) || mdb_dassert(#x, __FILE__, __LINE__)))
+#else
+#define ASSERT(x)
+#endif
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_DEBUG_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_demangle.c b/usr/src/cmd/mdb/common/mdb/mdb_demangle.c
new file mode 100644
index 0000000..7ab7d19
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_demangle.c
@@ -0,0 +1,352 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+#include <demangle.h>
+#include <strings.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <link.h>
+
+#ifdef _LP64
+static const char LIB_DEMANGLE[] = "/usr/lib/64/libdemangle.so.1";
+#else
+static const char LIB_DEMANGLE[] = "/usr/lib/libdemangle.so.1";
+#endif
+
+mdb_demangler_t *
+mdb_dem_load(const char *path)
+{
+ mdb_demangler_t *dmp;
+ void *hdl, *func;
+
+ if (access(path, F_OK) == -1)
+ return (NULL);
+
+ if ((hdl = dlmopen(LM_ID_BASE, path, RTLD_LAZY | RTLD_LOCAL)) == NULL) {
+ (void) set_errno(EMDB_RTLD);
+ return (NULL);
+ }
+
+ if ((func = dlsym(hdl, "cplus_demangle")) == NULL) {
+ (void) dlclose(hdl);
+ (void) set_errno(EMDB_NODEM);
+ return (NULL);
+ }
+
+ dmp = mdb_alloc(sizeof (mdb_demangler_t), UM_SLEEP);
+ (void) strncpy(dmp->dm_pathname, path, MAXPATHLEN);
+ dmp->dm_pathname[MAXPATHLEN - 1] = '\0';
+ dmp->dm_handle = hdl;
+ dmp->dm_convert = (int (*)())func;
+ dmp->dm_len = MDB_SYM_NAMLEN * 2;
+ dmp->dm_buf = mdb_alloc(dmp->dm_len, UM_SLEEP);
+ dmp->dm_flags = MDB_DM_SCOPE;
+
+ return (dmp);
+}
+
+void
+mdb_dem_unload(mdb_demangler_t *dmp)
+{
+ (void) dlclose(dmp->dm_handle);
+ mdb_free(dmp->dm_buf, dmp->dm_len);
+ mdb_free(dmp, sizeof (mdb_demangler_t));
+}
+
+static const char *
+mdb_dem_filter(mdb_demangler_t *dmp, const char *name)
+{
+ static const char s_pref[] = "static ";
+ static const char c_suff[] = " const";
+ static const char v_suff[] = " volatile";
+
+ /*
+ * We process dm_dem, which skips the prefix in dm_buf (if any)
+ */
+ size_t len = strlen(dmp->dm_dem);
+ char *end = dmp->dm_dem + len;
+ size_t resid;
+
+ /*
+ * If static, const, and volatile qualifiers should not be displayed,
+ * rip all of them out of dmp->dm_dem.
+ */
+ if (!(dmp->dm_flags & MDB_DM_QUAL)) {
+ if (strncmp(dmp->dm_dem, s_pref, sizeof (s_pref) - 1) == 0) {
+ bcopy(dmp->dm_dem + sizeof (s_pref) - 1, dmp->dm_dem,
+ len - (sizeof (s_pref) - 1) + 1);
+ end -= sizeof (s_pref) - 1;
+ len -= sizeof (s_pref) - 1;
+ }
+
+ for (;;) {
+ if (len > sizeof (c_suff) - 1 &&
+ strcmp(end - (sizeof (c_suff) - 1), c_suff) == 0) {
+ end -= sizeof (c_suff) - 1;
+ len -= sizeof (c_suff) - 1;
+ *end = '\0';
+ continue;
+ }
+ if (len > sizeof (v_suff) - 1 &&
+ strcmp(end - (sizeof (v_suff) - 1), v_suff) == 0) {
+ end -= sizeof (v_suff) - 1;
+ len -= sizeof (v_suff) - 1;
+ *end = '\0';
+ continue;
+ }
+ break;
+ }
+ }
+
+ /*
+ * If function arguments should not be displayed, remove everything
+ * between the outermost set of parentheses in dmp->dm_dem.
+ */
+ if (!(dmp->dm_flags & MDB_DM_FUNCARG)) {
+ char *lp = strchr(dmp->dm_dem, '(');
+ char *rp = strrchr(dmp->dm_dem, ')');
+
+ if (lp != NULL && rp != NULL)
+ bcopy(rp + 1, lp, strlen(rp) + 1);
+ }
+
+ /*
+ * If function scope specifiers should not be displayed, remove text
+ * from the leftmost space to the rightmost colon prior to any paren.
+ */
+ if (!(dmp->dm_flags & MDB_DM_SCOPE)) {
+ char *c, *s, *lp = strchr(dmp->dm_dem, '(');
+
+ if (lp != NULL)
+ *lp = '\0';
+
+ c = strrchr(dmp->dm_dem, ':');
+ s = strchr(dmp->dm_dem, ' ');
+
+ if (lp != NULL)
+ *lp = '(';
+
+ if (c != NULL) {
+ if (s == NULL || s > c)
+ bcopy(c + 1, dmp->dm_dem, strlen(c + 1) + 1);
+ else
+ bcopy(c + 1, s + 1, strlen(c + 1) + 1);
+ }
+ }
+
+ len = strlen(dmp->dm_dem); /* recompute length of buffer */
+
+ /*
+ * Compute bytes remaining
+ */
+ resid = (dmp->dm_buf + dmp->dm_len) - (dmp->dm_dem + len);
+
+ /*
+ * If we want to append the mangled name as well and there is enough
+ * space for "[]\0" and at least one character, append "["+name+"]".
+ */
+ if ((dmp->dm_flags & MDB_DM_MANGLED) && resid > 3) {
+ char *p = dmp->dm_dem + len;
+
+ *p++ = '[';
+ (void) strncpy(p, name, resid - 3);
+ p[resid - 3] = '\0';
+ p += strlen(p);
+ (void) strcpy(p, "]");
+ }
+
+ /*
+ * We return the whole string
+ */
+ return (dmp->dm_buf);
+}
+
+/*
+ * Take a name: (the foo`bar` is optional)
+ * foo`bar`__mangled_
+ * and put:
+ * foo`bar`demangled
+ * into dmp->dm_buf. Point dmp->dm_dem to the beginning of the
+ * demangled section of the result.
+ */
+static int
+mdb_dem_process(mdb_demangler_t *dmp, const char *name)
+{
+ char *buf = dmp->dm_buf;
+ size_t len = dmp->dm_len;
+
+ char *prefix = strrchr(name, '`');
+ size_t prefixlen;
+
+ if (prefix) {
+ prefix++; /* the ` is part of the prefix */
+ prefixlen = prefix - name;
+
+ if (prefixlen >= len)
+ return (DEMANGLE_ESPACE);
+
+ (void) strncpy(buf, name, prefixlen);
+
+ /*
+ * Fix up the arguments to dmp->dm_convert()
+ */
+ name += prefixlen;
+ buf += prefixlen;
+ len -= prefixlen;
+ }
+
+ /*
+ * Save the position of the demangled string for mdb_dem_filter()
+ */
+ dmp->dm_dem = buf;
+
+ return (dmp->dm_convert(name, buf, len));
+}
+
+const char *
+mdb_dem_convert(mdb_demangler_t *dmp, const char *name)
+{
+ int err;
+
+ while ((err = mdb_dem_process(dmp, name)) == DEMANGLE_ESPACE) {
+ size_t len = dmp->dm_len * 2;
+ char *buf = mdb_alloc(len, UM_NOSLEEP);
+
+ if (buf == NULL) {
+ mdb_warn("failed to allocate memory for demangling");
+ return (name); /* just return original name */
+ }
+
+ mdb_free(dmp->dm_buf, dmp->dm_len);
+ dmp->dm_buf = buf;
+ dmp->dm_len = len;
+ }
+
+ if (err != 0 || strcmp(dmp->dm_buf, name) == 0)
+ return (name); /* return original name if not mangled */
+
+ return (mdb_dem_filter(dmp, name));
+}
+
+/*ARGSUSED*/
+int
+cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_demangler_t *dmp = mdb.m_demangler;
+ const char *path = LIB_DEMANGLE;
+
+ if (argc > 1 || (argc > 0 && argv->a_type != MDB_TYPE_STRING))
+ return (DCMD_USAGE);
+
+ if (argc > 0) {
+ if (dmp != NULL)
+ mdb_dem_unload(mdb.m_demangler);
+ path = argv->a_un.a_str;
+ }
+
+ if (dmp != NULL && argc == 0 && !(mdb.m_flags & MDB_FL_DEMANGLE)) {
+ mdb_printf("C++ symbol demangling enabled\n");
+ mdb.m_flags |= MDB_FL_DEMANGLE;
+
+ } else if (dmp == NULL || argc > 0) {
+ if ((mdb.m_demangler = mdb_dem_load(path)) != NULL) {
+ mdb_printf("C++ symbol demangling enabled\n");
+ mdb.m_flags |= MDB_FL_DEMANGLE;
+ } else {
+ mdb_warn("failed to load C++ demangler %s", path);
+ mdb.m_flags &= ~MDB_FL_DEMANGLE;
+ }
+
+ } else {
+ mdb_dem_unload(mdb.m_demangler);
+ mdb.m_flags &= ~MDB_FL_DEMANGLE;
+ mdb.m_demangler = NULL;
+ mdb_printf("C++ symbol demangling disabled\n");
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+int
+cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ static const char *const dm_desc[] = {
+ "static/const/volatile member func qualifiers displayed",
+ "scope resolution specifiers displayed",
+ "function arguments displayed",
+ "mangled name displayed"
+ };
+
+ mdb_demangler_t *dmp = mdb.m_demangler;
+ int i;
+
+ if (argc > 0)
+ return (DCMD_USAGE);
+
+ if (dmp == NULL || !(mdb.m_flags & MDB_FL_DEMANGLE)) {
+ mdb_warn("C++ demangling facility is currently disabled\n");
+ return (DCMD_ERR);
+ }
+
+ if (flags & DCMD_ADDRSPEC)
+ dmp->dm_flags = ((uint_t)addr & MDB_DM_ALL);
+
+ for (i = 0; i < sizeof (dm_desc) / sizeof (dm_desc[0]); i++) {
+ mdb_printf("0x%x\t%s\t%s\n", 1 << i,
+ (dmp->dm_flags & (1 << i)) ? "on" : "off", dm_desc[i]);
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+int
+cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if ((flags & DCMD_ADDRSPEC) || argc == 0)
+ return (DCMD_USAGE);
+
+ if (mdb.m_demangler == NULL && (mdb.m_demangler =
+ mdb_dem_load(LIB_DEMANGLE)) == NULL) {
+ mdb_warn("failed to load C++ demangler %s", LIB_DEMANGLE);
+ return (DCMD_ERR);
+ }
+
+ for (; argc != 0; argc--, argv++) {
+ mdb_printf("%s == %s\n", argv->a_un.a_str,
+ mdb_dem_convert(mdb.m_demangler, argv->a_un.a_str));
+ }
+
+ return (DCMD_OK);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_demangle.h b/usr/src/cmd/mdb/common/mdb/mdb_demangle.h
new file mode 100644
index 0000000..7c9e99f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_demangle.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_DEMANGLE_H
+#define _MDB_DEMANGLE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <mdb/mdb_modapi.h>
+
+typedef struct mdb_demangler {
+ char dm_pathname[MAXPATHLEN]; /* pathname of demangling library */
+ void *dm_handle; /* rtld handle to demangling library */
+ int (*dm_convert)(const char *, char *, size_t); /* demangler */
+ char *dm_buf; /* demangling buffer */
+ size_t dm_len; /* size of dm_buf in bytes */
+ char *dm_dem; /* start of demangled string (in buf) */
+ uint_t dm_flags; /* convert flags (see below) */
+} mdb_demangler_t;
+
+#define MDB_DM_QUAL 0x1 /* show static/const/volatile */
+#define MDB_DM_SCOPE 0x2 /* show function scope specifiers */
+#define MDB_DM_FUNCARG 0x4 /* show function arguments */
+#define MDB_DM_MANGLED 0x8 /* show mangled name */
+#define MDB_DM_ALL 0xf /* mask of all valid flags */
+
+extern mdb_demangler_t *mdb_dem_load(const char *);
+extern void mdb_dem_unload(mdb_demangler_t *);
+extern const char *mdb_dem_convert(mdb_demangler_t *, const char *);
+
+extern int cmd_demangle(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_demflags(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_demstr(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_DEMANGLE_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_disasm.c b/usr/src/cmd/mdb/common/mdb/mdb_disasm.c
new file mode 100644
index 0000000..dd95cc9
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_disasm.c
@@ -0,0 +1,234 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_disasm_impl.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb.h>
+
+int
+mdb_dis_select(const char *name)
+{
+ mdb_var_t *v = mdb_nv_lookup(&mdb.m_disasms, name);
+
+ if (v != NULL) {
+ mdb.m_disasm = mdb_nv_get_cookie(v);
+ return (0);
+ }
+
+ if (mdb.m_target == NULL) {
+ if (mdb.m_defdisasm != NULL)
+ strfree(mdb.m_defdisasm);
+ mdb.m_defdisasm = strdup(name);
+ return (0);
+ }
+
+ return (set_errno(EMDB_NODIS));
+}
+
+mdb_disasm_t *
+mdb_dis_create(mdb_dis_ctor_f *ctor)
+{
+ mdb_disasm_t *dp = mdb_zalloc(sizeof (mdb_disasm_t), UM_SLEEP);
+
+ if ((dp->dis_module = mdb.m_lmod) == NULL)
+ dp->dis_module = &mdb.m_rmod;
+
+ if (ctor(dp) == 0) {
+ mdb_var_t *v = mdb_nv_lookup(&mdb.m_disasms, dp->dis_name);
+
+ if (v != NULL) {
+ dp->dis_ops->dis_destroy(dp);
+ mdb_free(dp, sizeof (mdb_disasm_t));
+ (void) set_errno(EMDB_DISEXISTS);
+ return (NULL);
+ }
+
+ (void) mdb_nv_insert(&mdb.m_disasms, dp->dis_name, NULL,
+ (uintptr_t)dp, MDB_NV_RDONLY | MDB_NV_SILENT);
+
+ if (mdb.m_disasm == NULL) {
+ mdb.m_disasm = dp;
+ } else if (mdb.m_defdisasm != NULL &&
+ strcmp(mdb.m_defdisasm, dp->dis_name) == 0) {
+ mdb.m_disasm = dp;
+ strfree(mdb.m_defdisasm);
+ mdb.m_defdisasm = NULL;
+ }
+
+ return (dp);
+ }
+
+ mdb_free(dp, sizeof (mdb_disasm_t));
+ return (NULL);
+}
+
+void
+mdb_dis_destroy(mdb_disasm_t *dp)
+{
+ mdb_var_t *v = mdb_nv_lookup(&mdb.m_disasms, dp->dis_name);
+
+ ASSERT(v != NULL);
+ mdb_nv_remove(&mdb.m_disasms, v);
+ dp->dis_ops->dis_destroy(dp);
+ mdb_free(dp, sizeof (mdb_disasm_t));
+
+ if (mdb.m_disasm == dp)
+ (void) mdb_dis_select("default");
+}
+
+mdb_tgt_addr_t
+mdb_dis_ins2str(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
+ char *buf, size_t len, mdb_tgt_addr_t addr)
+{
+ return (dp->dis_ops->dis_ins2str(dp, t, as, buf, len, addr));
+}
+
+mdb_tgt_addr_t
+mdb_dis_previns(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
+ mdb_tgt_addr_t addr, uint_t n)
+{
+ return (dp->dis_ops->dis_previns(dp, t, as, addr, n));
+}
+
+mdb_tgt_addr_t
+mdb_dis_nextins(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
+ mdb_tgt_addr_t addr)
+{
+ return (dp->dis_ops->dis_nextins(dp, t, as, addr));
+}
+
+/*ARGSUSED*/
+int
+cmd_dismode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if ((flags & DCMD_ADDRSPEC) || argc > 1)
+ return (DCMD_USAGE);
+
+ if (argc != 0) {
+ const char *name;
+
+ if (argv->a_type == MDB_TYPE_STRING)
+ name = argv->a_un.a_str;
+ else
+ name = numtostr(argv->a_un.a_val, 10, NTOS_UNSIGNED);
+
+ if (mdb_dis_select(name) == -1) {
+ warn("failed to set disassembly mode");
+ return (DCMD_ERR);
+ }
+ }
+
+ mdb_printf("disassembly mode is %s (%s)\n",
+ mdb.m_disasm->dis_name, mdb.m_disasm->dis_desc);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_dis(mdb_var_t *v, void *ignore)
+{
+ mdb_disasm_t *dp = mdb_nv_get_cookie(v);
+
+ mdb_printf("%-24s - %s\n", dp->dis_name, dp->dis_desc);
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cmd_disasms(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if ((flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ mdb_nv_sort_iter(&mdb.m_disasms, print_dis, NULL, UM_SLEEP | UM_GC);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static void
+defdis_destroy(mdb_disasm_t *dp)
+{
+ /* Nothing to do here */
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+defdis_ins2str(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
+ char *buf, size_t len, mdb_tgt_addr_t addr)
+{
+ return (addr);
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+defdis_previns(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
+ mdb_tgt_addr_t addr, uint_t n)
+{
+ return (addr);
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+defdis_nextins(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
+ mdb_tgt_addr_t addr)
+{
+ return (addr);
+}
+
+static const mdb_dis_ops_t defdis_ops = {
+ defdis_destroy,
+ defdis_ins2str,
+ defdis_previns,
+ defdis_nextins
+};
+
+static int
+defdis_create(mdb_disasm_t *dp)
+{
+ dp->dis_name = "default";
+ dp->dis_desc = "default no-op disassembler";
+ dp->dis_ops = &defdis_ops;
+
+ return (0);
+}
+
+mdb_dis_ctor_f *const mdb_dis_builtins[] = {
+#if defined(__amd64)
+ ia32_create,
+ amd64_create,
+#elif defined(__i386)
+ ia32_create,
+#endif
+ defdis_create,
+ NULL
+};
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_disasm.h b/usr/src/cmd/mdb/common/mdb/mdb_disasm.h
new file mode 100644
index 0000000..50a84ec
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_disasm.h
@@ -0,0 +1,83 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_DISASM_H
+#define _MDB_DISASM_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_modapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+/*
+ * Forward declaration of the disassembler structure: the internals are defined
+ * in mdb_disasm_impl.h and is opaque with respect to callers of this interface.
+ */
+
+struct mdb_disasm;
+typedef struct mdb_disasm mdb_disasm_t;
+
+/*
+ * Disassemblers are created by calling mdb_dis_create() with a disassembler
+ * constructor function. A constructed disassembler can be selected (made
+ * the current disassembler) by invoking mdb_dis_select().
+ */
+
+typedef int mdb_dis_ctor_f(mdb_disasm_t *);
+
+extern int mdb_dis_select(const char *);
+extern mdb_disasm_t *mdb_dis_create(mdb_dis_ctor_f *);
+extern void mdb_dis_destroy(mdb_disasm_t *);
+
+/*
+ * Disassembler operations - instruction-to-string and backstep.
+ */
+extern mdb_tgt_addr_t mdb_dis_ins2str(mdb_disasm_t *, mdb_tgt_t *,
+ mdb_tgt_as_t, char *, size_t, mdb_tgt_addr_t);
+extern mdb_tgt_addr_t mdb_dis_previns(mdb_disasm_t *, mdb_tgt_t *,
+ mdb_tgt_as_t, mdb_tgt_addr_t, uint_t);
+extern mdb_tgt_addr_t mdb_dis_nextins(mdb_disasm_t *, mdb_tgt_t *,
+ mdb_tgt_as_t, mdb_tgt_addr_t);
+
+/*
+ * Builtin dcmds for selecting and listing disassemblers:
+ */
+extern int cmd_dismode(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_disasms(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_DISASM_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_disasm_impl.h b/usr/src/cmd/mdb/common/mdb/mdb_disasm_impl.h
new file mode 100644
index 0000000..9f68c5e
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_disasm_impl.h
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_DISASM_IMPL_H
+#define _MDB_DISASM_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Disassembler Implementation
+ *
+ * Each disassembler provides a string name (for selection with $V or -V),
+ * a brief description, and the set of operations defined in mdb_dis_ops_t.
+ * Currently the interface defined here is very primitive, but we hope to
+ * greatly enhance it in the future if we have a two-pass disassembler.
+ */
+
+#include <mdb/mdb_disasm.h>
+#include <mdb/mdb_module.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_dis_ops {
+ void (*dis_destroy)(mdb_disasm_t *);
+ mdb_tgt_addr_t (*dis_ins2str)(mdb_disasm_t *, mdb_tgt_t *,
+ mdb_tgt_as_t, char *, size_t, mdb_tgt_addr_t);
+ mdb_tgt_addr_t (*dis_previns)(mdb_disasm_t *, mdb_tgt_t *,
+ mdb_tgt_as_t, mdb_tgt_addr_t, uint_t);
+ mdb_tgt_addr_t (*dis_nextins)(mdb_disasm_t *, mdb_tgt_t *,
+ mdb_tgt_as_t, mdb_tgt_addr_t);
+} mdb_dis_ops_t;
+
+struct mdb_disasm {
+ const char *dis_name; /* Disassembler name */
+ const char *dis_desc; /* Brief description */
+ mdb_module_t *dis_module; /* Backpointer to containing module */
+ const mdb_dis_ops_t *dis_ops; /* Pointer to ops vector */
+ void *dis_data; /* Private storage */
+};
+
+#ifdef _MDB
+
+#if defined(__sparc)
+extern mdb_dis_ctor_f sparc1_create;
+extern mdb_dis_ctor_f sparc2_create;
+extern mdb_dis_ctor_f sparc4_create;
+extern mdb_dis_ctor_f sparcv8_create;
+extern mdb_dis_ctor_f sparcv9_create;
+extern mdb_dis_ctor_f sparcv9plus_create;
+#else /* __i386 */
+extern mdb_dis_ctor_f ia32_create;
+#if defined(__amd64)
+extern mdb_dis_ctor_f amd64_create;
+#endif /* __amd64 */
+#endif /* __sparc */
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_DISASM_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_dump.c b/usr/src/cmd/mdb/common/mdb/mdb_dump.c
new file mode 100644
index 0000000..0aeac63
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_dump.c
@@ -0,0 +1,421 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_dump.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+#include <limits.h>
+
+#define DUMP_PARAGRAPH 16
+#define DUMP_WIDTH(x) (DUMP_PARAGRAPH * ((((x) >> 16) & 0xf) + 1))
+#define DUMP_GROUP(x) ((((x) >> 20) & 0xff) + 1)
+#define DUMP_MAXWIDTH DUMP_WIDTH(MDB_DUMP_WIDTH(0x10))
+
+/*
+ * This is the implementation of mdb's generic hexdump facility (though
+ * not named such in case we decide to add support for other radices).
+ * While it is possible to call mdb_dump_internal directly, it is
+ * recommended that you use mdb_dumpptr or mdb_dump64 instead.
+ */
+
+
+/*
+ * Output the header for the dump. pad is the width of the address
+ * field, and offset is the index of the byte that we want highlighted.
+ * If the output isn't MDB_DUMP_ALIGNed, we use offset to adjust the
+ * labels to reflect the true least significant address nibble.
+ */
+
+static void
+mdb_dump_header(int flags, int pad, int offset)
+{
+ int nalign = !(flags & MDB_DUMP_ALIGN);
+ int group = DUMP_GROUP(flags);
+ int width = DUMP_WIDTH(flags);
+ int i;
+
+ mdb_printf("%*s ", pad, "");
+ for (i = 0; i < width; i++) {
+ if (!(i % group))
+ mdb_printf((group == 1 && i && !(i % 8)) ? " " : " ");
+ if (i == offset && !nalign)
+ mdb_printf("\\/");
+ else
+ mdb_printf("%2x", (i + (nalign * offset)) & 0xf);
+ }
+
+ if (flags & MDB_DUMP_ASCII) {
+ mdb_printf(" ");
+ for (i = 0; i < width; i++) {
+ if (i == offset && !nalign)
+ mdb_printf("v");
+ else
+ mdb_printf("%x", (i + (nalign * offset)) & 0xf);
+ }
+ }
+
+ mdb_printf("\n");
+}
+
+
+/*
+ * Output a line of data. pad is as defined above. A non-zero lmargin
+ * and/or rmargin indicate a set of bytes that shouldn't be printed.
+ */
+
+static void
+mdb_dump_data(uint64_t addr, uchar_t *buf, int flags, int pad,
+ int lmargin, int rmargin)
+{
+ uchar_t abuf[DUMP_MAXWIDTH + 1];
+ int group = DUMP_GROUP(flags);
+ int width = DUMP_WIDTH(flags);
+ int i;
+#ifdef _LITTLE_ENDIAN
+ int flip = FALSE;
+
+ if (flags & MDB_DUMP_ENDIAN)
+ flip = TRUE;
+#endif
+
+ mdb_printf("%0*llx: ", pad, addr);
+
+ for (i = 0; i < width; i++) {
+ if (!(i % group))
+ mdb_printf((group == 1 && i && !(i % 8)) ? " " : " ");
+ if (i < lmargin || (width - i) <= rmargin) {
+ mdb_printf(" ");
+#ifdef _LITTLE_ENDIAN
+ } else if (flip) {
+ int j = group * ((i / group) + 1) - (i % group) - 1;
+ mdb_printf("%02x", buf[j]);
+#endif
+ } else {
+ mdb_printf("%02x", buf[i]);
+ }
+ }
+
+ if (flags & MDB_DUMP_ASCII) {
+ for (i = 0; i < width; i++)
+ if (i < lmargin || (width - i) <= rmargin)
+ abuf[i] = ' ';
+ else if (buf[i] < ' ' || buf[i] > '~')
+ abuf[i] = '.';
+ else
+ abuf[i] = buf[i];
+ abuf[width] = '\0';
+ mdb_printf(" %s", abuf);
+ }
+
+ mdb_printf("\n");
+}
+
+
+/*
+ * Given an address and a length, compute the number of characters
+ * needed to display addresses within that range.
+ */
+
+static int
+mdb_dump_pad(uint64_t addr, uint64_t len, int flags, int bytes)
+{
+ uint64_t x;
+ int bits;
+
+ if (flags & MDB_DUMP_PEDANT) {
+ /*
+ * Assume full width pointers
+ */
+ bits = NBBY * bytes;
+ } else {
+ /*
+ * Vary width based on address and length, but first
+ * check to see if the address is relevant.
+ */
+ if (len > 1 || (addr && len == 1))
+ len--;
+ if (flags & MDB_DUMP_RELATIVE)
+ x = len;
+ else
+ x = len + addr;
+
+ bits = 0;
+ while (x) {
+ bits++;
+ x >>= 1;
+ }
+ }
+
+ return ((bits + 3) / 4);
+}
+
+
+/*
+ * The main dump routine, called by mdb_dump64 and (indirectly) by
+ * mdb_dumpptr. Arguments:
+ * addr - the address to start dumping at
+ * len - the amount of data to dump
+ * flags - to tune operation (see mdb_modapi.h)
+ * func - callback function used to obtain data
+ * arg - argument to pass to callback function
+ * bytes - size of pointer type
+ */
+
+int
+mdb_dump_internal(uint64_t addr, uint64_t len, int flags, mdb_dump64_cb_t func,
+ void *arg, int bytes)
+{
+ uchar_t buffers[2][DUMP_MAXWIDTH];
+ uchar_t *buf, *pbuf;
+ uint64_t i;
+ ssize_t j;
+ uint64_t addrmax;
+ uint64_t offset; /* bytes between first position and addr */
+ uint64_t reqlen = len; /* requested length */
+ int l, r; /* left and right margins */
+ int pskip; /* previous line was skipped */
+ int pvalid; /* previous line was valid (we may skip) */
+ int bread, bwanted; /* used to handle partial reads */
+ int pad, n;
+ int group, width;
+ int err = 0;
+
+ addrmax = (1LL << (bytes * NBBY - 1)) - 1 + (1LL << (bytes * NBBY - 1));
+
+ /*
+ * Ensure that len doesn't wrap around the end of addressable
+ * memory. Note that because we take an address and a length,
+ * it isn't possible to dump from 0 to UINT64_MAX if
+ * MDB_DUMP_TRIM is set.
+ */
+ if (len && (len - 1 > addrmax - addr)) {
+ len = addrmax - addr;
+ if (addr || (addrmax < UINT64_MAX))
+ len++;
+ }
+
+ /*
+ * If a) the grouping isn't a power of two, or
+ * b) the display width is not evenly divisible by the grouping
+ * we ignore the specified grouping (and default to 4).
+ */
+ group = DUMP_GROUP(flags);
+ width = DUMP_WIDTH(flags);
+ if (((group - 1) & group) || (width % group)) {
+ group = 4;
+ flags = (flags & 0xfffff) | MDB_DUMP_GROUP(group);
+ }
+
+ /*
+ * If we are reordering bytes to adjust for endianness, turn
+ * off text output, headers, and alignment to cut down on the
+ * number of special cases (and confusing output). For
+ * correctness, we will continue to observe MDB_DUMP_TRIM, but
+ * will truncate output if the specified length isn't a
+ * multiple of the grouping.
+ */
+ if (flags & MDB_DUMP_ENDIAN) {
+ flags &= ~(MDB_DUMP_ALIGN | MDB_DUMP_HEADER | MDB_DUMP_ASCII);
+ if (flags & MDB_DUMP_TRIM)
+ len -= len % group;
+ }
+
+ /*
+ * If we are interested in seeing the data indexed relative to
+ * the starting location, paragraph alignment is irrelevant.
+ * The left margin will always be 0.
+ */
+ if (flags & MDB_DUMP_RELATIVE) {
+ flags &= ~MDB_DUMP_ALIGN;
+ l = 0;
+ } else {
+ l = addr % DUMP_PARAGRAPH;
+ }
+
+ /*
+ * Compute the width of our addresses, and adjust our starting
+ * point based on the address and the state of the alignment
+ * flag.
+ */
+ pad = mdb_dump_pad(addr, len, flags, bytes);
+ if (flags & MDB_DUMP_ALIGN) {
+ len += l;
+ addr -= l;
+ offset = l;
+ } else {
+ offset = 0;
+ }
+
+ /*
+ * Display the header (if appropriate), using the left margin
+ * to determine what our column header offset should be.
+ */
+ if (flags & MDB_DUMP_HEADER)
+ mdb_dump_header(flags, pad, l);
+
+ /*
+ * If we aren't trimming and aligning the output, the left
+ * margin is now irrelevant and should be zeroed.
+ */
+ if (!(flags & MDB_DUMP_TRIM) || !(flags & MDB_DUMP_ALIGN))
+ l = 0;
+
+ /*
+ * We haven't skipped the previous line, it isn't valid to skip
+ * the current line, and we use buffer 0 first. lint doesn't
+ * realize that this implies pbuf won't be accessed until after
+ * it is set, so we explicitly initialize that here, too.
+ */
+ pskip = pvalid = FALSE;
+ pbuf = NULL;
+ n = 0;
+ r = 0;
+
+ for (i = 0; i < len && r == 0; i += width) {
+ /*
+ * Select the current buffer.
+ */
+ buf = buffers[n];
+
+ /*
+ * We have a right margin only if we are on the last
+ * line and either (1) MDB_DUMP_TRIM is set or (2) our
+ * untrimmed output would require reading past the end
+ * of addressable memory. In either case, we clear
+ * pvalid since we don't want to skip the last line.
+ */
+ if ((uint64_t)width >= len - i) {
+ pvalid = FALSE;
+ if (flags & MDB_DUMP_TRIM)
+ r = width - (len - i);
+ if ((uint64_t)width - 1 > addrmax - (addr + i)) {
+ int nr = width - (addrmax - (addr + i)) - 1;
+ r = MAX(r, nr);
+ }
+ }
+
+ /*
+ * Read data into the current buffer, obeying the left
+ * and right margins.
+ *
+ * We handle read(2)-style partial results by
+ * repeatedly calling the callback until we fill the
+ * buffer, we get a 0 (end of file), or we get a -1
+ * (error). We take care to never read the same data
+ * twice, though.
+ *
+ * mdb(1)-style partial results (i.e. EMDB_PARTIAL) are
+ * treated like any other error. If more exotic
+ * handling is desired, the caller is free to wrap
+ * their callback with an auxiliary function. See
+ * mdb_dumpptr and mdb_dump64 for examples of this.
+ */
+ bread = l;
+ bwanted = width - r;
+ while (bread < bwanted) {
+ j = func(buf + bread, bwanted - bread,
+ addr + i + bread, arg);
+ if (j <= 0) {
+ if (i + bread < offset) {
+ l++;
+ j = 1;
+ } else {
+ r += bwanted - bread;
+ pvalid = FALSE;
+ if (j == -1)
+ err = errno;
+ if (bread == l) {
+ i += width;
+ goto out;
+ }
+ break;
+ }
+ }
+ bread += j;
+ }
+
+ /*
+ * If we are eliminating repeated lines, AND it is
+ * valid to eliminate this line, AND the current line
+ * is the same as the previous line, don't print the
+ * current line. If we didn't skip the previous line,
+ * print an asterisk and set the previous-line-skipped
+ * flag.
+ *
+ * Otherwise, print the line and clear the
+ * previous-line-skipped flag.
+ */
+ if ((flags & MDB_DUMP_SQUISH) && pvalid &&
+ (memcmp(buf, pbuf, width) == 0)) {
+ if (!pskip) {
+ mdb_printf("*\n");
+ pskip = TRUE;
+ }
+ } else {
+ if (flags & MDB_DUMP_RELATIVE)
+ mdb_dump_data(i, buf, flags, pad, l, r);
+ else
+ mdb_dump_data(addr + i, buf, flags, pad, l, r);
+ pskip = FALSE;
+ }
+
+ /*
+ * If we have a non-zero left margin then we don't have
+ * a full buffer of data and we shouldn't try to skip
+ * the next line. It doesn't matter if the right
+ * margin is non-zero since we'll fall out of the loop.
+ */
+ if (!l)
+ pvalid = TRUE;
+
+ /*
+ * Swap buffers, and zero the left margin.
+ */
+ n = (n + 1) % 2;
+ pbuf = buf;
+ l = 0;
+ }
+
+out:
+ /*
+ * If we successfully dumped everything, update . to be the
+ * address following that of the last byte requested.
+ */
+ if (i - r - offset >= reqlen) {
+ if (flags & MDB_DUMP_NEWDOT)
+ mdb_set_dot(addr + offset + reqlen);
+ } else if (err) {
+ errno = err;
+ mdb_warn("failed to read data at %#llx", addr + i - r);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_dump.h b/usr/src/cmd/mdb/common/mdb/mdb_dump.h
new file mode 100644
index 0000000..ce659ef
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_dump.h
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001-2002 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_DUMP_H
+#define _MDB_DUMP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_modapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int mdb_dump_internal(uint64_t, uint64_t, int, mdb_dump64_cb_t,
+ void *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_DUMP_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_err.c b/usr/src/cmd/mdb/common/mdb/mdb_err.c
new file mode 100644
index 0000000..4c31551
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_err.c
@@ -0,0 +1,320 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_signal.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+#include <thread_db.h>
+#include <rtld_db.h>
+#include <libctf.h>
+#include <strings.h>
+#include <stdlib.h>
+
+static const char *const _mdb_errlist[] = {
+ "unknown symbol name", /* EMDB_NOSYM */
+ "unknown object file name", /* EMDB_NOOBJ */
+ "no mapping for address", /* EMDB_NOMAP */
+ "unknown dcmd name", /* EMDB_NODCMD */
+ "unknown walk name", /* EMDB_NOWALK */
+ "dcmd name already in use", /* EMDB_DCMDEXISTS */
+ "walk name already in use", /* EMDB_WALKEXISTS */
+ "no support for platform", /* EMDB_NOPLAT */
+ "no process active", /* EMDB_NOPROC */
+ "specified name is too long", /* EMDB_NAME2BIG */
+ "specified name contains illegal characters", /* EMDB_NAMEBAD */
+ "failed to allocate needed memory", /* EMDB_ALLOC */
+ "specified module is not loaded", /* EMDB_NOMOD */
+ "cannot unload built-in module", /* EMDB_BUILTINMOD */
+ "no walk is currently active", /* EMDB_NOWCB */
+ "invalid walk state argument", /* EMDB_BADWCB */
+ "walker does not accept starting address", /* EMDB_NOWALKLOC */
+ "walker requires starting address", /* EMDB_NOWALKGLOB */
+ "failed to initialize walk", /* EMDB_WALKINIT */
+ "walker cannot be layered on itself", /* EMDB_WALKLOOP */
+ "i/o stream is read-only", /* EMDB_IORO */
+ "i/o stream is write-only", /* EMDB_IOWO */
+ "no symbol corresponds to address", /* EMDB_NOSYMADDR */
+ "unknown disassembler name", /* EMDB_NODIS */
+ "disassembler name already in use", /* EMDB_DISEXISTS */
+ "no such software event specifier", /* EMDB_NOSESPEC */
+ "no such xdata available", /* EMDB_NOXD */
+ "xdata name already in use", /* EMDB_XDEXISTS */
+ "operation not supported by target", /* EMDB_TGTNOTSUP */
+ "target is not open for writing", /* EMDB_TGTRDONLY */
+ "invalid register name", /* EMDB_BADREG */
+ "no register set available for thread", /* EMDB_NOREGS */
+ "stack address is not properly aligned", /* EMDB_STKALIGN */
+ "no executable file is open", /* EMDB_NOEXEC */
+ "failed to evaluate command", /* EMDB_EVAL */
+ "command cancelled by user", /* EMDB_CANCEL */
+ "only %lu of %lu bytes could be read", /* EMDB_PARTIAL */
+ "dcmd failed", /* EMDB_DCFAIL */
+ "improper dcmd usage", /* EMDB_DCUSAGE */
+ "target error", /* EMDB_TGT */
+ "invalid system call number", /* EMDB_BADSYSNUM */
+ "invalid signal number", /* EMDB_BADSIGNUM */
+ "invalid fault number", /* EMDB_BADFLTNUM */
+ "target is currently executing", /* EMDB_TGTBUSY */
+ "target has completed execution", /* EMDB_TGTZOMB */
+ "target is a core file", /* EMDB_TGTCORE */
+ "debugger lost control of target", /* EMDB_TGTLOST */
+ "libthread_db call failed unexpectedly", /* EMDB_TDB */
+ "failed to dlopen library", /* EMDB_RTLD */
+ "librtld_db call failed unexpectedly", /* EMDB_RTLD_DB */
+ "runtime linker data not available", /* EMDB_NORTLD */
+ "invalid thread identifier", /* EMDB_NOTHREAD */
+ "event specifier disabled", /* EMDB_SPECDIS */
+ "unknown link map id", /* EMDB_NOLMID */
+ "failed to determine return address", /* EMDB_NORETADDR */
+ "watchpoint size exceeds address space limit", /* EMDB_WPRANGE */
+ "conflict with existing watchpoint", /* EMDB_WPDUP */
+ "address not aligned on an instruction boundary", /* EMDB_BPALIGN */
+ "library is missing demangler entry point", /* EMDB_NODEM */
+ "cannot read past current end of file", /* EMDB_EOF */
+ "no symbolic debug information available for module", /* EMDB_NOCTF */
+ "libctf call failed unexpectedly", /* EMDB_CTF */
+ "thread local storage has not yet been allocated", /* EMDB_TLS */
+ "object does not support thread local storage", /* EMDB_NOTLS */
+ "no such member of structure or union", /* EMDB_CTFNOMEMB */
+ "inappropriate context for action", /* EMDB_CTX */
+ "module incompatible with target", /* EMDB_INCOMPAT */
+ "operation not supported by target on this platform",
+ /* EMDB_TGTHWNOTSUP */
+ "kmdb is not loaded", /* EMDB_KINACTIVE */
+ "kmdb is loading", /* EMDB_KACTIVATING */
+ "kmdb is already loaded", /* EMDB_KACTIVE */
+ "kmdb is unloading", /* EMDB_KDEACTIVATING */
+ "kmdb could not be loaded", /* EMDB_KNOLOAD */
+ "boot-loaded kmdb cannot be unloaded", /* EMDB_KNOUNLOAD */
+ "too many enabled watchpoints for this machine", /* EMDB_WPTOOMANY */
+ "DTrace is active", /* EMDB_DTACTIVE */
+ "boot-loaded module cannot be unloaded" /* EMDB_KMODNOUNLOAD */
+};
+
+static const int _mdb_nerr = sizeof (_mdb_errlist) / sizeof (_mdb_errlist[0]);
+
+static size_t errno_rbytes; /* EMDB_PARTIAL actual bytes read */
+static size_t errno_nbytes; /* EMDB_PARTIAL total bytes requested */
+static int errno_libctf; /* EMDB_CTF underlying error code */
+#ifndef _KMDB
+static int errno_rtld_db; /* EMDB_RTLD_DB underlying error code */
+#endif
+
+const char *
+mdb_strerror(int err)
+{
+ static char buf[256];
+ const char *str;
+
+ if (err >= EMDB_BASE && (err - EMDB_BASE) < _mdb_nerr)
+ str = _mdb_errlist[err - EMDB_BASE];
+ else
+ str = strerror(err);
+
+ switch (err) {
+ case EMDB_PARTIAL:
+ (void) mdb_iob_snprintf(buf, sizeof (buf), str,
+ errno_rbytes, errno_nbytes);
+ str = buf;
+ break;
+
+#ifndef _KMDB
+ case EMDB_RTLD_DB:
+ if (rd_errstr(errno_rtld_db) != NULL)
+ str = rd_errstr(errno_rtld_db);
+ break;
+#endif
+
+ case EMDB_CTF:
+ if (ctf_errmsg(errno_libctf) != NULL)
+ str = ctf_errmsg(errno_libctf);
+ break;
+ }
+
+ return (str ? str : "unknown error");
+}
+
+void
+vwarn(const char *format, va_list alist)
+{
+ int err = errno;
+
+ mdb_iob_printf(mdb.m_err, "%s: ", mdb.m_pname);
+ mdb_iob_vprintf(mdb.m_err, format, alist);
+
+ if (strchr(format, '\n') == NULL)
+ mdb_iob_printf(mdb.m_err, ": %s\n", mdb_strerror(err));
+}
+
+void
+vdie(const char *format, va_list alist)
+{
+ vwarn(format, alist);
+ mdb_destroy();
+ exit(1);
+}
+
+void
+vfail(const char *format, va_list alist)
+{
+ extern const char *volatile _mdb_abort_str;
+ static char buf[256];
+ static int nfail;
+
+ if (_mdb_abort_str == NULL) {
+ _mdb_abort_str = buf; /* Do this first so we don't recurse */
+ (void) mdb_iob_vsnprintf(buf, sizeof (buf), format, alist);
+
+ nfail = 1;
+ }
+
+ /*
+ * We'll try to print failure messages twice. Any more than that,
+ * and we're probably hitting an assertion or some other problem in
+ * the printing routines, and will recurse until we run out of stack.
+ */
+ if (nfail++ < 3) {
+ mdb_iob_printf(mdb.m_err, "%s ABORT: ", mdb.m_pname);
+ mdb_iob_vprintf(mdb.m_err, format, alist);
+ mdb_iob_flush(mdb.m_err);
+
+ (void) mdb_signal_blockall();
+ (void) mdb_signal_raise(SIGABRT);
+ (void) mdb_signal_unblock(SIGABRT);
+ }
+
+ exit(1);
+}
+
+/*PRINTFLIKE1*/
+void
+warn(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vwarn(format, alist);
+ va_end(alist);
+}
+
+/*PRINTFLIKE1*/
+void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vdie(format, alist);
+ va_end(alist);
+}
+
+/*PRINTFLIKE1*/
+void
+fail(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vfail(format, alist);
+ va_end(alist);
+}
+
+int
+set_errbytes(size_t rbytes, size_t nbytes)
+{
+ errno_rbytes = rbytes;
+ errno_nbytes = nbytes;
+ errno = EMDB_PARTIAL;
+ return (-1);
+}
+
+int
+set_errno(int err)
+{
+ errno = err;
+ return (-1);
+}
+
+int
+ctf_to_errno(int err)
+{
+ errno_libctf = err;
+ return (EMDB_CTF);
+}
+
+#ifndef _KMDB
+/*
+ * The libthread_db interface is a superfund site and provides no strerror
+ * equivalent for us to call: we try to provide some sensible handling for its
+ * garbage bin of error return codes here. First of all, we don't bother
+ * interpreting all of the possibilities, since many of them aren't even used
+ * in the implementation anymore. We try to map thread_db errors we may see
+ * to UNIX errnos or mdb errnos as appropriate.
+ */
+int
+tdb_to_errno(int err)
+{
+ switch (err) {
+ case TD_OK:
+ case TD_PARTIALREG:
+ return (0);
+ case TD_NOCAPAB:
+ return (ENOTSUP);
+ case TD_BADPH:
+ case TD_BADTH:
+ case TD_BADSH:
+ case TD_BADTA:
+ case TD_BADKEY:
+ case TD_NOEVENT:
+ return (EINVAL);
+ case TD_NOFPREGS:
+ case TD_NOXREGS:
+ return (EMDB_NOREGS);
+ case TD_NOTHR:
+ return (EMDB_NOTHREAD);
+ case TD_MALLOC:
+ return (EMDB_ALLOC);
+ case TD_TLSDEFER:
+ return (EMDB_TLS);
+ case TD_NOTLS:
+ return (EMDB_NOTLS);
+ case TD_DBERR:
+ case TD_ERR:
+ default:
+ return (EMDB_TDB);
+ }
+}
+
+int
+rdb_to_errno(int err)
+{
+ errno_rtld_db = err;
+ return (EMDB_RTLD_DB);
+}
+#endif /* _KMDB */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_err.h b/usr/src/cmd/mdb/common/mdb/mdb_err.h
new file mode 100644
index 0000000..e0834c2
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_err.h
@@ -0,0 +1,70 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_ERR_H
+#define _MDB_ERR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <mdb/mdb_errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+extern const char *mdb_strerror(int);
+
+extern void vwarn(const char *, va_list);
+extern void vdie(const char *, va_list);
+extern void vfail(const char *, va_list);
+
+extern void warn(const char *, ...);
+extern void die(const char *, ...);
+extern void fail(const char *, ...);
+
+extern int set_errbytes(size_t, size_t);
+extern int set_errno(int);
+
+extern int ctf_to_errno(int);
+
+#ifndef _KMDB
+extern int tdb_to_errno(int);
+extern int rdb_to_errno(int);
+#endif
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_ERR_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_errno.h b/usr/src/cmd/mdb/common/mdb/mdb_errno.h
new file mode 100644
index 0000000..09dd877
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_errno.h
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_ERRNO_H
+#define _MDB_ERRNO_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+#define EMDB_BASE 1000 /* Base value for mdb errnos */
+
+enum {
+ EMDB_NOSYM = EMDB_BASE, /* Symbol not found */
+ EMDB_NOOBJ, /* Object file not found */
+ EMDB_NOMAP, /* No mapping for address */
+ EMDB_NODCMD, /* Dcmd not found */
+ EMDB_NOWALK, /* Walk not found */
+ EMDB_DCMDEXISTS, /* Dcmd already exists */
+ EMDB_WALKEXISTS, /* Walk already exists */
+ EMDB_NOPLAT, /* No platform support */
+ EMDB_NOPROC, /* No process created yet */
+ EMDB_NAME2BIG, /* Name is too long */
+ EMDB_NAMEBAD, /* Name is invalid */
+ EMDB_ALLOC, /* Failed to allocate memory */
+ EMDB_NOMOD, /* Module not found */
+ EMDB_BUILTINMOD, /* Cannot unload builtin mod */
+ EMDB_NOWCB, /* No walk is active */
+ EMDB_BADWCB, /* Invalid walk state */
+ EMDB_NOWALKLOC, /* Walker doesn't accept addr */
+ EMDB_NOWALKGLOB, /* Walker requires addr */
+ EMDB_WALKINIT, /* Walker init failed */
+ EMDB_WALKLOOP, /* Walker layering loop */
+ EMDB_IORO, /* I/O stream is read-only */
+ EMDB_IOWO, /* I/O stream is write-only */
+ EMDB_NOSYMADDR, /* No symbol for address */
+ EMDB_NODIS, /* Disassembler not found */
+ EMDB_DISEXISTS, /* Disassembler exists */
+ EMDB_NOSESPEC, /* No software event spec */
+ EMDB_NOXD, /* No such xdata */
+ EMDB_XDEXISTS, /* Xdata name already exists */
+ EMDB_TGTNOTSUP, /* Op not supported by tgt */
+ EMDB_TGTRDONLY, /* Tgt not open for writing */
+ EMDB_BADREG, /* Invalid register name */
+ EMDB_NOREGS, /* No registers for thread */
+ EMDB_STKALIGN, /* Bad stack pointer align */
+ EMDB_NOEXEC, /* No executable file open */
+ EMDB_EVAL, /* Failed to mdb_eval() */
+ EMDB_CANCEL, /* Command cancelled by user */
+ EMDB_PARTIAL, /* Partial read occurred */
+ EMDB_DCFAIL, /* Dcmd failed */
+ EMDB_DCUSAGE, /* Dcmd usage error */
+ EMDB_TGT, /* Internal target error */
+ EMDB_BADSYSNUM, /* Invalid system call code */
+ EMDB_BADSIGNUM, /* Invalid signal number */
+ EMDB_BADFLTNUM, /* Invalid fault number */
+ EMDB_TGTBUSY, /* Target is busy executing */
+ EMDB_TGTZOMB, /* Target is a zombie */
+ EMDB_TGTCORE, /* Target is a core file */
+ EMDB_TGTLOST, /* Target is lost to mdb */
+ EMDB_TDB, /* libthread_db error */
+ EMDB_RTLD, /* libdl error */
+ EMDB_RTLD_DB, /* librtld_db error */
+ EMDB_NORTLD, /* no librtld_db */
+ EMDB_NOTHREAD, /* Invalid thread identifier */
+ EMDB_SPECDIS, /* Event specifier disabled */
+ EMDB_NOLMID, /* Link map not found */
+ EMDB_NORETADDR, /* No return address found */
+ EMDB_WPRANGE, /* Watchpoint size overflow */
+ EMDB_WPDUP, /* Watchpoint duplicate */
+ EMDB_BPALIGN, /* Breakpoint alignment err */
+ EMDB_NODEM, /* Bad demangler library */
+ EMDB_EOF, /* Read failed at EOF */
+ EMDB_NOCTF, /* No CTF data for module */
+ EMDB_CTF, /* libctf error */
+ EMDB_TLS, /* TLS not allocated */
+ EMDB_NOTLS, /* TLS not supported in obj */
+ EMDB_CTFNOMEMB, /* No CTF member of type */
+ EMDB_CTX, /* Action in invalid context */
+ EMDB_INCOMPAT, /* Mod incompat. w/ target */
+ EMDB_TGTHWNOTSUP, /* Not sup by tgt on this h/w */
+ EMDB_KINACTIVE, /* kmdb is not loaded */
+ EMDB_KACTIVATING, /* kmdb is loading */
+ EMDB_KACTIVE, /* kmdb is already loaded */
+ EMDB_KDEACTIVATING, /* kmdb is unloading */
+ EMDB_KNOLOAD, /* kmdb could not be loaded */
+ EMDB_KNOUNLOAD, /* kmdb cannot be unloaded */
+ EMDB_WPTOOMANY, /* Too many watchpoints */
+ EMDB_DTACTIVE, /* DTrace is active */
+ EMDB_KMODNOUNLOAD /* module can't be unloaded */
+};
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_ERRNO_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_evset.c b/usr/src/cmd/mdb/common/mdb/mdb_evset.c
new file mode 100644
index 0000000..a30b29b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_evset.c
@@ -0,0 +1,718 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb.h>
+
+#include <libproc.h>
+#include <string.h>
+
+/*ARGSUSED*/
+void
+cmd_event(mdb_tgt_t *t, int vid, void *s)
+{
+ if (s != NULL && mdb_eval(s) == -1)
+ mdb_warn("failed to eval [ %d ] command \"%s\"", vid, s);
+}
+
+int
+cmd_evset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ uint_t setb = 0, clrb = 0;
+ const char *opt_c = NULL;
+ uint_t opt_F = FALSE;
+ uintptr_t opt_n = 0;
+
+ int *idv = mdb_zalloc(sizeof (int) * (argc + 1), UM_SLEEP | UM_GC);
+ int idc = 0;
+
+ int status = DCMD_OK;
+ const char *p;
+ void *data;
+ int argi;
+
+ if (flags & DCMD_ADDRSPEC)
+ idv[idc++] = (int)(intptr_t)addr;
+
+ /*
+ * Perform an initial pass through argv: we accumulate integer ids into
+ * idv, and compute a group of bits to set and a group to clear.
+ */
+ while (argc != 0 && (argi = mdb_getopts(argc, argv,
+ 'c', MDB_OPT_STR, &opt_c,
+ 'd', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODIS, &setb,
+ 'D', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODEL, &setb,
+ 'e', MDB_OPT_SETBITS, MDB_TGT_SPEC_DISABLED, &clrb,
+ 'F', MDB_OPT_SETBITS, TRUE, &opt_F,
+ 'n', MDB_OPT_UINTPTR, &opt_n,
+ 's', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTOSTOP, &setb,
+ 't', MDB_OPT_SETBITS, MDB_TGT_SPEC_TEMPORARY, &setb,
+ 'T', MDB_OPT_SETBITS, MDB_TGT_SPEC_STICKY, &setb,
+ NULL)) != argc) {
+
+ argv += argi; /* advance past elements processed by getopts */
+ argc -= argi; /* decrement argc by number of args processed */
+
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (argv->a_un.a_str[0] == '+') {
+ for (p = argv->a_un.a_str + 1; *p != '\0'; ) {
+ switch (*p++) {
+ case 'd':
+ clrb |= MDB_TGT_SPEC_AUTODIS;
+ break;
+ case 'D':
+ clrb |= MDB_TGT_SPEC_AUTODEL;
+ break;
+ case 'e':
+ setb |= MDB_TGT_SPEC_DISABLED;
+ break;
+ case 's':
+ clrb |= MDB_TGT_SPEC_AUTOSTOP;
+ break;
+ case 't':
+ clrb |= MDB_TGT_SPEC_TEMPORARY;
+ break;
+ case 'T':
+ clrb |= MDB_TGT_SPEC_STICKY;
+ break;
+ default:
+ mdb_warn("illegal option -- "
+ "+%c\n", p[-1]);
+ return (DCMD_USAGE);
+ }
+ }
+ } else if (argv->a_un.a_str[0] != '-') {
+ idv[idc++] = (int)(intmax_t)
+ strtonum(argv->a_un.a_str, 10);
+ } else
+ return (DCMD_USAGE);
+ } else
+ idv[idc++] = (int)(intmax_t)argv->a_un.a_val;
+
+ argc--;
+ argv++;
+ }
+
+ if (idc == 0) {
+ mdb_warn("expected one or more event IDs to be specified\n");
+ return (DCMD_USAGE);
+ }
+
+ /*
+ * If -n was not specified, then -d means "disable now" instead of
+ * meaning "set auto-disable after n hits".
+ */
+ if (opt_n == 0 && (setb & MDB_TGT_SPEC_AUTODIS))
+ setb = (setb & ~MDB_TGT_SPEC_AUTODIS) | MDB_TGT_SPEC_DISABLED;
+
+ while (idc-- != 0) {
+ mdb_tgt_spec_desc_t sp;
+ int id = *idv++;
+
+ bzero(&sp, sizeof (mdb_tgt_spec_desc_t));
+ (void) mdb_tgt_vespec_info(mdb.m_target, id, &sp, NULL, 0);
+ data = sp.spec_data;
+
+ if (opt_F == FALSE && (sp.spec_flags & MDB_TGT_SPEC_HIDDEN)) {
+ mdb_warn("cannot modify event %d: internal "
+ "debugger event\n", id);
+ status = DCMD_ERR;
+ continue;
+ }
+
+ sp.spec_flags |= setb;
+ sp.spec_flags &= ~clrb;
+
+ if (opt_c && !(sp.spec_flags & MDB_TGT_SPEC_HIDDEN)) {
+ if (opt_c[0] != '\0')
+ sp.spec_data = strdup(opt_c);
+ else
+ sp.spec_data = NULL;
+ }
+
+ if (opt_n)
+ sp.spec_limit = opt_n;
+
+ if (mdb_tgt_vespec_modify(mdb.m_target, id, sp.spec_flags,
+ sp.spec_limit, sp.spec_data) == -1) {
+ mdb_warn("failed to modify event %d", id);
+ data = sp.spec_data;
+ status = DCMD_ERR;
+ }
+
+ if (opt_c && data && !(sp.spec_flags & MDB_TGT_SPEC_HIDDEN))
+ strfree(data);
+ }
+
+ return (status);
+}
+
+/*
+ * Utility routine for performing the stock argument processing that is common
+ * among the dcmds that create event specifiers. We parse out the standard set
+ * of event property options from the command-line, and return a copy of the
+ * argument list to the caller that consists solely of the remaining non-option
+ * arguments. If a parsing error occurs, NULL is returned.
+ */
+static const mdb_arg_t *
+ev_getopts(uintmax_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
+ uint_t *evflags, char **opt_c, uint_t *opt_i, uint_t *opt_l,
+ uint64_t *opt_L, uintptr_t *opt_n, uint_t *opt_o, uint_t *opt_p,
+ uint_t *rwx)
+{
+ uint_t setb = 0, clrb = 0;
+ const char *p;
+ int argi;
+
+ mdb_arg_t *av;
+ int ac = 0;
+
+ /* keep lint happy */
+ *opt_p = FALSE;
+
+ av = mdb_alloc(sizeof (mdb_arg_t) * (argc + 2), UM_SLEEP | UM_GC);
+
+ /*
+ * If an address was specified, take it as an additional immediate
+ * value argument by adding it to the argument list.
+ */
+ if (flags & DCMD_ADDRSPEC) {
+ av[ac].a_type = MDB_TYPE_IMMEDIATE;
+ av[ac++].a_un.a_val = addr;
+ }
+
+ /*
+ * Now call mdb_getopts repeatedly to parse the argument list. We need
+ * to handle '+[a-z]' processing manually, and we also manually copy
+ * each non-option argument into the av[] array as we encounter them.
+ */
+ while (argc != 0 && (argi = mdb_getopts(argc, argv,
+ 'c', MDB_OPT_STR, opt_c,
+ 'd', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODIS, &setb,
+ 'D', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTODEL, &setb,
+ 'e', MDB_OPT_SETBITS, MDB_TGT_SPEC_DISABLED, &clrb,
+ 'i', MDB_OPT_SETBITS, TRUE, opt_i,
+ 'n', MDB_OPT_UINTPTR, opt_n,
+ 'o', MDB_OPT_SETBITS, TRUE, opt_o,
+#ifdef _KMDB
+ 'p', MDB_OPT_SETBITS, TRUE, opt_p,
+#endif
+ 'r', MDB_OPT_SETBITS, MDB_TGT_WA_R, rwx,
+ 's', MDB_OPT_SETBITS, MDB_TGT_SPEC_AUTOSTOP, &setb,
+ 'l', MDB_OPT_SETBITS, TRUE, opt_l,
+ 'L', MDB_OPT_UINT64, opt_L,
+ 't', MDB_OPT_SETBITS, MDB_TGT_SPEC_TEMPORARY, &setb,
+ 'T', MDB_OPT_SETBITS, MDB_TGT_SPEC_STICKY, &setb,
+ 'w', MDB_OPT_SETBITS, MDB_TGT_WA_W, rwx,
+ 'x', MDB_OPT_SETBITS, MDB_TGT_WA_X, rwx, NULL)) != argc) {
+
+ argv += argi; /* advance past elements processed by getopts */
+ argc -= argi; /* decrement argc by number of args processed */
+
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (argv->a_un.a_str[0] == '+') {
+ for (p = argv->a_un.a_str + 1; *p != '\0'; ) {
+ switch (*p++) {
+ case 'd':
+ clrb |= MDB_TGT_SPEC_AUTODIS;
+ break;
+ case 'D':
+ clrb |= MDB_TGT_SPEC_AUTODEL;
+ break;
+ case 'e':
+ setb |= MDB_TGT_SPEC_DISABLED;
+ break;
+ case 's':
+ clrb |= MDB_TGT_SPEC_AUTOSTOP;
+ break;
+ case 't':
+ clrb |= MDB_TGT_SPEC_TEMPORARY;
+ break;
+ case 'T':
+ clrb |= MDB_TGT_SPEC_STICKY;
+ break;
+ default:
+ mdb_warn("illegal option -- "
+ "+%c\n", p[-1]);
+ return (NULL);
+ }
+ }
+ } else if (argv->a_un.a_str[0] != '-') {
+ av[ac++] = *argv;
+ } else
+ return (NULL);
+ } else
+ av[ac++] = *argv;
+
+ argc--;
+ argv++;
+ }
+
+ /*
+ * If no arguments were found on the command-line, return NULL to
+ * indicate that the caller should return DCMD_USAGE.
+ */
+ if (ac == 0)
+ return (NULL);
+
+ /*
+ * If -n was not specified, then -d means "disable now" instead of
+ * meaning "set auto-disable after n hits".
+ */
+ if (opt_n == 0 && (setb & MDB_TGT_SPEC_AUTODIS))
+ setb = (setb & ~MDB_TGT_SPEC_AUTODIS) | MDB_TGT_SPEC_DISABLED;
+
+ /*
+ * Return the final set of flags, and terminate the argument array
+ * with a NULL string argument.
+ */
+ *evflags = setb & ~clrb;
+
+ av[ac].a_type = MDB_TYPE_STRING;
+ av[ac].a_un.a_str = NULL;
+
+ return (av);
+}
+
+/*
+ * Utility function for modifying the spec_data and spec_limit properties of an
+ * event specifier. We use this for handling the -c and -n options below.
+ */
+static void
+ev_setopts(mdb_tgt_t *t, int id, const char *opt_c, uintptr_t opt_n)
+{
+ mdb_tgt_spec_desc_t sp;
+
+ (void) mdb_tgt_vespec_info(t, id, &sp, NULL, 0);
+
+ if (opt_c != NULL)
+ sp.spec_data = strdup(opt_c);
+ if (opt_n != 0)
+ sp.spec_limit = opt_n;
+
+ if (mdb_tgt_vespec_modify(t, id, sp.spec_flags,
+ sp.spec_limit, sp.spec_data) == -1) {
+ mdb_warn("failed to modify event %d", id);
+ if (opt_c != NULL)
+ strfree(sp.spec_data);
+ }
+}
+
+int
+cmd_bp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char *opt_c = NULL;
+ uint_t opt_i = FALSE;
+ uint_t opt_l = FALSE;
+ uint64_t opt_L = 0;
+ uintptr_t opt_n = 0;
+ uint_t opt_o = FALSE;
+ uint_t opt_p = FALSE;
+ uint_t opt_rwx = 0;
+ int status = DCMD_OK;
+ int id;
+
+ if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
+ &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
+ opt_i || opt_o || opt_rwx != 0 || opt_l || opt_L != 0 || opt_p)
+ return (DCMD_USAGE);
+
+ while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
+ if (argv->a_type == MDB_TYPE_STRING) {
+ id = mdb_tgt_add_sbrkpt(mdb.m_target, argv->a_un.a_str,
+ flags, cmd_event, NULL);
+ } else {
+ id = mdb_tgt_add_vbrkpt(mdb.m_target, argv->a_un.a_val,
+ flags, cmd_event, NULL);
+ }
+
+ if (id == 0) {
+ mdb_warn("failed to add breakpoint at %s",
+ argv->a_type == MDB_TYPE_STRING ? argv->a_un.a_str :
+ numtostr(argv->a_un.a_val, mdb.m_radix,
+ NTOS_UNSIGNED | NTOS_SHOWBASE));
+ status = DCMD_ERR;
+
+ } else if (opt_c || opt_n)
+ ev_setopts(mdb.m_target, id, opt_c, opt_n);
+
+ argv++;
+ }
+
+ return (status);
+}
+
+
+int
+cmd_sigbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char *opt_c = NULL;
+ uint_t opt_i = FALSE;
+ uint_t opt_l = FALSE;
+ uint64_t opt_L = 0;
+ uintptr_t opt_n = 0;
+ uint_t opt_o = FALSE;
+ uint_t opt_p = FALSE;
+ uint_t opt_rwx = 0;
+ int status = DCMD_OK;
+ int id, sig;
+
+ if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
+ &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
+ opt_i || opt_l || opt_L != 0 || opt_o || opt_p || opt_rwx != 0)
+ return (DCMD_USAGE);
+
+ while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
+ mdb_warn("invalid signal name -- %s\n",
+ argv->a_un.a_str);
+ status = DCMD_ERR;
+ argv++;
+ continue;
+ }
+ } else
+ sig = (int)(intmax_t)argv->a_un.a_val;
+
+ if ((id = mdb_tgt_add_signal(mdb.m_target, sig, flags,
+ cmd_event, NULL)) == 0) {
+ mdb_warn("failed to trace signal %d", sig);
+ status = DCMD_ERR;
+ } else if (opt_c || opt_n)
+ ev_setopts(mdb.m_target, id, opt_c, opt_n);
+
+ argv++;
+ }
+
+ return (status);
+}
+
+int
+cmd_sysbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char *opt_c = NULL;
+ uint_t opt_i = FALSE;
+ uint_t opt_l = FALSE;
+ uint64_t opt_L = 0;
+ uintptr_t opt_n = 0;
+ uint_t opt_o = FALSE;
+ uint_t opt_p = FALSE;
+ uint_t opt_rwx = 0;
+ int status = DCMD_OK;
+ int id, sysnum;
+
+ if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
+ &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
+ (opt_i && opt_o) || opt_l || opt_L != 0 || opt_p || opt_rwx != 0)
+ return (DCMD_USAGE);
+
+ while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (proc_str2sys(argv->a_un.a_str, &sysnum) == -1) {
+ mdb_warn("invalid system call name -- %s\n",
+ argv->a_un.a_str);
+ status = DCMD_ERR;
+ argv++;
+ continue;
+ }
+ } else
+ sysnum = (int)(intmax_t)argv->a_un.a_val;
+
+ if (opt_o) {
+ id = mdb_tgt_add_sysexit(mdb.m_target, sysnum,
+ flags, cmd_event, NULL);
+ } else {
+ id = mdb_tgt_add_sysenter(mdb.m_target, sysnum,
+ flags, cmd_event, NULL);
+ }
+
+ if (id == 0) {
+ mdb_warn("failed to trace system call %d", sysnum);
+ status = DCMD_ERR;
+ } else if (opt_c || opt_n)
+ ev_setopts(mdb.m_target, id, opt_c, opt_n);
+
+ argv++;
+ }
+
+ return (status);
+}
+
+int
+cmd_fltbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char *opt_c = NULL;
+ uint_t opt_i = FALSE;
+ uint_t opt_l = FALSE;
+ uint64_t opt_L = 0;
+ uintptr_t opt_n = 0;
+ uint_t opt_o = FALSE;
+ uint_t opt_p = FALSE;
+ uint_t opt_rwx = 0;
+ int status = DCMD_OK;
+ int id, fltnum;
+
+ if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c,
+ &opt_i, &opt_l, &opt_L, &opt_n, &opt_o, &opt_p,
+ &opt_rwx)) == NULL || opt_i || opt_l || opt_L != 0 || opt_o ||
+ opt_p || opt_rwx != 0)
+ return (DCMD_USAGE);
+
+ while (argv->a_type != MDB_TYPE_STRING || argv->a_un.a_str != NULL) {
+ if (argv->a_type == MDB_TYPE_STRING) {
+ if (proc_str2flt(argv->a_un.a_str, &fltnum) == -1) {
+ mdb_warn("invalid fault name -- %s\n",
+ argv->a_un.a_str);
+ status = DCMD_ERR;
+ argv++;
+ continue;
+ }
+ } else
+ fltnum = (int)(intmax_t)argv->a_un.a_val;
+
+ id = mdb_tgt_add_fault(mdb.m_target, fltnum,
+ flags, cmd_event, NULL);
+
+ if (id == 0) {
+ mdb_warn("failed to trace fault %d", fltnum);
+ status = DCMD_ERR;
+ } else if (opt_c || opt_n)
+ ev_setopts(mdb.m_target, id, opt_c, opt_n);
+
+ argv++;
+ }
+
+ return (status);
+}
+
+/*ARGSUSED*/
+int
+cmd_wp(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ mdb_tgt_addr_t addr = mdb_get_dot();
+ char *opt_c = NULL;
+ uint_t opt_i = FALSE;
+ uint_t opt_l = FALSE;
+ uint64_t opt_L = 0;
+ uintptr_t opt_n = 0;
+ uint_t opt_o = FALSE;
+ uint_t opt_p = FALSE;
+ uint_t opt_rwx = 0;
+ int id;
+ char buf[MDB_SYM_NAMLEN];
+ GElf_Sym gsym;
+ int size;
+
+ if ((argv = ev_getopts(addr, flags, argc, argv, &flags, &opt_c, &opt_i,
+ &opt_l, &opt_L, &opt_n, &opt_o, &opt_p, &opt_rwx)) == NULL ||
+ opt_o || (opt_p && opt_i))
+ return (DCMD_USAGE);
+
+#ifndef _KMDB
+ if (opt_i)
+ return (DCMD_USAGE);
+#endif
+
+ if (argv->a_type != MDB_TYPE_IMMEDIATE)
+ return (DCMD_USAGE);
+
+ if (opt_rwx == 0) {
+ mdb_warn("at least one of -r, -w, or -x must be specified\n");
+ return (DCMD_USAGE);
+ }
+
+ if ((opt_l) + (opt_L > 0) + (mdb.m_dcount != 1) > 1) {
+ mdb_warn("only one of -l, -L, or command count can be "
+ "specified\n");
+ return (DCMD_ABORT);
+ }
+
+ if (opt_l) {
+ if (mdb_lookup_by_addr(addr, MDB_SYM_EXACT, buf,
+ sizeof (buf), &gsym) == -1) {
+ mdb_warn("failed to lookup symbol at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (gsym.st_size == 0) {
+ mdb_warn("cannot set watchpoint: symbol '%s' has zero "
+ "size\n", buf);
+ return (DCMD_ERR);
+ }
+ size = gsym.st_size;
+ } else if (opt_L != 0) {
+ size = opt_L;
+ } else
+ size = mdb.m_dcount;
+
+ if (opt_p) {
+ id = mdb_tgt_add_pwapt(mdb.m_target, addr, size, opt_rwx,
+ flags, cmd_event, NULL);
+ } else if (opt_i) {
+ id = mdb_tgt_add_iowapt(mdb.m_target, addr, size, opt_rwx,
+ flags, cmd_event, NULL);
+ } else {
+ id = mdb_tgt_add_vwapt(mdb.m_target, addr, size, opt_rwx,
+ flags, cmd_event, NULL);
+ }
+
+ if (id == 0) {
+ mdb_warn("failed to set watchpoint at %p", addr);
+ return ((opt_l || opt_L) ? DCMD_ERR : DCMD_ABORT);
+ }
+
+ if (opt_c || opt_n)
+ ev_setopts(mdb.m_target, id, opt_c, opt_n);
+
+ /*
+ * We use m_dcount as an argument; don't loop. We ignore this
+ * restriction with the -l and -L options, since we read the size from
+ * the symbol and don't rely on the count.
+ */
+ return ((opt_l || opt_L) ? DCMD_OK : DCMD_ABORT);
+}
+
+/*ARGSUSED*/
+int
+cmd_oldbp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ char *s = mdb_argv_to_str(argc, argv);
+
+ if (mdb_tgt_add_vbrkpt(mdb.m_target, addr, 0, cmd_event, s) == 0) {
+ mdb_warn("failed to add breakpoint");
+ if (s != NULL)
+ strfree(s);
+ return (DCMD_ERR);
+ }
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+oldwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, uint_t rwx)
+{
+ char *s = mdb_argv_to_str(argc, argv);
+
+ if (mdb_tgt_add_vwapt(mdb.m_target, addr, mdb.m_dcount, rwx, 0,
+ cmd_event, s) == 0) {
+ mdb_warn("failed to add watchpoint");
+ if (s != NULL)
+ strfree(s);
+ return (DCMD_ABORT);
+ }
+
+ return (DCMD_ABORT); /* we use m_dcount as an argument; don't loop */
+}
+
+int
+cmd_oldwpr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (oldwp(addr, flags, argc, argv, MDB_TGT_WA_R));
+}
+
+int
+cmd_oldwpw(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (oldwp(addr, flags, argc, argv, MDB_TGT_WA_W));
+}
+
+int
+cmd_oldwpx(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ return (oldwp(addr, flags, argc, argv, MDB_TGT_WA_X));
+}
+
+static const char _evset_help[] =
+"+/-d disable specifier when hit count reaches limit (+d to unset);\n"
+" if -n is not present with -d, specifier is disabled immediately\n\n"
+"+/-D delete specifier when hit count reaches limit (+D to unset);\n"
+"+/-e enable specifier (+e or -d to disable)\n"
+"+/-s stop target when hit count reaches limit (+s to unset)\n"
+"+/-t delete specifier the next time the target stops (+t to unset)\n"
+"+/-T sticky bit: ::delete all will not remove specifier (+T to unset)\n\n"
+"-c cmd execute \"cmd\" each time the corresponding event occurs\n"
+"-n count set limit for -D, -d, or -s to \"count\" (default 1)\n\n";
+
+void
+bp_help(void)
+{
+ mdb_printf(_evset_help);
+ mdb_printf("addr set breakpoint at specified virtual address\n");
+ mdb_printf("sym set deferred breakpoint at specified symbol\n");
+}
+
+void
+evset_help(void)
+{
+ mdb_printf(_evset_help);
+ mdb_printf("addr/id set properties of specified event ids\n");
+}
+
+void
+fltbp_help(void)
+{
+ mdb_printf(_evset_help);
+ mdb_printf("flt fault name (see <sys/fault.h>) or number\n");
+}
+
+void
+sigbp_help(void)
+{
+ mdb_printf(_evset_help);
+ mdb_printf("SIG signal name (see signal(3HEAD)) or number\n");
+}
+
+void
+sysbp_help(void)
+{
+ mdb_printf(_evset_help);
+ mdb_printf("-i trace system call on entry into kernel (default)\n"
+ "-o trace system call on exit from kernel\n\n"
+ "syscall system call name (see <sys/syscall.h>) or number\n");
+}
+
+void
+wp_help(void)
+{
+ mdb_printf(_evset_help);
+ mdb_printf(
+#ifdef _KMDB
+ "-p treat addr as a physical address\n"
+ "-i treat addr as an I/O port address\n"
+#endif
+ "-l use size of addr's type for watched region\n"
+ "-L size set size of watched region (default 1)\n"
+ "-r trace read access to watched region\n"
+ "-w trace write access to watched region\n"
+ "-x trace execute access to watched region\n\n"
+ "addr address for base of watched region\n"
+ "repeat size of watched region (equivalent to -L)\n");
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_evset.h b/usr/src/cmd/mdb/common/mdb/mdb_evset.h
new file mode 100644
index 0000000..9dfb84c
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_evset.h
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_EVSET_H
+#define _MDB_EVSET_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+extern void cmd_event(mdb_tgt_t *, int, void *);
+extern int cmd_evset(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+extern int cmd_bp(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_sigbp(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_sysbp(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_fltbp(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_wp(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+extern int cmd_oldbp(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_oldwpr(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_oldwpw(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_oldwpx(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+extern void bp_help(void);
+extern void evset_help(void);
+extern void fltbp_help(void);
+extern void sigbp_help(void);
+extern void sysbp_help(void);
+extern void wp_help(void);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_EVSET_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_fdio.c b/usr/src/cmd/mdb/common/mdb/mdb_fdio.c
new file mode 100644
index 0000000..80e1cb5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_fdio.c
@@ -0,0 +1,326 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * File Descriptor I/O Backend
+ *
+ * Simple backend to pass though io_ops to the corresponding system calls on
+ * an underlying fd. We provide functions to create fdio objects using file
+ * descriptors, explicit file names, and path lookups. We save the complete
+ * filename so that mdb_iob_name can be used to report the complete filename
+ * of an open macro file in syntax error messages.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/dkio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb.h>
+
+typedef struct fd_data {
+ char fd_name[MAXPATHLEN]; /* Save filename for error messages */
+ int fd_fd; /* File descriptor */
+} fd_data_t;
+
+static ssize_t
+fdio_read(mdb_io_t *io, void *buf, size_t nbytes)
+{
+ fd_data_t *fdp = io->io_data;
+
+ if (io->io_next == NULL)
+ return (read(fdp->fd_fd, buf, nbytes));
+
+ return (IOP_READ(io->io_next, buf, nbytes));
+}
+
+static ssize_t
+fdio_write(mdb_io_t *io, const void *buf, size_t nbytes)
+{
+ fd_data_t *fdp = io->io_data;
+
+ if (io->io_next == NULL)
+ return (write(fdp->fd_fd, buf, nbytes));
+
+ return (IOP_WRITE(io->io_next, buf, nbytes));
+}
+
+static off64_t
+fdio_seek(mdb_io_t *io, off64_t offset, int whence)
+{
+ fd_data_t *fdp = io->io_data;
+
+ if (io->io_next == NULL)
+ return (lseek64(fdp->fd_fd, offset, whence));
+
+ return (IOP_SEEK(io->io_next, offset, whence));
+}
+
+static int
+fdio_ctl(mdb_io_t *io, int req, void *arg)
+{
+ fd_data_t *fdp = io->io_data;
+
+ if (io->io_next != NULL)
+ return (IOP_CTL(io->io_next, req, arg));
+
+ if (req == MDB_IOC_GETFD)
+ return (fdp->fd_fd);
+ else
+ return (ioctl(fdp->fd_fd, req, arg));
+}
+
+static void
+fdio_close(mdb_io_t *io)
+{
+ fd_data_t *fdp = io->io_data;
+
+ (void) close(fdp->fd_fd);
+ mdb_free(fdp, sizeof (fd_data_t));
+}
+
+static const char *
+fdio_name(mdb_io_t *io)
+{
+ fd_data_t *fdp = io->io_data;
+
+ if (io->io_next == NULL)
+ return (fdp->fd_name);
+
+ return (IOP_NAME(io->io_next));
+}
+
+mdb_io_t *
+mdb_fdio_create_path(const char *path[], const char *fname,
+ int flags, mode_t mode)
+{
+ int fd;
+
+ if (path != NULL && strchr(fname, '/') == NULL) {
+ char buf[MAXPATHLEN];
+ int i;
+
+ for (fd = -1, i = 0; path[i] != NULL; i++) {
+ (void) mdb_iob_snprintf(buf, MAXPATHLEN, "%s/%s",
+ path[i], fname);
+
+ if (access(buf, F_OK) == 0) {
+ fd = open64(buf, flags, mode);
+ fname = buf;
+ break;
+ }
+ }
+
+ if (fd == -1)
+ (void) set_errno(ENOENT);
+ } else
+ fd = open64(fname, flags, mode);
+
+ if (fd >= 0)
+ return (mdb_fdio_create_named(fd, fname));
+
+ return (NULL);
+}
+
+static const mdb_io_ops_t fdio_file_ops = {
+ fdio_read,
+ fdio_write,
+ fdio_seek,
+ fdio_ctl,
+ fdio_close,
+ fdio_name,
+ no_io_link,
+ no_io_unlink,
+ no_io_setattr,
+ no_io_suspend,
+ no_io_resume
+};
+
+/*
+ * In order to read from a block-oriented device, we pick up the seek pointer,
+ * read each containing block, and then copy the desired range of bytes back
+ * into the caller's buffer. Unfortunately Solaris hardcodes the notion of
+ * DEV_BSIZE as the transfer unit for such devices; no ioctl() to obtain the
+ * transfer unit dynamically is currently available. At the end of the
+ * transfer we reset the seek pointer to where the caller thinks it should be.
+ */
+static ssize_t
+fdio_bdev_read(mdb_io_t *io, void *buf, size_t nbytes)
+{
+ fd_data_t *fdp = io->io_data;
+ ssize_t resid = nbytes;
+ uchar_t blk[DEV_BSIZE];
+ off64_t off;
+
+ if (io->io_next != NULL)
+ return (IOP_READ(io->io_next, buf, nbytes));
+
+ if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
+ return (-1); /* errno is set for us */
+
+ while (resid != 0) {
+ off64_t devoff = off & ~(DEV_BSIZE - 1);
+ size_t blkoff = off & (DEV_BSIZE - 1);
+ size_t len = MIN(resid, DEV_BSIZE - blkoff);
+
+ if (pread64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
+ break; /* errno is set for us, unless EOF */
+
+ bcopy(&blk[blkoff], buf, len);
+ resid -= len;
+ off += len;
+ buf = (char *)buf + len;
+ }
+
+ if (resid == nbytes && nbytes != 0)
+ return (set_errno(EMDB_EOF));
+
+ (void) lseek64(fdp->fd_fd, off, SEEK_SET);
+ return (nbytes - resid);
+}
+
+/*
+ * To perform a write to a block-oriented device, we use the same basic
+ * algorithm as fdio_bdev_read(), above. In the inner loop, we read an
+ * entire block, modify it using the data from the caller's buffer, and
+ * then write the entire block back to the device.
+ */
+static ssize_t
+fdio_bdev_write(mdb_io_t *io, const void *buf, size_t nbytes)
+{
+ fd_data_t *fdp = io->io_data;
+ ssize_t resid = nbytes;
+ uchar_t blk[DEV_BSIZE];
+ off64_t off;
+
+ if (io->io_next != NULL)
+ return (IOP_WRITE(io->io_next, buf, nbytes));
+
+ if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
+ return (-1); /* errno is set for us */
+
+ while (resid != 0) {
+ off64_t devoff = off & ~(DEV_BSIZE - 1);
+ size_t blkoff = off & (DEV_BSIZE - 1);
+ size_t len = MIN(resid, DEV_BSIZE - blkoff);
+
+ if (pread64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
+ break; /* errno is set for us, unless EOF */
+
+ bcopy(buf, &blk[blkoff], len);
+
+ if (pwrite64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
+ break; /* errno is set for us, unless EOF */
+
+ resid -= len;
+ off += len;
+ buf = (char *)buf + len;
+ }
+
+ if (resid == nbytes && nbytes != 0)
+ return (set_errno(EMDB_EOF));
+
+ (void) lseek64(fdp->fd_fd, off, SEEK_SET);
+ return (nbytes - resid);
+}
+
+static const mdb_io_ops_t fdio_bdev_ops = {
+ fdio_bdev_read,
+ fdio_bdev_write,
+ fdio_seek,
+ fdio_ctl,
+ fdio_close,
+ fdio_name,
+ no_io_link,
+ no_io_unlink,
+ no_io_setattr,
+ no_io_suspend,
+ no_io_resume
+};
+
+mdb_io_t *
+mdb_fdio_create(int fd)
+{
+ mdb_io_t *io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
+ fd_data_t *fdp = mdb_alloc(sizeof (fd_data_t), UM_SLEEP);
+
+ struct dk_cinfo info;
+ struct stat64 st;
+
+ switch (fd) {
+ case STDIN_FILENO:
+ (void) strcpy(fdp->fd_name, "(stdin)");
+ break;
+ case STDOUT_FILENO:
+ (void) strcpy(fdp->fd_name, "(stdout)");
+ break;
+ case STDERR_FILENO:
+ (void) strcpy(fdp->fd_name, "(stderr)");
+ break;
+ default:
+ (void) mdb_iob_snprintf(fdp->fd_name, MAXPATHLEN, "fd %d", fd);
+ }
+
+ fdp->fd_fd = fd;
+
+ /*
+ * We determine if something is a raw block-oriented disk device by
+ * testing to see if it is a character device that supports DKIOCINFO.
+ * If we are operating on a disk in raw mode, we must do our own
+ * block-oriented i/o; otherwise we can just use read() and write().
+ */
+ if (fstat64(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
+ ioctl(fd, DKIOCINFO, &info) == 0)
+ io->io_ops = &fdio_bdev_ops;
+ else
+ io->io_ops = &fdio_file_ops;
+
+ io->io_data = fdp;
+ io->io_next = NULL;
+ io->io_refcnt = 0;
+
+ return (io);
+}
+
+mdb_io_t *
+mdb_fdio_create_named(int fd, const char *name)
+{
+ mdb_io_t *io = mdb_fdio_create(fd);
+ fd_data_t *fdp = io->io_data;
+
+ (void) strncpy(fdp->fd_name, name, MAXPATHLEN);
+ fdp->fd_name[MAXPATHLEN - 1] = '\0';
+
+ return (io);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_fmt.c b/usr/src/cmd/mdb/common/mdb/mdb_fmt.c
new file mode 100644
index 0000000..49a302a
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_fmt.c
@@ -0,0 +1,746 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Format String Decoder
+ *
+ * This file provides the core engine for converting strings of format
+ * characters into formatted output. The various format dcmds invoke the
+ * mdb_fmt_print() function below with a target, address space identifier,
+ * address, count, and format character, and it reads the required data from
+ * the target and prints the formatted output to stdout. Since nearly two
+ * thirds of the format characters can be expressed as simple printf format
+ * strings, we implement the engine using the lookup table below. Each entry
+ * provides either a pointer to a printf format string or a pointer to a
+ * function to perform special processing. For the printf case, the
+ * corresponding data size in bytes is also supplied. The printf processing
+ * code handles 1, 2, 4, and 8-byte reads into an unsigned integer container
+ * of the given size, and then simply calls mdb_iob_printf with the integer
+ * and format string. This handles all printf cases, except when unsigned
+ * promotion of an integer type in the varargs list does not perform the
+ * conversion we require to get the proper result. With the current set of
+ * format characters, this case only occurs twice: we need a 4-byte float
+ * to get promoted to 8-byte double for the 'f' format so it can be
+ * correctly formatted by %f, and we need a 1-byte int8_t to get promoted
+ * with sign extension to a 4-byte int32_t for the 'v' format so it can be
+ * correctly formatted by %d. We provide explicit functions to handle these
+ * cases, as well as to handle special format characters such as 'i', etc.
+ * We also provide a cmd_formats() dcmd function below which prints a table
+ * of the output formats and their sizes. Format characters that provide
+ * custom functions provide their help description string explicitly. All
+ * the printf formats have their help strings generated automatically by
+ * our printf "unparser" mdb_iob_format2str().
+ */
+
+#include <mdb/mdb_types.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb.h>
+
+#define FUNCP(p) ((void *)(p)) /* Cast to f_ptr type */
+#define SZ_NONE ((size_t)-1L) /* Format does not change dot */
+
+typedef mdb_tgt_addr_t mdb_fmt_func_f(mdb_tgt_t *,
+ mdb_tgt_as_t, mdb_tgt_addr_t, size_t);
+
+/*
+ * There are several 'special' characters that are handled outside of
+ * mdb_fmt_print(). These are characters that write (vwWZ) and characters that
+ * match (lLM). We include them here so that ::formats can display an
+ * appropriate message, but they are handled specially by write_arglist() and
+ * match_arglist() in mdb_cmds.c.
+ */
+#define FMT_NONE 0x0 /* Format character is not supported */
+#define FMT_FUNC 0x1 /* f_ptr is a mdb_fmt_func_f to call */
+#define FMT_PRINTF 0x2 /* f_ptr is a const char * format string */
+#define FMT_MATCH 0x4 /* Match command (not supported here) */
+#define FMT_WRITE 0x8 /* Command writes to address space */
+
+#define FMT_TYPE(x) ((x) & 0x7) /* Excludes modifying flags (FMT_WRITE) */
+
+typedef struct mdb_fmt_desc {
+ int f_type; /* Type of format (see above) */
+ void *f_ptr; /* Data pointer (see above) */
+ const char *f_help; /* Additional help string */
+ size_t f_size; /* Size of type in bytes, or SZ_NONE */
+} mdb_fmt_desc_t;
+
+static const char help_plus[] = "increment dot by the count";
+static const char help_minus[] = "decrement dot by the count";
+static const char help_escchr[] = "character using C character notation";
+static const char help_swapint[] = "swap bytes and shorts";
+static const char help_dotinstr[] = "address and disassembled instruction";
+static const char help_instr[] = "disassembled instruction";
+static const char help_escstr[] = "string using C string notation";
+static const char help_time32[] = "decoded time32_t";
+static const char help_carat[] = "decrement dot by increment * count";
+static const char help_dot[] = "dot as symbol+offset";
+#ifndef _KMDB
+static const char help_f[] = "float";
+#endif
+static const char help_swapshort[] = "swap bytes";
+static const char help_nl[] = "newline";
+static const char help_ws[] = "whitespace";
+static const char help_rawstr[] = "raw string";
+static const char help_tab[] = "horizontal tab";
+static const char help_sdbyte[] = "decimal signed int";
+static const char help_time64[] = "decoded time64_t";
+static const char help_binary[] = "binary unsigned long long";
+static const char help_hex64[] = "hexadecimal long long";
+static const char help_match32[] = "int";
+static const char help_match64[] = "long long";
+static const char help_match16[] = "short";
+static const char help_uintptr[] = "hexadecimal uintptr_t";
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_dot(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
+ char buf[24];
+
+ mdb_iob_clrflags(mdb.m_out, oflags);
+
+ if (mdb.m_flags & MDB_FL_PSYM) {
+ while (cnt-- != 0)
+ mdb_iob_printf(mdb.m_out, "%-#16lla%16T", addr);
+ } else {
+ (void) mdb_iob_snprintf(buf, sizeof (buf),
+ "%#llx:", (u_longlong_t)addr);
+ while (cnt-- != 0)
+ mdb_iob_printf(mdb.m_out, "%-16s%16T", buf);
+ }
+
+ mdb_iob_setflags(mdb.m_out, oflags);
+ mdb_nv_set_value(mdb.m_rvalue, addr);
+ return (addr);
+}
+
+#ifndef _KMDB
+static mdb_tgt_addr_t
+fmt_float(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ float f;
+ /*
+ * We need to handle float as a special case because we need it to be
+ * promoted to a double by virtue of appearing as a parameter, and all
+ * our generic format handling below is based on integer types.
+ */
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &f, sizeof (f), addr) != sizeof (f)) {
+ warn("failed to read data from target");
+ break;
+ }
+ mdb_iob_printf(mdb.m_out, "%e", f);
+ addr += sizeof (f);
+ }
+ return (addr);
+}
+#endif
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_plus(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ return (addr + cnt);
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_minus(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ return (addr - cnt);
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_carat(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ return (addr - (mdb.m_incr * cnt));
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_nl(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ while (cnt-- != 0)
+ mdb_iob_nl(mdb.m_out);
+
+ return (addr);
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_ws(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ mdb_iob_ws(mdb.m_out, cnt);
+ return (addr);
+}
+
+/*ARGSUSED*/
+static mdb_tgt_addr_t
+fmt_tab(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ size_t ts = mdb_iob_gettabstop(mdb.m_out);
+
+ mdb_iob_tabstop(mdb.m_out, cnt);
+ mdb_iob_tab(mdb.m_out);
+ mdb_iob_tabstop(mdb.m_out, ts);
+
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_rawstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
+ char buf[BUFSIZ];
+ ssize_t nbytes;
+
+ mdb_iob_clrflags(mdb.m_out, oflags);
+
+ for (; cnt-- != 0; addr++) {
+ do {
+ nbytes = mdb_tgt_readstr(t, as, buf, BUFSIZ, addr);
+ if (nbytes > 0) {
+ mdb_iob_puts(mdb.m_out, buf);
+ addr += MIN(nbytes, BUFSIZ - 1);
+ } else if (nbytes < 0) {
+ warn("failed to read data from target");
+ goto out;
+ }
+ } while (nbytes == BUFSIZ);
+
+ if (cnt != 0)
+ mdb_iob_puts(mdb.m_out, "\\0");
+ }
+out:
+ mdb_iob_setflags(mdb.m_out, oflags);
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_escstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
+ char buf[BUFSIZ];
+ ssize_t nbytes;
+ char *s;
+
+ mdb_iob_clrflags(mdb.m_out, oflags);
+
+ for (; cnt-- != 0; addr++) {
+ do {
+ nbytes = mdb_tgt_readstr(t, as, buf, BUFSIZ, addr);
+ if (nbytes > 0) {
+ s = strchr2esc(buf, strlen(buf));
+ mdb_iob_puts(mdb.m_out, s);
+ strfree(s);
+ addr += MIN(nbytes, BUFSIZ - 1);
+ } else if (nbytes < 0) {
+ warn("failed to read data from target");
+ goto out;
+ }
+ } while (nbytes == BUFSIZ);
+
+ if (cnt != 0)
+ mdb_iob_puts(mdb.m_out, "\\0");
+ }
+out:
+ mdb_iob_setflags(mdb.m_out, oflags);
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_escchr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ char *(*convert)(const char *, size_t);
+ ssize_t nbytes;
+ char *buf, *s;
+
+ if (mdb.m_flags & MDB_FL_ADB)
+ convert = &strchr2adb;
+ else
+ convert = &strchr2esc;
+
+ buf = mdb_alloc(cnt + 1, UM_SLEEP);
+ buf[cnt] = 0;
+
+ if ((nbytes = mdb_tgt_aread(t, as, buf, cnt, addr)) > 0) {
+ s = convert(buf, nbytes);
+ mdb_iob_puts(mdb.m_out, s);
+ strfree(s);
+ addr += nbytes;
+ }
+
+ mdb_free(buf, cnt + 1);
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_swapshort(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ ushort_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ x = (x << 8) | (x >> 8);
+ mdb_iob_printf(mdb.m_out, "%-8x", x);
+ mdb_nv_set_value(mdb.m_rvalue, x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_swapint(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ x = ((x << 24) | ((x << 8) & 0xff0000) |
+ ((x >> 8) & 0xff00) | ((x >> 24) & 0xff));
+ mdb_iob_printf(mdb.m_out, "%-16x", x);
+ mdb_nv_set_value(mdb.m_rvalue, x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_time32(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint32_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ mdb_iob_printf(mdb.m_out, "%-24Y", (time_t)x);
+ mdb_nv_set_value(mdb.m_rvalue, x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_time64(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint64_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ mdb_iob_printf(mdb.m_out, "%-24Y", (time_t)x);
+ mdb_nv_set_value(mdb.m_rvalue, x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_sdbyte(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ int8_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ mdb_iob_printf(mdb.m_out, "%-8d", (int32_t)x);
+ mdb_nv_set_value(mdb.m_rvalue, (uint8_t)x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_instr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ char buf[BUFSIZ];
+ uintptr_t naddr;
+
+ while (cnt-- != 0) {
+ naddr = mdb_dis_ins2str(mdb.m_disasm, t, as,
+ buf, sizeof (buf), addr);
+ if (naddr == addr)
+ return (addr); /* If we didn't move, we failed */
+ mdb_iob_printf(mdb.m_out, "%s\n", buf);
+ addr = naddr;
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_dotinstr(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT;
+
+ char buf[BUFSIZ];
+ uintptr_t naddr;
+ uint32_t i;
+
+ for (mdb_iob_clrflags(mdb.m_out, oflags); cnt-- != 0; addr = naddr) {
+ if (mdb_tgt_aread(t, as, &i, sizeof (i), addr) != sizeof (i)) {
+ warn("failed to read data from target");
+ break; /* Fail if we can't read instruction */
+ }
+ naddr = mdb_dis_ins2str(mdb.m_disasm, t, as,
+ buf, sizeof (buf), addr);
+ if (naddr == addr)
+ break; /* Fail if we didn't advance */
+ mdb_iob_printf(mdb.m_out, "%lx %x: %s\n", (long)addr, i, buf);
+ }
+
+ mdb_iob_setflags(mdb.m_out, oflags);
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_binary(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ uint64_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ mdb_iob_printf(mdb.m_out, "%-64s",
+ numtostr(x, 2, NTOS_UNSIGNED));
+ mdb_nv_set_value(mdb.m_rvalue, x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static mdb_tgt_addr_t
+fmt_hex64(mdb_tgt_t *t, mdb_tgt_as_t as, mdb_tgt_addr_t addr, size_t cnt)
+{
+ const char *fmts[] = { "%-16llx", "%-17llx" };
+ const uint64_t mask = 0xf000000000000000ull;
+ uint64_t x;
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, &x, sizeof (x), addr) == sizeof (x)) {
+ mdb_iob_printf(mdb.m_out, fmts[(x & mask) != 0], x);
+ mdb_nv_set_value(mdb.m_rvalue, x);
+ addr += sizeof (x);
+ } else {
+ warn("failed to read data from target");
+ break;
+ }
+ }
+ return (addr);
+}
+
+static const mdb_fmt_desc_t fmttab[] = {
+ { FMT_NONE, NULL, NULL, 0 }, /* 0 = NUL */
+ { FMT_NONE, NULL, NULL, 0 }, /* 1 = SOH */
+ { FMT_NONE, NULL, NULL, 0 }, /* 2 = STX */
+ { FMT_NONE, NULL, NULL, 0 }, /* 3 = ETX */
+ { FMT_NONE, NULL, NULL, 0 }, /* 4 = EOT */
+ { FMT_NONE, NULL, NULL, 0 }, /* 5 = ENQ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 6 = ACK */
+ { FMT_NONE, NULL, NULL, 0 }, /* 7 = BEL */
+ { FMT_NONE, NULL, NULL, 0 }, /* 8 = BS */
+ { FMT_NONE, NULL, NULL, 0 }, /* 9 = \t */
+ { FMT_NONE, NULL, NULL, 0 }, /* 10 = \n */
+ { FMT_NONE, NULL, NULL, 0 }, /* 11 = VT */
+ { FMT_NONE, NULL, NULL, 0 }, /* 12 = FF */
+ { FMT_NONE, NULL, NULL, 0 }, /* 13 = \r */
+ { FMT_NONE, NULL, NULL, 0 }, /* 14 = SO */
+ { FMT_NONE, NULL, NULL, 0 }, /* 15 = SI */
+ { FMT_NONE, NULL, NULL, 0 }, /* 16 = DLE */
+ { FMT_NONE, NULL, NULL, 0 }, /* 17 = DC1 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 18 = DC2 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 19 = DC3 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 20 = DC4 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 21 = NAK */
+ { FMT_NONE, NULL, NULL, 0 }, /* 22 = EYC */
+ { FMT_NONE, NULL, NULL, 0 }, /* 23 = ETB */
+ { FMT_NONE, NULL, NULL, 0 }, /* 24 = CAN */
+ { FMT_NONE, NULL, NULL, 0 }, /* 25 = EM */
+ { FMT_NONE, NULL, NULL, 0 }, /* 26 = SUB */
+ { FMT_NONE, NULL, NULL, 0 }, /* 27 = ESC */
+ { FMT_NONE, NULL, NULL, 0 }, /* 28 = FS */
+ { FMT_NONE, NULL, NULL, 0 }, /* 29 = GS */
+ { FMT_NONE, NULL, NULL, 0 }, /* 30 = RS */
+ { FMT_NONE, NULL, NULL, 0 }, /* 31 = US */
+ { FMT_NONE, NULL, NULL, 0 }, /* 32 = SPACE */
+ { FMT_NONE, NULL, NULL, 0 }, /* 33 = ! */
+ { FMT_NONE, NULL, NULL, 0 }, /* 34 = " */
+ { FMT_NONE, NULL, NULL, 0 }, /* 35 = # */
+ { FMT_NONE, NULL, NULL, 0 }, /* 36 = $ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 37 = % */
+ { FMT_NONE, NULL, NULL, 0 }, /* 38 = & */
+ { FMT_NONE, NULL, NULL, 0 }, /* 39 = ' */
+ { FMT_NONE, NULL, NULL, 0 }, /* 40 = ( */
+ { FMT_NONE, NULL, NULL, 0 }, /* 41 = ) */
+ { FMT_NONE, NULL, NULL, 0 }, /* 42 = * */
+ { FMT_FUNC, FUNCP(fmt_plus), help_plus, 0 }, /* 43 = + */
+ { FMT_NONE, NULL, NULL, 0 }, /* 44 = , */
+ { FMT_FUNC, FUNCP(fmt_minus), help_minus, 0 }, /* 45 = - */
+ { FMT_NONE, NULL, NULL, 0 }, /* 46 = . */
+ { FMT_NONE, NULL, NULL, 0 }, /* 47 = / */
+ { FMT_NONE, NULL, NULL, 0 }, /* 48 = 0 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 49 = 1 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 50 = 2 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 51 = 3 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 52 = 4 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 53 = 5 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 54 = 6 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 55 = 7 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 56 = 8 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 57 = 9 */
+ { FMT_NONE, NULL, NULL, 0 }, /* 58 = : */
+ { FMT_NONE, NULL, NULL, 0 }, /* 59 = ; */
+ { FMT_NONE, NULL, NULL, 0 }, /* 60 = < */
+ { FMT_NONE, NULL, NULL, 0 }, /* 61 = = */
+ { FMT_NONE, NULL, NULL, 0 }, /* 62 = > */
+ { FMT_NONE, NULL, NULL, 0 }, /* 63 = ? */
+ { FMT_NONE, NULL, NULL, 0 }, /* 64 = @ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 65 = A */
+ { FMT_PRINTF, "%-8x", NULL, 1 }, /* 66 = B */
+ { FMT_FUNC, FUNCP(fmt_escchr), help_escchr, 1 }, /* 67 = C */
+ { FMT_PRINTF, "%-16d", NULL, 4 }, /* 68 = D */
+ { FMT_PRINTF, "%-16llu", NULL, 8 }, /* 69 = E */
+#ifdef _KMDB
+ { FMT_NONE, NULL, NULL, 0 }, /* 70 = F */
+#else
+ { FMT_PRINTF, "%g", NULL, sizeof (double) }, /* 70 = F */
+#endif
+ { FMT_PRINTF, "%-16llo", NULL, 8 }, /* 71 = G */
+ { FMT_FUNC, FUNCP(fmt_swapint), help_swapint, 4 }, /* 72 = H */
+ { FMT_FUNC, FUNCP(fmt_dotinstr), help_dotinstr, 0 }, /* 73 = I */
+ { FMT_FUNC, FUNCP(fmt_hex64), help_hex64, 8 }, /* 74 = J */
+#ifdef _LP64
+ { FMT_FUNC, FUNCP(fmt_hex64), help_uintptr, 8 }, /* 75 = K (J) */
+#else
+ { FMT_PRINTF, "%-16x", help_uintptr, 4 }, /* 75 = K (X) */
+#endif
+ { FMT_MATCH, NULL, help_match32, 4 }, /* 76 = L */
+ { FMT_MATCH, NULL, help_match64, 8 }, /* 77 = M */
+ { FMT_FUNC, FUNCP(fmt_nl), help_nl, SZ_NONE }, /* 78 = N */
+ { FMT_PRINTF, "%-#16o", NULL, 4 }, /* 79 = O */
+ { FMT_PRINTF, "%-16a", NULL, sizeof (uintptr_t) }, /* 80 = P */
+ { FMT_PRINTF, "%-#16q", NULL, 4 }, /* 81 = Q */
+ { FMT_FUNC, FUNCP(fmt_binary), help_binary, 8 }, /* 82 = R */
+ { FMT_FUNC, FUNCP(fmt_escstr), help_escstr, 0 }, /* 83 = S */
+ { FMT_FUNC, FUNCP(fmt_tab), help_tab, SZ_NONE }, /* 84 = T */
+ { FMT_PRINTF, "%-16u", NULL, 4 }, /* 85 = U */
+ { FMT_PRINTF, "%-8u", NULL, 1 }, /* 86 = V */
+ { FMT_PRINTF|FMT_WRITE, "%-16r", NULL, 4 }, /* 87 = W */
+ { FMT_PRINTF, "%-16x", NULL, 4 }, /* 88 = X */
+ { FMT_FUNC, FUNCP(fmt_time32), help_time32, 4 }, /* 89 = Y */
+ { FMT_FUNC|FMT_WRITE, FUNCP(fmt_hex64), help_hex64, 8 }, /* 90 = Z */
+ { FMT_NONE, NULL, NULL, 0 }, /* 91 = [ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 92 = \ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 93 = ] */
+ { FMT_FUNC, FUNCP(fmt_carat), help_carat, 0 }, /* 94 = ^ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 95 = _ */
+ { FMT_NONE, NULL, NULL, 0 }, /* 96 = ` */
+ { FMT_FUNC, FUNCP(fmt_dot), help_dot, SZ_NONE }, /* 97 = a */
+ { FMT_PRINTF, "%-#8o", NULL, 1 }, /* 98 = b */
+ { FMT_PRINTF, "%c", NULL, 1 }, /* 99 = c */
+ { FMT_PRINTF, "%-8hd", NULL, 2 }, /* 100 = d */
+ { FMT_PRINTF, "%-16lld", NULL, 8 }, /* 101 = e */
+#ifdef _KMDB
+ { FMT_NONE, NULL, NULL, 0 }, /* 102 = f */
+#else
+ { FMT_FUNC, FUNCP(fmt_float), help_f, sizeof (float) }, /* 102 = f */
+#endif
+ { FMT_PRINTF, "%-16llq", NULL, 8 }, /* 103 = g */
+ { FMT_FUNC, FUNCP(fmt_swapshort), help_swapshort, 2 }, /* 104 = h */
+ { FMT_FUNC, FUNCP(fmt_instr), help_instr, 0 }, /* 105 = i */
+ { FMT_NONE, NULL, NULL, 0 }, /* 106 = j */
+ { FMT_NONE, NULL, NULL, 0 }, /* 107 = k */
+ { FMT_MATCH, NULL, help_match16, 2 }, /* 108 = l */
+ { FMT_NONE, NULL, NULL, 0 }, /* 109 = m */
+ { FMT_FUNC, FUNCP(fmt_nl), help_nl, SZ_NONE }, /* 110 = n */
+ { FMT_PRINTF, "%-#8ho", NULL, 2 }, /* 111 = o */
+ { FMT_PRINTF, "%-16a", NULL, sizeof (uintptr_t) }, /* 112 = p */
+ { FMT_PRINTF, "%-#8hq", NULL, 2 }, /* 113 = q */
+ { FMT_FUNC, FUNCP(fmt_ws), help_ws, SZ_NONE }, /* 114 = r */
+ { FMT_FUNC, FUNCP(fmt_rawstr), help_rawstr, 0 }, /* 115 = s */
+ { FMT_FUNC, FUNCP(fmt_tab), help_tab, SZ_NONE }, /* 116 = t */
+ { FMT_PRINTF, "%-8hu", NULL, 2 }, /* 117 = u */
+ { FMT_FUNC|FMT_WRITE, FUNCP(fmt_sdbyte), help_sdbyte, 1 }, /* 118 = v */
+ { FMT_PRINTF|FMT_WRITE, "%-8hr", NULL, 2 }, /* 119 = w */
+ { FMT_PRINTF, "%-8hx", NULL, 2 }, /* 120 = x */
+ { FMT_FUNC, FUNCP(fmt_time64), help_time64, 8 }, /* 121 = y */
+ { FMT_NONE, NULL, NULL, 0 }, /* 122 = z */
+};
+
+mdb_tgt_addr_t
+mdb_fmt_print(mdb_tgt_t *t, mdb_tgt_as_t as,
+ mdb_tgt_addr_t addr, size_t cnt, char fmt)
+{
+ const mdb_fmt_desc_t *fp = &fmttab[fmt];
+ mdb_fmt_func_f *funcp;
+ uintmax_t rvalue;
+ void *buf;
+
+ union {
+ uint64_t i8;
+ uint32_t i4;
+ uint16_t i2;
+ uint8_t i1;
+ } u;
+
+ if (fmt < 0 || fmt > (sizeof (fmttab) / sizeof (fmttab[0]))) {
+ warn("invalid format character -- '%c'\n", fmt);
+ return (addr);
+ }
+
+ switch (FMT_TYPE(fp->f_type)) {
+ case FMT_FUNC:
+ funcp = (mdb_fmt_func_f *)fp->f_ptr;
+ addr = funcp(t, as, addr, cnt);
+ break;
+
+ case FMT_PRINTF:
+ switch (fp->f_size) {
+ case 1:
+ buf = &u.i1;
+ break;
+ case 2:
+ buf = &u.i2;
+ break;
+ case 4:
+ buf = &u.i4;
+ break;
+ case 8:
+ buf = &u.i8;
+ break;
+ default:
+ fail("format %c is defined using illegal size\n", fmt);
+ }
+
+ while (cnt-- != 0) {
+ if (mdb_tgt_aread(t, as, buf, fp->f_size, addr) !=
+ fp->f_size) {
+ warn("failed to read data from target");
+ return (addr);
+ }
+
+ switch (fp->f_size) {
+ case 1:
+ mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i1);
+ rvalue = u.i1;
+ break;
+ case 2:
+ mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i2);
+ rvalue = u.i2;
+ break;
+ case 4:
+ mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i4);
+ rvalue = u.i4;
+ break;
+ case 8:
+ mdb_iob_printf(mdb.m_out, fp->f_ptr, u.i8);
+ rvalue = u.i8;
+ break;
+ }
+
+ mdb_nv_set_value(mdb.m_rvalue, rvalue);
+ addr += fp->f_size;
+ }
+ break;
+
+ default:
+ warn("invalid format character -- '%c'\n", fmt);
+ }
+
+ return (addr);
+}
+
+/*ARGSUSED*/
+int
+cmd_formats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const mdb_fmt_desc_t *fp = &fmttab[0];
+ int i;
+ const char *write;
+
+ if ((flags & DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ for (i = 0; i < (sizeof (fmttab) / sizeof (fmttab[0])); i++, fp++) {
+ if (fp->f_type == FMT_NONE)
+ continue;
+
+ write = (fp->f_type & FMT_WRITE) ? "write " : "";
+
+ if (fp->f_type & FMT_FUNC)
+ mdb_printf("%c - %s%s", i, write, fp->f_help);
+ else if (fp->f_type & FMT_MATCH)
+ mdb_printf("%c - match %s", i, fp->f_help);
+ else if (fp->f_help != NULL)
+ mdb_printf("%c - %s%s", i, write, fp->f_help);
+ else
+ mdb_printf("%c - %s%s", i, write,
+ mdb_iob_format2str(fp->f_ptr));
+
+ switch (fp->f_size) {
+ case SZ_NONE:
+ mdb_printf("\n");
+ break;
+ case 0:
+ mdb_printf(" (variable size)\n");
+ break;
+ case 1:
+ mdb_printf(" (1 byte)\n");
+ break;
+ default:
+ mdb_printf(" (%lu bytes)\n", fp->f_size);
+ }
+ }
+
+ return (DCMD_OK);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_fmt.h b/usr/src/cmd/mdb/common/mdb/mdb_fmt.h
new file mode 100644
index 0000000..87b1005
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_fmt.h
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_FMT_H
+#define _MDB_FMT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_target.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+mdb_tgt_addr_t mdb_fmt_print(mdb_tgt_t *, mdb_tgt_as_t,
+ mdb_tgt_addr_t, size_t, char);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_FMT_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_frame.c b/usr/src/cmd/mdb/common/mdb/mdb_frame.c
new file mode 100644
index 0000000..eb5536a
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_frame.c
@@ -0,0 +1,172 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Utility routines to manage debugger frames and commands. A debugger frame
+ * is used by each invocation of mdb_run() (the main parsing loop) to manage
+ * its state. Refer to the comments in mdb.c for more information on frames.
+ * Each frame has a list of commands (that is, a dcmd, argument list, and
+ * optional address list) that represent a pipeline after it has been parsed.
+ */
+
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb.h>
+
+mdb_cmd_t *
+mdb_cmd_create(mdb_idcmd_t *idcp, mdb_argvec_t *argv)
+{
+ mdb_cmd_t *cp = mdb_zalloc(sizeof (mdb_cmd_t), UM_NOSLEEP);
+
+ if (cp == NULL) {
+ warn("failed to allocate memory for command");
+ longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
+ }
+
+ mdb_list_append(&mdb.m_frame->f_cmds, cp);
+ mdb_argvec_copy(&cp->c_argv, argv);
+ mdb_argvec_zero(argv);
+ cp->c_dcmd = idcp;
+
+ return (cp);
+}
+
+void
+mdb_cmd_destroy(mdb_cmd_t *cp)
+{
+ mdb_addrvec_destroy(&cp->c_addrv);
+ mdb_argvec_destroy(&cp->c_argv);
+ mdb_vcb_purge(cp->c_vcbs);
+ mdb_free(cp, sizeof (mdb_cmd_t));
+}
+
+void
+mdb_cmd_reset(mdb_cmd_t *cp)
+{
+ mdb_addrvec_destroy(&cp->c_addrv);
+ mdb_vcb_purge(cp->c_vcbs);
+ cp->c_vcbs = NULL;
+}
+
+void
+mdb_frame_reset(mdb_frame_t *fp)
+{
+ mdb_cmd_t *cp;
+
+ while ((cp = mdb_list_next(&fp->f_cmds)) != NULL) {
+ mdb_list_delete(&fp->f_cmds, cp);
+ mdb_cmd_destroy(cp);
+ }
+ fp->f_cp = NULL;
+ fp->f_pcmd = NULL;
+
+ while (mdb_iob_stack_size(&fp->f_ostk) != 0) {
+ mdb_iob_destroy(mdb.m_out);
+ mdb.m_out = mdb_iob_stack_pop(&fp->f_ostk);
+ }
+
+ mdb_wcb_purge(&fp->f_wcbs);
+ mdb_recycle(&fp->f_mblks);
+}
+
+void
+mdb_frame_push(mdb_frame_t *fp)
+{
+ mdb_intr_disable();
+
+ if (mdb.m_fmark == NULL)
+ mdb.m_fmark = fp;
+
+ mdb_lex_state_save(mdb.m_frame->f_lstate);
+
+ bzero(fp, sizeof (mdb_frame_t));
+ mdb_lex_state_create(fp);
+ mdb_list_append(&mdb.m_flist, fp);
+
+ fp->f_flags = mdb.m_flags & MDB_FL_VOLATILE;
+ fp->f_pcmd = mdb.m_frame->f_pcmd;
+ fp->f_id = mdb.m_fid++;
+ mdb.m_frame->f_dot = mdb_nv_get_value(mdb.m_dot);
+
+ mdb.m_frame = fp;
+ mdb.m_depth++;
+
+ mdb_dprintf(MDB_DBG_DSTK, "push frame <%u> mark=%p in=%s out=%s\n",
+ fp->f_id, (void *)mdb.m_fmark,
+ mdb_iob_name(mdb.m_in), mdb_iob_name(mdb.m_out));
+
+ mdb_intr_enable();
+}
+
+void
+mdb_frame_pop(mdb_frame_t *fp, int err)
+{
+ mdb_intr_disable();
+
+ ASSERT(mdb_iob_stack_size(&fp->f_istk) == 0);
+ ASSERT(mdb_iob_stack_size(&fp->f_ostk) == 0);
+ ASSERT(mdb_list_next(&fp->f_cmds) == NULL);
+ ASSERT(fp->f_mblks == NULL);
+ ASSERT(fp->f_wcbs == NULL);
+
+ mdb_dprintf(MDB_DBG_DSTK, "pop frame <%u> status=%s\n",
+ fp->f_id, mdb_err2str(err));
+
+ if (mdb.m_frame == fp) {
+ mdb.m_flags &= ~MDB_FL_VOLATILE;
+ mdb.m_flags |= fp->f_flags;
+ mdb_frame_switch(mdb_list_prev(fp));
+ }
+
+ if (mdb.m_fmark == fp)
+ mdb.m_fmark = NULL;
+
+ mdb_lex_state_destroy(fp);
+
+ mdb_list_delete(&mdb.m_flist, fp);
+ ASSERT(mdb.m_depth != 0);
+ mdb.m_depth--;
+
+ mdb_intr_enable();
+}
+
+void
+mdb_frame_switch(mdb_frame_t *frame)
+{
+ mdb_lex_state_save(mdb.m_frame->f_lstate);
+ mdb.m_frame->f_dot = mdb_nv_get_value(mdb.m_dot);
+ mdb.m_frame = frame;
+ mdb_lex_state_restore(mdb.m_frame->f_lstate);
+ mdb_dprintf(MDB_DBG_DSTK, "switch to frame <%u>\n", mdb.m_frame->f_id);
+
+ mdb_nv_set_value(mdb.m_dot, frame->f_dot);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_frame.h b/usr/src/cmd/mdb/common/mdb/mdb_frame.h
new file mode 100644
index 0000000..b163814
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_frame.h
@@ -0,0 +1,90 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_FRAME_H
+#define _MDB_FRAME_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_addrvec.h>
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_umem.h>
+#include <mdb/mdb_vcb.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_wcb.h>
+#include <mdb/mdb.h>
+#include <setjmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_cmd {
+ mdb_list_t c_list; /* List forward/back pointers */
+ mdb_idcmd_t *c_dcmd; /* Dcmd to invoke */
+ mdb_argvec_t c_argv; /* Arguments for this command */
+ mdb_addrvec_t c_addrv; /* Addresses for this command */
+ mdb_vcb_t *c_vcbs; /* Variable control block list */
+} mdb_cmd_t;
+
+typedef struct mdb_frame {
+ mdb_list_t f_list; /* Frame stack forward/back pointers */
+ mdb_list_t f_cmds; /* List of commands to execute */
+ mdb_wcb_t *f_wcbs; /* Walk control blocks for GC */
+ mdb_mblk_t *f_mblks; /* Memory blocks for GC */
+ mdb_cmd_t *f_pcmd; /* Next cmd in pipe (if pipe active) */
+ mdb_cmd_t *f_cp; /* Pointer to executing command */
+ mdb_iob_stack_t f_istk; /* Stack of input i/o buffers */
+ mdb_iob_stack_t f_ostk; /* Stack of output i/o buffers */
+ jmp_buf f_pcb; /* Control block for longjmp */
+ uint_t f_flags; /* Volatile flags to save/restore */
+ uint_t f_id; /* ID for debugging purposes */
+ mdb_argvec_t f_argvec; /* Command arguments */
+ int f_oldstate; /* Last lex state */
+ struct mdb_lex_state *f_lstate; /* Current lex state */
+ uintmax_t f_dot; /* Value of '.' */
+} mdb_frame_t;
+
+#ifdef _MDB
+
+extern mdb_cmd_t *mdb_cmd_create(mdb_idcmd_t *, mdb_argvec_t *);
+extern void mdb_cmd_destroy(mdb_cmd_t *);
+extern void mdb_cmd_reset(mdb_cmd_t *);
+
+extern void mdb_frame_reset(mdb_frame_t *);
+extern void mdb_frame_push(mdb_frame_t *);
+extern void mdb_frame_pop(mdb_frame_t *, int);
+
+extern void mdb_frame_switch(mdb_frame_t *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_FRAME_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_gelf.c b/usr/src/cmd/mdb/common/mdb/mdb_gelf.c
new file mode 100644
index 0000000..95c96c2
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_gelf.c
@@ -0,0 +1,1816 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/isa_defs.h>
+#include <sys/link.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+#define GST_GROW 2 /* Mutable symbol table growth multiplier */
+#define GST_DEFSZ 16 /* Mutable symbol table initial size */
+
+#define GST_NVFLG (MDB_NV_EXTNAME | MDB_NV_SILENT)
+
+static const char *gelf_strtab; /* Active string table for qsort callbacks */
+
+static mdb_gelf_file_t *
+gelf_sect_init(mdb_gelf_file_t *gf)
+{
+ mdb_gelf_sect_t *gsp, *shstr = &gf->gf_sects[gf->gf_ehdr.e_shstrndx];
+ GElf_Half i, npbit = 0;
+ GElf_Shdr *shp;
+ GElf_Phdr *gpp;
+
+ if (gf->gf_mode == GF_PROGRAM)
+ gf->gf_ehdr.e_shnum = 0; /* Simplifies other code paths */
+
+ if (gf->gf_ehdr.e_shnum == 0)
+ return (gf); /* If no section headers we're done here */
+
+ if (IOP_SEEK(gf->gf_io, shstr->gs_shdr.sh_offset, SEEK_SET) == -1) {
+ warn("failed to seek %s to shdr strings", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ shstr->gs_data = mdb_zalloc(shstr->gs_shdr.sh_size + 1, UM_SLEEP);
+
+ if (IOP_READ(gf->gf_io, shstr->gs_data, shstr->gs_shdr.sh_size) !=
+ shstr->gs_shdr.sh_size) {
+ warn("failed to read %s shdr strings", IOP_NAME(gf->gf_io));
+ mdb_free(shstr->gs_data, shstr->gs_shdr.sh_size);
+ return (NULL);
+ }
+
+ for (gsp = gf->gf_sects, i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ shp = &gsp->gs_shdr;
+ gsp->gs_name = (const char *)shstr->gs_data + shp->sh_name;
+
+ if (shp->sh_name >= shstr->gs_shdr.sh_size) {
+ warn("section name for %s:[%u] is corrupt: %u\n",
+ IOP_NAME(gf->gf_io), (uint_t)i, shp->sh_name);
+ gsp->gs_name = shstr->gs_data; /* empty string */
+ }
+
+ if (shp->sh_type == SHT_PROGBITS && (shp->sh_flags & SHF_ALLOC))
+ npbit++; /* Keep count for ET_REL code below */
+ }
+
+ /*
+ * If the file is of type ET_REL, we would still like to provide file
+ * i/o using the mdb_gelf_rw() function defined below. To simplify
+ * things, we forge up a sequence of Phdrs based on Shdrs which have
+ * been marked SHF_ALLOC and are of type SHT_PROGBITS. We convert
+ * relevant Shdr fields to their Phdr equivalents, and then set the
+ * p_vaddr (virtual base address) to the section's file offset.
+ * This allows us to relocate a given symbol by simply incrementing
+ * its st_value by the file offset of the section corresponding to
+ * its st_shndx, and then perform i/o to read or write the symbol's
+ * value in the object file.
+ */
+ if (gf->gf_ehdr.e_type == ET_REL && npbit != 0) {
+ gf->gf_phdrs = mdb_zalloc(sizeof (GElf_Phdr) * npbit, UM_SLEEP);
+ gf->gf_ehdr.e_phnum = npbit;
+ gf->gf_npload = npbit;
+
+ gpp = gf->gf_phdrs;
+ gsp = gf->gf_sects;
+
+ for (i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ shp = &gsp->gs_shdr;
+
+ if ((shp->sh_type == SHT_PROGBITS) &&
+ (shp->sh_flags & SHF_ALLOC)) {
+ gpp->p_type = PT_LOAD;
+ gpp->p_flags = PF_R;
+
+ if (shp->sh_flags & SHF_EXECINSTR)
+ gpp->p_flags |= PF_X;
+ if (shp->sh_flags & SHF_WRITE)
+ gpp->p_flags |= PF_W;
+
+ gpp->p_offset = shp->sh_offset;
+ gpp->p_vaddr = shp->sh_offset;
+ gpp->p_filesz = shp->sh_size;
+ gpp->p_memsz = shp->sh_size;
+ gpp->p_align = shp->sh_addralign;
+
+ gpp++;
+ }
+ }
+ }
+
+ return (gf);
+}
+
+static void *
+gelf_sect_load(mdb_gelf_file_t *gf, mdb_gelf_sect_t *gsp)
+{
+ ssize_t nbytes;
+
+ if (gsp->gs_data != NULL)
+ return (gsp->gs_data);
+
+ mdb_dprintf(MDB_DBG_ELF, "loading %s:%s (%lu bytes)\n",
+ IOP_NAME(gf->gf_io), gsp->gs_name, (ulong_t)gsp->gs_shdr.sh_size);
+
+ gsp->gs_data = mdb_alloc(gsp->gs_shdr.sh_size, UM_SLEEP);
+
+ if (IOP_SEEK(gf->gf_io, gsp->gs_shdr.sh_offset, SEEK_SET) == -1) {
+ warn("failed to seek to start of %s:%s",
+ IOP_NAME(gf->gf_io), gsp->gs_name);
+ goto err;
+ }
+
+ nbytes = IOP_READ(gf->gf_io, gsp->gs_data, gsp->gs_shdr.sh_size);
+
+ if (nbytes < 0) {
+ warn("failed to read %s:%s", IOP_NAME(gf->gf_io), gsp->gs_name);
+ goto err;
+ }
+
+ if (nbytes < gsp->gs_shdr.sh_size) {
+ mdb_dprintf(MDB_DBG_ELF, "only %ld of %llu bytes of %s:%s "
+ "could be read\n", (long)nbytes, (u_longlong_t)
+ gsp->gs_shdr.sh_size, IOP_NAME(gf->gf_io), gsp->gs_name);
+ bzero((char *)gsp->gs_data + nbytes,
+ (size_t)gsp->gs_shdr.sh_size - nbytes);
+ }
+
+ return (gsp->gs_data);
+
+err:
+ mdb_free(gsp->gs_data, sizeof (gsp->gs_shdr.sh_size));
+ gsp->gs_data = NULL;
+ return (NULL);
+}
+
+void
+mdb_gelf_ehdr_to_gehdr(Ehdr *src, GElf_Ehdr *dst)
+{
+ bcopy(src->e_ident, dst->e_ident, sizeof (dst->e_ident));
+ dst->e_type = src->e_type;
+ dst->e_machine = src->e_machine;
+ dst->e_version = src->e_version;
+ dst->e_entry = src->e_entry;
+ dst->e_phoff = src->e_phoff;
+ dst->e_shoff = src->e_shoff;
+ dst->e_flags = src->e_flags;
+ dst->e_ehsize = src->e_ehsize;
+ dst->e_phentsize = src->e_phentsize;
+ dst->e_phnum = src->e_phnum;
+ dst->e_shentsize = src->e_shentsize;
+ dst->e_shnum = src->e_shnum;
+ dst->e_shstrndx = src->e_shstrndx;
+}
+
+static GElf_Shdr *
+gelf32_to_shdr(const Elf32_Shdr *src, GElf_Shdr *dst)
+{
+ if (src != NULL) {
+ dst->sh_name = src->sh_name;
+ dst->sh_type = src->sh_type;
+ dst->sh_flags = src->sh_flags;
+ dst->sh_addr = src->sh_addr;
+ dst->sh_offset = src->sh_offset;
+ dst->sh_size = src->sh_size;
+ dst->sh_link = src->sh_link;
+ dst->sh_info = src->sh_info;
+ dst->sh_addralign = src->sh_addralign;
+ dst->sh_entsize = src->sh_entsize;
+
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static GElf_Shdr *
+gelf64_to_shdr(const Elf64_Shdr *src, GElf_Shdr *dst)
+{
+ if (src != NULL) {
+ bcopy(src, dst, sizeof (Elf64_Shdr));
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static mdb_gelf_file_t *
+gelf_shdrs_init(mdb_gelf_file_t *gf, size_t shdr_size,
+ GElf_Shdr *(*elf2gelf)(const void *, GElf_Shdr *))
+{
+ caddr_t shdrs, shp;
+ GElf_Half i;
+
+ mdb_gelf_sect_t *gsp;
+ size_t nbytes;
+
+ mdb_dprintf(MDB_DBG_ELF, "loading %s section headers (%hu entries)\n",
+ IOP_NAME(gf->gf_io), gf->gf_ehdr.e_shnum);
+
+ if (gf->gf_ehdr.e_shnum == 0)
+ return (gf);
+
+ if (IOP_SEEK(gf->gf_io, (off64_t)gf->gf_ehdr.e_shoff, SEEK_SET) == -1) {
+ warn("failed to seek %s to shdrs", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ nbytes = shdr_size * gf->gf_ehdr.e_shnum;
+ shdrs = mdb_alloc(nbytes, UM_SLEEP);
+
+ if (IOP_READ(gf->gf_io, shdrs, nbytes) != nbytes) {
+ warn("failed to read %s section headers", IOP_NAME(gf->gf_io));
+ mdb_free(shdrs, nbytes);
+ return (NULL);
+ }
+
+ gf->gf_sects = mdb_zalloc(sizeof (mdb_gelf_sect_t) *
+ gf->gf_ehdr.e_shnum, UM_SLEEP);
+
+ shp = shdrs;
+ gsp = gf->gf_sects;
+
+ for (i = 0; i < gf->gf_ehdr.e_shnum; i++, shp += shdr_size, gsp++)
+ (void) elf2gelf(shp, &gsp->gs_shdr);
+
+ mdb_free(shdrs, nbytes);
+ return (gf);
+}
+
+static GElf_Phdr *
+gelf32_to_phdr(const Elf32_Phdr *src, GElf_Phdr *dst)
+{
+ if (src != NULL) {
+ dst->p_type = src->p_type;
+ dst->p_offset = src->p_offset;
+ dst->p_vaddr = src->p_vaddr;
+ dst->p_paddr = src->p_paddr;
+ dst->p_filesz = src->p_filesz;
+ dst->p_memsz = src->p_memsz;
+ dst->p_flags = src->p_flags;
+ dst->p_align = src->p_align;
+
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static GElf_Phdr *
+gelf64_to_phdr(const Elf64_Phdr *src, GElf_Phdr *dst)
+{
+ if (src != NULL) {
+ bcopy(src, dst, sizeof (Elf64_Phdr));
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static int
+gelf_phdr_compare(const void *lp, const void *rp)
+{
+ GElf_Phdr *lhs = (GElf_Phdr *)lp;
+ GElf_Phdr *rhs = (GElf_Phdr *)rp;
+
+ /*
+ * If both p_type fields are PT_LOAD, we want to sort by vaddr.
+ * Exception is that p_vaddr == 0 means ignore this (put at end).
+ */
+ if (lhs->p_type == PT_LOAD && rhs->p_type == PT_LOAD) {
+ if (lhs->p_vaddr != rhs->p_vaddr) {
+ if (lhs->p_vaddr == 0)
+ return (1); /* lhs is "greater" */
+
+ if (rhs->p_vaddr == 0)
+ return (-1); /* rhs is "greater" */
+
+ return (lhs->p_vaddr > rhs->p_vaddr ? 1 : -1);
+ }
+
+ return (0);
+ }
+
+ /*
+ * If the p_type fields don't match, we need to make sure that PT_LOAD
+ * entries are considered "less" (i.e. move towards the beginning
+ * of the array we are sorting)
+ */
+ if (lhs->p_type != rhs->p_type) {
+ if (lhs->p_type == PT_LOAD)
+ return (-1); /* rhs is "greater" */
+
+ if (rhs->p_type == PT_LOAD)
+ return (1); /* lhs is "greater" */
+
+ return (lhs->p_type > rhs->p_type ? 1 : -1);
+ }
+
+ /*
+ * If the p_type is the same but neither is PT_LOAD, then
+ * just sort by file offset (doesn't really matter)
+ */
+ if (lhs->p_offset != rhs->p_offset)
+ return (lhs->p_offset > rhs->p_offset ? 1 : -1);
+
+ return (0);
+}
+
+static mdb_gelf_file_t *
+gelf_phdrs_init(mdb_gelf_file_t *gf, size_t phdr_size,
+ GElf_Phdr *(*elf2gelf)(const void *, GElf_Phdr *))
+{
+ caddr_t phdrs, php;
+ GElf_Half i;
+
+ GElf_Phdr *gpp;
+ size_t nbytes;
+
+ mdb_dprintf(MDB_DBG_ELF, "loading %s program headers (%hu entries)\n",
+ IOP_NAME(gf->gf_io), gf->gf_ehdr.e_phnum);
+
+ if (gf->gf_ehdr.e_phnum == 0)
+ return (gf);
+
+ if (IOP_SEEK(gf->gf_io, (off64_t)gf->gf_ehdr.e_phoff, SEEK_SET) == -1) {
+ warn("failed to seek %s to phdrs", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ nbytes = phdr_size * gf->gf_ehdr.e_phnum;
+ phdrs = mdb_alloc(nbytes, UM_SLEEP);
+
+ if (IOP_READ(gf->gf_io, phdrs, nbytes) != nbytes) {
+ warn("failed to read %s program headers", IOP_NAME(gf->gf_io));
+ mdb_free(phdrs, nbytes);
+ return (NULL);
+ }
+
+ gf->gf_phdrs = mdb_zalloc(sizeof (GElf_Phdr) *
+ gf->gf_ehdr.e_phnum, UM_SLEEP);
+
+ php = phdrs;
+ gpp = gf->gf_phdrs;
+
+ /*
+ * Iterate through the list of phdrs locating those that are of type
+ * PT_LOAD; increment gf_npload so we know how many are loadable.
+ */
+ for (i = 0; i < gf->gf_ehdr.e_phnum; i++, php += phdr_size, gpp++) {
+ (void) elf2gelf(php, gpp);
+ if (gpp->p_type != PT_LOAD)
+ continue;
+
+ mdb_dprintf(MDB_DBG_ELF, "PT_LOAD va=0x%llx flags=0x%x "
+ "memsz=%llu filesz=%llu off=%llu\n", (u_longlong_t)
+ gpp->p_vaddr, gpp->p_flags, (u_longlong_t)gpp->p_memsz,
+ (u_longlong_t)gpp->p_filesz, (u_longlong_t)gpp->p_offset);
+
+ gf->gf_npload++;
+ }
+
+ /*
+ * Now we sort the phdrs array using a comparison routine which
+ * arranges for the PT_LOAD phdrs with non-zero virtual addresses
+ * to come first sorted by virtual address. This means that we
+ * can access the complete phdr table by examining the array
+ * gf->gf_phdrs[0 .. gf->gf_ehdr.e_phnum - 1], and we can access a
+ * sorted array of valid PT_LOAD pdhrs by examining the array
+ * gf->gf_phdrs[0 .. gf->gf_npload - 1].
+ */
+ qsort(gf->gf_phdrs, gf->gf_ehdr.e_phnum, sizeof (GElf_Phdr),
+ gelf_phdr_compare);
+
+ /*
+ * Locate the PT_DYNAMIC Phdr if one is present; we save this
+ * Phdr pointer in gf->gf_dynp for future use.
+ */
+ for (gpp = gf->gf_phdrs, i = 0; i < gf->gf_ehdr.e_phnum; i++, gpp++) {
+ if (gpp->p_type == PT_DYNAMIC) {
+ mdb_dprintf(MDB_DBG_ELF, "PT_DYNAMIC "
+ "filesize = %lluULL off=%lluULL\n",
+ (u_longlong_t)gpp->p_filesz,
+ (u_longlong_t)gpp->p_offset);
+
+ gf->gf_dynp = gpp;
+ break;
+ }
+ }
+
+ mdb_free(phdrs, nbytes);
+ return (gf);
+}
+
+static GElf_Dyn *
+gelf32_to_dyn(const Elf32_Dyn *src, GElf_Dyn *dst)
+{
+ if (src != NULL) {
+ dst->d_tag = (GElf_Xword)(Elf32_Word)src->d_tag;
+ dst->d_un.d_ptr = src->d_un.d_ptr;
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static GElf_Dyn *
+gelf64_to_dyn(const Elf64_Dyn *src, GElf_Dyn *dst)
+{
+ if (src != NULL) {
+ bcopy(src, dst, sizeof (Elf64_Dyn));
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static GElf_Xword
+gelf_dyn_lookup(mdb_gelf_file_t *gf, GElf_Xword tag)
+{
+ size_t i;
+
+ for (i = 0; i < gf->gf_ndyns; i++) {
+ if (gf->gf_dyns[i].d_tag == tag)
+ return (gf->gf_dyns[i].d_un.d_val);
+ }
+
+ return ((GElf_Xword)-1L);
+}
+
+static GElf_Dyn *
+gelf_dyns_init(mdb_gelf_file_t *gf, size_t dyn_size,
+ GElf_Dyn *(*elf2gelf)(const void *, GElf_Dyn *))
+{
+ size_t nbytes, ndyns, i;
+ caddr_t dyns, dp;
+ GElf_Dyn *gdp;
+
+ off64_t dyn_addr;
+
+ if (gf->gf_dyns != NULL)
+ return (gf->gf_dyns); /* Already loaded */
+
+ if (gf->gf_dynp == NULL)
+ return (NULL); /* No PT_DYNAMIC entry was found */
+
+ nbytes = gf->gf_dynp->p_filesz;
+ ndyns = nbytes / dyn_size;
+
+ /*
+ * If this is an executable in PROGRAM view, then p_vaddr is an
+ * absolute address; we need to subtract the virtual base address of
+ * the mapping. In FILE view, dyn_addr is just the file offset.
+ */
+ if (gf->gf_mode == GF_PROGRAM) {
+ if (gf->gf_ehdr.e_type == ET_EXEC && gf->gf_npload != 0)
+ dyn_addr = gf->gf_dynp->p_vaddr - gf->gf_phdrs->p_vaddr;
+ else
+ dyn_addr = gf->gf_dynp->p_vaddr;
+ } else {
+ mdb_gelf_sect_t *gsp = gf->gf_sects;
+
+ for (i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ if (gsp->gs_shdr.sh_type == SHT_DYNAMIC) {
+ dyn_addr = gsp->gs_shdr.sh_offset;
+ break;
+ }
+ }
+
+ if (i == gf->gf_ehdr.e_shnum)
+ return (NULL); /* No SHT_DYNAMIC entry was found */
+ }
+
+ mdb_dprintf(MDB_DBG_ELF, "loading _DYNAMIC[] (%lu entries) "
+ "from offset %llx\n", (ulong_t)ndyns, (longlong_t)dyn_addr);
+
+ if (IOP_SEEK(gf->gf_io, dyn_addr, SEEK_SET) == -1) {
+ warn("failed to seek %s to _DYNAMIC", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ dyns = mdb_alloc(nbytes, UM_SLEEP);
+
+ if (IOP_READ(gf->gf_io, dyns, nbytes) != nbytes) {
+ warn("failed to read %s:_DYNAMIC", IOP_NAME(gf->gf_io));
+ mdb_free(dyns, nbytes);
+ return (NULL);
+ }
+
+ gf->gf_dyns = mdb_zalloc(sizeof (GElf_Dyn) * ndyns, UM_SLEEP);
+ gf->gf_ndyns = ndyns;
+
+ dp = dyns;
+ gdp = gf->gf_dyns;
+
+ for (i = 0; i < ndyns; i++, dp += dyn_size, gdp++)
+ (void) elf2gelf(dp, gdp);
+
+ mdb_free(dyns, nbytes);
+ return (gf->gf_dyns);
+}
+
+static mdb_gelf_file_t *
+gelf32_init(mdb_gelf_file_t *gf, const Elf32_Ehdr *ehdr)
+{
+ /*
+ * Convert the Elf32_Ehdr to a GElf_Ehdr
+ */
+ bcopy(ehdr->e_ident, gf->gf_ehdr.e_ident, EI_NIDENT);
+
+ gf->gf_ehdr.e_type = ehdr->e_type;
+ gf->gf_ehdr.e_machine = ehdr->e_machine;
+ gf->gf_ehdr.e_version = ehdr->e_version;
+ gf->gf_ehdr.e_entry = ehdr->e_entry;
+ gf->gf_ehdr.e_phoff = ehdr->e_phoff;
+ gf->gf_ehdr.e_shoff = ehdr->e_shoff;
+ gf->gf_ehdr.e_flags = ehdr->e_flags;
+ gf->gf_ehdr.e_ehsize = ehdr->e_ehsize;
+ gf->gf_ehdr.e_phentsize = ehdr->e_phentsize;
+ gf->gf_ehdr.e_phnum = ehdr->e_phnum;
+ gf->gf_ehdr.e_shentsize = ehdr->e_shentsize;
+ gf->gf_ehdr.e_shnum = ehdr->e_shnum;
+ gf->gf_ehdr.e_shstrndx = ehdr->e_shstrndx;
+
+ /*
+ * Initialize the section and program headers. We skip initializing
+ * the section headers if this is a program image because they are
+ * not loadable and thus we can't get at them.
+ */
+ if (gf->gf_mode == GF_FILE && gelf_shdrs_init(gf, sizeof (Elf32_Shdr),
+ (GElf_Shdr *(*)(const void *, GElf_Shdr *))gelf32_to_shdr) == NULL)
+ return (NULL);
+
+ if (gelf_phdrs_init(gf, sizeof (Elf32_Phdr),
+ (GElf_Phdr *(*)(const void *, GElf_Phdr *))gelf32_to_phdr) == NULL)
+ return (NULL);
+
+ (void) gelf_dyns_init(gf, sizeof (Elf32_Dyn),
+ (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf32_to_dyn);
+
+ return (gf);
+}
+
+static mdb_gelf_file_t *
+gelf64_init(mdb_gelf_file_t *gf, Elf64_Ehdr *ehdr)
+{
+ /*
+ * Save a copy of the ELF file header
+ */
+ bcopy(ehdr, &gf->gf_ehdr, sizeof (Elf64_Ehdr));
+
+ /*
+ * Initialize the section and program headers. We skip initializing
+ * the section headers if this is a program image because they are
+ * not loadable and thus we can't get at them.
+ */
+ if (gf->gf_mode == GF_FILE && gelf_shdrs_init(gf, sizeof (Elf64_Shdr),
+ (GElf_Shdr *(*)(const void *, GElf_Shdr *))gelf64_to_shdr) == NULL)
+ return (NULL);
+
+ if (gelf_phdrs_init(gf, sizeof (Elf64_Phdr),
+ (GElf_Phdr *(*)(const void *, GElf_Phdr *))gelf64_to_phdr) == NULL)
+ return (NULL);
+
+ (void) gelf_dyns_init(gf, sizeof (Elf64_Dyn),
+ (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf64_to_dyn);
+
+ return (gf);
+}
+
+int
+mdb_gelf_check(mdb_io_t *io, Elf32_Ehdr *ehp, GElf_Half etype)
+{
+#ifdef _BIG_ENDIAN
+ uchar_t order = ELFDATA2MSB;
+#else
+ uchar_t order = ELFDATA2LSB;
+#endif
+ ssize_t nbytes;
+
+ (void) IOP_SEEK(io, (off64_t)0L, SEEK_SET);
+ nbytes = IOP_READ(io, ehp, sizeof (Elf32_Ehdr));
+
+ if (nbytes == -1) {
+ if (etype != ET_NONE)
+ warn("failed to read ELF header from %s", IOP_NAME(io));
+ return (-1);
+ }
+
+ if (nbytes != sizeof (Elf32_Ehdr) ||
+ bcmp(&ehp->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0) {
+ if (etype != ET_NONE)
+ warn("%s is not an ELF file\n", IOP_NAME(io));
+ return (-1);
+ }
+
+ if (ehp->e_ident[EI_DATA] != order) {
+ warn("ELF file %s has different endianness from debugger\n",
+ IOP_NAME(io));
+ return (-1);
+ }
+
+ if (ehp->e_version != EV_CURRENT) {
+ warn("ELF file %s uses different ELF version (%lu) than "
+ "debugger (%u)\n", IOP_NAME(io),
+ (ulong_t)ehp->e_version, EV_CURRENT);
+ return (-1);
+ }
+
+ if (etype != ET_NONE && ehp->e_type != etype) {
+ warn("ELF file %s is not of the expected type\n", IOP_NAME(io));
+ return (-1);
+ }
+
+ return (0);
+}
+
+mdb_gelf_file_t *
+mdb_gelf_create(mdb_io_t *io, GElf_Half etype, int mode)
+{
+ union {
+ Elf32_Ehdr h32;
+ Elf64_Ehdr h64;
+ } ehdr;
+
+ mdb_gelf_file_t *gf = mdb_zalloc(sizeof (mdb_gelf_file_t), UM_SLEEP);
+
+ ASSERT(mode == GF_FILE || mode == GF_PROGRAM);
+ gf->gf_mode = mode;
+
+ /*
+ * Assign the i/o backend now, but don't hold it until we're sure
+ * we're going to succeed; otherwise the caller will be responsible
+ * for mdb_io_destroy()ing it.
+ */
+ gf->gf_io = io;
+
+ if (mdb_gelf_check(io, &ehdr.h32, etype) == -1)
+ goto err;
+
+ switch (ehdr.h32.e_ident[EI_CLASS]) {
+ case ELFCLASS32:
+ gf = gelf32_init(gf, &ehdr.h32);
+ break;
+
+ case ELFCLASS64:
+ if (IOP_SEEK(io, (off64_t)0L, SEEK_SET) == -1) {
+ warn("failed to seek %s", IOP_NAME(io));
+ goto err;
+ }
+
+ if (IOP_READ(io, &ehdr.h64, sizeof (ehdr.h64)) !=
+ sizeof (ehdr.h64)) {
+ warn("failed to read ELF header from %s", IOP_NAME(io));
+ goto err;
+ }
+
+ gf = gelf64_init(gf, &ehdr.h64);
+ break;
+
+ default:
+ warn("%s is an unsupported ELF class: %u\n",
+ IOP_NAME(io), ehdr.h32.e_ident[EI_CLASS]);
+ goto err;
+ }
+
+ if (gf != NULL && gelf_sect_init(gf) != NULL) {
+ gf->gf_io = mdb_io_hold(io);
+ return (gf);
+ }
+
+err:
+ if (gf != NULL) {
+ if (gf->gf_sects != NULL) {
+ mdb_free(gf->gf_sects, gf->gf_ehdr.e_shnum *
+ sizeof (mdb_gelf_sect_t));
+ }
+ mdb_free(gf, sizeof (mdb_gelf_file_t));
+ }
+ return (NULL);
+}
+
+void
+mdb_gelf_destroy(mdb_gelf_file_t *gf)
+{
+ mdb_gelf_sect_t *gsp;
+ GElf_Half i;
+
+ for (gsp = gf->gf_sects, i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ if (gsp->gs_data != NULL)
+ mdb_free(gsp->gs_data, gsp->gs_shdr.sh_size);
+ }
+
+ mdb_free(gf->gf_sects,
+ gf->gf_ehdr.e_shnum * sizeof (mdb_gelf_sect_t));
+
+ mdb_free(gf->gf_phdrs,
+ gf->gf_ehdr.e_phnum * sizeof (GElf_Phdr));
+
+ mdb_io_rele(gf->gf_io);
+ mdb_free(gf, sizeof (mdb_gelf_file_t));
+}
+
+/*
+ * Sort comparison function for 32-bit symbol address-to-name lookups. We sort
+ * symbols by value. If values are equal, we prefer the symbol that is
+ * non-zero sized, typed, not weak, or lexically first, in that order.
+ */
+static int
+gelf32_sym_compare(const void *lp, const void *rp)
+{
+ Elf32_Sym *lhs = *((Elf32_Sym **)lp);
+ Elf32_Sym *rhs = *((Elf32_Sym **)rp);
+
+ if (lhs->st_value != rhs->st_value)
+ return (lhs->st_value > rhs->st_value ? 1 : -1);
+
+ if ((lhs->st_size == 0) != (rhs->st_size == 0))
+ return (lhs->st_size == 0 ? 1 : -1);
+
+ if ((ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE) !=
+ (ELF32_ST_TYPE(rhs->st_info) == STT_NOTYPE))
+ return (ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1);
+
+ if ((ELF32_ST_BIND(lhs->st_info) == STB_WEAK) !=
+ (ELF32_ST_BIND(rhs->st_info) == STB_WEAK))
+ return (ELF32_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1);
+
+ return (strcmp(gelf_strtab + lhs->st_name, gelf_strtab + rhs->st_name));
+}
+
+/*
+ * Sort comparison function for 64-bit symbol address-to-name lookups. We sort
+ * symbols by value. If values are equal, we prefer the symbol that is
+ * non-zero sized, typed, not weak, or lexically first, in that order.
+ */
+static int
+gelf64_sym_compare(const void *lp, const void *rp)
+{
+ Elf64_Sym *lhs = *((Elf64_Sym **)lp);
+ Elf64_Sym *rhs = *((Elf64_Sym **)rp);
+
+ if (lhs->st_value != rhs->st_value)
+ return (lhs->st_value > rhs->st_value ? 1 : -1);
+
+ if ((lhs->st_size == 0) != (rhs->st_size == 0))
+ return (lhs->st_size == 0 ? 1 : -1);
+
+ if ((ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE) !=
+ (ELF64_ST_TYPE(rhs->st_info) == STT_NOTYPE))
+ return (ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1);
+
+ if ((ELF64_ST_BIND(lhs->st_info) == STB_WEAK) !=
+ (ELF64_ST_BIND(rhs->st_info) == STB_WEAK))
+ return (ELF64_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1);
+
+ return (strcmp(gelf_strtab + lhs->st_name, gelf_strtab + rhs->st_name));
+}
+
+static void
+gelf32_symtab_sort(mdb_gelf_symtab_t *gst)
+{
+ Elf32_Sym **sympp = (Elf32_Sym **)gst->gst_asmap;
+ mdb_var_t *v;
+
+ mdb_nv_rewind(&gst->gst_nv);
+
+ while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
+ Elf32_Sym *sym = MDB_NV_COOKIE(v);
+ if (sym->st_value != 0 &&
+ (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
+ *sympp++ = sym;
+ }
+
+ gst->gst_aslen = (size_t)(sympp - (Elf32_Sym **)gst->gst_asmap);
+ ASSERT(gst->gst_aslen <= gst->gst_asrsv);
+
+ gelf_strtab = gst->gst_ssect ? gst->gst_ssect->gs_data : NULL;
+
+ qsort(gst->gst_asmap, gst->gst_aslen,
+ sizeof (Elf32_Sym *), gelf32_sym_compare);
+
+ gelf_strtab = NULL;
+}
+
+static void
+gelf32_symtab_init(mdb_gelf_symtab_t *gst)
+{
+ const char *base = (const char *)gst->gst_ssect->gs_data;
+ Elf32_Sym *sym = gst->gst_dsect->gs_data;
+ mdb_nv_t *nv = &gst->gst_nv;
+
+ Elf32_Word ss_size = gst->gst_ssect->gs_shdr.sh_size;
+ size_t asrsv = 0;
+ GElf_Word i, n;
+
+ if (gst->gst_dsect->gs_shdr.sh_entsize != sizeof (Elf32_Sym)) {
+ warn("%s sh_entsize %llu != sizeof (Elf32_Sym); "
+ "using %u instead\n", gst->gst_dsect->gs_name,
+ (u_longlong_t)gst->gst_dsect->gs_shdr.sh_entsize,
+ (uint_t)sizeof (Elf32_Sym));
+ gst->gst_dsect->gs_shdr.sh_entsize = sizeof (Elf32_Sym);
+ }
+
+ n = gst->gst_dsect->gs_shdr.sh_size /
+ gst->gst_dsect->gs_shdr.sh_entsize;
+
+ for (i = 0; i < n; i++, sym++) {
+ const char *name = base + sym->st_name;
+ uchar_t type = ELF32_ST_TYPE(sym->st_info);
+
+ if (type >= STT_NUM || type == STT_SECTION)
+ continue; /* skip sections and unknown types */
+
+ if (sym->st_name >= ss_size || name[0] < '!' || name[0] > '~') {
+ if (sym->st_name >= ss_size || name[0] != '\0') {
+ warn("ignoring %s symbol [%u]: invalid name\n",
+ gst->gst_dsect->gs_name, i);
+ sym->st_name = 0;
+ }
+ continue; /* skip corrupt or empty names */
+ }
+
+ (void) mdb_nv_insert(nv, name, NULL, (uintptr_t)sym, GST_NVFLG);
+
+ if (sym->st_value != 0 &&
+ (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
+ asrsv++; /* reserve space in the address map */
+ }
+
+ if (gst->gst_ehdr->e_type == ET_REL && gst->gst_file != NULL) {
+ GElf_Word smax = gst->gst_ehdr->e_shnum;
+ mdb_gelf_sect_t *gsp;
+
+ for (sym = gst->gst_dsect->gs_data, i = 0; i < n; i++, sym++) {
+ if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < smax) {
+ gsp = &gst->gst_file->gf_sects[sym->st_shndx];
+ sym->st_value += gsp->gs_shdr.sh_offset;
+
+ if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL ||
+ sym->st_size != 0)
+ asrsv++; /* reserve space in asmap */
+ }
+ }
+ }
+
+ gst->gst_asmap = mdb_alloc(sizeof (Elf32_Sym *) * asrsv, UM_SLEEP);
+ gst->gst_asrsv = asrsv;
+
+ gelf32_symtab_sort(gst);
+}
+
+static void
+gelf64_symtab_sort(mdb_gelf_symtab_t *gst)
+{
+ Elf64_Sym **sympp = (Elf64_Sym **)gst->gst_asmap;
+ mdb_var_t *v;
+
+ mdb_nv_rewind(&gst->gst_nv);
+
+ while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
+ Elf64_Sym *sym = MDB_NV_COOKIE(v);
+ if (sym->st_value != 0 &&
+ (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
+ *sympp++ = sym;
+ }
+
+ gst->gst_aslen = (size_t)(sympp - (Elf64_Sym **)gst->gst_asmap);
+ ASSERT(gst->gst_aslen <= gst->gst_asrsv);
+
+ gelf_strtab = gst->gst_ssect ? gst->gst_ssect->gs_data : NULL;
+
+ qsort(gst->gst_asmap, gst->gst_aslen,
+ sizeof (Elf64_Sym *), gelf64_sym_compare);
+
+ gelf_strtab = NULL;
+}
+
+static void
+gelf64_symtab_init(mdb_gelf_symtab_t *gst)
+{
+ const char *base = (const char *)gst->gst_ssect->gs_data;
+ Elf64_Sym *sym = gst->gst_dsect->gs_data;
+ mdb_nv_t *nv = &gst->gst_nv;
+
+ Elf64_Xword ss_size = gst->gst_ssect->gs_shdr.sh_size;
+ size_t asrsv = 0;
+ GElf_Word i, n;
+
+ if (gst->gst_dsect->gs_shdr.sh_entsize != sizeof (Elf64_Sym)) {
+ warn("%s sh_entsize %llu != sizeof (Elf64_Sym); "
+ "using %u instead\n", gst->gst_dsect->gs_name,
+ (u_longlong_t)gst->gst_dsect->gs_shdr.sh_entsize,
+ (uint_t)sizeof (Elf64_Sym));
+ gst->gst_dsect->gs_shdr.sh_entsize = sizeof (Elf64_Sym);
+ }
+
+ n = gst->gst_dsect->gs_shdr.sh_size /
+ gst->gst_dsect->gs_shdr.sh_entsize;
+
+ for (i = 0; i < n; i++, sym++) {
+ const char *name = base + sym->st_name;
+ uchar_t type = ELF64_ST_TYPE(sym->st_info);
+
+ if (type >= STT_NUM || type == STT_SECTION)
+ continue; /* skip sections and unknown types */
+
+ if (sym->st_name >= ss_size || name[0] < '!' || name[0] > '~') {
+ if (sym->st_name >= ss_size || name[0] != '\0') {
+ warn("ignoring %s symbol [%u]: invalid name\n",
+ gst->gst_dsect->gs_name, i);
+ sym->st_name = 0;
+ }
+ continue; /* skip corrupt or empty names */
+ }
+
+ (void) mdb_nv_insert(nv, name, NULL, (uintptr_t)sym, GST_NVFLG);
+
+ if (sym->st_value != 0 &&
+ (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
+ asrsv++; /* reserve space in the address map */
+ }
+
+ if (gst->gst_ehdr->e_type == ET_REL && gst->gst_file != NULL) {
+ GElf_Word smax = gst->gst_ehdr->e_shnum;
+ mdb_gelf_sect_t *gsp;
+
+ for (sym = gst->gst_dsect->gs_data, i = 0; i < n; i++, sym++) {
+ if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < smax) {
+ gsp = &gst->gst_file->gf_sects[sym->st_shndx];
+ sym->st_value += gsp->gs_shdr.sh_offset;
+
+ if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL ||
+ sym->st_size != 0)
+ asrsv++; /* reserve space in asmap */
+ }
+ }
+ }
+
+ gst->gst_asmap = mdb_alloc(sizeof (Elf64_Sym *) * asrsv, UM_SLEEP);
+ gst->gst_asrsv = asrsv;
+
+ gelf64_symtab_sort(gst);
+}
+
+mdb_gelf_symtab_t *
+mdb_gelf_symtab_create_file(mdb_gelf_file_t *gf, GElf_Word elftype,
+ uint_t tabid)
+{
+ mdb_gelf_sect_t *gsp;
+ const char *dsname = NULL;
+ const char *ssname;
+ GElf_Half i;
+ GElf_Word link;
+
+ /*
+ * Examine the sh_link field in the the Elf header to get the name
+ * of the corresponding strings section
+ */
+ for (gsp = gf->gf_sects, i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ if (gsp->gs_shdr.sh_type == elftype) {
+ dsname = gsp->gs_name;
+ link = gsp->gs_shdr.sh_link;
+ break;
+ }
+ }
+
+ if (dsname == NULL)
+ return (NULL);
+
+ if (link > gf->gf_ehdr.e_shnum) {
+ /*
+ * Invalid link number due to corrupt elf file.
+ */
+ warn("link number %ud larger than number of sections %d\n",
+ link, gf->gf_ehdr.e_shnum);
+ return (NULL);
+ }
+
+ ssname = (gf->gf_sects + link)->gs_name;
+
+ return (mdb_gelf_symtab_create_file_by_name(gf, dsname, ssname, tabid));
+}
+
+mdb_gelf_symtab_t *
+mdb_gelf_symtab_create_file_by_name(mdb_gelf_file_t *gf,
+ const char *dsname, const char *ssname, uint_t tabid)
+{
+ mdb_gelf_symtab_t *gst;
+ mdb_gelf_sect_t *gsp;
+ GElf_Half i;
+
+ gst = mdb_alloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
+ (void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
+
+ gst->gst_asmap = NULL;
+ gst->gst_aslen = 0;
+ gst->gst_asrsv = 0;
+ gst->gst_ehdr = &gf->gf_ehdr;
+ gst->gst_file = gf;
+ gst->gst_dsect = NULL;
+ gst->gst_ssect = NULL;
+ gst->gst_id = 0;
+ gst->gst_tabid = tabid;
+
+ for (gsp = gf->gf_sects, i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ if (strcmp(gsp->gs_name, dsname) == 0) {
+ gst->gst_dsect = gsp;
+ break;
+ }
+ }
+
+ for (gsp = gf->gf_sects, i = 0; i < gf->gf_ehdr.e_shnum; i++, gsp++) {
+ if (strcmp(gsp->gs_name, ssname) == 0) {
+ gst->gst_ssect = gsp;
+ break;
+ }
+ }
+
+ if (gst->gst_dsect == NULL || gst->gst_ssect == NULL)
+ goto err; /* Failed to locate data or string section */
+
+ if (gelf_sect_load(gf, gst->gst_dsect) == NULL)
+ goto err; /* Failed to load data section */
+
+ if (gelf_sect_load(gf, gst->gst_ssect) == NULL)
+ goto err; /* Failed to load string section */
+
+ if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32)
+ gelf32_symtab_init(gst);
+ else
+ gelf64_symtab_init(gst);
+
+ return (gst);
+
+err:
+ mdb_nv_destroy(&gst->gst_nv);
+ mdb_free(gst, sizeof (mdb_gelf_symtab_t));
+ return (NULL);
+}
+
+mdb_gelf_symtab_t *
+mdb_gelf_symtab_create_raw(const GElf_Ehdr *ehdr, const void *dshdr,
+ void *ddata, const void *sshdr, void *sdata, uint_t tabid)
+{
+ mdb_gelf_symtab_t *gst;
+
+ gst = mdb_alloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
+ (void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
+
+ gst->gst_asmap = NULL;
+ gst->gst_aslen = 0;
+ gst->gst_asrsv = 0;
+ gst->gst_ehdr = ehdr;
+ gst->gst_file = NULL; /* Flag for raw symtab */
+ gst->gst_id = 0;
+ gst->gst_tabid = tabid;
+
+ gst->gst_dsect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
+ gst->gst_dsect->gs_name = ".symtab";
+ gst->gst_dsect->gs_data = ddata;
+
+ gst->gst_ssect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
+ gst->gst_ssect->gs_name = ".strtab";
+ gst->gst_ssect->gs_data = sdata;
+
+ if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
+ (void) gelf32_to_shdr(dshdr, &gst->gst_dsect->gs_shdr);
+ (void) gelf32_to_shdr(sshdr, &gst->gst_ssect->gs_shdr);
+ gelf32_symtab_init(gst);
+ } else {
+ (void) gelf64_to_shdr(dshdr, &gst->gst_dsect->gs_shdr);
+ (void) gelf64_to_shdr(sshdr, &gst->gst_ssect->gs_shdr);
+ gelf64_symtab_init(gst);
+ }
+
+ return (gst);
+}
+
+mdb_gelf_symtab_t *
+mdb_gelf_symtab_create_dynamic(mdb_gelf_file_t *gf, uint_t tabid)
+{
+ GElf_Addr dt_symtab, dt_strtab, dt_hash;
+ GElf_Xword dt_syment, dt_strsz;
+
+ mdb_gelf_symtab_t *gst;
+ uint_t hash_h[2];
+ off64_t base = 0;
+
+ ASSERT(gf->gf_mode == GF_PROGRAM);
+
+ /*
+ * Read in and cache the array of GElf_Dyn structures from the
+ * PT_DYNAMIC phdr. Abort if this is not possible.
+ */
+ if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
+ (void) gelf_dyns_init(gf, sizeof (Elf32_Dyn),
+ (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf32_to_dyn);
+ } else {
+ (void) gelf_dyns_init(gf, sizeof (Elf64_Dyn),
+ (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf64_to_dyn);
+ }
+
+ /*
+ * Pre-fetch all the DT_* entries we will need for creating the
+ * dynamic symbol table; abort if any are missing.
+ */
+ if ((dt_hash = gelf_dyn_lookup(gf, DT_HASH)) == -1L) {
+ warn("failed to get DT_HASH for %s\n", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ if ((dt_symtab = gelf_dyn_lookup(gf, DT_SYMTAB)) == -1L) {
+ warn("failed to get DT_SYMTAB for %s\n", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ if ((dt_syment = gelf_dyn_lookup(gf, DT_SYMENT)) == -1L) {
+ warn("failed to get DT_SYMENT for %s\n", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ if ((dt_strtab = gelf_dyn_lookup(gf, DT_STRTAB)) == -1L) {
+ warn("failed to get DT_STRTAB for %s\n", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ if ((dt_strsz = gelf_dyn_lookup(gf, DT_STRSZ)) == -1L) {
+ warn("failed to get DT_STRSZ for %s\n", IOP_NAME(gf->gf_io));
+ return (NULL);
+ }
+
+ /*
+ * If this is an executable, then DT_HASH is an absolute address;
+ * we need to subtract the virtual base address of the mapping.
+ */
+ if (gf->gf_ehdr.e_type == ET_EXEC && gf->gf_npload != 0)
+ base = (off64_t)gf->gf_phdrs->p_vaddr;
+
+ /*
+ * Read in the header for the DT_HASH: this consists of nbucket
+ * and nchain values (nchain is the number of hashed symbols).
+ */
+ if (IOP_SEEK(gf->gf_io, (off64_t)dt_hash - base, SEEK_SET) == -1) {
+ warn("failed to seek ELF file to start of DT_HASH");
+ return (NULL);
+ }
+
+ if (IOP_READ(gf->gf_io, hash_h, sizeof (hash_h)) != sizeof (hash_h)) {
+ warn("failed to read DT_HASH header");
+ return (NULL);
+ }
+
+ gst = mdb_zalloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
+ (void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
+
+ gst->gst_asmap = NULL;
+ gst->gst_aslen = 0;
+ gst->gst_asrsv = 0;
+ gst->gst_ehdr = &gf->gf_ehdr;
+ gst->gst_file = gf;
+ gst->gst_id = 0;
+ gst->gst_tabid = tabid;
+
+ gst->gst_dsect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
+ gst->gst_dsect->gs_name = ".dynsym";
+ gst->gst_dsect->gs_shdr.sh_offset = dt_symtab - (GElf_Addr)base;
+ gst->gst_dsect->gs_shdr.sh_size = hash_h[1] * dt_syment;
+ gst->gst_dsect->gs_shdr.sh_entsize = dt_syment;
+
+ gst->gst_ssect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
+ gst->gst_ssect->gs_name = ".dynstr";
+ gst->gst_ssect->gs_shdr.sh_offset = dt_strtab - (GElf_Addr)base;
+ gst->gst_ssect->gs_shdr.sh_size = dt_strsz;
+ gst->gst_ssect->gs_shdr.sh_entsize = 0;
+
+ if (gelf_sect_load(gf, gst->gst_dsect) == NULL)
+ goto err;
+
+ if (gelf_sect_load(gf, gst->gst_ssect) == NULL)
+ goto err;
+
+ if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32)
+ gelf32_symtab_init(gst);
+ else
+ gelf64_symtab_init(gst);
+
+ return (gst);
+
+err:
+ mdb_gelf_symtab_destroy(gst);
+ return (NULL);
+}
+
+mdb_gelf_symtab_t *
+mdb_gelf_symtab_create_mutable(void)
+{
+ mdb_gelf_symtab_t *gst;
+ static GElf_Ehdr ehdr;
+
+ gst = mdb_zalloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
+ (void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
+ gst->gst_ehdr = &ehdr;
+
+ if (ehdr.e_version == 0) {
+#ifdef _LP64
+ uchar_t class = ELFCLASS64;
+#else
+ uchar_t class = ELFCLASS32;
+#endif
+
+#ifdef _BIG_ENDIAN
+ uchar_t data = ELFDATA2MSB;
+#else
+ uchar_t data = ELFDATA2LSB;
+#endif
+ /*
+ * Since all mutable symbol tables will use a native Ehdr,
+ * we can just have a single static copy which they all
+ * point to and we only need initialize once.
+ */
+ ehdr.e_ident[EI_MAG0] = ELFMAG0;
+ ehdr.e_ident[EI_MAG1] = ELFMAG1;
+ ehdr.e_ident[EI_MAG2] = ELFMAG2;
+ ehdr.e_ident[EI_MAG3] = ELFMAG3;
+ ehdr.e_ident[EI_CLASS] = class;
+ ehdr.e_ident[EI_DATA] = data;
+ ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr.e_type = ET_NONE;
+ ehdr.e_version = EV_CURRENT;
+ }
+
+ return (gst);
+}
+
+void
+mdb_gelf_symtab_destroy(mdb_gelf_symtab_t *gst)
+{
+ if (gst->gst_file == NULL) {
+ if (gst->gst_dsect == NULL && gst->gst_ssect == NULL) {
+ mdb_var_t *v;
+
+ mdb_nv_rewind(&gst->gst_nv);
+ while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
+ char *name = (char *)mdb_nv_get_name(v);
+ mdb_gelf_dsym_t *dsp = mdb_nv_get_cookie(v);
+
+ mdb_free(name, strlen(name) + 1);
+ mdb_free(dsp, sizeof (mdb_gelf_dsym_t));
+ }
+
+ } else {
+ mdb_free(gst->gst_dsect, sizeof (mdb_gelf_sect_t));
+ mdb_free(gst->gst_ssect, sizeof (mdb_gelf_sect_t));
+ }
+
+ } else if (gst->gst_file->gf_mode == GF_PROGRAM) {
+ mdb_gelf_sect_t *dsect = gst->gst_dsect;
+ mdb_gelf_sect_t *ssect = gst->gst_ssect;
+
+ if (dsect->gs_data != NULL)
+ mdb_free(dsect->gs_data, dsect->gs_shdr.sh_size);
+ if (ssect->gs_data != NULL)
+ mdb_free(ssect->gs_data, ssect->gs_shdr.sh_size);
+
+ mdb_free(gst->gst_dsect, sizeof (mdb_gelf_sect_t));
+ mdb_free(gst->gst_ssect, sizeof (mdb_gelf_sect_t));
+ }
+
+ mdb_nv_destroy(&gst->gst_nv);
+ mdb_free(gst->gst_asmap, gst->gst_asrsv * sizeof (void *));
+ mdb_free(gst, sizeof (mdb_gelf_symtab_t));
+}
+
+size_t
+mdb_gelf_symtab_size(mdb_gelf_symtab_t *gst)
+{
+ return (mdb_nv_size(&gst->gst_nv));
+}
+
+static GElf_Sym *
+gelf32_to_sym(const Elf32_Sym *src, GElf_Sym *dst)
+{
+ if (src != NULL) {
+ dst->st_name = src->st_name;
+ dst->st_info = src->st_info;
+ dst->st_other = src->st_other;
+ dst->st_shndx = src->st_shndx;
+ dst->st_value = src->st_value;
+ dst->st_size = src->st_size;
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+static GElf_Sym *
+gelf64_to_sym(const Elf64_Sym *src, GElf_Sym *dst)
+{
+ if (src != NULL) {
+ bcopy(src, dst, sizeof (GElf_Sym));
+ return (dst);
+ }
+
+ return (NULL);
+}
+
+/*ARGSUSED*/
+static GElf_Sym *
+gelf64_nocopy(const Elf64_Sym *src, GElf_Sym *dst)
+{
+ return ((GElf_Sym *)src);
+}
+
+static const void *
+gelf32_sym_search(const Elf32_Sym **asmap, size_t aslen, uintptr_t addr)
+{
+ ulong_t i, mid, lo = 0, hi = aslen - 1;
+ const Elf32_Sym *symp;
+ Elf32_Addr v;
+ size_t size;
+
+ if (aslen == 0)
+ return (NULL);
+
+ while (hi - lo > 1) {
+ mid = (lo + hi) / 2;
+ if (addr >= asmap[mid]->st_value)
+ lo = mid;
+ else
+ hi = mid;
+ }
+
+ i = addr < asmap[hi]->st_value ? lo : hi;
+ symp = asmap[i];
+ v = symp->st_value;
+
+ /*
+ * If the previous entry has the same value, improve our choice. The
+ * order of equal-valued symbols is determined by gelf32_sym_compare().
+ */
+ while (i-- != 0 && asmap[i]->st_value == v)
+ symp = asmap[i];
+
+ /*
+ * If an absolute symbol distance was specified, use that; otherwise
+ * use the ELF symbol size, or 1 byte if the ELF size is zero.
+ */
+ if (mdb.m_symdist == 0)
+ size = MAX(symp->st_size, 1);
+ else
+ size = mdb.m_symdist;
+
+ if (addr - symp->st_value < size)
+ return (symp);
+
+ return (NULL);
+}
+
+static const void *
+gelf64_sym_search(const Elf64_Sym **asmap, size_t aslen, uintptr_t addr)
+{
+ ulong_t i, mid, lo = 0, hi = aslen - 1;
+ const Elf64_Sym *symp;
+ Elf64_Addr v;
+ size_t size;
+
+ if (aslen == 0)
+ return (NULL);
+
+ while (hi - lo > 1) {
+ mid = (lo + hi) / 2;
+ if (addr >= asmap[mid]->st_value)
+ lo = mid;
+ else
+ hi = mid;
+ }
+
+ i = addr < asmap[hi]->st_value ? lo : hi;
+ symp = asmap[i];
+ v = symp->st_value;
+
+ /*
+ * If the previous entry has the same value, improve our choice. The
+ * order of equal-valued symbols is determined by gelf64_sym_compare().
+ */
+ while (i-- != 0 && asmap[i]->st_value == v)
+ symp = asmap[i];
+
+ /*
+ * If an absolute symbol distance was specified, use that; otherwise
+ * use the ELF symbol size, or 1 byte if the ELF size is zero.
+ */
+ if (mdb.m_symdist == 0)
+ size = MAX(symp->st_size, 1);
+ else
+ size = mdb.m_symdist;
+
+ if (addr - symp->st_value < size)
+ return (symp);
+
+ return (NULL);
+}
+
+const char *
+mdb_gelf_sym_name(mdb_gelf_symtab_t *gst, const GElf_Sym *sym)
+{
+ const mdb_gelf_dsym_t *dsp;
+
+ if (gst->gst_ssect != NULL)
+ return ((const char *)gst->gst_ssect->gs_data + sym->st_name);
+
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ dsp = gelf32_sym_search(gst->gst_asmap,
+ gst->gst_aslen, sym->st_value);
+ else
+ dsp = gelf64_sym_search(gst->gst_asmap,
+ gst->gst_aslen, sym->st_value);
+
+ if (dsp != NULL)
+ return (mdb_nv_get_name(dsp->ds_var));
+
+ return (NULL);
+}
+
+int
+mdb_gelf_sym_closer(const GElf_Sym *s1, const GElf_Sym *s2, uintptr_t addr)
+{
+ uintptr_t v1 = (uintptr_t)s1->st_value;
+ uintptr_t v2 = (uintptr_t)s2->st_value;
+
+ uintptr_t d1 = v1 > addr ? v1 - addr : addr - v1;
+ uintptr_t d2 = v2 > addr ? v2 - addr : addr - v2;
+
+ return (d1 < d2);
+}
+
+int
+mdb_gelf_symtab_lookup_by_addr(mdb_gelf_symtab_t *gst, uintptr_t addr,
+ uint_t flags, char *buf, size_t nbytes, GElf_Sym *sym, uint_t *idp)
+{
+ union {
+ const mdb_gelf_dsym_t *dsp;
+ const Elf32_Sym *s32;
+ const Elf64_Sym *s64;
+ caddr_t sp;
+ } u;
+
+ const char *name;
+
+ if (gst == NULL)
+ return (set_errno(EMDB_NOSYMADDR));
+
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
+ u.s32 = gelf32_sym_search(gst->gst_asmap, gst->gst_aslen, addr);
+ if (gelf32_to_sym(u.s32, sym) == NULL)
+ return (set_errno(EMDB_NOSYMADDR));
+ } else {
+ u.s64 = gelf64_sym_search(gst->gst_asmap, gst->gst_aslen, addr);
+ if (gelf64_to_sym(u.s64, sym) == NULL)
+ return (set_errno(EMDB_NOSYMADDR));
+ }
+
+ if ((flags & GST_EXACT) && (sym->st_value != addr))
+ return (set_errno(EMDB_NOSYMADDR));
+
+ if (gst->gst_ssect != NULL) {
+ name = (const char *)gst->gst_ssect->gs_data + sym->st_name;
+ if (idp != NULL) {
+ *idp = (u.sp - (caddr_t)gst->gst_dsect->gs_data) /
+ gst->gst_dsect->gs_shdr.sh_entsize;
+ }
+ } else {
+ name = mdb_nv_get_name(u.dsp->ds_var);
+ if (idp != NULL)
+ *idp = u.dsp->ds_id;
+ }
+
+ if (nbytes > 0) {
+ (void) strncpy(buf, name, nbytes - 1);
+ buf[nbytes - 1] = '\0';
+ }
+ return (0);
+}
+
+int
+mdb_gelf_symtab_lookup_by_name(mdb_gelf_symtab_t *gst, const char *name,
+ GElf_Sym *sym, uint_t *idp)
+{
+ mdb_var_t *v;
+
+ if (gst != NULL && (v = mdb_nv_lookup(&gst->gst_nv, name)) != NULL) {
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ (void) gelf32_to_sym(mdb_nv_get_cookie(v), sym);
+ else
+ (void) gelf64_to_sym(mdb_nv_get_cookie(v), sym);
+
+ if (idp != NULL) {
+ if (gst->gst_file == NULL && gst->gst_dsect == NULL) {
+ mdb_gelf_dsym_t *dsp = mdb_nv_get_cookie(v);
+ *idp = dsp->ds_id;
+ } else {
+ *idp = ((uintptr_t)mdb_nv_get_cookie(v) -
+ (uintptr_t)gst->gst_dsect->gs_data) /
+ gst->gst_dsect->gs_shdr.sh_entsize;
+ }
+ }
+
+ return (0);
+ }
+
+ return (set_errno(EMDB_NOSYM));
+}
+
+int
+mdb_gelf_symtab_lookup_by_file(mdb_gelf_symtab_t *gst, const char *file,
+ const char *name, GElf_Sym *sym, uint_t *idp)
+{
+ GElf_Sym *(*s2gelf)(const void *, GElf_Sym *);
+ size_t sym_size;
+ caddr_t sp, ep;
+ mdb_var_t *v;
+
+ if (gst == NULL)
+ return (set_errno(EMDB_NOSYM));
+
+ if ((v = mdb_nv_lookup(&gst->gst_nv, file)) == NULL)
+ return (set_errno(EMDB_NOOBJ));
+
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
+ s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf32_to_sym;
+ sym_size = sizeof (Elf32_Sym);
+ } else {
+ s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf64_to_sym;
+ sym_size = sizeof (Elf64_Sym);
+ }
+
+ (void) s2gelf(mdb_nv_get_cookie(v), sym);
+
+ if (GELF_ST_TYPE(sym->st_info) != STT_FILE)
+ return (set_errno(EMDB_NOOBJ));
+
+ ep = (caddr_t)gst->gst_dsect->gs_data + gst->gst_dsect->gs_shdr.sh_size;
+ sp = (caddr_t)mdb_nv_get_cookie(v);
+
+ /*
+ * We assume that symbol lookups scoped by source file name are only
+ * relevant for userland debugging and are a relatively rare request,
+ * and so we use a simple but inefficient linear search with copying.
+ */
+ for (sp += sym_size; sp < ep; sp += sym_size) {
+ (void) s2gelf(sp, sym); /* Convert native symbol to GElf */
+
+ if (GELF_ST_TYPE(sym->st_info) == STT_SECTION ||
+ GELF_ST_TYPE(sym->st_info) == STT_FILE ||
+ GELF_ST_BIND(sym->st_info) != STB_LOCAL)
+ break; /* End of this file's locals */
+
+ if (strcmp(mdb_gelf_sym_name(gst, sym), name) == 0) {
+ if (idp != NULL) {
+ *idp = (sp - (caddr_t)
+ gst->gst_dsect->gs_data) / sym_size;
+ }
+ return (0);
+ }
+ }
+
+ return (set_errno(EMDB_NOSYM));
+}
+
+void
+mdb_gelf_symtab_iter(mdb_gelf_symtab_t *gst, int (*func)(void *,
+ const GElf_Sym *, const char *, uint_t), void *private)
+{
+ GElf_Sym *(*s2gelf)(const void *, GElf_Sym *);
+ GElf_Sym sym, *symp;
+ size_t sym_size;
+
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
+ s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf32_to_sym;
+ sym_size = sizeof (Elf32_Sym);
+ } else {
+ s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf64_nocopy;
+ sym_size = sizeof (Elf64_Sym);
+ }
+
+ /*
+ * If this is a mutable symbol table, we iterate over the hash table
+ * of symbol names; otherwise we go iterate over the data buffer. For
+ * non-mutable tables, this means that ::nm will show all symbols,
+ * including those with duplicate names (not present in gst_nv).
+ */
+ if (gst->gst_file == NULL && gst->gst_dsect == NULL) {
+ mdb_gelf_dsym_t *dsp;
+ mdb_var_t *v;
+
+ mdb_nv_rewind(&gst->gst_nv);
+ while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
+ dsp = mdb_nv_get_cookie(v);
+ symp = s2gelf(dsp, &sym);
+ if (func(private, symp, mdb_nv_get_name(v),
+ dsp->ds_id) == -1)
+ break;
+ }
+
+ } else {
+ const char *sbase = gst->gst_ssect->gs_data;
+ caddr_t sp = gst->gst_dsect->gs_data;
+ caddr_t ep = sp + gst->gst_dsect->gs_shdr.sh_size;
+ uint_t i;
+
+ for (i = 0; sp < ep; sp += sym_size, i++) {
+ symp = s2gelf(sp, &sym);
+ if (func(private, symp, sbase + symp->st_name, i) == -1)
+ break;
+ }
+ }
+}
+
+static void
+gelf_sym_to_32(const GElf_Sym *src, Elf32_Sym *dst)
+{
+ dst->st_name = src->st_name;
+ dst->st_info = src->st_info;
+ dst->st_other = src->st_other;
+ dst->st_shndx = src->st_shndx;
+ dst->st_value = (Elf32_Addr)src->st_value;
+ dst->st_size = (Elf32_Word)src->st_size;
+}
+
+static void
+gelf_sym_to_64(const GElf_Sym *src, Elf64_Sym *dst)
+{
+ bcopy(src, dst, sizeof (Elf64_Sym));
+}
+
+void
+mdb_gelf_symtab_insert(mdb_gelf_symtab_t *gst,
+ const char *name, const GElf_Sym *symp)
+{
+ mdb_gelf_dsym_t *dsp;
+ mdb_var_t *v;
+
+ ASSERT(gst->gst_file == NULL && gst->gst_dsect == NULL);
+ v = mdb_nv_lookup(&gst->gst_nv, name);
+
+ if (v == NULL) {
+ char *s = mdb_alloc(strlen(name) + 1, UM_SLEEP);
+ (void) strcpy(s, name);
+
+ dsp = mdb_alloc(sizeof (mdb_gelf_dsym_t), UM_SLEEP);
+ dsp->ds_id = gst->gst_id++;
+
+ dsp->ds_var = mdb_nv_insert(&gst->gst_nv, s, NULL,
+ (uintptr_t)dsp, GST_NVFLG);
+
+ gst->gst_aslen++;
+ ASSERT(gst->gst_aslen == mdb_nv_size(&gst->gst_nv));
+
+ if (gst->gst_aslen > gst->gst_asrsv) {
+ mdb_free(gst->gst_asmap,
+ sizeof (void *) * gst->gst_asrsv);
+
+ gst->gst_asrsv = gst->gst_asrsv != 0 ?
+ gst->gst_asrsv * GST_GROW : GST_DEFSZ;
+
+ gst->gst_asmap = mdb_alloc(sizeof (void *) *
+ gst->gst_asrsv, UM_SLEEP);
+ }
+ } else
+ dsp = mdb_nv_get_cookie(v);
+
+ mdb_dprintf(MDB_DBG_ELF, "added symbol (\"%s\", %llx)\n",
+ name, (u_longlong_t)symp->st_value);
+
+ bcopy(symp, &dsp->ds_sym, sizeof (GElf_Sym));
+ dsp->ds_sym.st_name = (uintptr_t)mdb_nv_get_name(dsp->ds_var);
+
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
+ gelf_sym_to_32(symp, &dsp->ds_u.ds_s32);
+ gelf32_symtab_sort(gst);
+ } else {
+ gelf_sym_to_64(symp, &dsp->ds_u.ds_s64);
+ gelf64_symtab_sort(gst);
+ }
+}
+
+void
+mdb_gelf_symtab_delete(mdb_gelf_symtab_t *gst,
+ const char *name, GElf_Sym *symp)
+{
+ mdb_var_t *v;
+
+ ASSERT(gst->gst_file == NULL && gst->gst_dsect == NULL);
+ v = mdb_nv_lookup(&gst->gst_nv, name);
+
+ if (v != NULL) {
+ char *name = (char *)mdb_nv_get_name(v);
+ mdb_gelf_dsym_t *dsp = mdb_nv_get_cookie(v);
+
+ if (symp != NULL)
+ bcopy(&dsp->ds_sym, symp, sizeof (GElf_Sym));
+
+ mdb_dprintf(MDB_DBG_ELF, "removed symbol (\"%s\", %llx)\n",
+ name, (u_longlong_t)dsp->ds_sym.st_value);
+
+ mdb_nv_remove(&gst->gst_nv, v);
+ gst->gst_aslen--;
+ ASSERT(gst->gst_aslen == mdb_nv_size(&gst->gst_nv));
+
+ mdb_free(name, strlen(name) + 1);
+ mdb_free(dsp, sizeof (mdb_gelf_dsym_t));
+
+ if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ gelf32_symtab_sort(gst);
+ else
+ gelf64_symtab_sort(gst);
+ }
+}
+
+static const GElf_Phdr *
+gelf_phdr_lookup(mdb_gelf_file_t *gf, uintptr_t addr)
+{
+ const GElf_Phdr *gpp = gf->gf_phdrs;
+ GElf_Half i;
+
+ for (i = 0; i < gf->gf_npload; i++, gpp++) {
+ if (addr >= gpp->p_vaddr && addr < gpp->p_vaddr + gpp->p_memsz)
+ return (gpp);
+ }
+
+ return (NULL);
+}
+
+ssize_t
+mdb_gelf_rw(mdb_gelf_file_t *gf, void *buf, size_t nbytes, uintptr_t addr,
+ ssize_t (*prw)(mdb_io_t *, void *, size_t), mdb_gelf_rw_t rw)
+{
+ ssize_t resid = nbytes;
+
+ while (resid != 0) {
+ const GElf_Phdr *php = gelf_phdr_lookup(gf, addr);
+
+ uintptr_t mapoff;
+ ssize_t memlen, filelen, len = 0;
+ off64_t off;
+
+ if (php == NULL)
+ break; /* No mapping for this address */
+
+ mapoff = addr - php->p_vaddr;
+ memlen = MIN(resid, php->p_memsz - mapoff);
+ filelen = MIN(resid, php->p_filesz - mapoff);
+ off = (off64_t)php->p_offset + mapoff;
+
+ if (filelen > 0 && (IOP_SEEK(gf->gf_io, off, SEEK_SET) != off ||
+ (len = prw(gf->gf_io, buf, filelen)) <= 0))
+ break;
+
+ if (rw == GIO_READ && len == filelen && filelen < memlen) {
+ bzero((char *)buf + len, memlen - filelen);
+ len += memlen - filelen;
+ }
+
+ resid -= len;
+ addr += len;
+ buf = (char *)buf + len;
+ }
+
+ if (resid == nbytes && nbytes != 0)
+ return (set_errno(EMDB_NOMAP));
+
+ return (nbytes - resid);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_gelf.h b/usr/src/cmd/mdb/common/mdb/mdb_gelf.h
new file mode 100644
index 0000000..e6d18a5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_gelf.h
@@ -0,0 +1,145 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_GELF_H
+#define _MDB_GELF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_io.h>
+
+#include <sys/types.h>
+#include <gelf.h>
+#include <sys/machelf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+#define GST_FUZZY 0 /* lookup_by_addr matches closest sym */
+#define GST_EXACT 1 /* lookup_by_addr must be exact */
+
+#define GF_FILE 0 /* Open as ELF file image */
+#define GF_PROGRAM 1 /* Open as ELF program image */
+
+typedef struct mdb_gelf_sect {
+ GElf_Shdr gs_shdr; /* ELF section header */
+ const char *gs_name; /* Section name */
+ void *gs_data; /* Section data */
+} mdb_gelf_sect_t;
+
+typedef struct mdb_gelf_file {
+ GElf_Ehdr gf_ehdr; /* ELF file header */
+ GElf_Phdr *gf_phdrs; /* Array of program headers */
+ size_t gf_npload; /* Number of sorted PT_LOAD phdrs */
+ GElf_Phdr *gf_dynp; /* Pointer to PT_DYNAMIC phdr */
+ GElf_Dyn *gf_dyns; /* Array of dynamic entries */
+ size_t gf_ndyns; /* Number of dynamic entries */
+ mdb_gelf_sect_t *gf_sects; /* Array of section structs */
+ mdb_io_t *gf_io; /* I/o backend for ELF file */
+ int gf_mode; /* Mode flag (see above) */
+} mdb_gelf_file_t;
+
+typedef struct mdb_gelf_symtab {
+ mdb_nv_t gst_nv; /* Name/value hash for name lookups */
+ void *gst_asmap; /* Sorted array of symbol pointers */
+ size_t gst_aslen; /* Number of entries in gst_asmap */
+ size_t gst_asrsv; /* Actual reserved size of gst_asmap */
+ const GElf_Ehdr *gst_ehdr; /* Associated ELF file ehdr */
+ mdb_gelf_file_t *gst_file; /* Associated ELF file */
+ mdb_gelf_sect_t *gst_dsect; /* Associated ELF data section */
+ mdb_gelf_sect_t *gst_ssect; /* Associated ELF string section */
+ uint_t gst_id; /* Next symbol ID to use if mutable */
+ uint_t gst_tabid; /* ID for symbol table */
+} mdb_gelf_symtab_t;
+
+typedef struct mdb_gelf_dsym {
+ union {
+ Elf32_Sym ds_s32; /* 32-bit native symbol data */
+ Elf64_Sym ds_s64; /* 64-bit native symbol data */
+ } ds_u;
+ GElf_Sym ds_sym; /* Generic ELF symbol data */
+ mdb_var_t *ds_var; /* Backpointer to nv element */
+ uint_t ds_id; /* Symbol id number */
+} mdb_gelf_dsym_t;
+
+extern int mdb_gelf_check(mdb_io_t *, Elf32_Ehdr *, GElf_Half);
+extern mdb_gelf_file_t *mdb_gelf_create(mdb_io_t *, GElf_Half, int);
+extern void mdb_gelf_destroy(mdb_gelf_file_t *);
+
+extern void mdb_gelf_ehdr_to_gehdr(Ehdr *, GElf_Ehdr *);
+
+typedef enum { GIO_READ, GIO_WRITE } mdb_gelf_rw_t;
+
+extern ssize_t mdb_gelf_rw(mdb_gelf_file_t *, void *, size_t, uintptr_t,
+ ssize_t (*)(mdb_io_t *, void *, size_t), mdb_gelf_rw_t);
+
+extern mdb_gelf_symtab_t *mdb_gelf_symtab_create_file(mdb_gelf_file_t *,
+ GElf_Word, uint_t);
+extern mdb_gelf_symtab_t *mdb_gelf_symtab_create_file_by_name(mdb_gelf_file_t *,
+ const char *, const char *, uint_t);
+
+extern mdb_gelf_symtab_t *mdb_gelf_symtab_create_raw(const GElf_Ehdr *,
+ const void *, void *, const void *, void *, uint_t);
+
+extern mdb_gelf_symtab_t *mdb_gelf_symtab_create_dynamic(mdb_gelf_file_t *,
+ uint_t);
+extern mdb_gelf_symtab_t *mdb_gelf_symtab_create_mutable(void);
+
+extern void mdb_gelf_symtab_destroy(mdb_gelf_symtab_t *);
+extern size_t mdb_gelf_symtab_size(mdb_gelf_symtab_t *);
+
+extern const char *mdb_gelf_sym_name(mdb_gelf_symtab_t *, const GElf_Sym *);
+extern int mdb_gelf_sym_closer(const GElf_Sym *, const GElf_Sym *, uintptr_t);
+
+extern int mdb_gelf_symtab_lookup_by_addr(mdb_gelf_symtab_t *,
+ uintptr_t, uint_t, char *, size_t, GElf_Sym *, uint_t *);
+
+extern int mdb_gelf_symtab_lookup_by_name(mdb_gelf_symtab_t *,
+ const char *, GElf_Sym *, uint_t *);
+
+extern int mdb_gelf_symtab_lookup_by_file(mdb_gelf_symtab_t *,
+ const char *, const char *, GElf_Sym *, uint_t *);
+
+extern void mdb_gelf_symtab_iter(mdb_gelf_symtab_t *, int (*)(void *,
+ const GElf_Sym *, const char *, uint_t), void *);
+
+extern void mdb_gelf_symtab_insert(mdb_gelf_symtab_t *,
+ const char *, const GElf_Sym *);
+
+extern void mdb_gelf_symtab_delete(mdb_gelf_symtab_t *,
+ const char *, GElf_Sym *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_GELF_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_grammar.y b/usr/src/cmd/mdb/common/mdb/mdb_grammar.y
new file mode 100644
index 0000000..d3ebeeb
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_grammar.y
@@ -0,0 +1,431 @@
+%{
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_types.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_shell.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb.h>
+
+/*
+ * Utility routines to fetch values from the target's virtual address space
+ * and object file, respectively. These are called from the handlers for
+ * the * /.../ and % /.../ code below.
+ */
+
+static void
+vfetch(void *buf, size_t nbytes, uintptr_t addr)
+{
+ if (mdb_tgt_vread(mdb.m_target, buf, nbytes, addr) != nbytes)
+ yyperror("failed to read from address %p", addr);
+}
+
+static void
+ffetch(void *buf, size_t nbytes, uintptr_t addr)
+{
+ if (mdb_tgt_fread(mdb.m_target, buf, nbytes, addr) != nbytes)
+ yyperror("failed to read from address %p", addr);
+}
+
+/*
+ * Because we define YYMAXDEPTH as zero below, we have to provide a YYEXPAND()
+ * function to expand our yys and yyv variables. For simplicity, we currently
+ * define these structures statically; a more complex solution can be defined if
+ * it is ever needed. If we return 'val', yacc assumes resize has failed.
+ */
+static int
+yyexpand(int val)
+{
+ return (val ? val : YYMAXDEPTH);
+}
+#define YYEXPAND yyexpand
+
+/*
+ * This will cause the rest of the yacc code to assume that yys and yyv are
+ * pointers, not static arrays.
+ */
+#undef YYMAXDEPTH
+#define YYMAXDEPTH 0
+%}
+
+%union {
+ char *l_string;
+ char l_char;
+ uintmax_t l_immediate;
+ mdb_var_t *l_var;
+ mdb_idcmd_t *l_dcmd;
+}
+
+%token <l_string> MDB_TOK_SYMBOL
+%token <l_string> MDB_TOK_STRING
+%token <l_char> MDB_TOK_CHAR
+%token <l_immediate> MDB_TOK_IMMEDIATE
+%token <l_dcmd> MDB_TOK_DCMD
+%token <l_var> MDB_TOK_VAR_REF
+%token <l_immediate> MDB_TOK_LEXPR
+%token <l_immediate> MDB_TOK_REXPR
+%token <l_immediate> MDB_TOK_COR1_DEREF
+%token <l_immediate> MDB_TOK_COR2_DEREF
+%token <l_immediate> MDB_TOK_COR4_DEREF
+%token <l_immediate> MDB_TOK_COR8_DEREF
+%token <l_immediate> MDB_TOK_OBJ1_DEREF
+%token <l_immediate> MDB_TOK_OBJ2_DEREF
+%token <l_immediate> MDB_TOK_OBJ4_DEREF
+%token <l_immediate> MDB_TOK_OBJ8_DEREF
+
+%left '|'
+%left '^'
+%left '&'
+%left MDB_TOK_EQUAL MDB_TOK_NOTEQUAL
+%left MDB_TOK_LSHIFT MDB_TOK_RSHIFT
+%left '-' '+'
+%left '*' '%' '#'
+
+%right MDB_COR_VALUE
+%right MDB_OBJ_VALUE
+%right MDB_INT_NEGATE
+%right MDB_BIT_COMPLEMENT
+%right MDB_LOG_NEGATE
+%right MDB_VAR_REFERENCE
+
+%type <l_immediate> expression
+%type <l_dcmd> command
+
+%%
+statement_list: /* Empty */
+ | statement_list statement { return (0); }
+ ;
+
+terminator: '\n'
+ | ';'
+
+statement: pipeline shell_pipe terminator {
+ if (!mdb_call(mdb_nv_get_value(mdb.m_dot), 1, 0))
+ return (0);
+ }
+
+ | expression pipeline shell_pipe terminator {
+ if (!mdb_call($1, 1, DCMD_ADDRSPEC))
+ return (0);
+ }
+
+ | expression ',' expression pipeline shell_pipe terminator {
+ if (!mdb_call($1, $3, DCMD_ADDRSPEC | DCMD_LOOP))
+ return (0);
+ }
+
+ | ',' expression pipeline shell_pipe terminator {
+ if (!mdb_call(mdb_nv_get_value(mdb.m_dot), $2,
+ DCMD_LOOP))
+ return (0);
+ }
+
+ | expression terminator {
+ mdb_frame_t *pfp = mdb_list_prev(mdb.m_frame);
+ /*
+ * The handling of naked expressions is slightly tricky:
+ * in a string context, we want to just set dot to the
+ * expression value. In a pipe context, we also set
+ * dot but need to record the address in the right-
+ * hand command's addrv and update any vcbs that are
+ * active. Otherwise, on the command-line, we have to
+ * support this as an alias for executing the previous
+ * command with the new value of dot. Sigh.
+ */
+ if (mdb_iob_isastr(mdb.m_in)) {
+ mdb_nv_set_value(mdb.m_dot, $1);
+ mdb.m_incr = 0;
+ } else if (pfp != NULL && pfp->f_pcmd != NULL) {
+ mdb_addrvec_unshift(&pfp->f_pcmd->c_addrv,
+ (uintptr_t)$1);
+ mdb_vcb_update(pfp, (uintptr_t)$1);
+ mdb_nv_set_value(mdb.m_dot, $1);
+ } else {
+ mdb_list_move(&mdb.m_lastc,
+ &mdb.m_frame->f_cmds);
+ if (!mdb_call($1, 1, DCMD_ADDRSPEC))
+ return (0);
+ }
+ }
+
+ | expression ',' expression shell_pipe terminator {
+ mdb_list_move(&mdb.m_lastc, &mdb.m_frame->f_cmds);
+ if (!mdb_call($1, $3, DCMD_ADDRSPEC | DCMD_LOOP))
+ return (0);
+ }
+
+ | ',' expression shell_pipe terminator {
+ uintmax_t dot = mdb_dot_incr(",");
+ mdb_list_move(&mdb.m_lastc, &mdb.m_frame->f_cmds);
+ if (!mdb_call(dot, $2, DCMD_LOOP))
+ return (0);
+ }
+
+ | '!' MDB_TOK_STRING terminator {
+ if (mdb_iob_isapipe(mdb.m_in))
+ yyerror("syntax error");
+ mdb_shell_exec($2);
+ }
+
+ | terminator {
+ if ((mdb.m_flags & MDB_FL_REPLAST) &&
+ !mdb_iob_isastr(mdb.m_in)) {
+ uintmax_t dot = mdb_dot_incr("\\n");
+ /*
+ * If a bare terminator is encountered, execute
+ * the previous command if -o repeatlast is set
+ * and stdin is not an mdb_eval() string.
+ */
+ mdb_list_move(&mdb.m_lastc,
+ &mdb.m_frame->f_cmds);
+ if (!mdb_call(dot, 1, 0))
+ return (0);
+ }
+ }
+ ;
+
+pipeline: pipeline '|' command { mdb_cmd_create($3, &mdb.m_frame->f_argvec); }
+ | command { mdb_cmd_create($1, &mdb.m_frame->f_argvec); }
+ ;
+
+command: '?' format_list { $$ = mdb_dcmd_lookup("?"); }
+ | '/' format_list { $$ = mdb_dcmd_lookup("/"); }
+ | '\\' format_list { $$ = mdb_dcmd_lookup("\\"); }
+ | '@' format_list { $$ = mdb_dcmd_lookup("@"); }
+ | '=' format_list { $$ = mdb_dcmd_lookup("="); }
+ | MDB_TOK_DCMD argument_list { $$ = $1; }
+ | '$' { $$ = mdb_dcmd_lookup("$?"); }
+ ;
+
+shell_pipe: /* Empty */
+ | '!' MDB_TOK_STRING { mdb_shell_pipe($2); }
+ ;
+
+format_list: /* Empty */
+ | format_list MDB_TOK_LEXPR expression MDB_TOK_REXPR {
+ mdb_arg_t arg;
+
+ arg.a_type = MDB_TYPE_IMMEDIATE;
+ arg.a_un.a_val = $3;
+
+ mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
+ }
+
+ | format_list MDB_TOK_IMMEDIATE {
+ mdb_arg_t arg;
+
+ arg.a_type = MDB_TYPE_IMMEDIATE;
+ arg.a_un.a_val = $2;
+
+ mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
+ }
+
+ | format_list MDB_TOK_STRING {
+ mdb_arg_t arg;
+
+ arg.a_type = MDB_TYPE_STRING;
+ arg.a_un.a_str = $2;
+
+ mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
+ }
+
+ | format_list MDB_TOK_CHAR {
+ mdb_arg_t arg;
+
+ arg.a_type = MDB_TYPE_CHAR;
+ arg.a_un.a_char = $2;
+
+ mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
+ }
+ ;
+
+argument_list: /* Empty */
+ | argument_list MDB_TOK_LEXPR expression MDB_TOK_REXPR {
+ mdb_arg_t arg;
+
+ arg.a_type = MDB_TYPE_IMMEDIATE;
+ arg.a_un.a_val = $3;
+
+ mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
+ }
+
+ | argument_list MDB_TOK_STRING {
+ mdb_arg_t arg;
+
+ arg.a_type = MDB_TYPE_STRING;
+ arg.a_un.a_str = $2;
+
+ mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
+ }
+ ;
+
+expression: expression '+' expression { $$ = $1 + $3; }
+ | expression '-' expression { $$ = $1 - $3; }
+ | expression '*' expression { $$ = $1 * $3; }
+
+ | expression '%' expression {
+ if ($3 == 0UL)
+ yyerror("attempted to divide by zero");
+
+ $$ = (intmax_t)$1 / (intmax_t)$3;
+ }
+
+ | expression '&' expression { $$ = $1 & $3; }
+ | expression '|' expression { $$ = $1 | $3; }
+ | expression '^' expression { $$ = $1 ^ $3; }
+
+ | expression MDB_TOK_EQUAL expression { $$ = ($1 == $3); }
+ | expression MDB_TOK_NOTEQUAL expression { $$ = ($1 != $3); }
+
+ | expression MDB_TOK_LSHIFT expression { $$ = $1 << $3; }
+ | expression MDB_TOK_RSHIFT expression { $$ = $1 >> $3; }
+
+ | expression '#' expression {
+ if ($3 == 0UL)
+ yyerror("attempted to divide by zero");
+
+ $$ = ((intptr_t)($1 + ($3 - 1)) / (intptr_t)$3) * $3;
+ }
+
+ | '*' expression %prec MDB_COR_VALUE {
+ uintptr_t value;
+
+ vfetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_COR1_DEREF expression %prec MDB_COR_VALUE {
+ uint8_t value;
+
+ vfetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_COR2_DEREF expression %prec MDB_COR_VALUE {
+ uint16_t value;
+
+ vfetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_COR4_DEREF expression %prec MDB_COR_VALUE {
+ uint32_t value;
+
+ vfetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_COR8_DEREF expression %prec MDB_COR_VALUE {
+ uint64_t value;
+
+ vfetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | '%' expression %prec MDB_OBJ_VALUE {
+ uintptr_t value;
+
+ ffetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_OBJ1_DEREF expression %prec MDB_OBJ_VALUE {
+ uint8_t value;
+
+ ffetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_OBJ2_DEREF expression %prec MDB_OBJ_VALUE {
+ uint16_t value;
+
+ ffetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_OBJ4_DEREF expression %prec MDB_OBJ_VALUE {
+ uint32_t value;
+
+ ffetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | MDB_TOK_OBJ8_DEREF expression %prec MDB_OBJ_VALUE {
+ uint64_t value;
+
+ ffetch(&value, sizeof (value), $2);
+ $$ = value;
+ }
+
+ | '-' expression %prec MDB_INT_NEGATE { $$ = -$2; }
+ | '~' expression %prec MDB_BIT_COMPLEMENT { $$ = ~$2; }
+ | '#' expression %prec MDB_LOG_NEGATE { $$ = !$2; }
+ | '(' expression ')' { $$ = $2; }
+
+ | MDB_TOK_VAR_REF %prec MDB_VAR_REFERENCE {
+ $$ = mdb_nv_get_value($1);
+ }
+
+ | MDB_TOK_SYMBOL {
+ if (strcmp($1, ".") == 0) {
+ $$ = mdb_nv_get_value(mdb.m_dot);
+ strfree($1);
+
+ } else {
+ const char *obj = MDB_TGT_OBJ_EVERY, *name = $1;
+ char *s = (char *)$1;
+ GElf_Sym sym;
+
+ if ((s = strrsplit(s, '`')) != NULL) {
+ name = s;
+ obj = $1;
+ }
+
+ if (mdb_tgt_lookup_by_name(mdb.m_target,
+ obj, name, &sym, NULL) == -1) {
+ strfree($1);
+ yyperror("failed to dereference "
+ "symbol");
+ }
+
+ strfree($1);
+ $$ = (uintmax_t)sym.st_value;
+ }
+ }
+
+ | '+' { $$ = mdb_dot_incr("+"); }
+ | '^' { $$ = mdb_dot_decr("^"); }
+ | '&' { $$ = mdb.m_raddr; }
+ | MDB_TOK_IMMEDIATE
+ ;
+
+%%
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_help.c b/usr/src/cmd/mdb/common/mdb/mdb_help.c
new file mode 100644
index 0000000..a29a561
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_help.c
@@ -0,0 +1,348 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_macalias.h>
+#include <mdb/mdb_fmt.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_help.h>
+#include <mdb/mdb.h>
+
+const char _mdb_help[] =
+"\nEach debugger command in %s is structured as follows:\n\n"
+" [ address [, count]] verb [ arguments ... ]\n"
+" ^ ^ ^ ^\n"
+" the start --+ | | +-- arguments are strings which can be\n"
+" address can be an | | quoted using \"\" or '' or\n"
+" expression | | expressions enclosed in $[ ]\n"
+" | |\n"
+" the repeat count --+ +--------- the verb is a name which begins\n"
+" is also an expression with either $, :, or ::. it can also\n"
+" be a format specifier (/ \\ ? or =)\n\n"
+"For information on debugger commands (dcmds) and walkers, type:\n\n"
+" ::help cmdname ... for more detailed information on a command\n"
+" ::dcmds ... for a list of dcmds and their descriptions\n"
+" ::walkers ... for a list of walkers and their descriptions\n"
+" ::dmods -l ... for a list of modules and their dcmds and walkers\n"
+" ::formats ... for a list of format characters for / \\ ? and =\n\n"
+"For information on command-line options, type:\n\n"
+" $ %s -? ... in your shell for a complete list of options\n\n";
+
+/*ARGSUSED*/
+static int
+print_dcmd(mdb_var_t *v, void *ignored)
+{
+ const mdb_idcmd_t *idcp = mdb_nv_get_cookie(v);
+ if (idcp->idc_descr != NULL)
+ mdb_printf(" dcmd %-20s - %s\n",
+ idcp->idc_name, idcp->idc_descr);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+print_walk(mdb_var_t *v, void *ignored)
+{
+ const mdb_iwalker_t *iwp = mdb_nv_get_cookie(v);
+ if (iwp->iwlk_descr != NULL)
+ mdb_printf(" walk %-20s - %s\n",
+ iwp->iwlk_name, iwp->iwlk_descr);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+print_dmod_long(mdb_var_t *v, void *ignored)
+{
+ mdb_module_t *mod = mdb_nv_get_cookie(v);
+
+ mdb_printf("\n%<u>%-70s%</u>\n", mod->mod_name);
+
+ if (mod->mod_tgt_ctor != NULL) {
+ mdb_printf(" ctor 0x%-18lx - target constructor\n",
+ (ulong_t)mod->mod_tgt_ctor);
+ }
+
+ if (mod->mod_dis_ctor != NULL) {
+ mdb_printf(" ctor 0x%-18lx - disassembler constructor\n",
+ (ulong_t)mod->mod_dis_ctor);
+ }
+
+ mdb_nv_sort_iter(&mod->mod_dcmds, print_dcmd, NULL, UM_SLEEP | UM_GC);
+ mdb_nv_sort_iter(&mod->mod_walkers, print_walk, NULL, UM_SLEEP | UM_GC);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+print_dmod_short(mdb_var_t *v, void *ignored)
+{
+ mdb_printf("%s\n", mdb_nv_get_name(v));
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cmd_dmods(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int (*func)(mdb_var_t *, void *);
+ uint_t opt_l = FALSE;
+ mdb_var_t *v;
+ int i;
+
+ if (flags & DCMD_ADDRSPEC)
+ return (DCMD_USAGE);
+
+ i = mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_l, NULL);
+ func = opt_l ? print_dmod_long : print_dmod_short;
+
+ if (i != argc) {
+ if (argc - i != 1 || argv[i].a_type != MDB_TYPE_STRING)
+ return (DCMD_USAGE);
+
+ v = mdb_nv_lookup(&mdb.m_modules, argv[i].a_un.a_str);
+
+ if (v == NULL)
+ mdb_warn("%s module not loaded\n", argv[i].a_un.a_str);
+ else
+ (void) func(v, NULL);
+
+ } else
+ mdb_nv_sort_iter(&mdb.m_modules, func, NULL, UM_SLEEP | UM_GC);
+
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_wdesc(mdb_var_t *v, void *ignored)
+{
+ mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
+
+ if (iwp->iwlk_descr != NULL)
+ mdb_printf("%-24s - %s\n", mdb_nv_get_name(v), iwp->iwlk_descr);
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cmd_walkers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if ((flags && DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ mdb_nv_sort_iter(&mdb.m_walkers, print_wdesc, NULL, UM_SLEEP | UM_GC);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_ddesc(mdb_var_t *v, void *ignored)
+{
+ mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
+
+ if (idcp->idc_descr != NULL)
+ mdb_printf("%-24s - %s\n", mdb_nv_get_name(v), idcp->idc_descr);
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cmd_dcmds(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ if ((flags && DCMD_ADDRSPEC) || argc != 0)
+ return (DCMD_USAGE);
+
+ mdb_nv_sort_iter(&mdb.m_dcmds, print_ddesc, NULL, UM_SLEEP | UM_GC);
+ return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+int
+cmd_help(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char *prefix, *usage;
+ const mdb_idcmd_t *idcp;
+
+ if ((flags & DCMD_ADDRSPEC) || argc > 1)
+ return (DCMD_USAGE);
+
+ if (argc == 0) {
+ mdb_printf(_mdb_help, mdb.m_pname, mdb.m_pname);
+ return (DCMD_OK);
+ }
+
+ if (argv->a_type != MDB_TYPE_STRING) {
+ warn("expected string argument\n");
+ return (DCMD_USAGE);
+ }
+
+ if (strncmp(argv->a_un.a_str, "::", 2) == 0)
+ idcp = mdb_dcmd_lookup(argv->a_un.a_str + 2);
+ else
+ idcp = mdb_dcmd_lookup(argv->a_un.a_str);
+
+ if (idcp == NULL) {
+ mdb_warn("unknown command: %s\n", argv->a_un.a_str);
+ return (DCMD_ERR);
+ }
+
+ prefix = strchr(":$=/\\?>", idcp->idc_name[0]) ? "" : "::";
+ usage = idcp->idc_usage ? idcp->idc_usage : "";
+
+ mdb_printf("\n%<b>NAME%</b>\n %s - %s\n\n",
+ idcp->idc_name, idcp->idc_descr);
+
+ mdb_printf("%<b>SYNOPSIS%</b>\n ");
+ if (usage[0] == '?') {
+ mdb_printf("[ %<u>addr%</u> ] ");
+ usage++;
+ } else if (usage[0] == ':') {
+ mdb_printf("%<u>addr%</u> ");
+ usage++;
+ }
+
+ mdb_printf("%s%s %s\n\n", prefix, idcp->idc_name, usage);
+
+ if (idcp->idc_help != NULL) {
+ mdb_printf("%<b>DESCRIPTION%</b>\n");
+ (void) mdb_inc_indent(2);
+ idcp->idc_help();
+ (void) mdb_dec_indent(2);
+ mdb_printf("\n");
+ }
+
+ /*
+ * For now, modules that are built-in mark their interfaces Evolving
+ * (documented in mdb(1)) and modules that are loaded mark their
+ * interfaces Unstable. In the future we could extend the dmod linkage
+ * to include the module's intended stability and then show it here.
+ */
+ mdb_printf("%<b>ATTRIBUTES%</b>\n\n");
+ mdb_printf(" Target: %s\n", mdb_tgt_name(mdb.m_target));
+ mdb_printf(" Module: %s\n", idcp->idc_modp->mod_name);
+ mdb_printf(" Interface Stability: %s\n\n",
+ (idcp->idc_descr != NULL && idcp->idc_modp->mod_hdl == NULL) ?
+ "Evolving" : "Unstable");
+
+ return (DCMD_OK);
+}
+
+static int
+print_dcmd_def(mdb_var_t *v, void *private)
+{
+ mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
+ int *ip = private;
+
+ mdb_printf(" [%d] %s`%s\n",
+ (*ip)++, idcp->idc_modp->mod_name, idcp->idc_name);
+
+ return (0);
+}
+
+static int
+print_walker_def(mdb_var_t *v, void *private)
+{
+ mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
+ int *ip = private;
+
+ mdb_printf(" [%d] %s`%s\n",
+ (*ip)++, iwp->iwlk_modp->mod_name, iwp->iwlk_name);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cmd_which(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ const char defn_hdr[] = " > definition list:\n";
+ uint_t opt_v = FALSE;
+ int i;
+
+ i = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL);
+
+ for (; i < argc; i++) {
+ const char *s = argv[i].a_un.a_str;
+ int found = FALSE;
+ mdb_iwalker_t *iwp;
+ mdb_idcmd_t *idcp;
+ const char *alias;
+
+ if (argv->a_type != MDB_TYPE_STRING)
+ continue;
+
+ if (s[0] == '$' && s[1] == '<')
+ s += 2;
+
+ if ((idcp = mdb_dcmd_lookup(s)) != NULL) {
+ mdb_var_t *v = idcp->idc_var;
+ int i = 1;
+
+ if (idcp->idc_modp != &mdb.m_rmod) {
+ mdb_printf("%s is a dcmd from module %s\n",
+ s, idcp->idc_modp->mod_name);
+ } else
+ mdb_printf("%s is a built-in dcmd\n", s);
+
+ if (opt_v) {
+ mdb_printf(defn_hdr);
+ mdb_nv_defn_iter(v, print_dcmd_def, &i);
+ }
+ found = TRUE;
+ }
+
+ if ((iwp = mdb_walker_lookup(s)) != NULL) {
+ mdb_var_t *v = iwp->iwlk_var;
+ int i = 1;
+
+ if (iwp->iwlk_modp != &mdb.m_rmod) {
+ mdb_printf("%s is a walker from module %s\n",
+ s, iwp->iwlk_modp->mod_name);
+ } else
+ mdb_printf("%s is a built-in walker\n", s);
+
+ if (opt_v) {
+ mdb_printf(defn_hdr);
+ mdb_nv_defn_iter(v, print_walker_def, &i);
+ }
+ found = TRUE;
+ }
+
+ if ((alias = mdb_macalias_lookup(s)) != NULL) {
+ mdb_printf("%s is a macro alias for '%s'\n", s, alias);
+ found = TRUE;
+ }
+
+ if (!found)
+ mdb_warn("%s not found\n", s);
+ }
+
+ return (DCMD_OK);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_help.h b/usr/src/cmd/mdb/common/mdb/mdb_help.h
new file mode 100644
index 0000000..3c086e4
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_help.h
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MDB_HELP_H
+#define _MDB_HELP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MDB
+
+extern int cmd_dmods(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_dcmds(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_walkers(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_formats(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_help(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int cmd_which(uintptr_t, uint_t, int, const mdb_arg_t *);
+
+#endif /* _MDB */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_HELP_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_io.c b/usr/src/cmd/mdb/common/mdb/mdb_io.c
new file mode 100644
index 0000000..2d0e6af
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_io.c
@@ -0,0 +1,2164 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * MDB uses its own enhanced standard i/o mechanism for all input and output.
+ * This file provides the underpinnings of this mechanism, including the
+ * printf-style formatting code, the output pager, and APIs for raw input
+ * and output. This mechanism is used throughout the debugger for everything
+ * from simple sprintf and printf-style formatting, to input to the lexer
+ * and parser, to raw file i/o for reading ELF files. In general, we divide
+ * our i/o implementation into two parts:
+ *
+ * (1) An i/o buffer (mdb_iob_t) provides buffered read or write capabilities,
+ * as well as access to formatting and the ability to invoke a pager. The
+ * buffer is constructed explicitly for use in either reading or writing; it
+ * may not be used for both simultaneously.
+ *
+ * (2) Each i/o buffer is associated with an underlying i/o backend (mdb_io_t).
+ * The backend provides, through an ops-vector, equivalents for the standard
+ * read, write, lseek, ioctl, and close operations. In addition, the backend
+ * can provide an IOP_NAME entry point for returning a name for the backend,
+ * IOP_LINK and IOP_UNLINK entry points that are called when the backend is
+ * connected or disconnected from an mdb_iob_t, and an IOP_SETATTR entry point
+ * for manipulating terminal attributes.
+ *
+ * The i/o objects themselves are reference counted so that more than one i/o
+ * buffer may make use of the same i/o backend. In addition, each buffer
+ * provides the ability to push or pop backends to interpose on input or output
+ * behavior. We make use of this, for example, to implement interactive
+ * session logging. Normally, the stdout iob has a backend that is either
+ * file descriptor 1, or a terminal i/o backend associated with the tty.
+ * However, we can push a log i/o backend on top that multiplexes stdout to
+ * the original back-end and another backend that writes to a log file. The
+ * use of i/o backends is also used for simplifying tasks such as making
+ * lex and yacc read from strings for mdb_eval(), and making our ELF file
+ * processing code read executable "files" from a crash dump via kvm_uread.
+ *
+ * Additionally, the formatting code provides auto-wrap and indent facilities
+ * that are necessary for compatibility with adb macro formatting. In auto-
+ * wrap mode, the formatting code examines each new chunk of output to determine
+ * if it will fit on the current line. If not, instead of having the chunk
+ * divided between the current line of output and the next, the auto-wrap
+ * code will automatically output a newline, auto-indent the next line,
+ * and then continue. Auto-indent is implemented by simply prepending a number
+ * of blanks equal to iob_margin to the start of each line. The margin is
+ * inserted when the iob is created, and following each flush of the buffer.
+ */
+
+#include <sys/types.h>
+#include <sys/termios.h>
+#include <stdarg.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <mdb/mdb_types.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_stdlib.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_signal.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb.h>
+
+/*
+ * Define list of possible integer sizes for conversion routines:
+ */
+typedef enum {
+ SZ_SHORT, /* format %h? */
+ SZ_INT, /* format %? */
+ SZ_LONG, /* format %l? */
+ SZ_LONGLONG /* format %ll? */
+} intsize_t;
+
+/*
+ * The iob snprintf family of functions makes use of a special "sprintf
+ * buffer" i/o backend in order to provide the appropriate snprintf semantics.
+ * This structure is maintained as the backend-specific private storage,
+ * and its use is described in more detail below (see spbuf_write()).
+ */
+typedef struct {
+ char *spb_buf; /* pointer to underlying buffer */
+ size_t spb_bufsiz; /* length of underlying buffer */
+ size_t spb_total; /* total of all bytes passed via IOP_WRITE */
+} spbuf_t;
+
+/*
+ * Define VA_ARG macro for grabbing the next datum to format for the printf
+ * family of functions. We use VA_ARG so that we can support two kinds of
+ * argument lists: the va_list type supplied by <stdarg.h> used for printf and
+ * vprintf, and an array of mdb_arg_t structures, which we expect will be
+ * either type STRING or IMMEDIATE. The vec_arg function takes care of
+ * handling the mdb_arg_t case.
+ */
+
+typedef enum {
+ VAT_VARARGS, /* va_list is a va_list */
+ VAT_ARGVEC /* va_list is a const mdb_arg_t[] in disguise */
+} vatype_t;
+
+typedef struct {
+ vatype_t val_type;
+ union {
+ va_list _val_valist;
+ const mdb_arg_t *_val_argv;
+ } _val_u;
+} varglist_t;
+
+#define val_valist _val_u._val_valist
+#define val_argv _val_u._val_argv
+
+#define VA_ARG(ap, type) ((ap->val_type == VAT_VARARGS) ? \
+ va_arg(ap->val_valist, type) : (type)vec_arg(&ap->val_argv))
+#define VA_PTRARG(ap) ((ap->val_type == VAT_VARARGS) ? \
+ (void *)va_arg(ap->val_valist, uintptr_t) : \
+ (void *)(uintptr_t)vec_arg(&ap->val_argv))
+
+/*
+ * Define macro for converting char constant to Ctrl-char equivalent:
+ */
+#ifndef CTRL
+#define CTRL(c) ((c) & 0x01f)
+#endif
+
+/*
+ * Define macro for determining if we should automatically wrap to the next
+ * line of output, based on the amount of consumed buffer space and the
+ * specified size of the next thing to be inserted (n).
+ */
+#define IOB_WRAPNOW(iob, n) \
+ (((iob)->iob_flags & MDB_IOB_AUTOWRAP) && ((iob)->iob_nbytes != 0) && \
+ ((n) + (iob)->iob_nbytes > (iob)->iob_cols))
+
+/*
+ * Define prompt string and string to erase prompt string for iob_pager
+ * function, which is invoked if the pager is enabled on an i/o buffer
+ * and we're about to print a line which would be the last on the screen.
+ */
+
+static const char io_prompt[] = ">> More [<space>, <cr>, q, n, c, a] ? ";
+static const char io_perase[] = " ";
+
+static const char io_pbcksp[] =
+/*CSTYLED*/
+"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
+
+static const size_t io_promptlen = sizeof (io_prompt) - 1;
+static const size_t io_peraselen = sizeof (io_perase) - 1;
+static const size_t io_pbcksplen = sizeof (io_pbcksp) - 1;
+
+static ssize_t
+iob_write(mdb_iob_t *iob, mdb_io_t *io, const void *buf, size_t n)
+{
+ ssize_t resid = n;
+ ssize_t len;
+
+ while (resid != 0) {
+ if ((len = IOP_WRITE(io, buf, resid)) <= 0)
+ break;
+
+ buf = (char *)buf + len;
+ resid -= len;
+ }
+
+ /*
+ * Note that if we had a partial write before an error, we still want
+ * to return the fact something was written. The caller will get an
+ * error next time it tries to write anything.
+ */
+ if (resid == n && n != 0) {
+ iob->iob_flags |= MDB_IOB_ERR;
+ return (-1);
+ }
+
+ return (n - resid);
+}
+
+static ssize_t
+iob_read(mdb_iob_t *iob, mdb_io_t *io)
+{
+ ssize_t len;
+
+ ASSERT(iob->iob_nbytes == 0);
+ len = IOP_READ(io, iob->iob_buf, iob->iob_bufsiz);
+ iob->iob_bufp = &iob->iob_buf[0];
+
+ switch (len) {
+ case -1:
+ iob->iob_flags |= MDB_IOB_ERR;
+ break;
+ case 0:
+ iob->iob_flags |= MDB_IOB_EOF;
+ break;
+ default:
+ iob->iob_nbytes = len;
+ }
+
+ return (len);
+}
+
+/*ARGSUSED*/
+static void
+iob_winch(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
+{
+ siglongjmp(*((sigjmp_buf *)data), sig);
+}
+
+static int
+iob_pager(mdb_iob_t *iob)
+{
+ int status = 0;
+ sigjmp_buf env;
+ uchar_t c;
+
+ mdb_signal_f *termio_winch;
+ void *termio_data;
+ size_t old_rows;
+
+ if (iob->iob_pgp == NULL || (iob->iob_flags & MDB_IOB_PGCONT))
+ return (0);
+
+ termio_winch = mdb_signal_gethandler(SIGWINCH, &termio_data);
+ (void) mdb_signal_sethandler(SIGWINCH, iob_winch, &env);
+
+ if (sigsetjmp(env, 1) != 0) {
+ /*
+ * Reset the cursor back to column zero before printing a new
+ * prompt, since its position is unreliable after a SIGWINCH.
+ */
+ (void) iob_write(iob, iob->iob_pgp, "\r", sizeof (char));
+ old_rows = iob->iob_rows;
+
+ /*
+ * If an existing SIGWINCH handler was present, call it. We
+ * expect that this will be termio: the handler will read the
+ * new window size, and then resize this iob appropriately.
+ */
+ if (termio_winch != (mdb_signal_f *)NULL)
+ termio_winch(SIGWINCH, NULL, NULL, termio_data);
+
+ /*
+ * If the window has increased in size, we treat this like a
+ * request to fill out the new remainder of the page.
+ */
+ if (iob->iob_rows > old_rows) {
+ iob->iob_flags &= ~MDB_IOB_PGSINGLE;
+ iob->iob_nlines = old_rows;
+ status = 0;
+ goto winch;
+ }
+ }
+
+ (void) iob_write(iob, iob->iob_pgp, io_prompt, io_promptlen);
+
+ for (;;) {
+ if (IOP_READ(iob->iob_pgp, &c, sizeof (c)) != sizeof (c)) {
+ status = MDB_ERR_PAGER;
+ break;
+ }
+
+ switch (c) {
+ case 'N':
+ case 'n':
+ case '\n':
+ case '\r':
+ iob->iob_flags |= MDB_IOB_PGSINGLE;
+ goto done;
+
+ case CTRL('c'):
+ case CTRL('\\'):
+ case 'Q':
+ case 'q':
+ mdb_iob_discard(iob);
+ status = MDB_ERR_PAGER;
+ goto done;
+
+ case 'A':
+ case 'a':
+ mdb_iob_discard(iob);
+ status = MDB_ERR_ABORT;
+ goto done;
+
+ case 'C':
+ case 'c':
+ iob->iob_flags |= MDB_IOB_PGCONT;
+ /*FALLTHRU*/
+
+ case ' ':
+ iob->iob_flags &= ~MDB_IOB_PGSINGLE;
+ goto done;
+ }
+ }
+
+done:
+ (void) iob_write(iob, iob->iob_pgp, io_pbcksp, io_pbcksplen);
+winch:
+ (void) iob_write(iob, iob->iob_pgp, io_perase, io_peraselen);
+ (void) iob_write(iob, iob->iob_pgp, io_pbcksp, io_pbcksplen);
+ (void) mdb_signal_sethandler(SIGWINCH, termio_winch, termio_data);
+
+ if ((iob->iob_flags & MDB_IOB_ERR) && status == 0)
+ status = MDB_ERR_OUTPUT;
+
+ return (status);
+}
+
+static void
+iob_indent(mdb_iob_t *iob)
+{
+ if (iob->iob_nbytes == 0 && iob->iob_margin != 0 &&
+ (iob->iob_flags & MDB_IOB_INDENT)) {
+ size_t i;
+
+ ASSERT(iob->iob_margin < iob->iob_cols);
+ ASSERT(iob->iob_bufp == iob->iob_buf);
+
+ for (i = 0; i < iob->iob_margin; i++)
+ *iob->iob_bufp++ = ' ';
+
+ iob->iob_nbytes = iob->iob_margin;
+ }
+}
+
+static void
+iob_unindent(mdb_iob_t *iob)
+{
+ if (iob->iob_nbytes != 0 && iob->iob_nbytes == iob->iob_margin) {
+ const char *p = iob->iob_buf;
+
+ while (p < &iob->iob_buf[iob->iob_margin]) {
+ if (*p++ != ' ')
+ return;
+ }
+
+ iob->iob_bufp = &iob->iob_buf[0];
+ iob->iob_nbytes = 0;
+ }
+}
+
+mdb_iob_t *
+mdb_iob_create(mdb_io_t *io, uint_t flags)
+{
+ mdb_iob_t *iob = mdb_alloc(sizeof (mdb_iob_t), UM_SLEEP);
+
+ iob->iob_buf = mdb_alloc(BUFSIZ, UM_SLEEP);
+ iob->iob_bufsiz = BUFSIZ;
+ iob->iob_bufp = &iob->iob_buf[0];
+ iob->iob_nbytes = 0;
+ iob->iob_nlines = 0;
+ iob->iob_lineno = 1;
+ iob->iob_rows = MDB_IOB_DEFROWS;
+ iob->iob_cols = MDB_IOB_DEFCOLS;
+ iob->iob_tabstop = MDB_IOB_DEFTAB;
+ iob->iob_margin = MDB_IOB_DEFMARGIN;
+ iob->iob_flags = flags & ~(MDB_IOB_EOF|MDB_IOB_ERR) | MDB_IOB_AUTOWRAP;
+ iob->iob_iop = mdb_io_hold(io);
+ iob->iob_pgp = NULL;
+ iob->iob_next = NULL;
+
+ IOP_LINK(io, iob);
+ iob_indent(iob);
+ return (iob);
+}
+
+void
+mdb_iob_pipe(mdb_iob_t **iobs, mdb_iobsvc_f *rdsvc, mdb_iobsvc_f *wrsvc)
+{
+ mdb_io_t *pio = mdb_pipeio_create(rdsvc, wrsvc);
+ int i;
+
+ iobs[0] = mdb_iob_create(pio, MDB_IOB_RDONLY);
+ iobs[1] = mdb_iob_create(pio, MDB_IOB_WRONLY);
+
+ for (i = 0; i < 2; i++) {
+ iobs[i]->iob_flags &= ~MDB_IOB_AUTOWRAP;
+ iobs[i]->iob_cols = iobs[i]->iob_bufsiz;
+ }
+}
+
+void
+mdb_iob_destroy(mdb_iob_t *iob)
+{
+ /*
+ * Don't flush a pipe, since it may cause a context swith when the
+ * other side has already been destroyed.
+ */
+ if (!mdb_iob_isapipe(iob))
+ mdb_iob_flush(iob);
+
+ if (iob->iob_pgp != NULL)
+ mdb_io_rele(iob->iob_pgp);
+
+ while (iob->iob_iop != NULL) {
+ IOP_UNLINK(iob->iob_iop, iob);
+ (void) mdb_iob_pop_io(iob);
+ }
+
+ mdb_free(iob->iob_buf, iob->iob_bufsiz);
+ mdb_free(iob, sizeof (mdb_iob_t));
+}
+
+void
+mdb_iob_discard(mdb_iob_t *iob)
+{
+ iob->iob_bufp = &iob->iob_buf[0];
+ iob->iob_nbytes = 0;
+}
+
+void
+mdb_iob_flush(mdb_iob_t *iob)
+{
+ int pgerr = 0;
+
+ if (iob->iob_nbytes == 0)
+ return; /* Nothing to do if buffer is empty */
+
+ if (iob->iob_flags & MDB_IOB_WRONLY) {
+ if (iob->iob_flags & MDB_IOB_PGSINGLE) {
+ iob->iob_flags &= ~MDB_IOB_PGSINGLE;
+ iob->iob_nlines = 0;
+ pgerr = iob_pager(iob);
+
+ } else if (iob->iob_nlines >= iob->iob_rows - 1) {
+ iob->iob_nlines = 0;
+ if (iob->iob_flags & MDB_IOB_PGENABLE)
+ pgerr = iob_pager(iob);
+ }
+
+ if (pgerr == 0) {
+ /*
+ * We only jump out of the dcmd on error if the iob is
+ * m_out. Presumably, if a dcmd has opened a special
+ * file and is writing to it, it will handle errors
+ * properly.
+ */
+ if (iob_write(iob, iob->iob_iop, iob->iob_buf,
+ iob->iob_nbytes) < 0 && iob == mdb.m_out)
+ pgerr = MDB_ERR_OUTPUT;
+ iob->iob_nlines++;
+ }
+ }
+
+ iob->iob_bufp = &iob->iob_buf[0];
+ iob->iob_nbytes = 0;
+ iob_indent(iob);
+
+ if (pgerr)
+ longjmp(mdb.m_frame->f_pcb, pgerr);
+}
+
+void
+mdb_iob_nlflush(mdb_iob_t *iob)
+{
+ iob_unindent(iob);
+
+ if (iob->iob_nbytes != 0)
+ mdb_iob_nl(iob);
+ else
+ iob_indent(iob);
+}
+
+void
+mdb_iob_push_io(mdb_iob_t *iob, mdb_io_t *io)
+{
+ ASSERT(io->io_next == NULL);
+
+ io->io_next = iob->iob_iop;
+ iob->iob_iop = mdb_io_hold(io);
+}
+
+mdb_io_t *
+mdb_iob_pop_io(mdb_iob_t *iob)
+{
+ mdb_io_t *io = iob->iob_iop;
+
+ if (io != NULL) {
+ iob->iob_iop = io->io_next;
+ io->io_next = NULL;
+ mdb_io_rele(io);
+ }
+
+ return (io);
+}
+
+void
+mdb_iob_resize(mdb_iob_t *iob, size_t rows, size_t cols)
+{
+ if (cols > iob->iob_bufsiz)
+ iob->iob_cols = iob->iob_bufsiz;
+ else
+ iob->iob_cols = cols != 0 ? cols : MDB_IOB_DEFCOLS;
+
+ iob->iob_rows = rows != 0 ? rows : MDB_IOB_DEFROWS;
+}
+
+void
+mdb_iob_setpager(mdb_iob_t *iob, mdb_io_t *pgio)
+{
+ struct winsize winsz;
+
+ if (iob->iob_pgp != NULL) {
+ IOP_UNLINK(iob->iob_pgp, iob);
+ mdb_io_rele(iob->iob_pgp);
+ }
+
+ iob->iob_flags |= MDB_IOB_PGENABLE;
+ iob->iob_flags &= ~(MDB_IOB_PGSINGLE | MDB_IOB_PGCONT);
+ iob->iob_pgp = mdb_io_hold(pgio);
+
+ IOP_LINK(iob->iob_pgp, iob);
+
+ if (IOP_CTL(pgio, TIOCGWINSZ, &winsz) == 0)
+ mdb_iob_resize(iob, (size_t)winsz.ws_row, (size_t)winsz.ws_col);
+}
+
+void
+mdb_iob_tabstop(mdb_iob_t *iob, size_t tabstop)
+{
+ iob->iob_tabstop = MIN(tabstop, iob->iob_cols - 1);
+}
+
+void
+mdb_iob_margin(mdb_iob_t *iob, size_t margin)
+{
+ iob_unindent(iob);
+ iob->iob_margin = MIN(margin, iob->iob_cols - 1);
+ iob_indent(iob);
+}
+
+void
+mdb_iob_setbuf(mdb_iob_t *iob, void *buf, size_t bufsiz)
+{
+ ASSERT(buf != NULL && bufsiz != 0);
+
+ mdb_free(iob->iob_buf, iob->iob_bufsiz);
+ iob->iob_buf = buf;
+ iob->iob_bufsiz = bufsiz;
+
+ if (iob->iob_flags & MDB_IOB_WRONLY)
+ iob->iob_cols = MIN(iob->iob_cols, iob->iob_bufsiz);
+}
+
+void
+mdb_iob_clearlines(mdb_iob_t *iob)
+{
+ iob->iob_flags &= ~(MDB_IOB_PGSINGLE | MDB_IOB_PGCONT);
+ iob->iob_nlines = 0;
+}
+
+void
+mdb_iob_setflags(mdb_iob_t *iob, uint_t flags)
+{
+ iob->iob_flags |= flags;
+ if (flags & MDB_IOB_INDENT)
+ iob_indent(iob);
+}
+
+void
+mdb_iob_clrflags(mdb_iob_t *iob, uint_t flags)
+{
+ iob->iob_flags &= ~flags;
+ if (flags & MDB_IOB_INDENT)
+ iob_unindent(iob);
+}
+
+uint_t
+mdb_iob_getflags(mdb_iob_t *iob)
+{
+ return (iob->iob_flags);
+}
+
+static uintmax_t
+vec_arg(const mdb_arg_t **app)
+{
+ uintmax_t value;
+
+ if ((*app)->a_type == MDB_TYPE_STRING)
+ value = (uintmax_t)(uintptr_t)(*app)->a_un.a_str;
+ else
+ value = (*app)->a_un.a_val;
+
+ (*app)++;
+ return (value);
+}
+
+static const char *
+iob_size2str(intsize_t size)
+{
+ switch (size) {
+ case SZ_SHORT:
+ return ("short");
+ case SZ_INT:
+ return ("int");
+ case SZ_LONG:
+ return ("long");
+ case SZ_LONGLONG:
+ return ("long long");
+ }
+ return ("");
+}
+
+/*
+ * In order to simplify maintenance of the ::formats display, we provide an
+ * unparser for mdb_printf format strings that converts a simple format
+ * string with one specifier into a descriptive representation, e.g.
+ * mdb_iob_format2str("%llx") returns "hexadecimal long long".
+ */
+const char *
+mdb_iob_format2str(const char *format)
+{
+ intsize_t size = SZ_INT;
+ const char *p;
+
+ static char buf[64];
+
+ buf[0] = '\0';
+
+ if ((p = strchr(format, '%')) == NULL)
+ goto done;
+
+fmt_switch:
+ switch (*++p) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ while (*p >= '0' && *p <= '9')
+ p++;
+ p--;
+ goto fmt_switch;
+
+ case 'a':
+ case 'A':
+ return ("symbol");
+
+ case 'b':
+ (void) strcpy(buf, "unsigned ");
+ (void) strcat(buf, iob_size2str(size));
+ (void) strcat(buf, " bitfield");
+ break;
+
+ case 'c':
+ return ("character");
+
+ case 'd':
+ case 'i':
+ (void) strcpy(buf, "decimal signed ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ return ("double");
+
+ case 'h':
+ size = SZ_SHORT;
+ goto fmt_switch;
+
+ case 'I':
+ return ("IPv4 address");
+
+ case 'l':
+ if (size >= SZ_LONG)
+ size = SZ_LONGLONG;
+ else
+ size = SZ_LONG;
+ goto fmt_switch;
+
+ case 'm':
+ return ("margin");
+
+ case 'N':
+ return ("IPv6 address");
+
+ case 'o':
+ (void) strcpy(buf, "octal unsigned ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 'p':
+ return ("pointer");
+
+ case 'q':
+ (void) strcpy(buf, "octal signed ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 'r':
+ (void) strcpy(buf, "default radix unsigned ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 'R':
+ (void) strcpy(buf, "default radix signed ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 's':
+ return ("string");
+
+ case 't':
+ case 'T':
+ return ("tab");
+
+ case 'u':
+ (void) strcpy(buf, "decimal unsigned ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 'x':
+ case 'X':
+ (void) strcat(buf, "hexadecimal ");
+ (void) strcat(buf, iob_size2str(size));
+ break;
+
+ case 'Y':
+ return ("time_t");
+
+ case '<':
+ return ("terminal attribute");
+
+ case '?':
+ case '#':
+ case '+':
+ case '-':
+ goto fmt_switch;
+ }
+
+done:
+ if (buf[0] == '\0')
+ (void) strcpy(buf, "text");
+
+ return ((const char *)buf);
+}
+
+static const char *
+iob_int2str(varglist_t *ap, intsize_t size, int base, uint_t flags, int *zero,
+ u_longlong_t *value)
+{
+ uintmax_t i;
+
+ switch (size) {
+ case SZ_LONGLONG:
+ if (flags & NTOS_UNSIGNED)
+ i = (u_longlong_t)VA_ARG(ap, u_longlong_t);
+ else
+ i = (longlong_t)VA_ARG(ap, longlong_t);
+ break;
+
+ case SZ_LONG:
+ if (flags & NTOS_UNSIGNED)
+ i = (ulong_t)VA_ARG(ap, ulong_t);
+ else
+ i = (long)VA_ARG(ap, long);
+ break;
+
+ case SZ_SHORT:
+ if (flags & NTOS_UNSIGNED)
+ i = (ushort_t)VA_ARG(ap, uint_t);
+ else
+ i = (short)VA_ARG(ap, int);
+ break;
+
+ default:
+ if (flags & NTOS_UNSIGNED)
+ i = (uint_t)VA_ARG(ap, uint_t);
+ else
+ i = (int)VA_ARG(ap, int);
+ }
+
+ *zero = i == 0; /* Return flag indicating if result was zero */
+ *value = i; /* Return value retrieved from va_list */
+
+ return (numtostr(i, base, flags));
+}
+
+static const char *
+iob_time2str(time_t *tmp)
+{
+ /*
+ * ctime(3c) returns a string of the form
+ * "Fri Sep 13 00:00:00 1986\n\0". We turn this into the canonical
+ * adb /y format "1986 Sep 13 00:00:00" below.
+ */
+ const char *src = ctime(tmp);
+ static char buf[32];
+ char *dst = buf;
+ int i;
+
+ if (src == NULL)
+ return (mdb_strerror(errno));
+
+ for (i = 20; i < 24; i++)
+ *dst++ = src[i]; /* Copy the 4-digit year */
+
+ for (i = 3; i < 19; i++)
+ *dst++ = src[i]; /* Copy month, day, and h:m:s */
+
+ *dst = '\0';
+ return (buf);
+}
+
+static const char *
+iob_addr2str(uintptr_t addr)
+{
+ static char buf[MDB_TGT_SYM_NAMLEN];
+ char *name = buf;
+ longlong_t offset;
+ GElf_Sym sym;
+
+ if (mdb_tgt_lookup_by_addr(mdb.m_target, addr,
+ MDB_TGT_SYM_FUZZY, buf, sizeof (buf), &sym, NULL) == -1)
+ return (NULL);
+
+ if (mdb.m_demangler != NULL && (mdb.m_flags & MDB_FL_DEMANGLE))
+ name = (char *)mdb_dem_convert(mdb.m_demangler, buf);
+
+ /*
+ * Here we provide a little cooperation between the %a formatting code
+ * and the proc target: if the initial address passed to %a is in fact
+ * a PLT address, the proc target's lookup_by_addr code will convert
+ * this to the PLT destination (a different address). We do not want
+ * to append a "+/-offset" suffix based on comparison with the query
+ * symbol in this case because the proc target has really done a hidden
+ * query for us with a different address. We detect this case by
+ * comparing the initial characters of buf to the special PLT= string.
+ */
+ if (sym.st_value != addr && strncmp(name, "PLT=", 4) != 0) {
+ if (sym.st_value > addr)
+ offset = -(longlong_t)(sym.st_value - addr);
+ else
+ offset = (longlong_t)(addr - sym.st_value);
+
+ (void) strcat(name, numtostr(offset, mdb.m_radix,
+ NTOS_SIGNPOS | NTOS_SHOWBASE));
+ }
+
+ return (name);
+}
+
+static int
+iob_setattr(mdb_iob_t *iob, const char *s, size_t nbytes)
+{
+ uint_t attr;
+ int req;
+
+ if (iob->iob_pgp == NULL)
+ return (set_errno(ENOTTY));
+
+ if (nbytes != 0 && *s == '/') {
+ req = ATT_OFF;
+ nbytes--;
+ s++;
+ } else
+ req = ATT_ON;
+
+ if (nbytes != 1)
+ return (set_errno(EINVAL));
+
+ switch (*s) {
+ case 's':
+ attr = ATT_STANDOUT;
+ break;
+ case 'u':
+ attr = ATT_UNDERLINE;
+ break;
+ case 'r':
+ attr = ATT_REVERSE;
+ break;
+ case 'b':
+ attr = ATT_BOLD;
+ break;
+ case 'd':
+ attr = ATT_DIM;
+ break;
+ case 'a':
+ attr = ATT_ALTCHARSET;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * We need to flush the current buffer contents before calling
+ * IOP_SETATTR because IOP_SETATTR may need to synchronously output
+ * terminal escape sequences directly to the underlying device.
+ */
+ (void) iob_write(iob, iob->iob_iop, iob->iob_buf, iob->iob_nbytes);
+ iob->iob_bufp = &iob->iob_buf[0];
+ iob->iob_nbytes = 0;
+
+ return (IOP_SETATTR(iob->iob_pgp, req, attr));
+}
+
+static void
+iob_bits2str(mdb_iob_t *iob, u_longlong_t value, const mdb_bitmask_t *bmp,
+ mdb_bool_t altflag)
+{
+ mdb_bool_t delim = FALSE;
+ const char *str;
+ size_t width;
+
+ if (bmp == NULL)
+ goto out;
+
+ for (; bmp->bm_name != NULL; bmp++) {
+ if ((value & bmp->bm_mask) == bmp->bm_bits) {
+ width = strlen(bmp->bm_name) + delim;
+
+ if (IOB_WRAPNOW(iob, width))
+ mdb_iob_nl(iob);
+
+ if (delim)
+ mdb_iob_putc(iob, ',');
+ else
+ delim = TRUE;
+
+ mdb_iob_puts(iob, bmp->bm_name);
+ value &= ~bmp->bm_bits;
+ }
+ }
+
+out:
+ if (altflag == TRUE && (delim == FALSE || value != 0)) {
+ str = numtostr(value, 16, NTOS_UNSIGNED | NTOS_SHOWBASE);
+ width = strlen(str) + delim;
+
+ if (IOB_WRAPNOW(iob, width))
+ mdb_iob_nl(iob);
+ if (delim)
+ mdb_iob_putc(iob, ',');
+ mdb_iob_puts(iob, str);
+ }
+}
+
+static const char *
+iob_inaddr2str(uint32_t addr)
+{
+ static char buf[INET_ADDRSTRLEN];
+
+ (void) mdb_inet_ntop(AF_INET, &addr, buf, sizeof (buf));
+
+ return (buf);
+}
+
+static const char *
+iob_ipv6addr2str(void *addr)
+{
+ static char buf[INET6_ADDRSTRLEN];
+
+ (void) mdb_inet_ntop(AF_INET6, addr, buf, sizeof (buf));
+
+ return (buf);
+}
+
+static const char *
+iob_getvar(const char *s, size_t len)
+{
+ mdb_var_t *val;
+ char *var;
+
+ if (len == 0) {
+ (void) set_errno(EINVAL);
+ return (NULL);
+ }
+
+ var = strndup(s, len);
+ val = mdb_nv_lookup(&mdb.m_nv, var);
+ strfree(var);
+
+ if (val == NULL) {
+ (void) set_errno(EINVAL);
+ return (NULL);
+ }
+
+ return (numtostr(mdb_nv_get_value(val), 10, 0));
+}
+
+/*
+ * The iob_doprnt function forms the main engine of the debugger's output
+ * formatting capabilities. Note that this is NOT exactly compatible with
+ * the printf(3S) family, nor is it intended to be so. We support some
+ * extensions and format characters not supported by printf(3S), and we
+ * explicitly do NOT provide support for %C, %S, %ws (wide-character strings),
+ * do NOT provide for the complete functionality of %f, %e, %E, %g, %G
+ * (alternate double formats), and do NOT support %.x (precision specification).
+ * Note that iob_doprnt consumes varargs off the original va_list.
+ */
+static void
+iob_doprnt(mdb_iob_t *iob, const char *format, varglist_t *ap)
+{
+ char c[2] = { 0, 0 }; /* Buffer for single character output */
+ const char *p; /* Current position in format string */
+ size_t len; /* Length of format string to copy verbatim */
+ size_t altlen; /* Length of alternate print format prefix */
+ const char *altstr; /* Alternate print format prefix */
+ const char *symstr; /* Symbol + offset string */
+
+ u_longlong_t val; /* Current integer value */
+ intsize_t size; /* Current integer value size */
+ uint_t flags; /* Current flags to pass to iob_int2str */
+ size_t width; /* Current field width */
+ int zero; /* If != 0, then integer value == 0 */
+
+ mdb_bool_t f_alt; /* Use alternate print format (%#) */
+ mdb_bool_t f_altsuff; /* Alternate print format is a suffix */
+ mdb_bool_t f_zfill; /* Zero-fill field (%0) */
+ mdb_bool_t f_left; /* Left-adjust field (%-) */
+ mdb_bool_t f_digits; /* Explicit digits used to set field width */
+
+ union {
+ const char *str;
+ uint32_t ui32;
+ void *ptr;
+ time_t tm;
+ char c;
+ double d;
+ long double ld;
+ } u;
+
+ ASSERT(iob->iob_flags & MDB_IOB_WRONLY);
+
+ while ((p = strchr(format, '%')) != NULL) {
+ /*
+ * Output the format string verbatim up to the next '%' char
+ */
+ if (p != format) {
+ len = p - format;
+ if (IOB_WRAPNOW(iob, len) && *format != '\n')
+ mdb_iob_nl(iob);
+ mdb_iob_nputs(iob, format, len);
+ }
+
+ /*
+ * Now we need to parse the sequence of format characters
+ * following the % marker and do the appropriate thing.
+ */
+ size = SZ_INT; /* Use normal-sized int by default */
+ flags = 0; /* Clear numtostr() format flags */
+ width = 0; /* No field width limit by default */
+ altlen = 0; /* No alternate format string yet */
+ altstr = NULL; /* No alternate format string yet */
+
+ f_alt = FALSE; /* Alternate format off by default */
+ f_altsuff = FALSE; /* Alternate format is a prefix */
+ f_zfill = FALSE; /* Zero-fill off by default */
+ f_left = FALSE; /* Left-adjust off by default */
+ f_digits = FALSE; /* No digits for width specified yet */
+
+ fmt_switch:
+ switch (*++p) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (f_digits == FALSE && *p == '0') {
+ f_zfill = TRUE;
+ goto fmt_switch;
+ }
+
+ if (f_digits == FALSE)
+ width = 0; /* clear any other width specifier */
+
+ for (u.c = *p; u.c >= '0' && u.c <= '9'; u.c = *++p)
+ width = width * 10 + u.c - '0';
+
+ p--;
+ f_digits = TRUE;
+ goto fmt_switch;
+
+ case 'a':
+ if (size < SZ_LONG)
+ size = SZ_LONG; /* Bump to size of uintptr_t */
+
+ u.str = iob_int2str(ap, size, 16,
+ NTOS_UNSIGNED | NTOS_SHOWBASE, &zero, &val);
+
+ if ((symstr = iob_addr2str(val)) != NULL)
+ u.str = symstr;
+
+ if (f_alt == TRUE) {
+ f_altsuff = TRUE;
+ altstr = ":";
+ altlen = 1;
+ }
+ break;
+
+ case 'A':
+ if (size < SZ_LONG)
+ size = SZ_LONG; /* Bump to size of uintptr_t */
+
+ (void) iob_int2str(ap, size, 16,
+ NTOS_UNSIGNED, &zero, &val);
+
+ u.str = iob_addr2str(val);
+
+ if (f_alt == TRUE && u.str == NULL)
+ u.str = "?";
+ break;
+
+ case 'b':
+ u.str = iob_int2str(ap, size, 16,
+ NTOS_UNSIGNED | NTOS_SHOWBASE, &zero, &val);
+
+ iob_bits2str(iob, val, VA_PTRARG(ap), f_alt);
+
+ format = ++p;
+ continue;
+
+ case 'c':
+ c[0] = (char)VA_ARG(ap, int);
+ u.str = c;
+ break;
+
+ case 'd':
+ case 'i':
+ if (f_alt)
+ flags |= NTOS_SHOWBASE;
+ u.str = iob_int2str(ap, size, 10, flags, &zero, &val);
+ break;
+
+ /* No floating point in kmdb */
+#ifndef _KMDB
+ case 'e':
+ case 'E':
+ u.d = VA_ARG(ap, double);
+ u.str = doubletos(u.d, 7, *p);
+ break;
+
+ case 'g':
+ case 'G':
+ if (size >= SZ_LONG) {
+ u.ld = VA_ARG(ap, long double);
+ u.str = longdoubletos(&u.ld, 16,
+ (*p == 'g') ? 'e' : 'E');
+ } else {
+ u.d = VA_ARG(ap, double);
+ u.str = doubletos(u.d, 16,
+ (*p == 'g') ? 'e' : 'E');
+ }
+ break;
+#endif
+
+ case 'h':
+ size = SZ_SHORT;
+ goto fmt_switch;
+
+ case 'I':
+ u.ui32 = VA_ARG(ap, uint32_t);
+ u.str = iob_inaddr2str(u.ui32);
+ break;
+
+ case 'l':
+ if (size >= SZ_LONG)
+ size = SZ_LONGLONG;
+ else
+ size = SZ_LONG;
+ goto fmt_switch;
+
+ case 'm':
+ if (iob->iob_nbytes == 0) {
+ mdb_iob_ws(iob, (width != 0) ? width :
+ iob->iob_margin);
+ }
+ format = ++p;
+ continue;
+
+ case 'N':
+ u.ptr = VA_PTRARG(ap);
+ u.str = iob_ipv6addr2str(u.ptr);
+ break;
+
+ case 'o':
+ u.str = iob_int2str(ap, size, 8, NTOS_UNSIGNED,
+ &zero, &val);
+
+ if (f_alt && !zero) {
+ altstr = "0";
+ altlen = 1;
+ }
+ break;
+
+ case 'p':
+ u.ptr = VA_PTRARG(ap);
+ u.str = numtostr((uintptr_t)u.ptr, 16, NTOS_UNSIGNED);
+ break;
+
+ case 'q':
+ u.str = iob_int2str(ap, size, 8, flags, &zero, &val);
+
+ if (f_alt && !zero) {
+ altstr = "0";
+ altlen = 1;
+ }
+ break;
+
+ case 'r':
+ if (f_alt)
+ flags |= NTOS_SHOWBASE;
+ u.str = iob_int2str(ap, size, mdb.m_radix,
+ NTOS_UNSIGNED | flags, &zero, &val);
+ break;
+
+ case 'R':
+ if (f_alt)
+ flags |= NTOS_SHOWBASE;
+ u.str = iob_int2str(ap, size, mdb.m_radix, flags,
+ &zero, &val);
+ break;
+
+ case 's':
+ u.str = VA_PTRARG(ap);
+ if (u.str == NULL)
+ u.str = "<NULL>"; /* Be forgiving of NULL */
+ break;
+
+ case 't':
+ if (width != 0) {
+ while (width-- > 0)
+ mdb_iob_tab(iob);
+ } else
+ mdb_iob_tab(iob);
+
+ format = ++p;
+ continue;
+
+ case 'T':
+ if (width != 0 && (iob->iob_nbytes % width) != 0) {
+ size_t ots = iob->iob_tabstop;
+ iob->iob_tabstop = width;
+ mdb_iob_tab(iob);
+ iob->iob_tabstop = ots;
+ }
+ format = ++p;
+ continue;
+
+ case 'u':
+ if (f_alt)
+ flags |= NTOS_SHOWBASE;
+ u.str = iob_int2str(ap, size, 10,
+ flags | NTOS_UNSIGNED, &zero, &val);
+ break;
+
+ case 'x':
+ u.str = iob_int2str(ap, size, 16, NTOS_UNSIGNED,
+ &zero, &val);
+
+ if (f_alt && !zero) {
+ altstr = "0x";
+ altlen = 2;
+ }
+ break;
+
+ case 'X':
+ u.str = iob_int2str(ap, size, 16,
+ NTOS_UNSIGNED | NTOS_UPCASE, &zero, &val);
+
+ if (f_alt && !zero) {
+ altstr = "0X";
+ altlen = 2;
+ }
+ break;
+
+ case 'Y':
+ u.tm = VA_ARG(ap, time_t);
+ u.str = iob_time2str(&u.tm);
+ break;
+
+ case '<':
+ /*
+ * Used to turn attributes on (<b>), to turn them
+ * off (</b>), or to print variables (<_var>).
+ */
+ for (u.str = ++p; *p != '\0' && *p != '>'; p++)
+ continue;
+
+ if (*p == '>') {
+ size_t paramlen = p - u.str;
+
+ if (paramlen > 0) {
+ if (*u.str == '_') {
+ u.str = iob_getvar(u.str + 1,
+ paramlen - 1);
+ break;
+ } else {
+ (void) iob_setattr(iob, u.str,
+ paramlen);
+ }
+ }
+
+ p++;
+ }
+
+ format = p;
+ continue;
+
+ case '*':
+ width = (size_t)(uint_t)VA_ARG(ap, int);
+ goto fmt_switch;
+
+ case '%':
+ u.str = "%";
+ break;
+
+ case '?':
+ width = sizeof (uintptr_t) * 2;
+ goto fmt_switch;
+
+ case '#':
+ f_alt = TRUE;
+ goto fmt_switch;
+
+ case '+':
+ flags |= NTOS_SIGNPOS;
+ goto fmt_switch;
+
+ case '-':
+ f_left = TRUE;
+ goto fmt_switch;
+
+ default:
+ c[0] = p[0];
+ u.str = c;
+ }
+
+ len = u.str != NULL ? strlen(u.str) : 0;
+
+ if (len + altlen > width)
+ width = len + altlen;
+
+ /*
+ * If the string and the option altstr won't fit on this line
+ * and auto-wrap is set (default), skip to the next line.
+ */
+ if (IOB_WRAPNOW(iob, width))
+ mdb_iob_nl(iob);
+
+ /*
+ * Optionally add whitespace or zeroes prefixing the value if
+ * we haven't filled the minimum width and we're right-aligned.
+ */
+ if (len < (width - altlen) && f_left == FALSE) {
+ mdb_iob_fill(iob, f_zfill ? '0' : ' ',
+ width - altlen - len);
+ }
+
+ /*
+ * Print the alternate string if it's a prefix, and then
+ * print the value string itself.
+ */
+ if (altstr != NULL && f_altsuff == FALSE)
+ mdb_iob_nputs(iob, altstr, altlen);
+ if (len != 0)
+ mdb_iob_nputs(iob, u.str, len);
+
+ /*
+ * If we have an alternate string and it's a suffix, print it.
+ */
+ if (altstr != NULL && f_altsuff == TRUE)
+ mdb_iob_nputs(iob, altstr, altlen);
+
+ /*
+ * Finally, if we haven't filled the field width and we're
+ * left-aligned, pad out the rest with whitespace.
+ */
+ if ((len + altlen) < width && f_left == TRUE)
+ mdb_iob_ws(iob, width - altlen - len);
+
+ format = (*p != '\0') ? ++p : p;
+ }
+
+ /*
+ * If there's anything left in the format string, output it now
+ */
+ if (*format != '\0') {
+ len = strlen(format);
+ if (IOB_WRAPNOW(iob, len) && *format != '\n')
+ mdb_iob_nl(iob);
+ mdb_iob_nputs(iob, format, len);
+ }
+}
+
+void
+mdb_iob_vprintf(mdb_iob_t *iob, const char *format, va_list alist)
+{
+ varglist_t ap = { VAT_VARARGS };
+ va_copy(ap.val_valist, alist);
+ iob_doprnt(iob, format, &ap);
+}
+
+void
+mdb_iob_aprintf(mdb_iob_t *iob, const char *format, const mdb_arg_t *argv)
+{
+ varglist_t ap = { VAT_ARGVEC };
+ ap.val_argv = argv;
+ iob_doprnt(iob, format, &ap);
+}
+
+void
+mdb_iob_printf(mdb_iob_t *iob, const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ mdb_iob_vprintf(iob, format, alist);
+ va_end(alist);
+}
+
+/*
+ * In order to handle the sprintf family of functions, we define a special
+ * i/o backend known as a "sprintf buf" (or spbuf for short). This back end
+ * provides an IOP_WRITE entry point that concatenates each buffer sent from
+ * mdb_iob_flush() onto the caller's buffer until the caller's buffer is
+ * exhausted. We also keep an absolute count of how many bytes were sent to
+ * this function during the lifetime of the snprintf call. This allows us
+ * to provide the ability to (1) return the total size required for the given
+ * format string and argument list, and (2) support a call to snprintf with a
+ * NULL buffer argument with no special case code elsewhere.
+ */
+static ssize_t
+spbuf_write(mdb_io_t *io, const void *buf, size_t buflen)
+{
+ spbuf_t *spb = io->io_data;
+
+ if (spb->spb_bufsiz != 0) {
+ size_t n = MIN(spb->spb_bufsiz, buflen);
+ bcopy(buf, spb->spb_buf, n);
+ spb->spb_buf += n;
+ spb->spb_bufsiz -= n;
+ }
+
+ spb->spb_total += buflen;
+ return (buflen);
+}
+
+static const mdb_io_ops_t spbuf_ops = {
+ no_io_read,
+ spbuf_write,
+ no_io_seek,
+ no_io_ctl,
+ no_io_close,
+ no_io_name,
+ no_io_link,
+ no_io_unlink,
+ no_io_setattr,
+ no_io_suspend,
+ no_io_resume
+};
+
+/*
+ * The iob_spb_create function initializes an iob suitable for snprintf calls,
+ * a spbuf i/o backend, and the spbuf private data, and then glues these
+ * objects together. The caller (either vsnprintf or asnprintf below) is
+ * expected to have allocated the various structures on their stack.
+ */
+static void
+iob_spb_create(mdb_iob_t *iob, char *iob_buf, size_t iob_len,
+ mdb_io_t *io, spbuf_t *spb, char *spb_buf, size_t spb_len)
+{
+ spb->spb_buf = spb_buf;
+ spb->spb_bufsiz = spb_len;
+ spb->spb_total = 0;
+
+ io->io_ops = &spbuf_ops;
+ io->io_data = spb;
+ io->io_next = NULL;
+ io->io_refcnt = 1;
+
+ iob->iob_buf = iob_buf;
+ iob->iob_bufsiz = iob_len;
+ iob->iob_bufp = iob_buf;
+ iob->iob_nbytes = 0;
+ iob->iob_nlines = 0;
+ iob->iob_lineno = 1;
+ iob->iob_rows = MDB_IOB_DEFROWS;
+ iob->iob_cols = iob_len;
+ iob->iob_tabstop = MDB_IOB_DEFTAB;
+ iob->iob_margin = MDB_IOB_DEFMARGIN;
+ iob->iob_flags = MDB_IOB_WRONLY;
+ iob->iob_iop = io;
+ iob->iob_pgp = NULL;
+ iob->iob_next = NULL;
+}
+
+/*ARGSUSED*/
+ssize_t
+null_io_write(mdb_io_t *io, const void *buf, size_t nbytes)
+{
+ return (nbytes);
+}
+
+static const mdb_io_ops_t null_ops = {
+ no_io_read,
+ null_io_write,
+ no_io_seek,
+ no_io_ctl,
+ no_io_close,
+ no_io_name,
+ no_io_link,
+ no_io_unlink,
+ no_io_setattr,
+ no_io_suspend,
+ no_io_resume
+};
+
+mdb_io_t *
+mdb_nullio_create(void)
+{
+ static mdb_io_t null_io = {
+ &null_ops,
+ NULL,
+ NULL,
+ 1
+ };
+
+ return (&null_io);
+}
+
+size_t
+mdb_iob_vsnprintf(char *buf, size_t nbytes, const char *format, va_list alist)
+{
+ varglist_t ap = { VAT_VARARGS };
+ char iob_buf[64];
+ mdb_iob_t iob;
+ mdb_io_t io;
+ spbuf_t spb;
+
+ ASSERT(buf != NULL || nbytes == 0);
+ iob_spb_create(&iob, iob_buf, sizeof (iob_buf), &io, &spb, buf, nbytes);
+ va_copy(ap.val_valist, alist);
+ iob_doprnt(&iob, format, &ap);
+ mdb_iob_flush(&iob);
+
+ if (spb.spb_bufsiz != 0)
+ *spb.spb_buf = '\0';
+ else if (buf != NULL && nbytes > 0)
+ *--spb.spb_buf = '\0';
+
+ return (spb.spb_total);
+}
+
+size_t
+mdb_iob_asnprintf(char *buf, size_t nbytes, const char *format,
+ const mdb_arg_t *argv)
+{
+ varglist_t ap = { VAT_ARGVEC };
+ char iob_buf[64];
+ mdb_iob_t iob;
+ mdb_io_t io;
+ spbuf_t spb;
+
+ ASSERT(buf != NULL || nbytes == 0);
+ iob_spb_create(&am