void kt_amd64_init(mdb_tgt_t *t) { kt_data_t *kt = t->t_data; panic_data_t pd; kreg_t *kregs; struct regs regs; uintptr_t addr; /* * Initialize the machine-dependent parts of the kernel target * structure. Once this is complete and we fill in the ops * vector, the target is now fully constructed and we can use * the target API itself to perform the rest of our initialization. */ kt->k_rds = mdb_amd64_kregs; kt->k_regs = mdb_zalloc(sizeof (mdb_tgt_gregset_t), UM_SLEEP); kt->k_regsize = sizeof (mdb_tgt_gregset_t); kt->k_dcmd_regs = kt_regs; kt->k_dcmd_stack = kt_stack; kt->k_dcmd_stackv = kt_stackv; kt->k_dcmd_stackr = kt_stackv; t->t_ops = &kt_amd64_ops; kregs = kt->k_regs->kregs; (void) mdb_dis_select("amd64"); /* * Lookup the symbols corresponding to subroutines in locore.s where * we expect a saved regs structure to be pushed on the stack. When * performing stack tracebacks we will attempt to detect interrupt * frames by comparing the %eip value to these symbols. */ (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, "cmnint", &kt->k_intr_sym, NULL); (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, "cmntrap", &kt->k_trap_sym, NULL); /* * Don't attempt to load any thread or register information if * we're examining the live operating system. */ if (strcmp(kt->k_symfile, "/dev/ksyms") == 0) return; /* * If the panicbuf symbol is present and we can consume a panicbuf * header of the appropriate version from this address, then we can * initialize our current register set based on its contents. * Prior to the re-structuring of panicbuf, our only register data * was the panic_regs label_t, into which a setjmp() was performed, * or the panic_reg register pointer, which was only non-zero if * the system panicked as a result of a trap calling die(). */ if (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &pd, sizeof (pd), MDB_TGT_OBJ_EXEC, "panicbuf") == sizeof (pd) && pd.pd_version == PANICBUFVERS) { size_t pd_size = MIN(PANICBUFSIZE, pd.pd_msgoff); panic_data_t *pdp = mdb_zalloc(pd_size, UM_SLEEP); uint_t i, n; (void) mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, pdp, pd_size, MDB_TGT_OBJ_EXEC, "panicbuf"); n = (pd_size - (sizeof (panic_data_t) - sizeof (panic_nv_t))) / sizeof (panic_nv_t); for (i = 0; i < n; i++) { (void) kt_putareg(t, kt->k_tid, pdp->pd_nvdata[i].pnv_name, pdp->pd_nvdata[i].pnv_value); } mdb_free(pdp, pd_size); } else if (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &addr, sizeof (addr), MDB_TGT_OBJ_EXEC, "panic_reg") == sizeof (addr) && addr != NULL && mdb_tgt_vread(t, ®s, sizeof (regs), addr) == sizeof (regs)) { kregs[KREG_SAVFP] = regs.r_savfp; kregs[KREG_SAVPC] = regs.r_savpc; kregs[KREG_RDI] = regs.r_rdi; kregs[KREG_RSI] = regs.r_rsi; kregs[KREG_RDX] = regs.r_rdx; kregs[KREG_RCX] = regs.r_rcx; kregs[KREG_R8] = regs.r_r8; kregs[KREG_R9] = regs.r_r9; kregs[KREG_RAX] = regs.r_rax; kregs[KREG_RBX] = regs.r_rbx; kregs[KREG_RBP] = regs.r_rbp; kregs[KREG_R10] = regs.r_r10; kregs[KREG_R11] = regs.r_r11; kregs[KREG_R12] = regs.r_r12; kregs[KREG_R13] = regs.r_r13; kregs[KREG_R14] = regs.r_r14; kregs[KREG_R15] = regs.r_r15; kregs[KREG_FSBASE] = regs.r_fsbase; kregs[KREG_GSBASE] = regs.r_gsbase; kregs[KREG_DS] = regs.r_ds; kregs[KREG_ES] = regs.r_es; kregs[KREG_FS] = regs.r_fs; kregs[KREG_GS] = regs.r_gs; kregs[KREG_TRAPNO] = regs.r_trapno; kregs[KREG_ERR] = regs.r_err; kregs[KREG_RIP] = regs.r_rip; kregs[KREG_CS] = regs.r_cs; kregs[KREG_RFLAGS] = regs.r_rfl; kregs[KREG_RSP] = regs.r_rsp; kregs[KREG_SS] = regs.r_ss; } else { warn("failed to read panicbuf and panic_reg -- " "current register set will be unavailable\n"); } }
void kt_amd64_init(mdb_tgt_t *t) { kt_data_t *kt = t->t_data; panic_data_t pd; struct regs regs; uintptr_t addr; /* * Initialize the machine-dependent parts of the kernel target * structure. Once this is complete and we fill in the ops * vector, the target is now fully constructed and we can use * the target API itself to perform the rest of our initialization. */ kt->k_rds = mdb_amd64_kregs; kt->k_regs = mdb_zalloc(sizeof (mdb_tgt_gregset_t), UM_SLEEP); kt->k_regsize = sizeof (mdb_tgt_gregset_t); kt->k_dcmd_regs = kt_regs; kt->k_dcmd_stack = kt_stack; kt->k_dcmd_stackv = kt_stackv; kt->k_dcmd_stackr = kt_stackv; kt->k_dcmd_cpustack = kt_cpustack; kt->k_dcmd_cpuregs = kt_cpuregs; t->t_ops = &kt_amd64_ops; (void) mdb_dis_select("amd64"); /* * Lookup the symbols corresponding to subroutines in locore.s where * we expect a saved regs structure to be pushed on the stack. When * performing stack tracebacks we will attempt to detect interrupt * frames by comparing the %eip value to these symbols. */ (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, "cmnint", &kt->k_intr_sym, NULL); (void) mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, "cmntrap", &kt->k_trap_sym, NULL); /* * Don't attempt to load any thread or register information if * we're examining the live operating system. */ if (kt->k_symfile != NULL && strcmp(kt->k_symfile, "/dev/ksyms") == 0) return; /* * If the panicbuf symbol is present and we can consume a panicbuf * header of the appropriate version from this address, then we can * initialize our current register set based on its contents. * Prior to the re-structuring of panicbuf, our only register data * was the panic_regs label_t, into which a setjmp() was performed, * or the panic_reg register pointer, which was only non-zero if * the system panicked as a result of a trap calling die(). */ if (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &pd, sizeof (pd), MDB_TGT_OBJ_EXEC, "panicbuf") == sizeof (pd) && pd.pd_version == PANICBUFVERS) { size_t pd_size = MIN(PANICBUFSIZE, pd.pd_msgoff); panic_data_t *pdp = mdb_zalloc(pd_size, UM_SLEEP); uint_t i, n; (void) mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, pdp, pd_size, MDB_TGT_OBJ_EXEC, "panicbuf"); n = (pd_size - (sizeof (panic_data_t) - sizeof (panic_nv_t))) / sizeof (panic_nv_t); for (i = 0; i < n; i++) { (void) kt_putareg(t, kt->k_tid, pdp->pd_nvdata[i].pnv_name, pdp->pd_nvdata[i].pnv_value); } mdb_free(pdp, pd_size); return; }; if (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &addr, sizeof (addr), MDB_TGT_OBJ_EXEC, "panic_reg") == sizeof (addr) && addr != NULL && mdb_tgt_vread(t, ®s, sizeof (regs), addr) == sizeof (regs)) { kt_regs_to_kregs(®s, kt->k_regs); return; } /* * If we can't read any panic regs, then our final try is for any CPU * context that may have been stored (for example, in Xen core dumps). */ if (kt_kvmregs(t, 0, kt->k_regs) == 0) return; warn("failed to read panicbuf and panic_reg -- " "current register set will be unavailable\n"); }
int main(int argc, char *argv[], char *envp[]) { extern int mdb_kvm_is_compressed_dump(mdb_io_t *); mdb_tgt_ctor_f *tgt_ctor = NULL; const char **tgt_argv = alloca(argc * 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; int fflag = 0, Kflag = 0, Rflag = 0, Sflag = 0, Oflag = 0, Uflag = 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, SIG_IGN, NULL); (void) mdb_signal_sethandler(SIGQUIT, 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 *)terminate, NULL); (void) mdb_signal_sethandler(SIGTERM, (mdb_signal_f *)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, "fkmo:p:s:uwyACD:FI:KL:MOP:R:SUV:W")) != (int)EOF) { switch (c) { 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 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 */ /* * 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] = mdb_alloc(len, UM_SLEEP); tgt_argv[1] = mdb_alloc(len, UM_SLEEP); (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) { (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]); /* * Check for a single vmdump.N compressed dump file, * and give a helpful message. */ if (tgt_argc == 1) { if (mdb_kvm_is_compressed_dump(io)) { mdb_iob_printf(mdb.m_err, "cannot open compressed dump; " "decompress using savecore -f %s\n", tgt_argv[0]); terminate(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) ? 0 : 1); /*NOTREACHED*/ return (0); 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; }