view usr/src/cmd/mdb/common/mdb/mdb_pservice.c @ 14167:7ac6fb740bcf

3946 ::gcore (fix sparc build)
author Christopher Siden <chris.siden@delphix.com>
date Tue, 27 Aug 2013 10:51:34 -0800
parents 76352486aa31
children
line wrap: on
line source

/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Proc Service API Interposition Layer
 *
 * In order to allow multiple MDB targets to make use of librtld_db, we
 * provide an interposition layer for functions in the proc_service.h API
 * that are used by librtld_db.  Each of the functions used by librtld_db
 * can be conveniently expressed in terms of the MDB target API, so this
 * layer simply selects the appropriate target, invokes the corresponding
 * target API function, and then translates the error codes appropriately.
 * We expect that each proc_service entry point will be invoked with a
 * cookie (struct ps_prochandle *) which matches either a known MDB target,
 * or the value of a target's t->t_pshandle.  This allows us to re-vector
 * calls to the proc service API around libproc (which also contains an
 * implementation of the proc_service API) like this:
 *
 * Linker map:
 * +---------+   +---------+   +------------+      Legend:
 * |   MDB   | ->| libproc | ->| librtld_db |      <1> function in this file
 * +---------+   +---------+   +------------+      <2> function in libproc
 * ps_pread<1>   ps_pread<2>   call ps_pread() --+
 *                                               |
 * +---------------------------------------------+
 * |
 * +-> ps_pread<1>(P, ...)
 *       t = mdb_tgt_from_pshandle(P);
 *       mdb_tgt_vread(t, ...);
 *
 * If we are debugging a user process, we then make these calls (which form
 * the equivalent of libproc's proc_service implementation):
 *
 * mdb_tgt_vread() -> proc target t->t_vread() -> libproc.so`Pread()
 *
 * If we are debugging a user process through a kernel crash dump (kproc
 * target), we make these calls:
 *
 * mdb_tgt_vread() -> kproc target t->t_vread() -> mdb_tgt_aread(kvm target) ->
 * 	kvm target t->t_aread() -> libkvm.so`kvm_aread()
 *
 * This design allows us to support both kproc's use of librtld_db, as well
 * as libproc's use of librtld_db, but it does lead to one unfortunate problem
 * in the creation of a proc target: when the proc target invokes libproc to
 * construct a ps_prochandle, and libproc in turn invokes librtld_db, MDB does
 * not yet know what ps_prochandle has been allocated inside of libproc since
 * this call has not yet returned.  We also can't translate this ps_prochandle
 * to the target itself, since that target isn't ready to handle requests yet;
 * we actually need to pass the call back through to libproc.  In order to
 * do that, we use libdl to lookup the address of libproc's definition of the
 * various functions (RTLD_NEXT on the link map chain) and store these in the
 * ps_ops structure below.  If we ever fail to translate a ps_prochandle to
 * an MDB target, we simply pass the call through to libproc.
 */

#include <proc_service.h>
#include <dlfcn.h>

#include <mdb/mdb_target_impl.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb.h>

static struct {
	ps_err_e (*ps_pread)(struct ps_prochandle *,
	    psaddr_t, void *, size_t);
	ps_err_e (*ps_pwrite)(struct ps_prochandle *,
	    psaddr_t, const void *, size_t);
	ps_err_e (*ps_pglobal_lookup)(struct ps_prochandle *,
	    const char *, const char *, psaddr_t *);
	ps_err_e (*ps_pglobal_sym)(struct ps_prochandle *P,
	    const char *, const char *, ps_sym_t *);
	ps_err_e (*ps_pauxv)(struct ps_prochandle *,
	    const auxv_t **);
	ps_err_e (*ps_pbrandname)(struct ps_prochandle *,
	    char *, size_t);
	ps_err_e (*ps_pdmodel)(struct ps_prochandle *,
	    int *);
} ps_ops;

static mdb_tgt_t *
mdb_tgt_from_pshandle(void *P)
{
	mdb_tgt_t *t;

	for (t = mdb_list_next(&mdb.m_tgtlist); t; t = mdb_list_next(t)) {
		if (t == P || t->t_pshandle == P)
			return (t);
	}

	return (NULL);
}

/*
 * Read from the specified target virtual address.
 */
ps_err_e
ps_pread(struct ps_prochandle *P, psaddr_t addr, void *buf, size_t size)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);

	if (t == NULL)
		return (ps_ops.ps_pread(P, addr, buf, size));

	if (mdb_tgt_vread(t, buf, size, addr) != size)
		return (PS_BADADDR);

	return (PS_OK);
}

/*
 * Write to the specified target virtual address.
 */
ps_err_e
ps_pwrite(struct ps_prochandle *P, psaddr_t addr, const void *buf, size_t size)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);

	if (t == NULL)
		return (ps_ops.ps_pwrite(P, addr, buf, size));

	if (mdb_tgt_vwrite(t, buf, size, addr) != size)
		return (PS_BADADDR);

	return (PS_OK);
}

/*
 * Search for a symbol by name and return the corresponding address.
 */
ps_err_e
ps_pglobal_lookup(struct ps_prochandle *P, const char *object,
    const char *name, psaddr_t *symp)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);
	GElf_Sym sym;

	if (t == NULL)
		return (ps_ops.ps_pglobal_lookup(P, object, name, symp));

	if (mdb_tgt_lookup_by_name(t, object, name, &sym, NULL) == 0) {
		*symp = (psaddr_t)sym.st_value;
		return (PS_OK);
	}

	return (PS_NOSYM);
}

/*
 * Search for a symbol by name and return the corresponding symbol data.
 * If we're compiled _LP64, we just call mdb_tgt_lookup_by_name and return
 * because ps_sym_t is defined to be an Elf64_Sym, which is the same as a
 * GElf_Sym.  In the _ILP32 case, we have to convert mdb_tgt_lookup_by_name's
 * result back to a ps_sym_t (which is an Elf32_Sym).
 */
ps_err_e
ps_pglobal_sym(struct ps_prochandle *P, const char *object,
    const char *name, ps_sym_t *symp)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);
#if defined(_ILP32)
	GElf_Sym sym;

	if (t == NULL)
		return (ps_ops.ps_pglobal_sym(P, object, name, symp));

	if (mdb_tgt_lookup_by_name(t, object, name, &sym, NULL) == 0) {
		symp->st_name = (Elf32_Word)sym.st_name;
		symp->st_value = (Elf32_Addr)sym.st_value;
		symp->st_size = (Elf32_Word)sym.st_size;
		symp->st_info = ELF32_ST_INFO(
		    GELF_ST_BIND(sym.st_info), GELF_ST_TYPE(sym.st_info));
		symp->st_other = sym.st_other;
		symp->st_shndx = sym.st_shndx;
		return (PS_OK);
	}

#elif defined(_LP64)
	if (t == NULL)
		return (ps_ops.ps_pglobal_sym(P, object, name, symp));

	if (mdb_tgt_lookup_by_name(t, object, name, symp, NULL) == 0)
		return (PS_OK);
#endif

	return (PS_NOSYM);
}

/*
 * Report a debug message.  We allow proc_service API clients to report
 * messages via our debug stream if the MDB_DBG_PSVC token is enabled.
 */
void
ps_plog(const char *format, ...)
{
	va_list alist;

	va_start(alist, format);
	mdb_dvprintf(MDB_DBG_PSVC, format, alist);
	va_end(alist);
}

/*
 * Return the auxv structure from the process being examined.
 */
ps_err_e
ps_pauxv(struct ps_prochandle *P, const auxv_t **auxvp)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);

	if (t == NULL)
		return (ps_ops.ps_pauxv(P, auxvp));

	if (mdb_tgt_auxv(t, auxvp) != 0)
		return (PS_ERR);

	return (PS_OK);
}

ps_err_e
ps_pbrandname(struct ps_prochandle *P, char *buf, size_t len)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);
	const auxv_t *auxv;

	if (t == NULL)
		return (ps_ops.ps_pbrandname(P, buf, len));

	if (mdb_tgt_auxv(t, &auxv) != 0)
		return (PS_ERR);

	while (auxv->a_type != AT_NULL) {
		if (auxv->a_type == AT_SUN_BRANDNAME)
			break;
		auxv++;
	}
	if (auxv->a_type == AT_NULL)
		return (PS_ERR);

	if (mdb_tgt_readstr(t, MDB_TGT_AS_VIRT,
	    buf, len, auxv->a_un.a_val) <= 0)
		return (PS_ERR);

	return (PS_OK);
}

/*
 * Return the data model of the target.
 */
ps_err_e
ps_pdmodel(struct ps_prochandle *P, int *dm)
{
	mdb_tgt_t *t = mdb_tgt_from_pshandle(P);

	if (t == NULL)
		return (ps_ops.ps_pdmodel(P, dm));

	switch (mdb_tgt_dmodel(t)) {
	case MDB_TGT_MODEL_LP64:
		*dm = PR_MODEL_LP64;
		return (PS_OK);
	case MDB_TGT_MODEL_ILP32:
		*dm = PR_MODEL_ILP32;
		return (PS_OK);
	}

	return (PS_ERR);
}

/*
 * Stub function in case we cannot find the necessary symbols from libproc.
 */
static ps_err_e
ps_fail(struct ps_prochandle *P)
{
	mdb_dprintf(MDB_DBG_PSVC, "failing call to pshandle %p\n", (void *)P);
	return (PS_BADPID);
}

/*
 * Initialization function for the proc service interposition layer: we use
 * libdl to look up the next definition of each function in the link map.
 */
void
mdb_pservice_init(void)
{
	if ((ps_ops.ps_pread = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pread")) == NULL)
		ps_ops.ps_pread = (ps_err_e (*)())ps_fail;

	if ((ps_ops.ps_pwrite = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pwrite")) == NULL)
		ps_ops.ps_pwrite = (ps_err_e (*)())ps_fail;

	if ((ps_ops.ps_pglobal_lookup = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pglobal_lookup")) == NULL)
		ps_ops.ps_pglobal_lookup = (ps_err_e (*)())ps_fail;

	if ((ps_ops.ps_pglobal_sym = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pglobal_sym")) == NULL)
		ps_ops.ps_pglobal_sym = (ps_err_e (*)())ps_fail;

	if ((ps_ops.ps_pauxv = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pauxv")) == NULL)
		ps_ops.ps_pauxv = (ps_err_e (*)())ps_fail;

	if ((ps_ops.ps_pbrandname = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pbrandname")) == NULL)
		ps_ops.ps_pbrandname = (ps_err_e (*)())ps_fail;

	if ((ps_ops.ps_pdmodel = (ps_err_e (*)())
	    dlsym(RTLD_NEXT, "ps_pdmodel")) == NULL)
		ps_ops.ps_pdmodel = (ps_err_e (*)())ps_fail;
}