mdb_kproc.c revision 22872efb9462b28180d11ea401344608e641a5aa
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Kernel Process View Target
*
* The kproc target is activated when the user is debugging a kernel using the
* kvm target and executes a ::context dcmd to change the debugger view to one
* of the running processes. The kvm target's t_setcontext operation will
* create and activate a kproc target in response to this call. The kproc
* target itself is built upon the kvm target's libkvm cookie and the ability
* to read information from the kernel itself and the ability to read the
* address space of a particular user process with kvm_aread(). It also relies
* on a special set of functions provided by the kvm target's mdb_ks support
* module in order to bootstrap: specifically, given the initial proc pointer,
* mdb_ks provides functions to return the set of address space mappings, the
* address space pointer itself, the aux vector vector saved in the u-area,
* and the process data model. The kproc target maintains a list of address
* space mappings (kp_map_t) and load objects (kp_file_t), and for each load
* object will attempt to read the corresponding dynamic symbol table. In
* order to bootstrap, the target uses the AT_BASE and AT_ENTRY aux vector
* elements to locate the dynamic linker and executable mappings. With these
* mappings in place, we initialize a librtld_db agent on the target (see
* mdb_pservice.c for how this is done), and then process each load object
* found in the link-map chain. In order to simplify the construction of
* symbol tables for each load object, we would like make use of our existing
* library of GElf processing code. Since the MDB GElf code uses mdb_io
* objects to read in an ELF file, we simply define a new type of mdb_io object
* where each read operation is translated into a call to kproc's t_vread
* function to read from the range of the address space defined by the mapping
* as if it were a file.
*/
#include <strings.h>
#include <limits.h>
#include <rtld_db.h>
#include <procfs.h>
#include <dlfcn.h>
#include <kvm.h>
#include <mdb/mdb_target_impl.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_gelf.h>
#include <mdb/mdb_io_impl.h>
typedef struct kp_symarg {
void *sym_data; /* Callback function argument */
const char *sym_obj; /* Containing object */
} kp_symarg_t;
typedef struct kp_file {
const char *kpf_basename; /* Mapping basename */
} kp_file_t;
typedef struct kp_map {
} kp_map_t;
typedef struct kp_io {
} kp_io_t;
typedef struct kp_data {
int kp_nauxv; /* Length of kp_auxv */
const char *kp_platform; /* Platform string from kvm target */
int kp_num_files; /* Length of load object list */
int kp_num_maps; /* Length of mapping list */
} kp_data_t;
static kp_map_t *
{
return (kpm);
}
return (NULL);
}
static long
{
}
return (-1L);
}
static void
{
else
kp->kp_num_maps++;
}
static kp_file_t *
{
void *dyns;
goto err; /* Failed to create ELF file */
goto err; /* Failed to create symbol table */
else
kp->kp_num_files++;
return (kpf);
err:
else
return (NULL);
}
static void
{
}
static int
{
mdb_tgt_destroy(t);
}
return (0);
}
static kp_map_t *
{
/*
* Find the first loadable, writeable Phdr and compute kpf_data_base
* as the virtual address at which is was loaded.
*/
for (i = 0; i < n; i++, gpp++) {
break;
}
}
/*
* If we found a suitable Phdr and set kpf_data_base, return
* the mapping information for this address; otherwise fail.
*/
if (kpf->kpf_data_base != 0)
return (NULL);
}
static int
{
char name[MDB_TGT_MAPSZ];
(void *)rlp->rl_nameaddr);
return (1); /* Keep going; forget this if we can't read name */
}
return (1); /* Keep going; no mapping at this address */
else
return (1); /* Keep going; failed to build ELF file */
} else
}
return (1);
}
/*ARGSUSED*/
static int
{
mdb_printf("debugging PID %d (%d-bit) in kernel crash dump\n",
mdb_printf("executable file: %s\n",
}
return (DCMD_OK);
}
static const mdb_dcmd_t kp_dcmds[] = {
{ NULL }
};
static void
kp_activate(mdb_tgt_t *t)
{
else
/*
* Initialize our rtld_db agent and then iterate over the link map,
* instantiating kp_file objects as we go.
*/
kp_iter_mapping, t);
} else {
mdb_warn("unable to initialize rtld_db agent for proc %p\n",
}
else
}
static void
{
const mdb_dcmd_t *dcp;
}
}
static void
kp_destroy(mdb_tgt_t *t)
{
}
}
}
/*ARGSUSED*/
static const char *
{
return ("kproc");
}
static const char *
{
#ifdef __sparc
#else
#endif
}
static const char *
kp_platform(mdb_tgt_t *t)
{
}
static int
{
}
static int
{
case PR_MODEL_ILP32:
return (MDB_TGT_MODEL_ILP32);
case PR_MODEL_LP64:
return (MDB_TGT_MODEL_LP64);
}
return (MDB_TGT_MODEL_UNKNOWN);
}
static kp_map_t *
{
/*
* Handle special reserved names (except for MDB_TGT_OBJ_EVERY):
*/
if (name == MDB_TGT_OBJ_EXEC)
return (kp->kp_map_exec);
if (name == MDB_TGT_OBJ_RTLD)
return (kp->kp_map_ldso);
/*
* First pass: look for exact matches on the entire pathname
* associated with the mapping or its basename.
*/
}
}
/*
* Second pass: look for partial matches (initial basename match
*/
}
}
/*
* One last check: we allow "a.out" to always alias the executable,
* assuming this name was not in use for something else.
*/
return (kp->kp_map_exec);
return (NULL);
}
static ssize_t
{
if (n == -1)
return (set_errno(EMDB_NOMAP));
return (n);
}
static ssize_t
{
if (n == -1)
return (set_errno(EMDB_NOMAP));
return (n);
}
int
{
if (as != MDB_TGT_AS_VIRT)
return (0);
}
return (set_errno(EMDB_NOMAP));
}
static int
{
int n;
int rv = -1;
/*
* Simplify our task: if object is EVERY, then we need to search
* kp_num_files files beginning at kp_file_head; otherwise we are
* searching 1 file whose file pointer is obtained via object_to_map.
*/
if (object != MDB_TGT_OBJ_EVERY) {
return (set_errno(EMDB_NOOBJ));
n = 1;
} else {
n = kp->kp_num_files;
}
/*
* Iterate through the load object files and look for the symbol name
* in the .dynsym of each. If we encounter a match with SHN_UNDEF,
* keep looking in hopes of finding a better match. This means that
* a name such as "puts" will match the puts function in libc instead
* of matching the puts PLT entry in the a.out file.
*/
continue; /* No symbols for this file */
continue; /* Symbol name not found */
return (0);
}
if (rv != 0) {
rv = 0;
}
}
if (rv != 0)
return (set_errno(EMDB_NOSYM));
return (0);
}
static int
{
const char *name;
int n;
/*
* Check the user's private symbol table first; if a match is
* found there, we're done or we have a first guess.
*/
if (flags & MDB_TGT_SYM_EXACT)
goto found;
}
/*
* If no mapping contains the address and EXACT mode is set, we're done.
* Otherwise we need to search all the symbol tables in fuzzy mode.
* If we find a mapping, then we only need to search that symtab.
*/
if (flags & MDB_TGT_SYM_EXACT)
return (set_errno(EMDB_NOSYMADDR));
n = kp->kp_num_files;
} else {
n = 1;
}
/*
* Iterate through our list of load objects, scanning each one which
* has a symbol table. In fuzzy mode, we continue looking and
* improve our choice if we find a closer symbol.
*/
continue; /* No symbols for this file */
continue; /* No symbol for this address */
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
return (0);
}
static int
{
}
return (0);
}
static void
{
}
}
/*ARGSUSED*/
static int
{
case (uintptr_t)MDB_TGT_OBJ_EVERY:
private);
}
private);
}
return (0);
case (uintptr_t)MDB_TGT_OBJ_EXEC:
break;
case (uintptr_t)MDB_TGT_OBJ_RTLD:
break;
default:
break;
} else
return (set_errno(EMDB_NOOBJ));
}
return (0);
}
static int
{
break;
}
return (0);
}
static int
{
break;
}
return (0);
}
static const mdb_map_t *
{
(void) set_errno(EMDB_NOMAP);
return (NULL);
}
static const mdb_map_t *
{
(void) set_errno(EMDB_NOOBJ);
return (NULL);
}
/*ARGSUSED*/
static int
{
return (0);
}
static int
{
return (0);
}
static const mdb_tgt_ops_t kproc_ops = {
(int (*)()) mdb_tgt_notsup, /* t_setflags */
kp_setcontext, /* t_setcontext */
kp_activate, /* t_activate */
kp_deactivate, /* t_deactivate */
(void (*)()) mdb_tgt_nop, /* t_periodic */
kp_destroy, /* t_destroy */
kp_name, /* t_name */
kp_isa, /* t_isa */
kp_platform, /* t_platform */
kp_uname, /* t_uname */
kp_dmodel, /* t_dmodel */
kp_vread, /* t_vread */
kp_vwrite, /* t_vwrite */
kp_vtop, /* t_vtop */
kp_lookup_by_name, /* t_lookup_by_name */
kp_lookup_by_addr, /* t_lookup_by_addr */
kp_symbol_iter, /* t_symbol_iter */
kp_mapping_iter, /* t_mapping_iter */
kp_object_iter, /* t_object_iter */
kp_addr_to_map, /* t_addr_to_map */
kp_name_to_map, /* t_name_to_map */
kp_status, /* t_status */
(int (*)()) mdb_tgt_notsup, /* t_run */
(int (*)()) mdb_tgt_notsup, /* t_step */
(int (*)()) mdb_tgt_notsup, /* t_step_out */
(int (*)()) mdb_tgt_notsup, /* t_step_branch */
(int (*)()) mdb_tgt_notsup, /* t_next */
(int (*)()) mdb_tgt_notsup, /* t_cont */
(int (*)()) mdb_tgt_notsup, /* t_signal */
(int (*)()) mdb_tgt_null, /* t_add_sbrkpt */
(int (*)()) mdb_tgt_null, /* t_add_vbrkpt */
(int (*)()) mdb_tgt_null, /* t_add_pwapt */
(int (*)()) mdb_tgt_null, /* t_add_vwapt */
(int (*)()) mdb_tgt_null, /* t_add_iowapt */
(int (*)()) mdb_tgt_null, /* t_add_sysenter */
(int (*)()) mdb_tgt_null, /* t_add_sysexit */
(int (*)()) mdb_tgt_null, /* t_add_signal */
(int (*)()) mdb_tgt_null, /* t_add_fault */
(int (*)()) mdb_tgt_notsup, /* t_getareg XXX */
(int (*)()) mdb_tgt_notsup, /* t_putareg XXX */
(int (*)()) mdb_tgt_notsup, /* t_stack_iter XXX */
kp_auxv /* t_auxv */
};
int
{
if (argc != 1)
t->t_flags &= ~MDB_TGT_F_RDWR;
warn("required kernel support module is not loaded\n");
goto err;
}
/*
* Here the kproc target relies on the fact that at the time of its
* instantiation, mdb.m_target is pointing at a kvm target, and
* that the kvm target has stored its libkvm handle in t_pshandle.
*/
goto err;
}
goto err;
}
warn("specified process is a system process (no context)\n");
goto err;
}
goto err;
}
goto err;
}
goto err;
}
goto err;
}
goto err;
}
/*
* If we're applying kproc to a live kernel, we need to force libkvm
* to set the current process to the process in question so we can
* read from its address space. If kvm_getproc returns NULL, the
* process may have gone away since our previous calls to mdb_ks.
*/
if (mdb_prop_postmortem == FALSE &&
return (0);
err:
kp_destroy(t);
return (-1);
}
static ssize_t
{
} else
left = 0;
if (left != 0) {
if (rbytes >= 0)
return (rbytes);
}
return (0); /* At end of segment or in hole; return EOF */
}
static off64_t
{
switch (whence) {
case SEEK_SET:
break;
case SEEK_CUR:
break;
case SEEK_END:
break;
default:
}
}
static void
{
}
static const char *
{
}
static const mdb_io_ops_t kp_io_ops = {
};
static mdb_io_t *
{
return (io);
}