/*
* 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
* 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) 2013, Joyent, Inc. All rights reserved.
*/
/*
* Libkvm Kernel Target
*
* The libkvm kernel target provides access to both crash dumps and live
* 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
* .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/kobj_impl.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_debug.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb_module.h>
typedef struct kt_symarg {
} kt_symarg_t;
typedef struct kt_maparg {
} kt_maparg_t;
static void
{
}
static void
{
warn("failed to get 'modules' symbol");
return;
}
warn("failed to read 'modules' struct");
return;
}
do {
break; /* Avoid spurious NULL pointers in list */
return;
}
continue; /* No associated krtld structure */
warn("failed to read module name at %p",
(void *)ctl.mod_modname);
continue;
}
warn("skipping duplicate module '%s', id=%d\n",
continue;
}
warn("failed to read module at %p\n",
continue;
}
/*
* 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;
}
warn("failed to read .symtab header for '%s', id=%d",
continue;
}
warn("failed to read .strtab header for '%s', id=%d",
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.
*/
/*
* Similar logic can be used to make educated guesses
*/
/*
* Make sure things seem reasonable before we proceed
* to actually read and decipher the symspace.
*/
warn("skipping module '%s', id=%d (corrupt symspace)\n",
continue;
}
if (kt->k_ctfvalid) {
}
/*
* 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.
*/
if (t->t_flags & MDB_TGT_F_PRELOAD) {
}
}
int
{
const char *kvmfile;
void *cookie;
int mode;
return (0);
if (kt->k_xpv_domu) {
warn("read-only target");
return (-1);
}
if (iochg) {
} else {
}
/* 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.
*/
}
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
{
return (KT_DUMPCONTENT_ALL);
return (KT_DUMPCONTENT_INVALID);
else
return (pid);
} else {
return (KT_DUMPCONTENT_KERNEL);
}
}
static int
{
switch (kt->k_dumpcontent) {
case KT_DUMPCONTENT_KERNEL:
return (0);
case KT_DUMPCONTENT_ALL:
return (1);
case KT_DUMPCONTENT_INVALID:
goto procnotfound;
default:
goto procnotfound;
if (reqpid == -1)
goto procnotfound;
}
return (1);
}
int
{
int argc = 0;
!kt_dump_contains_proc(t, context)) {
warn("dump does not contain pages for proc %p\n",
context);
return (-1);
}
return (-1);
} else
mdb_printf("debugger context set to kernel\n");
return (0);
}
static int
{
}
static int
{
}
static int
{
}
static int
{
return (DCMD_USAGE);
}
#ifdef __x86
static int
{
}
static int
{
}
#endif /* __x86 */
/*ARGSUSED*/
static int
{
if (mdb_prop_postmortem) {
mdb_printf("debugging %scrash dump %s (%d-bit) from %s\n",
} else {
mdb_printf("debugging live kernel (%d-bit) on %s\n",
}
mdb_printf("operating system: %s %s (%s)\n",
} else {
}
}
return (DCMD_OK);
}
#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
{ NULL }
};
static uintmax_t
{
mdb_tgt_t *t = MDB_NV_COOKIE(v);
mdb_tgt_reg_t r = 0;
return (r);
}
static kt_module_t *
{
return (km);
}
return (NULL);
}
void
{
void *sym;
int oflag;
/*
* 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
*/
mdb_warn("warning: dump is from %s %s %s; dcmds and "
"macros may not match kernel implementation\n",
}
warn("failed to load kernel support module -- "
"some modules may not load\n");
}
}
if (t->t_flags & MDB_TGT_F_PRELOAD) {
}
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.
*/
}
}
if (t->t_flags & MDB_TGT_F_PRELOAD) {
}
}
/* Export some of our registers as named variables */
}
void
{
mdb_var_t *v;
continue; /* Didn't export register as a variable */
v->v_flags &= ~MDB_NV_PERSIST;
}
}
}
}
/*ARGSUSED*/
const char *
{
return ("kvm");
}
const char *
{
return (kt->k_platform);
}
int
{
}
/*ARGSUSED*/
int
{
return (MDB_TGT_MODEL_NATIVE);
}
{
return (set_errno(EMDB_NOMAP));
return (rval);
}
{
return (set_errno(EMDB_NOMAP));
return (rval);
}
{
nbytes)) == -1)
return (set_errno(EMDB_NOMAP));
return (rval);
}
{
nbytes)) == -1)
return (set_errno(EMDB_NOMAP));
return (rval);
}
{
}
{
}
{
nbytes)) == -1)
return (set_errno(EMDB_NOMAP));
return (rval);
}
{
nbytes)) == -1)
return (set_errno(EMDB_NOMAP));
return (rval);
}
int
{
mdb_var_t *v;
case (uintptr_t)MDB_TGT_AS_PHYS:
case (uintptr_t)MDB_TGT_AS_FILE:
case (uintptr_t)MDB_TGT_AS_IO:
case (uintptr_t)MDB_TGT_AS_VIRT:
break;
default:
}
return (0);
}
return (0);
}
return (set_errno(EMDB_NOMAP));
}
int
{
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.
*/
case (uintptr_t)MDB_TGT_OBJ_EXEC:
n = 1;
break;
case (uintptr_t)MDB_TGT_OBJ_EVERY:
break;
case (uintptr_t)MDB_TGT_OBJ_RTLD:
/*FALLTHRU*/
default:
return (set_errno(EMDB_NOOBJ));
km = mdb_nv_get_cookie(v);
n = 1;
}
return (0);
}
}
return (set_errno(EMDB_NOSYM));
}
int
{
const char *name;
/*
* 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.
*/
}
}
/*
* 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.
*/
continue;
if (flags & MDB_TGT_SYM_EXACT) {
goto found;
}
}
}
return (set_errno(EMDB_NOSYMADDR));
/*
* Once we've found something, copy the final name into the caller's
* buffer and prefix it with the load object name if appropriate.
*/
} else if (nbytes > 0) {
}
else
} else {
}
return (0);
}
static int
{
}
return (0);
}
static void
mdb_tgt_sym_f *cb, void *p)
{
}
int
{
mdb_var_t *v;
case (uintptr_t)MDB_TGT_OBJ_EXEC:
if (which == MDB_TGT_SYMTAB)
else
break;
case (uintptr_t)MDB_TGT_OBJ_EVERY:
if (which == MDB_TGT_DYNSYM) {
break;
}
km = mdb_nv_get_cookie(v);
}
break;
case (uintptr_t)MDB_TGT_OBJ_RTLD:
/*FALLTHRU*/
default:
if (v == NULL)
return (set_errno(EMDB_NOOBJ));
km = mdb_nv_get_cookie(v);
}
if (symtab)
return (0);
}
static int
{
/*
* 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 {
"%lr", addr);
}
}
int
{
kt_maparg_t m;
m.map_target = t;
}
static const mdb_map_t *
{
return (map);
}
int
{
mdb_map_t m;
break;
}
return (0);
}
const mdb_map_t *
{
}
(void) set_errno(EMDB_NOMAP);
return (NULL);
}
const mdb_map_t *
{
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)
if (name == MDB_TGT_OBJ_RTLD)
return (kt_module_to_map(km, &m));
(void) set_errno(EMDB_NOOBJ);
return (NULL);
}
static ctf_file_t *
{
int err;
(void) set_errno(EMDB_NOCTF);
return (NULL);
}
warn("failed to allocate memory to load %s debugging "
return (NULL);
}
warn("failed to read %lu bytes of debug data for %s at %p",
return (NULL);
}
return (NULL);
}
mdb_var_t *v;
warn("failed to load CTF data for %s - parent %s not "
}
if (v != NULL) {
(void) kt_load_ctfdata(t, pm);
warn("failed to import parent types into "
}
}
}
}
{
return (kt_load_ctfdata(t, km));
}
(void) set_errno(EMDB_NOMAP);
return (NULL);
}
{
if (name == MDB_TGT_OBJ_EXEC)
name = KT_CTFPARENT;
else if (name == MDB_TGT_OBJ_RTLD)
return (kt_load_ctfdata(t, km));
(void) set_errno(EMDB_NOOBJ);
return (NULL);
}
/*ARGSUSED*/
int
{
return (0);
}
static ssize_t
{
return (sizeof (dumphdr_t));
return (nbytes);
}
void
{
(void) mdb_module_unload(KT_MODULE, 0);
}
}
static int
kt_data_stub(void)
{
return (-1);
}
int
{
if (argc == 2) {
goto err;
kt->k_xpv_domu = 0;
} else {
#ifndef __x86
#else
mdb_kb_ops_t *(*getops)(void);
/*
* Load mdb_kb if it's not already loaded during
* identification.
*/
(void) mdb_module_load("mdb_kb",
getops = (mdb_kb_ops_t *(*)())
}
warn("failed to load KVM backend ops\n");
goto err;
}
goto err;
#endif
}
goto err;
goto err;
}
warn("'kas' symbol is missing from kernel\n");
goto err;
}
warn("'platform' symbol is missing from kernel\n");
goto err;
}
warn("failed to read 'platform' string from kernel");
goto err;
}
warn("'utsname' symbol is missing from kernel\n");
goto err;
}
sizeof (uts)) <= 0) {
warn("failed to read 'utsname' struct from kernel");
goto err;
}
/*
* 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 defined(__sparc)
#if defined(__sparcv9)
kt_sparcv9_init(t);
#else
kt_sparcv7_init(t);
#endif
kt_amd64_init(t);
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.
*/
MDB_TGT_OBJ_EXEC, "panic_thread");
/*
* 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.
*/
goto err;
}
sizeof (dumphdr_t)) {
mdb_warn("failed to read dump header");
goto err;
}
(void) mdb_tgt_xdata_insert(t, "dumphdr",
"dump header structure", kt_xd_dumphdr);
}
return (0);
err:
return (-1);
}
int
{
dumphdr_t h;
h.dump_magic == DUMP_MAGIC);
}
int
{
dumphdr_t h;
h.dump_magic == DUMP_MAGIC &&
(h.dump_flags & DF_COMPRESSED) != 0);
}