int mdb_eval(const char *s) { mdb_frame_t *ofp = mdb.m_fmark; mdb_frame_t *fp = mdb.m_frame; int err; if (s == NULL) return (set_errno(EINVAL)); /* * Push m_in down onto the input stack, then set m_in to point to the * i/o buffer for our command string, and reset the frame marker. * The mdb_run() function returns when the new m_in iob reaches EOF. */ mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno); mdb.m_in = mdb_iob_create(mdb_strio_create(s), MDB_IOB_RDONLY); mdb.m_fmark = NULL; err = mdb_run(); mdb.m_fmark = ofp; /* * Now pop the old standard input stream and restore mdb.m_in and * the parser's saved current line number. */ mdb.m_in = mdb_iob_stack_pop(&fp->f_istk); yylineno = mdb_iob_lineno(mdb.m_in); /* * If mdb_run() returned an error, propagate this backward * up the stack of debugger environment frames. */ if (MDB_ERR_IS_FATAL(err)) longjmp(fp->f_pcb, err); if (err == MDB_ERR_PAGER || err == MDB_ERR_SIGINT) return (set_errno(EMDB_CANCEL)); if (err != 0) return (set_errno(EMDB_EVAL)); return (0); }
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; }