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 *)&dot;
+	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 *)&mid;
+
+	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 *)&mid;
+	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