/*ARGSUSED*/ void kmdb_kdi_init_isadep(kdi_t *kdi, kmdb_auxv_t *kav) { kdi_dcache_size = kdi_dcache_linesize = kdi_icache_size = kdi_icache_linesize = 0; kdi_max_cpu_freq = kdi_sticks_per_usec = 0; mdb_dprintf(MDB_DBG_KDI, "Initializing CPUs\n"); kmdb_prom_walk_cpus(kdi_init_cpus_cb, NULL, NULL); /* * If we can't find one, guess high. The CPU frequency is going to be * used to determine the length of various delays, such as the mondo * interrupt retry delay. Too long is generally better than too short. */ if (kdi_max_cpu_freq == 0) { mdb_dprintf(MDB_DBG_KDI, "No CPU freq found - assuming " "500MHz\n"); kdi_max_cpu_freq = 500 * MICROSEC; } kdi_sticks_per_usec = MAX((kdi_max_cpu_freq + (MICROSEC - 1)) / MICROSEC, 1); mdb.m_kdi->mkdi_cpu_init(kdi_dcache_size, kdi_dcache_linesize, kdi_icache_size, kdi_icache_linesize); #ifndef sun4v kmdb_prom_preserve_kctx_init(); #endif /* sun4v */ }
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); }
void kmdb_dpi_process_work_queue(void) { work_results_t res; (void) mdb_nv_create(&res.res_loads, UM_SLEEP); (void) mdb_nv_create(&res.res_unloads, UM_SLEEP); mdb_dprintf(MDB_DBG_DPI, "processing work queue\n"); (void) kmdb_wr_debugger_process(kmdb_dbgnotify_cb, &res); if (mdb_nv_size(&res.res_loads)) { mdb_printf("Loaded modules: ["); print_modules(&res.res_loads); mdb_printf(" ]\n"); } if (mdb_nv_size(&res.res_unloads)) { mdb_printf("Unloaded modules: ["); print_modules(&res.res_unloads); mdb_printf(" ]\n"); } mdb_nv_destroy(&res.res_loads); mdb_nv_destroy(&res.res_unloads); }
/* * Called on an individual CPU. Tries to send it off to the state saver if it * hasn't already entered the debugger. Returns non-zero if it *fails* to stop * the CPU. */ static int kdi_halt_cpu(int cpuid, void *state_saverp) { void (*state_saver)(void) = (void (*)(void))state_saverp; int state = kmdb_dpi_get_cpu_state(cpuid); const char *msg; int rc = 0; int res; if (state != DPI_CPU_STATE_MASTER && state != DPI_CPU_STATE_SLAVE) { res = kdi_xc_one(cpuid, state_saver); rc = 1; if (res == KDI_XC_RES_OK) msg = "accepted the"; else { if (res == KDI_XC_RES_BUSY) msg = "too busy for"; else if (res == KDI_XC_RES_NACK) msg = "NACKED the"; else msg = "errored the"; } mdb_dprintf(MDB_DBG_KDI, "CPU %d %s halt\n", cpuid, msg); } return (rc); }
int kmdb_dpi_reenter(void) { int cmd; kmdb_kdi_system_claim(); if ((cmd = setjmp(kmdb_dpi_entry_pcb)) == 0) { /* Direct entry from the driver */ if (kmdb_dpi_resume_requested) longjmp(kmdb_dpi_resume_pcb, 1); kmdb_first_start(); fail("kmdb_first_start returned"); /*NOTREACHED*/ } mdb_dprintf(MDB_DBG_DPI, "returning to driver - cmd %d%s\n", cmd, (kmdb_dpi_work_required() ? " (work required)" : "")); kmdb_kdi_system_release(); membar_producer(); /* * The debugger wants us to do something - it returned a command * via the setjmp(). The driver will know what to do with the * command. */ return (cmd); }
void kmdb_dpi_wapt_disarm(kmdb_wapt_t *wp) { mdb.m_dpi->dpo_wapt_disarm(wp); mdb_dprintf(MDB_DBG_DPI, "wapt disarmed at %p (type %d, priv %p)\n", (void *)wp->wp_addr, wp->wp_type, wp->wp_priv); }
void mdb_vcb_destroy(mdb_vcb_t *vcb) { mdb_dprintf(MDB_DBG_DSTK, "delete vcb %p (%s)\n", (void *)vcb, mdb_nv_get_name(vcb->vc_var)); mdb_addrvec_destroy(&vcb->vc_addrv); mdb_free(vcb, sizeof (mdb_vcb_t)); }
static int kp_iter_mapping(const rd_loadobj_t *rlp, mdb_tgt_t *t) { kp_data_t *kp = t->t_data; kp_file_t *kpf; kp_map_t *kpm; char name[MDB_TGT_MAPSZ]; if (mdb_tgt_readstr(t, MDB_TGT_AS_VIRT, name, sizeof (name), (mdb_tgt_addr_t)rlp->rl_nameaddr) <= 0) { mdb_dprintf(MDB_DBG_TGT, "failed to read name %p", (void *)rlp->rl_nameaddr); return (1); /* Keep going; forget this if we can't read name */ } mdb_dprintf(MDB_DBG_TGT, "rd_loadobj name = \"%s\" rl_base = %p\n", name, (void *)rlp->rl_base); if ((kpm = kp_addr_to_kpmap(kp, rlp->rl_base)) == NULL) return (1); /* Keep going; no mapping at this address */ (void) strncpy(kpm->kpm_map.map_name, name, MDB_TGT_MAPSZ); kpm->kpm_map.map_name[MDB_TGT_MAPSZ - 1] = '\0'; if ((kpf = kpm->kpm_file) == NULL) { if (kpm == kp->kp_map_exec) kpf = kp_file_create(t, kpm, ET_EXEC); else kpf = kp_file_create(t, kpm, ET_DYN); if (kpf == NULL) return (1); /* Keep going; failed to build ELF file */ } else kpf->kpf_basename = strbasename(kpm->kpm_map.map_name); if ((kpm = kp_find_data(kp, kpf, rlp)) != NULL) { mdb_dprintf(MDB_DBG_TGT, "found data for %s at %p\n", kpf->kpf_basename, (void *)kpm->kpm_map.map_base); kpm->kpm_file = kpf; } return (1); }
void kmdb_dpi_set_state(int state, int why) { if (kmdb_dpi_state != DPI_STATE_LOST) { mdb_dprintf(MDB_DBG_DPI, "dpi_set_state %d why %d\n", state, why); kmdb_dpi_state = state; kmdb_dpi_state_why = why; } }
int kmdb_dpi_wapt_reserve(kmdb_wapt_t *wp) { if (mdb.m_dpi->dpo_wapt_reserve(wp) < 0) return (-1); /* errno is set for us */ mdb_dprintf(MDB_DBG_DPI, "wapt reserve type %d at %p, priv %p\n", wp->wp_type, (void *)wp->wp_addr, wp->wp_priv); return (0); }
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--; }
static kp_file_t * kp_file_create(mdb_tgt_t *t, kp_map_t *kpm, GElf_Half etype) { kp_file_t *kpf = mdb_zalloc(sizeof (kp_file_t), UM_SLEEP); kp_data_t *kp = t->t_data; size_t dyns_sz; void *dyns; kpf->kpf_fio = kp_io_create(t, kpm); kpf->kpf_map = kpm; kpf->kpf_basename = strbasename(kpm->kpm_map.map_name); kpf->kpf_file = mdb_gelf_create(kpf->kpf_fio, etype, GF_PROGRAM); kpf->kpf_text_base = kpm->kpm_map.map_base; if (kpm != kp->kp_map_exec) kpf->kpf_dyn_base = kpf->kpf_text_base; if (kpf->kpf_file == NULL) goto err; /* Failed to create ELF file */ mdb_dprintf(MDB_DBG_TGT, "loading symbols for %s\n", kpm->kpm_map.map_name); if ((kp->kp_rap != NULL) && (rd_get_dyns(kp->kp_rap, kpf->kpf_text_base, &dyns, &dyns_sz) == RD_OK)) mdb_gelf_dyns_set(kpf->kpf_file, dyns, dyns_sz); kpf->kpf_dynsym = mdb_gelf_symtab_create_dynamic(kpf->kpf_file, MDB_TGT_DYNSYM); if (kpf->kpf_dynsym == NULL) goto err; /* Failed to create symbol table */ kpm->kpm_file = kpf; if (kp->kp_file_tail != NULL) kp->kp_file_tail->kpf_next = kpf; else kp->kp_file_head = kpf; kp->kp_file_tail = kpf; kp->kp_num_files++; return (kpf); err: if (kpf->kpf_file != NULL) mdb_gelf_destroy(kpf->kpf_file); else mdb_io_destroy(kpf->kpf_fio); mdb_free(kpf, sizeof (kp_file_t)); return (NULL); }
int kmdb_dpi_brkpt_arm(uintptr_t addr, mdb_instr_t *instrp) { int rc; if ((rc = mdb.m_dpi->dpo_brkpt_arm(addr, instrp)) < 0) mdb_warn("failed to arm breakpoint at %a", addr); mdb_dprintf(MDB_DBG_DPI, "brkpt armed at %p %A\n", (void *)addr, addr); return (rc); }
/* * 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); }
void mdb_vcb_insert(mdb_vcb_t *vcb, mdb_frame_t *fp) { if (fp->f_pcmd != NULL) { mdb_cmd_t *cp = fp->f_pcmd; mdb_dprintf(MDB_DBG_DSTK, "insert vcb %p (%s)\n", (void *)vcb, mdb_nv_get_name(vcb->vc_var)); ASSERT(vcb->vc_link == NULL); vcb->vc_link = cp->c_vcbs; cp->c_vcbs = vcb; } }
void mdb_recycle(mdb_mblk_t **blkpp) { mdb_mblk_t *blkp, *nblkp; for (blkp = *blkpp; blkp != NULL; blkp = nblkp) { mdb_dprintf(MDB_DBG_UMEM, "garbage collect %p size %lu bytes\n", blkp->blk_addr, (ulong_t)blkp->blk_size); nblkp = blkp->blk_next; mdb_free(blkp->blk_addr, blkp->blk_size); mdb_free(blkp, sizeof (mdb_mblk_t)); } *blkpp = NULL; }
/*ARGSUSED*/ int kmdb_prom_translate_virt(uintptr_t virt, physaddr_t *pap) { extern int prom_translate_virt(caddr_t, int *, u_longlong_t *, int *); int valid, mode; uintptr_t vabase = virt & ~(mdb.m_pagesize - 1); uintptr_t off = virt - vabase; u_longlong_t pa; mdb_dprintf(MDB_DBG_DPI, "using OBP for vtop of %p\n", (void *)virt); if (prom_translate_virt((caddr_t)vabase, &valid, &pa, &mode) != 0) return (set_errno(EMDB_NOMAP)); *pap = pa + off; return (0); }
static int kmdb_dbgnotify_cb(kmdb_wr_t *wn, void *arg) { work_results_t *res = arg; switch (WR_TASK(wn)) { case WNTASK_DMOD_LOAD: { /* * If this is an ack, the driver finished processing a load we * requested. We process it and free the message. If this * isn't an ack, then it's a driver-initiated load. We process * the message, and send it back as an ack so the driver can * free it. */ kmdb_wr_load_t *dlr = (kmdb_wr_load_t *)wn; mdb_dprintf(MDB_DBG_DPI, "received module load message\n"); if (kmdb_module_loaded(dlr) && res != NULL) { (void) mdb_nv_insert(&res->res_loads, strbasename(dlr->dlr_fname), NULL, 0, 0); } if (WR_ISACK(dlr)) { kmdb_module_load_ack(dlr); return (0); } /* Send it back as an ack */ mdb_dprintf(MDB_DBG_DPI, "Sending load request for %s back " "as an ack\n", dlr->dlr_fname); WR_ACK(wn); kmdb_wr_driver_notify(wn); return (0); } case WNTASK_DMOD_LOAD_ALL: /* * We initiated the load-all, so this must be an ack. The * individual module load messages will arrive separately - * there's no need to do anything further with this message. */ ASSERT(WR_ISACK(wn)); mdb_dprintf(MDB_DBG_DPI, "received module load all ack\n"); kmdb_module_load_all_ack(wn); return (0); case WNTASK_DMOD_UNLOAD: { /* * The debugger received an unload message. The driver isn't * supposed to initiate unloads, so we shouldn't see anything * but acks. We tell the dmod subsystem that the module has * been unloaded, and we free the message. */ kmdb_wr_unload_t *dur = (kmdb_wr_unload_t *)wn; ASSERT(WR_ISACK(dur)); mdb_dprintf(MDB_DBG_DPI, "received module unload ack\n"); if (kmdb_module_unloaded(dur) && res != NULL) { (void) mdb_nv_insert(&res->res_unloads, dur->dur_modname, NULL, 0, 0); } /* Done with message */ kmdb_module_unload_ack(dur); return (0); } case WNTASK_DMOD_PATH_CHANGE: { /* * The debugger received a path change message. The driver * can't initiate these, so it must be an acknowledgement. * There's no processing to be done, so just free the message. */ kmdb_wr_path_t *dpth = (kmdb_wr_path_t *)wn; ASSERT(WR_ISACK(dpth)); mdb_dprintf(MDB_DBG_DPI, "received path change ack\n"); kmdb_module_path_ack(dpth); return (0); } default: mdb_warn("Received unknown message type %d from driver\n", wn->wn_task); /* Ignore it */ return (0); } }
/* * 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; mdb_frame_set_pipe(fp); 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_frame_clear_pipe(fp); 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); }
static int pio_ctl(mdb_io_t *io, int req, void *arg) { pio_data_t *pdp = io->io_data; if (io->io_next != NULL) return (IOP_CTL(io->io_next, req, arg)); switch (req) { case TIOCGWINSZ: return (kmdb_prom_term_ctl(TIOCGWINSZ, arg)); case TCGETS: { struct termios *ti = arg; if (!(pdp->pio_flags & PIO_FL_TIO_READ)) { (void) kmdb_prom_term_ctl(TCGETS, &pdp->pio_ti); pdp->pio_flags |= PIO_FL_TIO_READ; } bcopy(&pdp->pio_ti, ti, sizeof (struct termios)); mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: gets: i: 0%o o: 0%o c: " "0%o l: 0%o\n", ti->c_iflag, ti->c_oflag, ti->c_cflag, ti->c_lflag); return (0); } case TCSETSW: { struct termios *ti = arg; mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: setsw: i: 0%o o: 0%o c: " "0%o l: 0%o\n", ti->c_iflag, ti->c_oflag, ti->c_cflag, ti->c_lflag); bcopy(ti, &pdp->pio_ti, sizeof (struct termios)); return (0); } case TIOCSPGRP: pio_pgrp = *(pid_t *)arg; mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: spgrp: %ld\n", (long)pio_pgrp); return (0); case TIOCGPGRP: mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: gpgrp: %ld\n", (long)pio_pgrp); *(pid_t *)arg = pio_pgrp; return (0); case MDB_IOC_CTTY: mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: ignoring MDB_IOC_CTTY\n"); return (0); case MDB_IOC_GETFD: return (set_errno(ENOTSUP)); default: warn("Unknown ioctl %d\n", req); return (set_errno(EINVAL)); } }
/* * 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); }
static void kt_load_modules(kt_data_t *kt, mdb_tgt_t *t) { char name[MAXNAMELEN]; uintptr_t addr, head; struct module kmod; struct modctl ctl; Shdr symhdr, strhdr; GElf_Sym sym; kt_module_t *km; if (mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, "modules", &sym, NULL) == -1) { warn("failed to get 'modules' symbol"); return; } if (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &ctl, sizeof (ctl), MDB_TGT_OBJ_EXEC, "modules") != sizeof (ctl)) { warn("failed to read 'modules' struct"); return; } addr = head = (uintptr_t)sym.st_value; do { if (addr == NULL) break; /* Avoid spurious NULL pointers in list */ if (mdb_tgt_vread(t, &ctl, sizeof (ctl), addr) == -1) { warn("failed to read modctl at %p", (void *)addr); return; } if (ctl.mod_mp == NULL) continue; /* No associated krtld structure */ if (mdb_tgt_readstr(t, MDB_TGT_AS_VIRT, name, MAXNAMELEN, (uintptr_t)ctl.mod_modname) <= 0) { warn("failed to read module name at %p", (void *)ctl.mod_modname); continue; } mdb_dprintf(MDB_DBG_KMOD, "reading mod %s (%p)\n", name, (void *)addr); if (mdb_nv_lookup(&kt->k_modules, name) != NULL) { warn("skipping duplicate module '%s', id=%d\n", name, ctl.mod_id); continue; } if (mdb_tgt_vread(t, &kmod, sizeof (kmod), (uintptr_t)ctl.mod_mp) == -1) { warn("failed to read module at %p\n", (void *)ctl.mod_mp); continue; } if (kmod.symspace == NULL || kmod.symhdr == NULL || kmod.strhdr == NULL) { /* * If no buffer for the symbols has been allocated, * or the shdrs for .symtab and .strtab are missing, * then we're out of luck. */ continue; } if (mdb_tgt_vread(t, &symhdr, sizeof (Shdr), (uintptr_t)kmod.symhdr) == -1) { warn("failed to read .symtab header for '%s', id=%d", name, ctl.mod_id); continue; } if (mdb_tgt_vread(t, &strhdr, sizeof (Shdr), (uintptr_t)kmod.strhdr) == -1) { warn("failed to read .strtab header for '%s', id=%d", name, ctl.mod_id); continue; } /* * Now get clever: f(*^ing krtld didn't used to bother updating * its own kmod.symsize value. We know that prior to this bug * being fixed, symspace was a contiguous buffer containing * .symtab, .strtab, and the symbol hash table in that order. * So if symsize is zero, recompute it as the size of .symtab * plus the size of .strtab. We don't need to load the hash * table anyway since we re-hash all the symbols internally. */ if (kmod.symsize == 0) kmod.symsize = symhdr.sh_size + strhdr.sh_size; /* * Similar logic can be used to make educated guesses * at the values of kmod.symtbl and kmod.strings. */ if (kmod.symtbl == NULL) kmod.symtbl = kmod.symspace; if (kmod.strings == NULL) kmod.strings = kmod.symspace + symhdr.sh_size; /* * Make sure things seem reasonable before we proceed * to actually read and decipher the symspace. */ if (KT_BAD_BUF(kmod.symtbl, kmod.symspace, kmod.symsize) || KT_BAD_BUF(kmod.strings, kmod.symspace, kmod.symsize)) { warn("skipping module '%s', id=%d (corrupt symspace)\n", name, ctl.mod_id); continue; } km = mdb_zalloc(sizeof (kt_module_t), UM_SLEEP); km->km_name = strdup(name); (void) mdb_nv_insert(&kt->k_modules, km->km_name, NULL, (uintptr_t)km, MDB_NV_EXTNAME); km->km_datasz = kmod.symsize; km->km_symspace_va = (uintptr_t)kmod.symspace; km->km_symtab_va = (uintptr_t)kmod.symtbl; km->km_strtab_va = (uintptr_t)kmod.strings; km->km_symtab_hdr = symhdr; km->km_strtab_hdr = strhdr; km->km_text_va = (uintptr_t)kmod.text; km->km_text_size = kmod.text_size; km->km_data_va = (uintptr_t)kmod.data; km->km_data_size = kmod.data_size; km->km_bss_va = (uintptr_t)kmod.bss; km->km_bss_size = kmod.bss_size; if (kt->k_ctfvalid) { km->km_ctf_va = (uintptr_t)kmod.ctfdata; km->km_ctf_size = kmod.ctfsize; } /* * Add the module to the end of the list of modules in load- * dependency order. This is needed to load the corresponding * debugger modules in the same order for layering purposes. */ mdb_list_append(&kt->k_modlist, km); if (t->t_flags & MDB_TGT_F_PRELOAD) { mdb_iob_printf(mdb.m_out, " %s", name); mdb_iob_flush(mdb.m_out); kt_load_module(kt, t, km); } } while ((addr = (uintptr_t)ctl.mod_next) != head); }
/*ARGSUSED*/ int mdb_module_unload(const char *name, int mode) { kmdb_modctl_t *kmc = NULL; const char *basename; mdb_var_t *v; /* * We may have been called with the name from the module itself * if the caller is iterating through the module list, so we need * to make a copy of the name. If we don't, we can't use it after * the call to unload_common(), which frees the module. */ name = strdup(name); basename = strbasename(name); /* * Make sure the module is in the proper state for unloading. Modules * may only be unloaded if they have properly completed loading. */ if ((v = mdb_nv_lookup(&mdb.m_dmodctl, basename)) != NULL) { kmc = MDB_NV_COOKIE(v); switch (kmc->kmc_state) { case KMDB_MC_STATE_LOADING: warn("%s is in the process of loading\n", basename); return (set_errno(EMDB_NOMOD)); case KMDB_MC_STATE_UNLOADING: warn("%s is already being unloaded\n", basename); return (set_errno(EMDB_NOMOD)); default: ASSERT(kmc->kmc_state == KMDB_MC_STATE_LOADED); } if (kmc->kmc_flags & KMDB_MC_FL_NOUNLOAD) return (set_errno(EMDB_KMODNOUNLOAD)); } if (mdb_module_unload_common(name) < 0) { if (!(mode & MDB_MOD_SILENT)) { mdb_dprintf(MDB_DBG_MODULE, "unload of %s failed\n", name); } return (-1); /* errno is set for us */ } /* * Any modules legitimately not listed in dmodctl (builtins, for * example) will be handled by mdb_module_unload_common. If any of * them get here, we've got a problem. */ if (v == NULL) { warn("unload of unregistered module %s\n", basename); return (set_errno(EMDB_NOMOD)); } ASSERT(kmc->kmc_dlrefcnt == 0); mdb_gelf_symtab_destroy(kmc->kmc_symtab); kmdb_module_request_unload(kmc, basename, mode); return (0); }