root/usr/src/cmd/mdb/common/mdb/mdb_main.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
 */

/*
 * Copyright 2019 Joyent, Inc.
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/resource.h>
#include <sys/termios.h>
#include <sys/param.h>
#include <sys/regset.h>
#include <sys/frame.h>
#include <sys/stack.h>
#include <sys/reg.h>

#include <libproc.h>
#include <libscf.h>
#include <alloca.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <libctf.h>
#include <errno.h>
#include <kvm.h>

#include <mdb/mdb_lex.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_signal.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_target.h>
#include <mdb/mdb_gelf.h>
#include <mdb/mdb_conf.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_set.h>
#include <kmdb/kmdb_kctl.h>
#include <mdb/mdb.h>

#ifndef STACK_BIAS
#define STACK_BIAS      0
#endif

#if defined(__sparc)
#define STACK_REGISTER  SP
#else
#define STACK_REGISTER  REG_FP
#endif

#ifdef _LP64
#define MDB_DEF_IPATH   \
        "%r/usr/platform/%p/lib/adb/%i:" \
        "%r/usr/platform/%m/lib/adb/%i:" \
        "%r/usr/lib/adb/%i"
#define MDB_DEF_LPATH   \
        "%r/usr/platform/%p/lib/mdb/%t/%i:" \
        "%r/usr/platform/%m/lib/mdb/%t/%i:" \
        "%r/usr/lib/mdb/%t/%i"
#else
#define MDB_DEF_IPATH   \
        "%r/usr/platform/%p/lib/adb:" \
        "%r/usr/platform/%m/lib/adb:" \
        "%r/usr/lib/adb"
#define MDB_DEF_LPATH   \
        "%r/usr/platform/%p/lib/mdb/%t:" \
        "%r/usr/platform/%m/lib/mdb/%t:" \
        "%r/usr/lib/mdb/%t"
#endif

#define MDB_DEF_PROMPT "> "

/*
 * Similar to the panic_* variables in the kernel, we keep some relevant
 * information stored in a set of global _mdb_abort_* variables; in the
 * event that the debugger dumps core, these will aid core dump analysis.
 */
const char *volatile _mdb_abort_str;    /* reason for failure */
siginfo_t _mdb_abort_info;              /* signal info for fatal signal */
ucontext_t _mdb_abort_ctx;              /* context fatal signal interrupted */
int _mdb_abort_rcount;                  /* number of times resume requested */
int _mdb_self_fd = -1;                  /* fd for self as for valid_frame */

__NORETURN static void
terminate(int status)
{
        (void) mdb_signal_blockall();
        mdb_destroy();
        exit(status);
}

static void
print_frame(uintptr_t pc, int fnum)
{
        Dl_info dli;

        if (dladdr((void *)pc, &dli)) {
                mdb_iob_printf(mdb.m_err, "    [%d] %s`%s+0x%lx()\n", fnum,
                    strbasename(dli.dli_fname), dli.dli_sname,
                    pc - (uintptr_t)dli.dli_saddr);
        } else
                mdb_iob_printf(mdb.m_err, "    [%d] %p()\n", fnum, pc);
}

static int
valid_frame(struct frame *fr)
{
        static struct frame fake;
        uintptr_t addr = (uintptr_t)fr;

        if (pread(_mdb_self_fd, &fake, sizeof (fake), addr) != sizeof (fake)) {
                mdb_iob_printf(mdb.m_err, "    invalid frame (%p)\n", fr);
                return (0);
        }

        if (addr & (STACK_ALIGN - 1)) {
                mdb_iob_printf(mdb.m_err, "    mis-aligned frame (%p)\n", fr);
                return (0);
        }

        return (1);
}

/*ARGSUSED*/
static void
flt_handler(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
{
        static const struct rlimit rl = {
                (rlim_t)RLIM_INFINITY, (rlim_t)RLIM_INFINITY
        };

        const mdb_idcmd_t *idcp = NULL;

        if (mdb.m_frame != NULL && mdb.m_frame->f_cp != NULL)
                idcp = mdb.m_frame->f_cp->c_dcmd;

        if (sip != NULL)
                bcopy(sip, &_mdb_abort_info, sizeof (_mdb_abort_info));
        if (ucp != NULL)
                bcopy(ucp, &_mdb_abort_ctx, sizeof (_mdb_abort_ctx));

        _mdb_abort_info.si_signo = sig;
        (void) mdb_signal_sethandler(sig, MDB_SIG_DFL, NULL);

        /*
         * If there is no current dcmd, or the current dcmd comes from a
         * builtin module, we don't allow resume and always core dump.
         */
        if (idcp == NULL || idcp->idc_modp == NULL ||
            idcp->idc_modp == &mdb.m_rmod || idcp->idc_modp->mod_hdl == NULL)
                goto dump;

        if (mdb.m_term != NULL) {
                struct frame *fr = (struct frame *)
                    (ucp->uc_mcontext.gregs[STACK_REGISTER] + STACK_BIAS);

                char signame[SIG2STR_MAX];
                int i = 1;
                char c;

                if (sig2str(sig, signame) == -1) {
                        mdb_iob_printf(mdb.m_err,
                            "\n*** %s: received signal %d at:\n",
                            mdb.m_pname, sig);
                } else {
                        mdb_iob_printf(mdb.m_err,
                            "\n*** %s: received signal %s at:\n",
                            mdb.m_pname, signame);
                }

                if (ucp->uc_mcontext.gregs[REG_PC] != 0)
                        print_frame(ucp->uc_mcontext.gregs[REG_PC], i++);

                while (fr != NULL && valid_frame(fr) && fr->fr_savpc != 0) {
                        print_frame(fr->fr_savpc, i++);
                        fr = (struct frame *)
                            ((uintptr_t)fr->fr_savfp + STACK_BIAS);
                }

query:
                mdb_iob_printf(mdb.m_err, "\n%s: (c)ore dump, (q)uit, "
                    "(r)ecover, or (s)top for debugger [cqrs]? ", mdb.m_pname);

                mdb_iob_flush(mdb.m_err);

                for (;;) {
                        if (IOP_READ(mdb.m_term, &c, sizeof (c)) != sizeof (c))
                                goto dump;

                        switch (c) {
                        case 'c':
                        case 'C':
                                (void) setrlimit(RLIMIT_CORE, &rl);
                                mdb_iob_printf(mdb.m_err, "\n%s: attempting "
                                    "to dump core ...\n", mdb.m_pname);
                                goto dump;

                        case 'q':
                        case 'Q':
                                mdb_iob_discard(mdb.m_out);
                                mdb_iob_nl(mdb.m_err);
                                (void) mdb_signal_unblockall();
                                terminate(1);
                                /*NOTREACHED*/

                        case 'r':
                        case 'R':
                                mdb_iob_printf(mdb.m_err, "\n%s: unloading "
                                    "module '%s' ...\n", mdb.m_pname,
                                    idcp->idc_modp->mod_name);

                                (void) mdb_module_unload(
                                    idcp->idc_modp->mod_name, 0);

                                (void) mdb_signal_sethandler(sig,
                                    flt_handler, NULL);

                                _mdb_abort_rcount++;
                                mdb.m_intr = 0;
                                mdb.m_pend = 0;

                                (void) mdb_signal_unblockall();
                                longjmp(mdb.m_frame->f_pcb, MDB_ERR_ABORT);
                                /*NOTREACHED*/

                        case 's':
                        case 'S':
                                mdb_iob_printf(mdb.m_err, "\n%s: "
                                    "attempting to stop pid %d ...\n",
                                    mdb.m_pname, (int)getpid());

                                /*
                                 * Stop ourself; if this fails or we are
                                 * subsequently continued, ask again.
                                 */
                                (void) mdb_signal_raise(SIGSTOP);
                                (void) mdb_signal_unblockall();
                                goto query;
                        }
                }
        }

dump:
        if (SI_FROMUSER(sip)) {
                (void) mdb_signal_block(sig);
                (void) mdb_signal_raise(sig);
        }

        (void) sigfillset(&ucp->uc_sigmask);
        (void) sigdelset(&ucp->uc_sigmask, sig);

        if (_mdb_abort_str == NULL)
                _mdb_abort_str = "fatal signal received";

        ucp->uc_flags |= UC_SIGMASK;
        (void) setcontext(ucp);
}

/*ARGSUSED*/
static void
int_handler(int sig, siginfo_t *sip, ucontext_t *ucp, void *data)
{
        if (mdb.m_intr == 0)
                longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
        else
                mdb.m_pend++;
}

static void
control_kmdb(int start)
{
        int fd;

        if ((fd = open("/dev/kmdb", O_RDONLY)) < 0)
                die("failed to open /dev/kmdb");

        if (start) {
                char *state = mdb_get_config();

                if (ioctl(fd, KMDB_IOC_START, state) < 0)
                        die("failed to start kmdb");

                strfree(state);
        } else {
                if (ioctl(fd, KMDB_IOC_STOP) < 0)
                        die("failed to stop kmdb");
        }

        (void) close(fd);
}

static void
usage(int status)
{
        mdb_iob_printf(mdb.m_err, "Usage: %s [-fkmuwyAFKMSUW] [+/-o option] "
            "[-b VM] [-p pid] [-s dist] [-I path] [-L path]\n\t[-P prompt] "
            "[-R root] [-V dis-version] [-e expr] "
            "[object [core] | core | suffix]\n\n",
            mdb.m_pname);

        mdb_iob_puts(mdb.m_err,
            "\t-b attach to specified bhyve VM\n"
            "\t-e evaluate expr and return status\n"
            "\t-f force raw file debugging mode\n"
            "\t-k force kernel debugging mode\n"
            "\t-m disable demand-loading of module symbols\n"
            "\t-o set specified debugger option (+o to unset)\n"
            "\t-p attach to specified process-id\n"
            "\t-s set symbol matching distance\n"
            "\t-u force user program debugging mode\n"
            "\t-w enable write mode\n"
            "\t-y send terminal initialization sequences for tty mode\n"
            "\t-A disable automatic loading of mdb modules\n"
            "\t-F enable forcible takeover mode\n"
            "\t-K stop operating system and enter live kernel debugger\n"
            "\t-M preload all module symbols\n"
            "\t-I set initial path for macro files\n"
            "\t-L set initial path for module libs\n"
            "\t-P set command-line prompt\n"
            "\t-R set root directory for pathname expansion\n"
            "\t-S suppress processing of ~/.mdbrc file\n"
            "\t-U unload live kernel debugger\n"
            "\t-W enable I/O-mapped memory access (kernel only)\n"
            "\t-V set disassembler version\n");

        terminate(status);
}

static char *
mdb_scf_console_term(void)
{
        scf_simple_prop_t *prop;
        char *term = NULL;

        if ((prop = scf_simple_prop_get(NULL,
            "svc:/system/console-login:default", "ttymon",
            "terminal_type")) == NULL)
                return (NULL);

        if (scf_simple_prop_type(prop) == SCF_TYPE_ASTRING &&
            (term = scf_simple_prop_next_astring(prop)) != NULL)
                term = strdup(term);

        scf_simple_prop_free(prop);
        return (term);
}

/*
 * Unpleasant hack: we might be debugging a hypervisor domain dump.
 * Earlier versions use a non-ELF file.  Later versions are ELF, but are
 * /always/ ELF64, so our standard ehdr check isn't good enough.  Since
 * we don't want to know too much about the file format, we'll ask
 * mdb_kb.
 */
#ifdef __x86
static int
identify_xvm_file(const char *file, int *longmode)
{
        int (*identify)(const char *, int *);

        if (mdb_module_load("mdb_kb", MDB_MOD_GLOBAL | MDB_MOD_SILENT) != 0)
                return (0);

        identify = (int (*)())dlsym(RTLD_NEXT, "xkb_identify");

        if (identify == NULL)
                return (0);

        return (identify(file, longmode));
}
#else
/*ARGSUSED*/
static int
identify_xvm_file(const char *file, int *longmode)
{
        return (0);
}
#endif /* __x86 */

#ifndef __amd64
/*
 * There is no bhyve target in a 32bit x86 or any SPARC mdb. This dummy helps
 * keep the code simpler.
 */
/*ARGSUSED*/
static int
mdb_bhyve_tgt_create(mdb_tgt_t *t, int argc, const char *argv[])
{
        return (set_errno(EINVAL));
}
#endif

int
main(int argc, char *argv[], char *envp[])
{
        extern int mdb_kvm_is_compressed_dump(mdb_io_t *);
        extern int mdb_kvm_is_dump(mdb_io_t *);
        mdb_tgt_ctor_f *tgt_ctor = NULL;
        const char **tgt_argv = alloca((argc + 2) * sizeof (char *));
        int tgt_argc = 0;
        mdb_tgt_t *tgt;

        char object[MAXPATHLEN], execname[MAXPATHLEN];
        mdb_io_t *in_io, *out_io, *err_io, *null_io;
        struct termios tios;
        int status, c;
        char *p;

        const char *Iflag = NULL, *Lflag = NULL, *Vflag = NULL, *pidarg = NULL;
        const char *eflag = NULL;
        int fflag = 0, Kflag = 0, Rflag = 0, Sflag = 0, Oflag = 0, Uflag = 0;
        int bflag = 0;

        int ttylike;
        int longmode = 0;

        stack_t sigstack;

        if (realpath(getexecname(), execname) == NULL) {
                (void) strncpy(execname, argv[0], MAXPATHLEN);
                execname[MAXPATHLEN - 1] = '\0';
        }

        mdb_create(execname, argv[0]);
        bzero(tgt_argv, argc * sizeof (char *));
        argv[0] = (char *)mdb.m_pname;
        _mdb_self_fd = open("/proc/self/as", O_RDONLY);

        mdb.m_env = envp;

        out_io = mdb_fdio_create(STDOUT_FILENO);
        mdb.m_out = mdb_iob_create(out_io, MDB_IOB_WRONLY);

        err_io = mdb_fdio_create(STDERR_FILENO);
        mdb.m_err = mdb_iob_create(err_io, MDB_IOB_WRONLY);
        mdb_iob_clrflags(mdb.m_err, MDB_IOB_AUTOWRAP);

        null_io = mdb_nullio_create();
        mdb.m_null = mdb_iob_create(null_io, MDB_IOB_WRONLY);

        in_io = mdb_fdio_create(STDIN_FILENO);
        if ((mdb.m_termtype = getenv("TERM")) != NULL) {
                mdb.m_termtype = strdup(mdb.m_termtype);
                mdb.m_flags |= MDB_FL_TERMGUESS;
        }
        mdb.m_term = NULL;

        mdb_dmode(mdb_dstr2mode(getenv("MDB_DEBUG")));
        mdb.m_pgid = getpgrp();

        if (getenv("_MDB_EXEC") != NULL)
                mdb.m_flags |= MDB_FL_EXEC;

        /*
         * Setup an alternate signal stack.  When tearing down pipelines in
         * terminate(), we may have to destroy the stack of the context in
         * which we are currently executing the signal handler.
         */
        sigstack.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,
            MAP_PRIVATE | MAP_ANON, -1, 0);
        if (sigstack.ss_sp == MAP_FAILED)
                die("could not allocate signal stack");
        sigstack.ss_size = SIGSTKSZ;
        sigstack.ss_flags = 0;
        if (sigaltstack(&sigstack, NULL) != 0)
                die("could not set signal stack");

        (void) mdb_signal_sethandler(SIGPIPE, MDB_SIG_IGN, NULL);
        (void) mdb_signal_sethandler(SIGQUIT, MDB_SIG_IGN, NULL);

        (void) mdb_signal_sethandler(SIGILL, flt_handler, NULL);
        (void) mdb_signal_sethandler(SIGTRAP, flt_handler, NULL);
        (void) mdb_signal_sethandler(SIGIOT, flt_handler, NULL);
        (void) mdb_signal_sethandler(SIGEMT, flt_handler, NULL);
        (void) mdb_signal_sethandler(SIGFPE, flt_handler, NULL);
        (void) mdb_signal_sethandler(SIGBUS, flt_handler, NULL);
        (void) mdb_signal_sethandler(SIGSEGV, flt_handler, NULL);

        (void) mdb_signal_sethandler(SIGHUP,
            (mdb_signal_f *)(uintptr_t)terminate, NULL);
        (void) mdb_signal_sethandler(SIGTERM,
            (mdb_signal_f *)(uintptr_t)terminate, NULL);

        for (mdb.m_rdvers = RD_VERSION; mdb.m_rdvers > 0; mdb.m_rdvers--) {
                if (rd_init(mdb.m_rdvers) == RD_OK)
                        break;
        }

        for (mdb.m_ctfvers = CTF_VERSION; mdb.m_ctfvers > 0; mdb.m_ctfvers--) {
                if (ctf_version(mdb.m_ctfvers) != -1)
                        break;
        }

        if ((p = getenv("HISTSIZE")) != NULL && strisnum(p)) {
                mdb.m_histlen = strtoi(p);
                if (mdb.m_histlen < 1)
                        mdb.m_histlen = 1;
        }

        while (optind < argc) {
                while ((c = getopt(argc, argv,
                    "be:fkmo:p:s:uwyACD:FI:KL:MOP:R:SUV:W")) != (int)EOF) {
                        switch (c) {
                        case 'b':
                                bflag++;
                                tgt_ctor = mdb_bhyve_tgt_create;
                                break;
                        case 'e':
                                if (eflag != NULL) {
                                        warn("-e already specified\n");
                                        terminate(2);
                                }
                                eflag = optarg;
                                break;
                        case 'f':
                                fflag++;
                                tgt_ctor = mdb_rawfile_tgt_create;
                                break;
                        case 'k':
                                tgt_ctor = mdb_kvm_tgt_create;
                                break;
                        case 'm':
                                mdb.m_tgtflags |= MDB_TGT_F_NOLOAD;
                                mdb.m_tgtflags &= ~MDB_TGT_F_PRELOAD;
                                break;
                        case 'o':
                                if (!mdb_set_options(optarg, TRUE))
                                        terminate(2);
                                break;
                        case 'p':
                                tgt_ctor = mdb_proc_tgt_create;
                                pidarg = optarg;
                                break;
                        case 's':
                                if (!strisnum(optarg)) {
                                        warn("expected integer following -s\n");
                                        terminate(2);
                                }
                                mdb.m_symdist = (size_t)(uint_t)strtoi(optarg);
                                break;
                        case 'u':
                                tgt_ctor = mdb_proc_tgt_create;
                                break;
                        case 'w':
                                mdb.m_tgtflags |= MDB_TGT_F_RDWR;
                                break;
                        case 'y':
                                mdb.m_flags |= MDB_FL_USECUP;
                                break;
                        case 'A':
                                (void) mdb_set_options("nomods", TRUE);
                                break;
                        case 'C':
                                (void) mdb_set_options("noctf", TRUE);
                                break;
                        case 'D':
                                mdb_dmode(mdb_dstr2mode(optarg));
                                break;
                        case 'F':
                                mdb.m_tgtflags |= MDB_TGT_F_FORCE;
                                break;
                        case 'I':
                                Iflag = optarg;
                                break;
                        case 'L':
                                Lflag = optarg;
                                break;
                        case 'K':
                                Kflag++;
                                break;
                        case 'M':
                                mdb.m_tgtflags |= MDB_TGT_F_PRELOAD;
                                mdb.m_tgtflags &= ~MDB_TGT_F_NOLOAD;
                                break;
                        case 'O':
                                Oflag++;
                                break;
                        case 'P':
                                if (!mdb_set_prompt(optarg))
                                        terminate(2);
                                break;
                        case 'R':
                                (void) strncpy(mdb.m_root, optarg, MAXPATHLEN);
                                mdb.m_root[MAXPATHLEN - 1] = '\0';
                                Rflag++;
                                break;
                        case 'S':
                                Sflag++;
                                break;
                        case 'U':
                                Uflag++;
                                break;
                        case 'V':
                                Vflag = optarg;
                                break;
                        case 'W':
                                mdb.m_tgtflags |= MDB_TGT_F_ALLOWIO;
                                break;
                        case '?':
                                if (optopt == '?')
                                        usage(0);
                                /* FALLTHROUGH */
                        default:
                                usage(2);
                        }
                }

                if (optind < argc) {
                        const char *arg = argv[optind++];

                        if (arg[0] == '+' && strlen(arg) == 2) {
                                if (arg[1] != 'o') {
                                        warn("illegal option -- %s\n", arg);
                                        terminate(2);
                                }
                                if (optind >= argc) {
                                        warn("option requires an argument -- "
                                            "%s\n", arg);
                                        terminate(2);
                                }
                                if (!mdb_set_options(argv[optind++], FALSE))
                                        terminate(2);
                        } else
                                tgt_argv[tgt_argc++] = arg;
                }
        }

        if (rd_ctl(RD_CTL_SET_HELPPATH, (void *)mdb.m_root) != RD_OK) {
                warn("cannot set librtld_db helper path to %s\n", mdb.m_root);
                terminate(2);
        }

        if (mdb.m_debug & MDB_DBG_HELP)
                terminate(0); /* Quit here if we've printed out the tokens */


        if (Iflag != NULL && strchr(Iflag, ';') != NULL) {
                warn("macro path cannot contain semicolons\n");
                terminate(2);
        }

        if (Lflag != NULL && strchr(Lflag, ';') != NULL) {
                warn("module path cannot contain semicolons\n");
                terminate(2);
        }

        if (Kflag || Uflag) {
                char *nm;

                if (tgt_ctor != NULL || Iflag != NULL) {
                        warn("neither -f, -k, -p, -u, nor -I "
                            "may be used with -K\n");
                        usage(2);
                }

                if (Lflag != NULL)
                        mdb_set_lpath(Lflag);

                if ((nm = ttyname(STDIN_FILENO)) == NULL ||
                    strcmp(nm, "/dev/console") != 0) {
                        /*
                         * Due to the consequences of typing mdb -K instead of
                         * mdb -k on a tty other than /dev/console, we require
                         * -F when starting kmdb from a tty other than
                         * /dev/console.
                         */
                        if (!(mdb.m_tgtflags & MDB_TGT_F_FORCE)) {
                                die("-F must also be supplied to start kmdb "
                                    "from non-console tty\n");
                        }

                        if (mdb.m_termtype == NULL || (mdb.m_flags &
                            MDB_FL_TERMGUESS)) {
                                if (mdb.m_termtype != NULL)
                                        strfree(mdb.m_termtype);

                                if ((mdb.m_termtype = mdb_scf_console_term()) !=
                                    NULL)
                                        mdb.m_flags |= MDB_FL_TERMGUESS;
                        }
                } else {
                        /*
                         * When on console, $TERM (if set) takes precedence over
                         * the SMF setting.
                         */
                        if (mdb.m_termtype == NULL && (mdb.m_termtype =
                            mdb_scf_console_term()) != NULL)
                                mdb.m_flags |= MDB_FL_TERMGUESS;
                }

                control_kmdb(Kflag);
                terminate(0);
                /*NOTREACHED*/
        }

        if (eflag != NULL) {
                IOP_CLOSE(in_io);
                in_io = mdb_strio_create(eflag);
                mdb.m_lastret = 0;
        }

        /*
         * If standard input appears to have tty attributes, attempt to
         * initialize a terminal i/o backend on top of stdin and stdout.
         */
        ttylike = (IOP_CTL(in_io, TCGETS, &tios) == 0);
        if (ttylike) {
                if ((mdb.m_term = mdb_termio_create(mdb.m_termtype,
                    in_io, out_io)) == NULL) {
                        if (!(mdb.m_flags & MDB_FL_EXEC)) {
                                warn("term init failed: command-line editing "
                                    "and prompt will not be available\n");
                        }
                } else {
                        in_io = mdb.m_term;
                }
        }

        mdb.m_in = mdb_iob_create(in_io, MDB_IOB_RDONLY);
        if (mdb.m_term != NULL) {
                mdb_iob_setpager(mdb.m_out, mdb.m_term);
                if (mdb.m_flags & MDB_FL_PAGER)
                        mdb_iob_setflags(mdb.m_out, MDB_IOB_PGENABLE);
                else
                        mdb_iob_clrflags(mdb.m_out, MDB_IOB_PGENABLE);
        } else if (ttylike)
                mdb_iob_setflags(mdb.m_in, MDB_IOB_TTYLIKE);
        else
                mdb_iob_setbuf(mdb.m_in, mdb_alloc(1, UM_SLEEP), 1);

        mdb_pservice_init();
        mdb_lex_reset();

        if ((mdb.m_shell = getenv("SHELL")) == NULL)
                mdb.m_shell = "/bin/sh";

        /*
         * If the debugger state is to be inherited from a previous instance,
         * restore it now prior to path evaluation so that %R is updated.
         */
        if ((p = getenv(MDB_CONFIG_ENV_VAR)) != NULL) {
                mdb_set_config(p);
                (void) unsetenv(MDB_CONFIG_ENV_VAR);
        }

        /*
         * Path evaluation part 1: Create the initial module path to allow
         * the target constructor to load a support module.  Then expand
         * any command-line arguments that modify the paths.
         */
        if (Iflag != NULL)
                mdb_set_ipath(Iflag);
        else
                mdb_set_ipath(MDB_DEF_IPATH);

        if (Lflag != NULL)
                mdb_set_lpath(Lflag);
        else
                mdb_set_lpath(MDB_DEF_LPATH);

        if (mdb_get_prompt() == NULL && !(mdb.m_flags & MDB_FL_ADB))
                (void) mdb_set_prompt(MDB_DEF_PROMPT);

        if (tgt_ctor == mdb_kvm_tgt_create) {
                if (pidarg != NULL) {
                        warn("-p and -k options are mutually exclusive\n");
                        terminate(2);
                }

                if (tgt_argc == 0)
                        tgt_argv[tgt_argc++] = "/dev/ksyms";
                if (tgt_argc == 1 && strisnum(tgt_argv[0]) == 0) {
                        if (mdb.m_tgtflags & MDB_TGT_F_ALLOWIO)
                                tgt_argv[tgt_argc++] = "/dev/allkmem";
                        else
                                tgt_argv[tgt_argc++] = "/dev/kmem";
                }
        }

        if (pidarg != NULL) {
                if (tgt_argc != 0) {
                        warn("-p may not be used with other arguments\n");
                        terminate(2);
                }
                if (proc_arg_psinfo(pidarg, PR_ARG_PIDS, NULL, &status) == -1) {
                        die("cannot attach to %s: %s\n",
                            pidarg, Pgrab_error(status));
                }
                if (strchr(pidarg, '/') != NULL)
                        (void) mdb_iob_snprintf(object, MAXPATHLEN,
                            "%s/object/a.out", pidarg);
                else
                        (void) mdb_iob_snprintf(object, MAXPATHLEN,
                            "/proc/%s/object/a.out", pidarg);
                tgt_argv[tgt_argc++] = object;
                tgt_argv[tgt_argc++] = pidarg;
        }

        /*
         * Find the first argument that is not a special "-" token.  If one is
         * found, we will examine this file and make some inferences below.
         */
        for (c = 0; c < tgt_argc && strcmp(tgt_argv[c], "-") == 0; c++)
                continue;

        if (c < tgt_argc) {
                Elf32_Ehdr ehdr;
                mdb_io_t *io;

                /*
                 * If special "-" tokens preceded an argument, shift the entire
                 * argument list to the left to remove the leading "-" args.
                 */
                if (c > 0) {
                        bcopy(&tgt_argv[c], tgt_argv,
                            sizeof (const char *) * (tgt_argc - c));
                        tgt_argc -= c;
                }

                if (fflag)
                        goto tcreate; /* skip re-exec and just create target */

                /* bhyve: directly create target, or re-exec in case of 32bit */
                if (bflag) {
#ifndef __amd64
                        goto reexec;
#else
                        goto tcreate;
#endif
                }

                /*
                 * If we just have an object file name, and that file doesn't
                 * exist, and it's a string of digits, infer it to be a
                 * sequence number referring to a pair of crash dump files.
                 */
                if (tgt_argc == 1 && access(tgt_argv[0], F_OK) == -1 &&
                    strisnum(tgt_argv[0])) {

                        size_t len = strlen(tgt_argv[0]) + 8;
                        const char *object = tgt_argv[0];

                        tgt_argv[0] = alloca(len);
                        tgt_argv[1] = alloca(len);

                        (void) strcpy((char *)tgt_argv[0], "unix.");
                        (void) strcat((char *)tgt_argv[0], object);
                        (void) strcpy((char *)tgt_argv[1], "vmcore.");
                        (void) strcat((char *)tgt_argv[1], object);

                        if (access(tgt_argv[0], F_OK) == -1 &&
                            access(tgt_argv[1], F_OK) != -1) {
                                /*
                                 * If we have a vmcore but not a unix file,
                                 * set the symbol table to be the vmcore to
                                 * force libkvm to extract it out of the dump.
                                 */
                                tgt_argv[0] = tgt_argv[1];
                        } else if (access(tgt_argv[0], F_OK) == -1 &&
                            access(tgt_argv[1], F_OK) == -1) {
                                (void) strcpy((char *)tgt_argv[1], "vmdump.");
                                (void) strcat((char *)tgt_argv[1], object);
                                if (access(tgt_argv[1], F_OK) == 0) {
                                        mdb_iob_printf(mdb.m_err,
                                            "cannot open compressed dump; "
                                            "decompress using savecore -f %s\n",
                                            tgt_argv[1]);
                                        terminate(0);
                                }
                        }

                        tgt_argc = 2;
                }

                /*
                 * We need to open the object file in order to determine its
                 * ELF class and potentially re-exec ourself.
                 */
                if ((io = mdb_fdio_create_path(NULL, tgt_argv[0],
                    O_RDONLY, 0)) == NULL)
                        die("failed to open %s", tgt_argv[0]);

                if (tgt_argc == 1) {
                        if (mdb_kvm_is_compressed_dump(io)) {
                                /*
                                 * We have a single vmdump.N compressed dump
                                 * file; give a helpful message.
                                 */
                                mdb_iob_printf(mdb.m_err,
                                    "cannot open compressed dump; "
                                    "decompress using savecore -f %s\n",
                                    tgt_argv[0]);
                                terminate(0);
                        } else if (mdb_kvm_is_dump(io)) {
                                /*
                                 * We have an uncompressed dump as our only
                                 * argument; specify the dump as the symbol
                                 * table to force libkvm to dig it out of the
                                 * dump.
                                 */
                                tgt_argv[tgt_argc++] = tgt_argv[0];
                        }
                }

                /*
                 * If the target is unknown or is not the rawfile target, do
                 * a gelf_check to determine if the file is an ELF file.  If
                 * it is not and the target is unknown, use the rawfile tgt.
                 * Otherwise an ELF-based target is needed, so we must abort.
                 */
                if (mdb_gelf_check(io, &ehdr, ET_NONE) == -1) {
                        if (tgt_ctor != NULL) {
                                (void) mdb_gelf_check(io, &ehdr, ET_EXEC);
                                mdb_io_destroy(io);
                                terminate(1);
                        } else
                                tgt_ctor = mdb_rawfile_tgt_create;
                }

                mdb_io_destroy(io);

                if (identify_xvm_file(tgt_argv[0], &longmode) == 1) {
#ifdef _LP64
                        if (!longmode)
                                goto reexec;
#else
                        if (longmode)
                                goto reexec;
#endif
                        tgt_ctor = mdb_kvm_tgt_create;
                        goto tcreate;
                }

                /*
                 * The object file turned out to be a user core file (ET_CORE),
                 * and no other arguments were specified, swap 0 and 1.  The
                 * proc target will infer the executable for us.
                 */
                if (ehdr.e_type == ET_CORE) {
                        tgt_argv[tgt_argc++] = tgt_argv[0];
                        tgt_argv[0] = NULL;
                        tgt_ctor = mdb_proc_tgt_create;
                }

                /*
                 * If tgt_argv[1] is filled in, open it up and determine if it
                 * is a vmcore file.  If it is, gelf_check will fail and we
                 * set tgt_ctor to 'kvm'; otherwise we use the default.
                 */
                if (tgt_argc > 1 && strcmp(tgt_argv[1], "-") != 0 &&
                    tgt_argv[0] != NULL && pidarg == NULL) {
                        Elf32_Ehdr chdr;

                        if (access(tgt_argv[1], F_OK) == -1)
                                die("failed to access %s", tgt_argv[1]);

                        /* *.N case: drop vmdump.N from the list */
                        if (tgt_argc == 3) {
                                if ((io = mdb_fdio_create_path(NULL,
                                    tgt_argv[2], O_RDONLY, 0)) == NULL)
                                        die("failed to open %s", tgt_argv[2]);
                                if (mdb_kvm_is_compressed_dump(io))
                                        tgt_argv[--tgt_argc] = NULL;
                                mdb_io_destroy(io);
                        }

                        if ((io = mdb_fdio_create_path(NULL, tgt_argv[1],
                            O_RDONLY, 0)) == NULL)
                                die("failed to open %s", tgt_argv[1]);

                        if (mdb_gelf_check(io, &chdr, ET_NONE) == -1)
                                tgt_ctor = mdb_kvm_tgt_create;

                        mdb_io_destroy(io);
                }

                /*
                 * At this point, we've read the ELF header for either an
                 * object file or core into ehdr.  If the class does not match
                 * ours, attempt to exec the mdb of the appropriate class.
                 */
#ifdef _LP64
                if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
                        goto reexec;
#else
                if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
                        goto reexec;
#endif
        }

tcreate:
        if (tgt_ctor == NULL)
                tgt_ctor = mdb_proc_tgt_create;

        tgt = mdb_tgt_create(tgt_ctor, mdb.m_tgtflags, tgt_argc, tgt_argv);

        if (tgt == NULL) {
                if (errno == EINVAL)
                        usage(2); /* target can return EINVAL to get usage */
                if (errno == EMDB_TGT)
                        terminate(1); /* target already printed error msg */
                die("failed to initialize target");
        }

        mdb_tgt_activate(tgt);

        mdb_create_loadable_disasms();

        if (Vflag != NULL && mdb_dis_select(Vflag) == -1)
                warn("invalid disassembler mode -- %s\n", Vflag);


        if (Rflag && mdb.m_term != NULL)
                warn("Using proto area %s\n", mdb.m_root);

        /*
         * If the target was successfully constructed and -O was specified,
         * we now attempt to enter piggy-mode for debugging jurassic problems.
         */
        if (Oflag) {
                pcinfo_t pci;

                (void) strcpy(pci.pc_clname, "RT");

                if (priocntl(P_LWPID, P_MYID, PC_GETCID, (caddr_t)&pci) != -1) {
                        pcparms_t pcp;
                        rtparms_t *rtp = (rtparms_t *)pcp.pc_clparms;

                        rtp->rt_pri = 35;
                        rtp->rt_tqsecs = 0;
                        rtp->rt_tqnsecs = RT_TQDEF;

                        pcp.pc_cid = pci.pc_cid;

                        if (priocntl(P_LWPID, P_MYID, PC_SETPARMS,
                            (caddr_t)&pcp) == -1) {
                                warn("failed to set RT parameters");
                                Oflag = 0;
                        }
                } else {
                        warn("failed to get RT class id");
                        Oflag = 0;
                }

                if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
                        warn("failed to lock address space");
                        Oflag = 0;
                }

                if (Oflag)
                        mdb_printf("%s: oink, oink!\n", mdb.m_pname);
        }

        /*
         * Path evaluation part 2: Re-evaluate the path now that the target
         * is ready (and thus we have access to the real platform string).
         * Do this before reading ~/.mdbrc to allow path modifications prior
         * to performing module auto-loading.
         */
        mdb_set_ipath(mdb.m_ipathstr);
        mdb_set_lpath(mdb.m_lpathstr);

        if (!Sflag && (p = getenv("HOME")) != NULL) {
                char rcpath[MAXPATHLEN];
                mdb_io_t *rc_io;
                int fd;

                (void) mdb_iob_snprintf(rcpath, MAXPATHLEN, "%s/.mdbrc", p);
                fd = open64(rcpath, O_RDONLY);

                if (fd >= 0 && (rc_io = mdb_fdio_create_named(fd, rcpath))) {
                        mdb_iob_t *iob = mdb_iob_create(rc_io, MDB_IOB_RDONLY);
                        mdb_iob_t *old = mdb.m_in;

                        mdb.m_in = iob;
                        (void) mdb_run();
                        mdb.m_in = old;
                }
        }

        if (!(mdb.m_flags & MDB_FL_NOMODS))
                mdb_module_load_all(0);

        (void) mdb_signal_sethandler(SIGINT, int_handler, NULL);
        while ((status = mdb_run()) == MDB_ERR_ABORT ||
            status == MDB_ERR_OUTPUT) {
                /*
                 * If a write failed on stdout, give up.  A more informative
                 * error message will already have been printed by mdb_run().
                 */
                if (status == MDB_ERR_OUTPUT &&
                    mdb_iob_getflags(mdb.m_out) & MDB_IOB_ERR) {
                        mdb_warn("write to stdout failed, exiting\n");
                        break;
                }
                continue;
        }

        terminate((status == MDB_ERR_QUIT || status == 0) ?
            (eflag != NULL && mdb.m_lastret != 0 ? 1 : 0) : 1);
        /*NOTREACHED*/

reexec:
        if ((p = strrchr(execname, '/')) == NULL)
                die("cannot determine absolute pathname\n");
#ifdef _LP64
#ifdef __sparc
        (void) strcpy(p, "/../sparcv7/");
#else
        (void) strcpy(p, "/../i86/");
#endif
#else
#ifdef __sparc
        (void) strcpy(p, "/../sparcv9/");
#else
        (void) strcpy(p, "/../amd64/");
#endif
#endif
        (void) strcat(p, mdb.m_pname);

        if (mdb.m_term != NULL)
                (void) IOP_CTL(in_io, TCSETSW, &tios);

        (void) putenv("_MDB_EXEC=1");
        (void) execv(execname, argv);

        /*
         * If execv fails, suppress ENOEXEC.  Experience shows the most common
         * reason is that the machine is booted under a 32-bit kernel, in which
         * case it is clearer to only print the message below.
         */
        if (errno != ENOEXEC)
                warn("failed to exec %s", execname);
#ifdef _LP64
        die("64-bit %s cannot debug 32-bit program %s\n",
            mdb.m_pname, tgt_argv[0] ?
            tgt_argv[0] : tgt_argv[1]);
#else
        die("32-bit %s cannot debug 64-bit program %s\n",
            mdb.m_pname, tgt_argv[0] ?
            tgt_argv[0] : tgt_argv[1]);
#endif

        goto tcreate;
}