2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <libproc.h>
2N/A#include <proc_service.h>
2N/A#include <synch.h>
2N/A#include <sys/types.h>
2N/A#include <sys/link.h>
2N/A#include <rtld_db.h>
2N/A#include <sys/brand.h>
2N/A
2N/A/*
2N/A * ATTENTION:
2N/A * Librtl_db brand plugin libraries should NOT directly invoke any
2N/A * libproc.so interfaces or be linked against libproc. If a librtl_db
2N/A * brand plugin library uses libproc.so interfaces then it may break
2N/A * any other librtld_db consumers (like mdb) that tries to attach
2N/A * to a branded process. The only safe interfaces that the a librtld_db
2N/A * brand plugin library can use to access a target process are the
2N/A * proc_service(3PROC) apis.
2N/A */
2N/A
2N/A/*
2N/A * M_DATA comes from some streams header file but is also redifined in
2N/A * _rtld_db.h, so nuke the old streams definition here.
2N/A */
2N/A#ifdef M_DATA
2N/A#undef M_DATA
2N/A#endif /* M_DATA */
2N/A
2N/A/*
2N/A * For 32-bit versions of this library, this file get's compiled once.
2N/A * For 64-bit versions of this library, this file get's compiled twice,
2N/A * once with _ELF64 defined and once without. The expectation is that
2N/A * the 64-bit version of the library can properly deal with both 32-bit
2N/A * and 64-bit elf files, hence in the 64-bit library there are two copies
2N/A * of all the interfaces in this file, one set named *32 and one named *64.
2N/A *
2N/A * This also means that we need to be careful when declaring local pointers
2N/A * that point to objects in another processes address space, since these
2N/A * pointers may not match the current processes pointer width. Basically,
2N/A * we should not use any objects that change size between 32 and 64 bit
2N/A * modes like: long, void *, uintprt_t, caddr_t, psaddr_t, size_t, etc.
2N/A * Instead we should declare all pointers as uint32_t. Then when we
2N/A * are compiled to deal with 64-bit targets we'll re-define uing32_t
2N/A * to be a uint64_t.
2N/A */
2N/A#ifdef _LP64
2N/A#ifdef _ELF64
2N/A#define uint32_t uint64_t
2N/A#define Elf32_Dyn Elf64_Dyn
2N/A#define validate_rdebug32 validate_rdebug64
2N/A#define _rd_loadobj_iter32 _rd_loadobj_iter64
2N/A#define _rd_get_dyns32 _rd_get_dyns64
2N/A#define dummy_ldb32 dummy_ldb64
2N/A#define dummy_ldb_init32 dummy_ldb_init64
2N/A#define dummy_ldb_fini32 dummy_ldb_fini64
2N/A#define dummy_ldb_loadobj_iter32 dummy_ldb_loadobj_iter64
2N/A#define dummy_ldb_get_dyns32 dummy_ldb_get_dyns64
2N/A#define brand_ldb_init32 brand_ldb_init64
2N/A#define brand_ldb_fini32 brand_ldb_fini64
2N/A#define brand_ldb_loadobj_iter32 brand_ldb_loadobj_iter64
2N/A#define brand_ldb_get_dyns32 brand_ldb_get_dyns64
2N/A#endif /* _ELF64 */
2N/A#endif /* _LP64 */
2N/A
2N/A/* Included from usr/src/cmd/sgs/librtld_db/common */
2N/A#include <_rtld_db.h>
2N/A
2N/A/*ARGSUSED*/
2N/Astatic rd_helper_data_t
2N/Adummy_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
2N/A{
2N/A return (NULL);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Adummy_ldb_fini32(rd_helper_data_t rhd)
2N/A{
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Adummy_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
2N/A{
2N/A return (RD_OK);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic rd_err_e
2N/Adummy_ldb_get_dyns32(rd_helper_data_t rhd,
2N/A psaddr_t addr, void **dynpp, size_t *dynpp_sz)
2N/A{
2N/A *dynpp = NULL;
2N/A *dynpp_sz = 0;
2N/A return (RD_OK);
2N/A}
2N/A
2N/Astatic rd_helper_ops_t dummy_ldb32 = {
2N/A LM_ID_BRAND,
2N/A dummy_ldb_init32,
2N/A dummy_ldb_fini32,
2N/A dummy_ldb_loadobj_iter32,
2N/A dummy_ldb_get_dyns32
2N/A};
2N/A
2N/Astatic uint32_t
2N/Abrand_ldb_getauxval32(struct ps_prochandle *php, int type)
2N/A{
2N/A const auxv_t *auxvp = NULL;
2N/A
2N/A if (ps_pauxv(php, &auxvp) != PS_OK)
2N/A return ((uint32_t)-1);
2N/A
2N/A while (auxvp->a_type != AT_NULL) {
2N/A if (auxvp->a_type == type)
2N/A return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr);
2N/A auxvp++;
2N/A }
2N/A return ((uint32_t)-1);
2N/A}
2N/A
2N/A/*
2N/A * Normally, the native Solaris librtldb_db plugin uses a bunch of different
2N/A * methods to try and find the rdebug structure associated with the target
2N/A * process we're debugging. For details on the different methods see
2N/A * _rd_reset32(). Thankfully our job is easier. We know that the brand
2N/A * library is always linked against the native linker, and when the
2N/A * process was first executed we saved off a pointer to the brand linkers
2N/A * rdebug structure in one of our brand specific aux vectors,
2N/A * AT_SUN_BRAND_COMMON_LDDATA. So we'll just look that up here.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic rd_helper_data_t
2N/Abrand_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
2N/A{
2N/A struct rd_agent *rap_new;
2N/A uint32_t lddata_addr;
2N/A int rd_dmodel;
2N/A
2N/A if (ps_pdmodel(php, &rd_dmodel) != PS_OK) {
2N/A ps_plog("brand_ldb_init: lookup of data model failed");
2N/A return (NULL);
2N/A }
2N/A#ifdef _ELF64
2N/A assert(rd_dmodel == PR_MODEL_LP64);
2N/A#else /* !_ELF64 */
2N/A assert(rd_dmodel == PR_MODEL_ILP32);
2N/A#endif /* !_ELF64 */
2N/A
2N/A lddata_addr = brand_ldb_getauxval32(php, AT_SUN_BRAND_COMMON_LDDATA);
2N/A if (lddata_addr == (uint32_t)-1) {
2N/A ps_plog("brand_ldb_init: no LDDATA found in aux vector");
2N/A return (NULL);
2N/A }
2N/A ps_plog("brand_ldb_init: found LDDATA auxv ld.so.1 data seg "
2N/A "at: 0x%p", lddata_addr);
2N/A
2N/A /*
2N/A * Ok. So this is kinda ugly. Basically we know that we're going to
2N/A * be parsing data from link maps that are generated by a Solaris
2N/A * linker. As it turns out, that's exactly what the default
2N/A * Solaris librtld_db library is designed to do. So rather than
2N/A * duplicate all that link map parsing code here we'll simply
2N/A * invoke the native librtld_db that normally does this, and when
2N/A * we do we'll point them at our emulation libraries link map.
2N/A *
2N/A * Of course these interfacess aren't really public interfaces
2N/A * and they take a "struct rd_agent" as a parameter. So here
2N/A * we'll allocate and initialize a new "struct rd_agent", point
2N/A * it at our emulation libraries link map, and initialize just
2N/A * enough of the structure to make the librtld_db interfaces
2N/A * that we want to use happy.
2N/A */
2N/A if ((rap_new = calloc(sizeof (*rap_new), 1)) == NULL) {
2N/A ps_plog("brand_ldb_init: can't allocate memory");
2N/A return (NULL);
2N/A }
2N/A rap_new->rd_dmodel = rd_dmodel;
2N/A rap_new->rd_psp = php;
2N/A rap_new->rd_rdebug = lddata_addr;
2N/A (void) mutex_init(&rap_new->rd_mutex, USYNC_THREAD, 0);
2N/A
2N/A /*
2N/A * When we get invoked from librtld_db, and we call back into it,
2N/A * librtld_db will once again check if there is a plugin and
2N/A * invoke it. Since we don't want to enter a recursive loop
2N/A * we're going to specify a different plugin interface for
2N/A * our linkmap, and these new plugin interfaces won't actually
2N/A * do anything other than return.
2N/A */
2N/A rap_new->rd_helper.rh_ops = &dummy_ldb32;
2N/A
2N/A /*
2N/A * validate_rdebug32() requires the following "struct rd_agent"
2N/A * members to be initialized:
2N/A * rd_psp, rd_rdebug
2N/A *
2N/A * validate_rdebug32() initializes the following "struct rd_agent"
2N/A * members:
2N/A * rd_flags, rd_rdebugvers, rd_rtlddbpriv
2N/A */
2N/A if (validate_rdebug32(rap_new) != RD_OK) {
2N/A ps_plog("brand_ldb_init: can't find valid r_debug data");
2N/A free(rap_new);
2N/A return (NULL);
2N/A }
2N/A
2N/A ps_plog("brand_ldb_init: finished, helper_data=0x%p", rap_new);
2N/A return ((rd_helper_data_t)rap_new);
2N/A}
2N/A
2N/Astatic void
2N/Abrand_ldb_fini32(rd_helper_data_t rhd)
2N/A{
2N/A struct rd_agent *rap = (struct rd_agent *)rhd;
2N/A ps_plog("brand_ldb_fini: cleaning up brand helper");
2N/A free(rap);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Abrand_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
2N/A{
2N/A struct rd_agent *rap = (struct rd_agent *)rhd;
2N/A int err;
2N/A
2N/A ps_plog("brand_ldb_loadobj_iter(helper_data=0x%p)", rhd);
2N/A assert(rap->rd_psp == php);
2N/A RDAGLOCK(rap);
2N/A /*
2N/A * _rd_loadobj_iter32() requires the following "struct rd_agent"
2N/A * members to be initialized:
2N/A * rd_rtlddbpriv, rd_rdebugvers, rd_flags,
2N/A * rd_helper.rh_ops, rd_dmodel
2N/A */
2N/A err = _rd_loadobj_iter32(rap, cb, client_data);
2N/A RDAGUNLOCK(rap);
2N/A ps_plog("brand_ldb_loadobj_iter: finished, err = %d", err);
2N/A return (err);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic rd_err_e
2N/Abrand_ldb_get_dyns32(rd_helper_data_t rhd,
2N/A psaddr_t addr, void **dynpp, size_t *dynpp_sz)
2N/A{
2N/A struct rd_agent *rap = (struct rd_agent *)rhd;
2N/A int err;
2N/A
2N/A ps_plog("brand_ldb_get_dyns(helper_data=0x%p)", rhd);
2N/A err = _rd_get_dyns32(rap, addr, (Elf32_Dyn **)dynpp, dynpp_sz);
2N/A ps_plog("brand_ldb_get_dyns: finished, err = %d", err);
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Librtld_db plugin linkage struct.
2N/A *
2N/A * When we get loaded by librtld_db, it will look for the symbol below
2N/A * to find our plugin entry points.
2N/A */
2N/Ard_helper_ops_t RTLD_DB_BRAND_OPS = {
2N/A LM_ID_NONE,
2N/A brand_ldb_init32,
2N/A brand_ldb_fini32,
2N/A brand_ldb_loadobj_iter32,
2N/A brand_ldb_get_dyns32
2N/A};