| /* |
| * 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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. |
| */ |
| |
| /* |
| * Copyright (c) 2013, Joyent, Inc. All rights reserved. |
| */ |
| |
| /* |
| * Libkvm Kernel Target |
| * |
| * The libkvm kernel target provides access to both crash dumps and live |
| * kernels through /dev/ksyms and /dev/kmem, using the facilities provided by |
| * the libkvm.so library. The target-specific data structures are shared |
| * between this file (common code) and the ISA-dependent parts of the target, |
| * and so they are defined in the mdb_kvm.h header. The target processes an |
| * "executable" (/dev/ksyms or the unix.X file) which contains a primary |
| * .symtab and .dynsym, and then also iterates over the krtld module chain in |
| * the kernel in order to obtain a list of loaded modules and per-module symbol |
| * tables. To improve startup performance, the per-module symbol tables are |
| * instantiated on-the-fly whenever an address lookup falls within the text |
| * section of a given module. The target also relies on services from the |
| * mdb_ks (kernel support) module, which contains pieces of the implementation |
| * that must be compiled against the kernel implementation. |
| */ |
| |
| #include <sys/modctl.h> |
| #include <sys/kobj.h> |
| #include <sys/kobj_impl.h> |
| #include <sys/utsname.h> |
| #include <sys/panic.h> |
| #include <sys/dumphdr.h> |
| #include <sys/dumpadm.h> |
| |
| #include <dlfcn.h> |
| #include <libctf.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| |
| #include <mdb/mdb_target_impl.h> |
| #include <mdb/mdb_err.h> |
| #include <mdb/mdb_debug.h> |
| #include <mdb/mdb_string.h> |
| #include <mdb/mdb_modapi.h> |
| #include <mdb/mdb_io_impl.h> |
| #include <mdb/mdb_ctf.h> |
| #include <mdb/mdb_kvm.h> |
| #include <mdb/mdb_module.h> |
| #include <mdb/mdb_kb.h> |
| #include <mdb/mdb.h> |
| |
| #define KT_RELOC_BUF(buf, obase, nbase) \ |
| ((uintptr_t)(buf) - (uintptr_t)(obase) + (uintptr_t)(nbase)) |
| |
| #define KT_BAD_BUF(buf, base, size) \ |
| ((uintptr_t)(buf) < (uintptr_t)(base) || \ |
| ((uintptr_t)(buf) >= (uintptr_t)(base) + (uintptr_t)(size))) |
| |
| typedef struct kt_symarg { |
| mdb_tgt_sym_f *sym_cb; /* Caller's callback function */ |
| void *sym_data; /* Callback function argument */ |
| uint_t sym_type; /* Symbol type/binding filter */ |
| mdb_syminfo_t sym_info; /* Symbol id and table id */ |
| const char *sym_obj; /* Containing object */ |
| } kt_symarg_t; |
| |
| typedef struct kt_maparg { |
| mdb_tgt_t *map_target; /* Target used for mapping iter */ |
| mdb_tgt_map_f *map_cb; /* Caller's callback function */ |
| void *map_data; /* Callback function argument */ |
| } kt_maparg_t; |
| |
| static const char KT_MODULE[] = "mdb_ks"; |
| static const char KT_CTFPARENT[] = "genunix"; |
| |
| static void |
| kt_load_module(kt_data_t *kt, mdb_tgt_t *t, kt_module_t *km) |
| { |
| km->km_data = mdb_alloc(km->km_datasz, UM_SLEEP); |
| |
| (void) mdb_tgt_vread(t, km->km_data, km->km_datasz, km->km_symspace_va); |
| |
| km->km_symbuf = (void *) |
| KT_RELOC_BUF(km->km_symtab_va, km->km_symspace_va, km->km_data); |
| |
| km->km_strtab = (char *) |
| KT_RELOC_BUF(km->km_strtab_va, km->km_symspace_va, km->km_data); |
| |
| km->km_symtab = mdb_gelf_symtab_create_raw(&kt->k_file->gf_ehdr, |
| &km->km_symtab_hdr, km->km_symbuf, |
| &km->km_strtab_hdr, km->km_strtab, MDB_TGT_SYMTAB); |
| } |
| |
| 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); |
| } |
| |
| int |
| kt_setflags(mdb_tgt_t *t, int flags) |
| { |
| int iochg = ((flags ^ t->t_flags) & MDB_TGT_F_ALLOWIO) && |
| !mdb_prop_postmortem; |
| int rwchg = (flags ^ t->t_flags) & MDB_TGT_F_RDWR; |
| kt_data_t *kt = t->t_data; |
| const char *kvmfile; |
| void *cookie; |
| int mode; |
| |
| if (!iochg && !rwchg) |
| return (0); |
| |
| if (kt->k_xpv_domu) { |
| warn("read-only target"); |
| return (-1); |
| } |
| |
| if (iochg) { |
| kvmfile = (flags & MDB_TGT_F_ALLOWIO) ? "/dev/allkmem" : |
| "/dev/kmem"; |
| } else { |
| kvmfile = kt->k_kvmfile; |
| } |
| |
| mode = (flags & MDB_TGT_F_RDWR) ? O_RDWR : O_RDONLY; |
| |
| if ((cookie = kt->k_kb_ops->kb_open(kt->k_symfile, kvmfile, NULL, mode, |
| mdb.m_pname)) == NULL) { |
| /* We failed to re-open, so don't change t_flags */ |
| warn("failed to re-open target"); |
| return (-1); |
| } |
| |
| /* |
| * We successfully reopened the target, so update k_kvmfile. Also set |
| * the RDWR and ALLOWIO bits in t_flags to match those in flags. |
| */ |
| (void) kt->k_kb_ops->kb_close(kt->k_cookie); |
| kt->k_cookie = cookie; |
| |
| if (kvmfile != kt->k_kvmfile) { |
| strfree(kt->k_kvmfile); |
| kt->k_kvmfile = strdup(kvmfile); |
| } |
| |
| t->t_flags = (t->t_flags & ~(MDB_TGT_F_RDWR | MDB_TGT_F_ALLOWIO)) | |
| (flags & (MDB_TGT_F_RDWR | MDB_TGT_F_ALLOWIO)); |
| |
| return (0); |
| } |
| |
| /* |
| * Determine which PIDs (if any) have their pages saved in the dump. We |
| * do this by looking for content flags in dump_flags in the header. These |
| * flags, which won't be set in older dumps, tell us whether a single process |
| * has had its pages included in the dump. If a single process has been |
| * included, we need to get the PID for that process from the dump_pids |
| * array in the dump. |
| */ |
| static int |
| kt_find_dump_contents(kt_data_t *kt) |
| { |
| dumphdr_t *dh = kt->k_dumphdr; |
| pid_t pid = -1; |
| |
| if (dh->dump_flags & DF_ALL) |
| return (KT_DUMPCONTENT_ALL); |
| |
| if (dh->dump_flags & DF_CURPROC) { |
| if ((pid = kt->k_dump_find_curproc()) == -1) |
| return (KT_DUMPCONTENT_INVALID); |
| else |
| return (pid); |
| } else { |
| return (KT_DUMPCONTENT_KERNEL); |
| } |
| } |
| |
| static int |
| kt_dump_contains_proc(mdb_tgt_t *t, void *context) |
| { |
| kt_data_t *kt = t->t_data; |
| pid_t (*f_pid)(uintptr_t); |
| pid_t reqpid; |
| |
| switch (kt->k_dumpcontent) { |
| case KT_DUMPCONTENT_KERNEL: |
| return (0); |
| case KT_DUMPCONTENT_ALL: |
| return (1); |
| case KT_DUMPCONTENT_INVALID: |
| goto procnotfound; |
| default: |
| f_pid = (pid_t (*)()) dlsym(RTLD_NEXT, "mdb_kproc_pid"); |
| if (f_pid == NULL) |
| goto procnotfound; |
| |
| reqpid = f_pid((uintptr_t)context); |
| if (reqpid == -1) |
| goto procnotfound; |
| |
| return (kt->k_dumpcontent == reqpid); |
| } |
| |
| procnotfound: |
| warn("unable to determine whether dump contains proc %p\n", context); |
| return (1); |
| } |
| |
| int |
| kt_setcontext(mdb_tgt_t *t, void *context) |
| { |
| if (context != NULL) { |
| const char *argv[2]; |
| int argc = 0; |
| mdb_tgt_t *ct; |
| kt_data_t *kt = t->t_data; |
| |
| argv[argc++] = (const char *)context; |
| argv[argc] = NULL; |
| |
| if (kt->k_dumphdr != NULL && |
| !kt_dump_contains_proc(t, context)) { |
| warn("dump does not contain pages for proc %p\n", |
| context); |
| return (-1); |
| } |
| |
| if ((ct = mdb_tgt_create(mdb_kproc_tgt_create, |
| t->t_flags, argc, argv)) == NULL) |
| return (-1); |
| |
| mdb_printf("debugger context set to proc %p\n", context); |
| mdb_tgt_activate(ct); |
| } else |
| mdb_printf("debugger context set to kernel\n"); |
| |
| return (0); |
| } |
| |
| static int |
| kt_stack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| return (kt->k_dcmd_stack(addr, flags, argc, argv)); |
| } |
| |
| static int |
| kt_stackv(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| return (kt->k_dcmd_stackv(addr, flags, argc, argv)); |
| } |
| |
| static int |
| kt_stackr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| return (kt->k_dcmd_stackr(addr, flags, argc, argv)); |
| } |
| |
| static int |
| kt_regs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| |
| if (argc != 0 || (flags & DCMD_ADDRSPEC)) |
| return (DCMD_USAGE); |
| |
| addr = (uintptr_t)kt->k_regs; |
| |
| return (kt->k_dcmd_regs(addr, flags, argc, argv)); |
| } |
| |
| #ifdef __x86 |
| static int |
| kt_cpustack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| return (kt->k_dcmd_cpustack(addr, flags, argc, argv)); |
| } |
| |
| static int |
| kt_cpuregs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| return (kt->k_dcmd_cpuregs(addr, flags, argc, argv)); |
| } |
| #endif /* __x86 */ |
| |
| /*ARGSUSED*/ |
| static int |
| kt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| kt_data_t *kt = mdb.m_target->t_data; |
| struct utsname uts; |
| |
| bzero(&uts, sizeof (uts)); |
| (void) strcpy(uts.nodename, "unknown machine"); |
| (void) kt_uname(mdb.m_target, &uts); |
| |
| if (mdb_prop_postmortem) { |
| mdb_printf("debugging %scrash dump %s (%d-bit) from %s\n", |
| kt->k_xpv_domu ? "domain " : "", kt->k_kvmfile, |
| (int)(sizeof (void *) * NBBY), uts.nodename); |
| } else { |
| mdb_printf("debugging live kernel (%d-bit) on %s\n", |
| (int)(sizeof (void *) * NBBY), uts.nodename); |
| } |
| |
| mdb_printf("operating system: %s %s (%s)\n", |
| uts.release, uts.version, uts.machine); |
| |
| if (kt->k_dumphdr) { |
| dumphdr_t *dh = kt->k_dumphdr; |
| |
| mdb_printf("image uuid: %s\n", dh->dump_uuid[0] != '\0' ? |
| dh->dump_uuid : "(not set)"); |
| mdb_printf("panic message: %s\n", dh->dump_panicstring); |
| |
| kt->k_dump_print_content(dh, kt->k_dumpcontent); |
| } else { |
| char uuid[37]; |
| |
| if (mdb_readsym(uuid, 37, "dump_osimage_uuid") == 37 && |
| uuid[36] == '\0') { |
| mdb_printf("image uuid: %s\n", uuid); |
| } |
| } |
| |
| return (DCMD_OK); |
| } |
| |
| static const mdb_dcmd_t kt_dcmds[] = { |
| { "$c", "?[cnt]", "print stack backtrace", kt_stack }, |
| { "$C", "?[cnt]", "print stack backtrace", kt_stackv }, |
| { "$r", NULL, "print general-purpose registers", kt_regs }, |
| { "$?", NULL, "print status and registers", kt_regs }, |
| { "regs", NULL, "print general-purpose registers", kt_regs }, |
| { "stack", "?[cnt]", "print stack backtrace", kt_stack }, |
| { "stackregs", "?", "print stack backtrace and registers", kt_stackr }, |
| #ifdef __x86 |
| { "cpustack", "?[-v] [-c cpuid] [cnt]", "print stack backtrace for a " |
| "specific CPU", kt_cpustack }, |
| { "cpuregs", "?[-c cpuid]", "print general-purpose registers for a " |
| "specific CPU", kt_cpuregs }, |
| #endif |
| { "status", NULL, "print summary of current target", kt_status_dcmd }, |
| { NULL } |
| }; |
| |
| static uintmax_t |
| reg_disc_get(const mdb_var_t *v) |
| { |
| mdb_tgt_t *t = MDB_NV_COOKIE(v); |
| kt_data_t *kt = t->t_data; |
| mdb_tgt_reg_t r = 0; |
| |
| (void) mdb_tgt_getareg(t, kt->k_tid, mdb_nv_get_name(v), &r); |
| return (r); |
| } |
| |
| static kt_module_t * |
| kt_module_by_name(kt_data_t *kt, const char *name) |
| { |
| kt_module_t *km; |
| |
| for (km = mdb_list_next(&kt->k_modlist); km; km = mdb_list_next(km)) { |
| if (strcmp(name, km->km_name) == 0) |
| return (km); |
| } |
| |
| return (NULL); |
| } |
| |
| void |
| kt_activate(mdb_tgt_t *t) |
| { |
| static const mdb_nv_disc_t reg_disc = { NULL, reg_disc_get }; |
| kt_data_t *kt = t->t_data; |
| void *sym; |
| |
| int oflag; |
| |
| mdb_prop_postmortem = kt->k_xpv_domu || (kt->k_dumphdr != NULL); |
| mdb_prop_kernel = TRUE; |
| mdb_prop_datamodel = MDB_TGT_MODEL_NATIVE; |
| |
| if (kt->k_activated == FALSE) { |
| struct utsname u1, u2; |
| /* |
| * If we're examining a crash dump, root is /, and uname(2) |
| * does not match the utsname in the dump, issue a warning. |
| * Note that we are assuming that the modules and macros in |
| * /usr/lib are compiled against the kernel from uname -rv. |
| */ |
| if (mdb_prop_postmortem && strcmp(mdb.m_root, "/") == 0 && |
| uname(&u1) >= 0 && kt_uname(t, &u2) >= 0 && |
| (strcmp(u1.release, u2.release) || |
| strcmp(u1.version, u2.version))) { |
| mdb_warn("warning: dump is from %s %s %s; dcmds and " |
| "macros may not match kernel implementation\n", |
| u2.sysname, u2.release, u2.version); |
| } |
| |
| if (mdb_module_load(KT_MODULE, MDB_MOD_GLOBAL) < 0) { |
| warn("failed to load kernel support module -- " |
| "some modules may not load\n"); |
| } |
| |
| if (mdb_prop_postmortem && kt->k_dumphdr != NULL) { |
| sym = dlsym(RTLD_NEXT, "mdb_dump_print_content"); |
| if (sym != NULL) |
| kt->k_dump_print_content = (void (*)())sym; |
| |
| sym = dlsym(RTLD_NEXT, "mdb_dump_find_curproc"); |
| if (sym != NULL) |
| kt->k_dump_find_curproc = (int (*)())sym; |
| |
| kt->k_dumpcontent = kt_find_dump_contents(kt); |
| } |
| |
| if (t->t_flags & MDB_TGT_F_PRELOAD) { |
| oflag = mdb_iob_getflags(mdb.m_out) & MDB_IOB_PGENABLE; |
| |
| mdb_iob_clrflags(mdb.m_out, oflag); |
| mdb_iob_puts(mdb.m_out, "Preloading module symbols: ["); |
| mdb_iob_flush(mdb.m_out); |
| } |
| |
| if (!(t->t_flags & MDB_TGT_F_NOLOAD)) { |
| kt_load_modules(kt, t); |
| |
| /* |
| * Determine where the CTF data for krtld is. If krtld |
| * is rolled into unix, force load the MDB krtld |
| * module. |
| */ |
| kt->k_rtld_name = "krtld"; |
| |
| if (kt_module_by_name(kt, "krtld") == NULL) { |
| (void) mdb_module_load("krtld", MDB_MOD_SILENT); |
| kt->k_rtld_name = "unix"; |
| } |
| } |
| |
| |
| if (t->t_flags & MDB_TGT_F_PRELOAD) { |
| mdb_iob_puts(mdb.m_out, " ]\n"); |
| mdb_iob_setflags(mdb.m_out, oflag); |
| } |
| |
| kt->k_activated = TRUE; |
| } |
| |
| (void) mdb_tgt_register_dcmds(t, &kt_dcmds[0], MDB_MOD_FORCE); |
| |
| /* Export some of our registers as named variables */ |
| mdb_tgt_register_regvars(t, kt->k_rds, ®_disc, MDB_NV_RDONLY); |
| |
| mdb_tgt_elf_export(kt->k_file); |
| } |
| |
| void |
| kt_deactivate(mdb_tgt_t *t) |
| { |
| kt_data_t *kt = t->t_data; |
| |
| const mdb_tgt_regdesc_t *rdp; |
| const mdb_dcmd_t *dcp; |
| |
| for (rdp = kt->k_rds; rdp->rd_name != NULL; rdp++) { |
| mdb_var_t *v; |
| |
| if (!(rdp->rd_flags & MDB_TGT_R_EXPORT)) |
| continue; /* Didn't export register as a variable */ |
| |
| if ((v = mdb_nv_lookup(&mdb.m_nv, rdp->rd_name)) != NULL) { |
| v->v_flags &= ~MDB_NV_PERSIST; |
| mdb_nv_remove(&mdb.m_nv, v); |
| } |
| } |
| |
| for (dcp = &kt_dcmds[0]; dcp->dc_name != NULL; dcp++) { |
| if (mdb_module_remove_dcmd(t->t_module, dcp->dc_name) == -1) |
| warn("failed to remove dcmd %s", dcp->dc_name); |
| } |
| |
| mdb_prop_postmortem = FALSE; |
| mdb_prop_kernel = FALSE; |
| mdb_prop_datamodel = MDB_TGT_MODEL_UNKNOWN; |
| } |
| |
| /*ARGSUSED*/ |
| const char * |
| kt_name(mdb_tgt_t *t) |
| { |
| return ("kvm"); |
| } |
| |
| const char * |
| kt_platform(mdb_tgt_t *t) |
| { |
| kt_data_t *kt = t->t_data; |
| return (kt->k_platform); |
| } |
| |
| int |
| kt_uname(mdb_tgt_t *t, struct utsname *utsp) |
| { |
| return (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, utsp, |
| sizeof (struct utsname), MDB_TGT_OBJ_EXEC, "utsname")); |
| } |
| |
| /*ARGSUSED*/ |
| int |
| kt_dmodel(mdb_tgt_t *t) |
| { |
| return (MDB_TGT_MODEL_NATIVE); |
| } |
| |
| ssize_t |
| kt_aread(mdb_tgt_t *t, mdb_tgt_as_t as, void *buf, |
| size_t nbytes, mdb_tgt_addr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| ssize_t rval; |
| |
| if ((rval = kt->k_kb_ops->kb_aread(kt->k_cookie, addr, buf, |
| nbytes, as)) == -1) |
| return (set_errno(EMDB_NOMAP)); |
| |
| return (rval); |
| } |
| |
| ssize_t |
| kt_awrite(mdb_tgt_t *t, mdb_tgt_as_t as, const void *buf, |
| size_t nbytes, mdb_tgt_addr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| ssize_t rval; |
| |
| if ((rval = kt->k_kb_ops->kb_awrite(kt->k_cookie, addr, buf, |
| nbytes, as)) == -1) |
| return (set_errno(EMDB_NOMAP)); |
| |
| return (rval); |
| } |
| |
| ssize_t |
| kt_vread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| ssize_t rval; |
| |
| if ((rval = kt->k_kb_ops->kb_kread(kt->k_cookie, addr, buf, |
| nbytes)) == -1) |
| return (set_errno(EMDB_NOMAP)); |
| |
| return (rval); |
| } |
| |
| ssize_t |
| kt_vwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| ssize_t rval; |
| |
| if ((rval = kt->k_kb_ops->kb_kwrite(kt->k_cookie, addr, buf, |
| nbytes)) == -1) |
| return (set_errno(EMDB_NOMAP)); |
| |
| return (rval); |
| } |
| |
| ssize_t |
| kt_fread(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr) |
| { |
| return (kt_vread(t, buf, nbytes, addr)); |
| } |
| |
| ssize_t |
| kt_fwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, uintptr_t addr) |
| { |
| return (kt_vwrite(t, buf, nbytes, addr)); |
| } |
| |
| ssize_t |
| kt_pread(mdb_tgt_t *t, void *buf, size_t nbytes, physaddr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| ssize_t rval; |
| |
| if ((rval = kt->k_kb_ops->kb_pread(kt->k_cookie, addr, buf, |
| nbytes)) == -1) |
| return (set_errno(EMDB_NOMAP)); |
| |
| return (rval); |
| } |
| |
| ssize_t |
| kt_pwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, physaddr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| ssize_t rval; |
| |
| if ((rval = kt->k_kb_ops->kb_pwrite(kt->k_cookie, addr, buf, |
| nbytes)) == -1) |
| return (set_errno(EMDB_NOMAP)); |
| |
| return (rval); |
| } |
| |
| int |
| kt_vtop(mdb_tgt_t *t, mdb_tgt_as_t as, uintptr_t va, physaddr_t *pap) |
| { |
| kt_data_t *kt = t->t_data; |
| |
| struct as *asp; |
| physaddr_t pa; |
| mdb_module_t *mod; |
| mdb_var_t *v; |
| int (*fptr)(uintptr_t, struct as *, physaddr_t *); |
| |
| switch ((uintptr_t)as) { |
| case (uintptr_t)MDB_TGT_AS_PHYS: |
| case (uintptr_t)MDB_TGT_AS_FILE: |
| case (uintptr_t)MDB_TGT_AS_IO: |
| return (set_errno(EINVAL)); |
| case (uintptr_t)MDB_TGT_AS_VIRT: |
| asp = kt->k_as; |
| break; |
| default: |
| asp = (struct as *)as; |
| } |
| |
| if ((pa = kt->k_kb_ops->kb_vtop(kt->k_cookie, asp, va)) != -1ULL) { |
| *pap = pa; |
| return (0); |
| } |
| |
| if ((v = mdb_nv_lookup(&mdb.m_modules, "unix")) != NULL && |
| (mod = mdb_nv_get_cookie(v)) != NULL) { |
| |
| fptr = (int (*)(uintptr_t, struct as *, physaddr_t *)) |
| dlsym(mod->mod_hdl, "platform_vtop"); |
| |
| if ((fptr != NULL) && ((*fptr)(va, asp, pap) == 0)) |
| return (0); |
| } |
| |
| return (set_errno(EMDB_NOMAP)); |
| } |
| |
| int |
| kt_lookup_by_name(mdb_tgt_t *t, const char *obj, const char *name, |
| GElf_Sym *symp, mdb_syminfo_t *sip) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km, kmod; |
| mdb_var_t *v; |
| int n; |
| |
| /* |
| * To simplify the implementation, we create a fake module on the stack |
| * which is "prepended" to k_modlist and whose symtab is kt->k_symtab. |
| */ |
| kmod.km_symtab = kt->k_symtab; |
| kmod.km_list.ml_next = mdb_list_next(&kt->k_modlist); |
| |
| switch ((uintptr_t)obj) { |
| case (uintptr_t)MDB_TGT_OBJ_EXEC: |
| km = &kmod; |
| n = 1; |
| break; |
| |
| case (uintptr_t)MDB_TGT_OBJ_EVERY: |
| km = &kmod; |
| n = mdb_nv_size(&kt->k_modules) + 1; |
| break; |
| |
| case (uintptr_t)MDB_TGT_OBJ_RTLD: |
| obj = kt->k_rtld_name; |
| /*FALLTHRU*/ |
| |
| default: |
| if ((v = mdb_nv_lookup(&kt->k_modules, obj)) == NULL) |
| return (set_errno(EMDB_NOOBJ)); |
| |
| km = mdb_nv_get_cookie(v); |
| n = 1; |
| |
| if (km->km_symtab == NULL) |
| kt_load_module(kt, t, km); |
| } |
| |
| for (; n > 0; n--, km = mdb_list_next(km)) { |
| if (mdb_gelf_symtab_lookup_by_name(km->km_symtab, name, |
| symp, &sip->sym_id) == 0) { |
| sip->sym_table = MDB_TGT_SYMTAB; |
| return (0); |
| } |
| } |
| |
| return (set_errno(EMDB_NOSYM)); |
| } |
| |
| int |
| kt_lookup_by_addr(mdb_tgt_t *t, uintptr_t addr, uint_t flags, |
| char *buf, size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t kmods[3], *kmods_begin = &kmods[0], *kmods_end; |
| const char *name; |
| |
| kt_module_t *km = &kmods[0]; /* Point km at first fake module */ |
| kt_module_t *sym_km = NULL; /* Module associated with best sym */ |
| GElf_Sym sym; /* Best symbol found so far if !exact */ |
| uint_t symid; /* ID of best symbol found so far */ |
| |
| /* |
| * To simplify the implementation, we create fake modules on the stack |
| * that are "prepended" to k_modlist and whose symtab is set to |
| * each of three special symbol tables, in order of precedence. |
| */ |
| km->km_symtab = mdb.m_prsym; |
| |
| if (kt->k_symtab != NULL) { |
| km->km_list.ml_next = (mdb_list_t *)(km + 1); |
| km = mdb_list_next(km); |
| km->km_symtab = kt->k_symtab; |
| } |
| |
| if (kt->k_dynsym != NULL) { |
| km->km_list.ml_next = (mdb_list_t *)(km + 1); |
| km = mdb_list_next(km); |
| km->km_symtab = kt->k_dynsym; |
| } |
| |
| km->km_list.ml_next = mdb_list_next(&kt->k_modlist); |
| kmods_end = km; |
| |
| /* |
| * Now iterate over the list of fake and real modules. If the module |
| * has no symbol table and the address is in the text section, |
| * instantiate the module's symbol table. In exact mode, we can |
| * jump to 'found' immediately if we match. Otherwise we continue |
| * looking and improve our choice if we find a closer symbol. |
| */ |
| for (km = &kmods[0]; km != NULL; km = mdb_list_next(km)) { |
| if (km->km_symtab == NULL && addr >= km->km_text_va && |
| addr < km->km_text_va + km->km_text_size) |
| kt_load_module(kt, t, km); |
| |
| if (mdb_gelf_symtab_lookup_by_addr(km->km_symtab, addr, |
| flags, buf, nbytes, symp, &sip->sym_id) != 0 || |
| symp->st_value == 0) |
| continue; |
| |
| if (flags & MDB_TGT_SYM_EXACT) { |
| sym_km = km; |
| goto found; |
| } |
| |
| if (sym_km == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) { |
| sym_km = km; |
| sym = *symp; |
| symid = sip->sym_id; |
| } |
| } |
| |
| if (sym_km == NULL) |
| return (set_errno(EMDB_NOSYMADDR)); |
| |
| *symp = sym; /* Copy our best symbol into the caller's symbol */ |
| sip->sym_id = symid; |
| found: |
| /* |
| * Once we've found something, copy the final name into the caller's |
| * buffer and prefix it with the load object name if appropriate. |
| */ |
| if (sym_km != NULL) { |
| name = mdb_gelf_sym_name(sym_km->km_symtab, symp); |
| |
| if (sym_km < kmods_begin || sym_km > kmods_end) { |
| (void) mdb_snprintf(buf, nbytes, "%s`%s", |
| sym_km->km_name, name); |
| } else if (nbytes > 0) { |
| (void) strncpy(buf, name, nbytes); |
| buf[nbytes - 1] = '\0'; |
| } |
| |
| if (sym_km->km_symtab == mdb.m_prsym) |
| sip->sym_table = MDB_TGT_PRVSYM; |
| else |
| sip->sym_table = MDB_TGT_SYMTAB; |
| } else { |
| sip->sym_table = MDB_TGT_SYMTAB; |
| } |
| |
| return (0); |
| } |
| |
| static int |
| kt_symtab_func(void *data, const GElf_Sym *sym, const char *name, uint_t id) |
| { |
| kt_symarg_t *argp = data; |
| |
| if (mdb_tgt_sym_match(sym, argp->sym_type)) { |
| argp->sym_info.sym_id = id; |
| |
| return (argp->sym_cb(argp->sym_data, sym, name, |
| &argp->sym_info, argp->sym_obj)); |
| } |
| |
| return (0); |
| } |
| |
| static void |
| kt_symtab_iter(mdb_gelf_symtab_t *gst, uint_t type, const char *obj, |
| mdb_tgt_sym_f *cb, void *p) |
| { |
| kt_symarg_t arg; |
| |
| arg.sym_cb = cb; |
| arg.sym_data = p; |
| arg.sym_type = type; |
| arg.sym_info.sym_table = gst->gst_tabid; |
| arg.sym_obj = obj; |
| |
| mdb_gelf_symtab_iter(gst, kt_symtab_func, &arg); |
| } |
| |
| int |
| kt_symbol_iter(mdb_tgt_t *t, const char *obj, uint_t which, uint_t type, |
| mdb_tgt_sym_f *cb, void *data) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km; |
| |
| mdb_gelf_symtab_t *symtab = NULL; |
| mdb_var_t *v; |
| |
| switch ((uintptr_t)obj) { |
| case (uintptr_t)MDB_TGT_OBJ_EXEC: |
| if (which == MDB_TGT_SYMTAB) |
| symtab = kt->k_symtab; |
| else |
| symtab = kt->k_dynsym; |
| break; |
| |
| case (uintptr_t)MDB_TGT_OBJ_EVERY: |
| if (which == MDB_TGT_DYNSYM) { |
| symtab = kt->k_dynsym; |
| obj = MDB_TGT_OBJ_EXEC; |
| break; |
| } |
| |
| mdb_nv_rewind(&kt->k_modules); |
| while ((v = mdb_nv_advance(&kt->k_modules)) != NULL) { |
| km = mdb_nv_get_cookie(v); |
| |
| if (km->km_symtab == NULL) |
| kt_load_module(kt, t, km); |
| |
| if (km->km_symtab != NULL) |
| kt_symtab_iter(km->km_symtab, type, |
| km->km_name, cb, data); |
| } |
| break; |
| |
| case (uintptr_t)MDB_TGT_OBJ_RTLD: |
| obj = kt->k_rtld_name; |
| /*FALLTHRU*/ |
| |
| default: |
| v = mdb_nv_lookup(&kt->k_modules, obj); |
| |
| if (v == NULL) |
| return (set_errno(EMDB_NOOBJ)); |
| |
| km = mdb_nv_get_cookie(v); |
| |
| if (km->km_symtab == NULL) |
| kt_load_module(kt, t, km); |
| |
| symtab = km->km_symtab; |
| } |
| |
| if (symtab) |
| kt_symtab_iter(symtab, type, obj, cb, data); |
| |
| return (0); |
| } |
| |
| static int |
| kt_mapping_walk(uintptr_t addr, const void *data, kt_maparg_t *marg) |
| { |
| /* |
| * This is a bit sketchy but avoids problematic compilation of this |
| * target against the current VM implementation. Now that we have |
| * vmem, we can make this less broken and more informative by changing |
| * this code to invoke the vmem walker in the near future. |
| */ |
| const struct kt_seg { |
| caddr_t s_base; |
| size_t s_size; |
| } *segp = (const struct kt_seg *)data; |
| |
| mdb_map_t map; |
| GElf_Sym sym; |
| mdb_syminfo_t info; |
| |
| map.map_base = (uintptr_t)segp->s_base; |
| map.map_size = segp->s_size; |
| map.map_flags = MDB_TGT_MAP_R | MDB_TGT_MAP_W | MDB_TGT_MAP_X; |
| |
| if (kt_lookup_by_addr(marg->map_target, addr, MDB_TGT_SYM_EXACT, |
| map.map_name, MDB_TGT_MAPSZ, &sym, &info) == -1) { |
| |
| (void) mdb_iob_snprintf(map.map_name, MDB_TGT_MAPSZ, |
| "%lr", addr); |
| } |
| |
| return (marg->map_cb(marg->map_data, &map, map.map_name)); |
| } |
| |
| int |
| kt_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_maparg_t m; |
| |
| m.map_target = t; |
| m.map_cb = func; |
| m.map_data = private; |
| |
| return (mdb_pwalk("seg", (mdb_walk_cb_t)kt_mapping_walk, &m, |
| (uintptr_t)kt->k_as)); |
| } |
| |
| static const mdb_map_t * |
| kt_module_to_map(kt_module_t *km, mdb_map_t *map) |
| { |
| (void) strncpy(map->map_name, km->km_name, MDB_TGT_MAPSZ); |
| map->map_name[MDB_TGT_MAPSZ - 1] = '\0'; |
| map->map_base = km->km_text_va; |
| map->map_size = km->km_text_size; |
| map->map_flags = MDB_TGT_MAP_R | MDB_TGT_MAP_W | MDB_TGT_MAP_X; |
| |
| return (map); |
| } |
| |
| int |
| kt_object_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km; |
| mdb_map_t m; |
| |
| for (km = mdb_list_next(&kt->k_modlist); km; km = mdb_list_next(km)) { |
| if (func(private, kt_module_to_map(km, &m), km->km_name) == -1) |
| break; |
| } |
| |
| return (0); |
| } |
| |
| const mdb_map_t * |
| kt_addr_to_map(mdb_tgt_t *t, uintptr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km; |
| |
| for (km = mdb_list_next(&kt->k_modlist); km; km = mdb_list_next(km)) { |
| if (addr - km->km_text_va < km->km_text_size || |
| addr - km->km_data_va < km->km_data_size || |
| addr - km->km_bss_va < km->km_bss_size) |
| return (kt_module_to_map(km, &kt->k_map)); |
| } |
| |
| (void) set_errno(EMDB_NOMAP); |
| return (NULL); |
| } |
| |
| const mdb_map_t * |
| kt_name_to_map(mdb_tgt_t *t, const char *name) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km; |
| mdb_map_t m; |
| |
| /* |
| * If name is MDB_TGT_OBJ_EXEC, return the first module on the list, |
| * which will be unix since we keep k_modlist in load order. |
| */ |
| if (name == MDB_TGT_OBJ_EXEC) |
| return (kt_module_to_map(mdb_list_next(&kt->k_modlist), &m)); |
| |
| if (name == MDB_TGT_OBJ_RTLD) |
| name = kt->k_rtld_name; |
| |
| if ((km = kt_module_by_name(kt, name)) != NULL) |
| return (kt_module_to_map(km, &m)); |
| |
| (void) set_errno(EMDB_NOOBJ); |
| return (NULL); |
| } |
| |
| static ctf_file_t * |
| kt_load_ctfdata(mdb_tgt_t *t, kt_module_t *km) |
| { |
| kt_data_t *kt = t->t_data; |
| int err; |
| |
| if (km->km_ctfp != NULL) |
| return (km->km_ctfp); |
| |
| if (km->km_ctf_va == NULL) { |
| (void) set_errno(EMDB_NOCTF); |
| return (NULL); |
| } |
| |
| if (km->km_symtab == NULL) |
| kt_load_module(t->t_data, t, km); |
| |
| if ((km->km_ctf_buf = mdb_alloc(km->km_ctf_size, UM_NOSLEEP)) == NULL) { |
| warn("failed to allocate memory to load %s debugging " |
| "information", km->km_name); |
| return (NULL); |
| } |
| |
| if (mdb_tgt_vread(t, km->km_ctf_buf, km->km_ctf_size, |
| km->km_ctf_va) != km->km_ctf_size) { |
| warn("failed to read %lu bytes of debug data for %s at %p", |
| (ulong_t)km->km_ctf_size, km->km_name, |
| (void *)km->km_ctf_va); |
| mdb_free(km->km_ctf_buf, km->km_ctf_size); |
| km->km_ctf_buf = NULL; |
| return (NULL); |
| } |
| |
| if ((km->km_ctfp = mdb_ctf_bufopen((const void *)km->km_ctf_buf, |
| km->km_ctf_size, km->km_symbuf, &km->km_symtab_hdr, |
| km->km_strtab, &km->km_strtab_hdr, &err)) == NULL) { |
| mdb_free(km->km_ctf_buf, km->km_ctf_size); |
| km->km_ctf_buf = NULL; |
| (void) set_errno(ctf_to_errno(err)); |
| return (NULL); |
| } |
| |
| mdb_dprintf(MDB_DBG_KMOD, "loaded %lu bytes of CTF data for %s\n", |
| (ulong_t)km->km_ctf_size, km->km_name); |
| |
| if (ctf_parent_name(km->km_ctfp) != NULL) { |
| mdb_var_t *v; |
| |
| if ((v = mdb_nv_lookup(&kt->k_modules, |
| ctf_parent_name(km->km_ctfp))) == NULL) { |
| warn("failed to load CTF data for %s - parent %s not " |
| "loaded\n", km->km_name, |
| ctf_parent_name(km->km_ctfp)); |
| } |
| |
| if (v != NULL) { |
| kt_module_t *pm = mdb_nv_get_cookie(v); |
| |
| if (pm->km_ctfp == NULL) |
| (void) kt_load_ctfdata(t, pm); |
| |
| if (pm->km_ctfp != NULL && ctf_import(km->km_ctfp, |
| pm->km_ctfp) == CTF_ERR) { |
| warn("failed to import parent types into " |
| "%s: %s\n", km->km_name, |
| ctf_errmsg(ctf_errno(km->km_ctfp))); |
| } |
| } |
| } |
| |
| return (km->km_ctfp); |
| } |
| |
| ctf_file_t * |
| kt_addr_to_ctf(mdb_tgt_t *t, uintptr_t addr) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km; |
| |
| for (km = mdb_list_next(&kt->k_modlist); km; km = mdb_list_next(km)) { |
| if (addr - km->km_text_va < km->km_text_size || |
| addr - km->km_data_va < km->km_data_size || |
| addr - km->km_bss_va < km->km_bss_size) |
| return (kt_load_ctfdata(t, km)); |
| } |
| |
| (void) set_errno(EMDB_NOMAP); |
| return (NULL); |
| } |
| |
| ctf_file_t * |
| kt_name_to_ctf(mdb_tgt_t *t, const char *name) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km; |
| |
| if (name == MDB_TGT_OBJ_EXEC) |
| name = KT_CTFPARENT; |
| else if (name == MDB_TGT_OBJ_RTLD) |
| name = kt->k_rtld_name; |
| |
| if ((km = kt_module_by_name(kt, name)) != NULL) |
| return (kt_load_ctfdata(t, km)); |
| |
| (void) set_errno(EMDB_NOOBJ); |
| return (NULL); |
| } |
| |
| /*ARGSUSED*/ |
| int |
| kt_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp) |
| { |
| kt_data_t *kt = t->t_data; |
| bzero(tsp, sizeof (mdb_tgt_status_t)); |
| tsp->st_state = (kt->k_xpv_domu || (kt->k_dumphdr != NULL)) ? |
| MDB_TGT_DEAD : MDB_TGT_RUNNING; |
| return (0); |
| } |
| |
| static ssize_t |
| kt_xd_dumphdr(mdb_tgt_t *t, void *buf, size_t nbytes) |
| { |
| kt_data_t *kt = t->t_data; |
| |
| if (buf == NULL && nbytes == 0) |
| return (sizeof (dumphdr_t)); |
| |
| if (kt->k_dumphdr == NULL) |
| return (set_errno(ENODATA)); |
| |
| nbytes = MIN(nbytes, sizeof (dumphdr_t)); |
| bcopy(kt->k_dumphdr, buf, nbytes); |
| |
| return (nbytes); |
| } |
| |
| void |
| kt_destroy(mdb_tgt_t *t) |
| { |
| kt_data_t *kt = t->t_data; |
| kt_module_t *km, *nkm; |
| |
| (void) mdb_module_unload(KT_MODULE, 0); |
| |
| if (kt->k_regs != NULL) |
| mdb_free(kt->k_regs, kt->k_regsize); |
| |
| if (kt->k_symtab != NULL) |
| mdb_gelf_symtab_destroy(kt->k_symtab); |
| |
| if (kt->k_dynsym != NULL) |
| mdb_gelf_symtab_destroy(kt->k_dynsym); |
| |
| if (kt->k_dumphdr != NULL) |
| mdb_free(kt->k_dumphdr, sizeof (dumphdr_t)); |
| |
| mdb_gelf_destroy(kt->k_file); |
| |
| (void) kt->k_kb_ops->kb_close(kt->k_cookie); |
| |
| for (km = mdb_list_next(&kt->k_modlist); km; km = nkm) { |
| if (km->km_symtab) |
| mdb_gelf_symtab_destroy(km->km_symtab); |
| |
| if (km->km_data) |
| mdb_free(km->km_data, km->km_datasz); |
| |
| if (km->km_ctfp) |
| ctf_close(km->km_ctfp); |
| |
| if (km->km_ctf_buf != NULL) |
| mdb_free(km->km_ctf_buf, km->km_ctf_size); |
| |
| nkm = mdb_list_next(km); |
| strfree(km->km_name); |
| mdb_free(km, sizeof (kt_module_t)); |
| } |
| |
| mdb_nv_destroy(&kt->k_modules); |
| |
| strfree(kt->k_kvmfile); |
| if (kt->k_symfile != NULL) |
| strfree(kt->k_symfile); |
| |
| mdb_free(kt, sizeof (kt_data_t)); |
| } |
| |
| static int |
| kt_data_stub(void) |
| { |
| return (-1); |
| } |
| |
| int |
| mdb_kvm_tgt_create(mdb_tgt_t *t, int argc, const char *argv[]) |
| { |
| kt_data_t *kt = mdb_zalloc(sizeof (kt_data_t), UM_SLEEP); |
| mdb_kb_ops_t *kvm_kb_ops = libkvm_kb_ops(); |
| int oflag = (t->t_flags & MDB_TGT_F_RDWR) ? O_RDWR : O_RDONLY; |
| struct utsname uts; |
| GElf_Sym sym; |
| pgcnt_t pmem; |
| |
| |
| if (argc == 2) { |
| kt->k_symfile = strdup(argv[0]); |
| kt->k_kvmfile = strdup(argv[1]); |
| |
| kt->k_cookie = kvm_kb_ops->kb_open(kt->k_symfile, |
| kt->k_kvmfile, NULL, oflag, (char *)mdb.m_pname); |
| |
| if (kt->k_cookie == NULL) |
| goto err; |
| |
| kt->k_xpv_domu = 0; |
| kt->k_kb_ops = kvm_kb_ops; |
| } else { |
| #ifndef __x86 |
| return (set_errno(EINVAL)); |
| #else |
| mdb_kb_ops_t *(*getops)(void); |
| |
| kt->k_symfile = NULL; |
| kt->k_kvmfile = strdup(argv[0]); |
| |
| getops = (mdb_kb_ops_t *(*)())dlsym(RTLD_NEXT, "mdb_kb_ops"); |
| |
| /* |
| * Load mdb_kb if it's not already loaded during |
| * identification. |
| */ |
| if (getops == NULL) { |
| (void) mdb_module_load("mdb_kb", |
| MDB_MOD_GLOBAL | MDB_MOD_SILENT); |
| getops = (mdb_kb_ops_t *(*)()) |
| dlsym(RTLD_NEXT, "mdb_kb_ops"); |
| } |
| |
| if (getops == NULL || (kt->k_kb_ops = getops()) == NULL) { |
| warn("failed to load KVM backend ops\n"); |
| goto err; |
| } |
| |
| kt->k_cookie = kt->k_kb_ops->kb_open(NULL, kt->k_kvmfile, NULL, |
| oflag, (char *)mdb.m_pname); |
| |
| if (kt->k_cookie == NULL) |
| goto err; |
| |
| kt->k_xpv_domu = 1; |
| #endif |
| } |
| |
| if ((kt->k_fio = kt->k_kb_ops->kb_sym_io(kt->k_cookie, |
| kt->k_symfile)) == NULL) |
| goto err; |
| |
| if ((kt->k_file = mdb_gelf_create(kt->k_fio, |
| ET_EXEC, GF_FILE)) == NULL) { |
| mdb_io_destroy(kt->k_fio); |
| goto err; |
| } |
| |
| kt->k_symtab = |
| mdb_gelf_symtab_create_file(kt->k_file, SHT_SYMTAB, MDB_TGT_SYMTAB); |
| |
| kt->k_dynsym = |
| mdb_gelf_symtab_create_file(kt->k_file, SHT_DYNSYM, MDB_TGT_DYNSYM); |
| |
| if (mdb_gelf_symtab_lookup_by_name(kt->k_symtab, "kas", |
| &sym, NULL) == -1) { |
| warn("'kas' symbol is missing from kernel\n"); |
| goto err; |
| } |
| |
| kt->k_as = (struct as *)(uintptr_t)sym.st_value; |
| |
| if (mdb_gelf_symtab_lookup_by_name(kt->k_symtab, "platform", |
| &sym, NULL) == -1) { |
| warn("'platform' symbol is missing from kernel\n"); |
| goto err; |
| } |
| |
| if (kt->k_kb_ops->kb_kread(kt->k_cookie, sym.st_value, |
| kt->k_platform, MAXNAMELEN) <= 0) { |
| warn("failed to read 'platform' string from kernel"); |
| goto err; |
| } |
| |
| if (mdb_gelf_symtab_lookup_by_name(kt->k_symtab, "utsname", |
| &sym, NULL) == -1) { |
| warn("'utsname' symbol is missing from kernel\n"); |
| goto err; |
| } |
| |
| if (kt->k_kb_ops->kb_kread(kt->k_cookie, sym.st_value, &uts, |
| sizeof (uts)) <= 0) { |
| warn("failed to read 'utsname' struct from kernel"); |
| goto err; |
| } |
| |
| kt->k_dump_print_content = (void (*)())kt_data_stub; |
| kt->k_dump_find_curproc = kt_data_stub; |
| |
| /* |
| * We set k_ctfvalid based on the presence of the CTF vmem arena |
| * symbol. The CTF members were added to the end of struct module at |
| * the same time, so this allows us to know whether we can use them. |
| */ |
| if (mdb_gelf_symtab_lookup_by_name(kt->k_symtab, "ctf_arena", &sym, |
| NULL) == 0 && !(mdb.m_flags & MDB_FL_NOCTF)) |
| kt->k_ctfvalid = 1; |
| |
| (void) mdb_nv_create(&kt->k_modules, UM_SLEEP); |
| t->t_pshandle = kt->k_cookie; |
| t->t_data = kt; |
| |
| #if defined(__sparc) |
| #if defined(__sparcv9) |
| kt_sparcv9_init(t); |
| #else |
| kt_sparcv7_init(t); |
| #endif |
| #elif defined(__amd64) |
| kt_amd64_init(t); |
| #elif defined(__i386) |
| kt_ia32_init(t); |
| #else |
| #error "unknown ISA" |
| #endif |
| |
| /* |
| * We read our representative thread ID (address) from the kernel's |
| * global panic_thread. It will remain 0 if this is a live kernel. |
| */ |
| (void) mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &kt->k_tid, sizeof (void *), |
| MDB_TGT_OBJ_EXEC, "panic_thread"); |
| |
| if ((mdb.m_flags & MDB_FL_ADB) && mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, |
| &pmem, sizeof (pmem), MDB_TGT_OBJ_EXEC, "physmem") == sizeof (pmem)) |
| mdb_printf("physmem %lx\n", (ulong_t)pmem); |
| |
| /* |
| * If this is not a live kernel or a hypervisor dump, read the dump |
| * header. We don't have to sanity-check the header, as the open would |
| * not have succeeded otherwise. |
| */ |
| if (!kt->k_xpv_domu && strcmp(kt->k_symfile, "/dev/ksyms") != 0) { |
| mdb_io_t *vmcore; |
| |
| kt->k_dumphdr = mdb_alloc(sizeof (dumphdr_t), UM_SLEEP); |
| |
| if ((vmcore = mdb_fdio_create_path(NULL, kt->k_kvmfile, |
| O_RDONLY, 0)) == NULL) { |
| mdb_warn("failed to open %s", kt->k_kvmfile); |
| goto err; |
| } |
| |
| if (IOP_READ(vmcore, kt->k_dumphdr, sizeof (dumphdr_t)) != |
| sizeof (dumphdr_t)) { |
| mdb_warn("failed to read dump header"); |
| mdb_io_destroy(vmcore); |
| goto err; |
| } |
| |
| mdb_io_destroy(vmcore); |
| |
| (void) mdb_tgt_xdata_insert(t, "dumphdr", |
| "dump header structure", kt_xd_dumphdr); |
| } |
| |
| return (0); |
| |
| err: |
| if (kt->k_dumphdr != NULL) |
| mdb_free(kt->k_dumphdr, sizeof (dumphdr_t)); |
| |
| if (kt->k_symtab != NULL) |
| mdb_gelf_symtab_destroy(kt->k_symtab); |
| |
| if (kt->k_dynsym != NULL) |
| mdb_gelf_symtab_destroy(kt->k_dynsym); |
| |
| if (kt->k_file != NULL) |
| mdb_gelf_destroy(kt->k_file); |
| |
| if (kt->k_cookie != NULL) |
| (void) kt->k_kb_ops->kb_close(kt->k_cookie); |
| |
| mdb_free(kt, sizeof (kt_data_t)); |
| return (-1); |
| } |
| |
| int |
| mdb_kvm_is_dump(mdb_io_t *io) |
| { |
| dumphdr_t h; |
| |
| (void) IOP_SEEK(io, (off64_t)0L, SEEK_SET); |
| |
| return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) && |
| h.dump_magic == DUMP_MAGIC); |
| } |
| |
| int |
| mdb_kvm_is_compressed_dump(mdb_io_t *io) |
| { |
| dumphdr_t h; |
| |
| (void) IOP_SEEK(io, (off64_t)0L, SEEK_SET); |
| |
| return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) && |
| h.dump_magic == DUMP_MAGIC && |
| (h.dump_flags & DF_COMPRESSED) != 0); |
| } |