OpenSolaris_b135/lib/brand/lx/librtld_db/common/lx_librtld_db.c
/*
* 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"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/link.h>
#include <libproc.h>
#include <proc_service.h>
#include <rtld_db.h>
#include <synch.h>
#include <sys/lx_brand.h>
/*
* ATTENTION:
* Librtl_db brand plugin libraries should NOT directly invoke any
* libproc.so interfaces or be linked against libproc. If a librtl_db
* brand plugin library uses libproc.so interfaces then it may break
* any other librtld_db consumers (like mdb) that tries to attach
* to a branded process. The only safe interfaces that the a librtld_db
* brand plugin library can use to access a target process are the
* proc_service(3PROC) apis.
*/
/*
* M_DATA comes from some streams header file but is also redifined in
* _rtld_db.h, so nuke the old streams definition here.
*/
#ifdef M_DATA
#undef M_DATA
#endif /* M_DATA */
/*
* For 32-bit versions of this library, this file get's compiled once.
* For 64-bit versions of this library, this file get's compiled twice,
* once with _ELF64 defined and once without. The expectation is that
* the 64-bit version of the library can properly deal with both 32-bit
* and 64-bit elf files, hence in the 64-bit library there are two copies
* of all the interfaces in this file, one set named *32 and one named *64.
*
* This also means that we need to be careful when declaring local pointers
* that point to objects in another processes address space, since these
* pointers may not match the current processes pointer width. Basically,
* we should avoid using data types that change size between 32 and 64 bit
* modes like: long, void *, uintprt_t, caddr_t, psaddr_t, size_t, etc.
* Instead we should declare all pointers as uint32_t. Then when we
* are compiled to deal with 64-bit targets we'll re-define uint32_t
* to be a uint64_t.
*
* Finally, one last importante note. All the 64-bit elf file code
* is never used and can't be tested. This is because we don't actually
* support 64-bit Linux processes yet. The reason that we have it here
* is because we want to support debugging 32-bit elf targets with the
* 64-bit version of this library, so we need to have a 64-bit version
* of this library. But a 64-bit version of this library is expected
* to provide debugging interfaces for both 32 and 64-bit elf targets.
* So we provide the 64-bit elf target interfaces, but they will never
* be invoked and are untested. If we ever add support for 64-bit elf
* Linux processes, we'll need to verify that this code works correctly
* for those targets.
*/
#ifdef _LP64
#ifdef _ELF64
#define lx_ldb_get_dyns32 lx_ldb_get_dyns64
#define lx_ldb_init32 lx_ldb_init64
#define lx_ldb_fini32 lx_ldb_fini64
#define lx_ldb_loadobj_iter32 lx_ldb_loadobj_iter64
#define lx_ldb_getauxval32 lx_ldb_getauxval64
#define lx_elf_props32 lx_elf_props64
#define _rd_get_dyns32 _rd_get_dyns64
#define _rd_get_ehdr32 _rd_get_ehdr64
#define uint32_t uint64_t
#define Elf32_Dyn Elf64_Dyn
#define Elf32_Ehdr Elf64_Ehdr
#define Elf32_Phdr Elf64_Phdr
#endif /* _ELF64 */
#endif /* _LP64 */
/* Included from usr/src/cmd/sgs/librtld_db/common */
#include <_rtld_db.h>
typedef struct lx_rd {
rd_agent_t *lr_rap;
struct ps_prochandle *lr_php; /* proc handle pointer */
uint32_t lr_rdebug; /* address of lx r_debug */
uint32_t lr_exec; /* base address of executable */
} lx_rd_t;
typedef struct lx_link_map {
uint32_t lxm_addr; /* Base address shared object is loaded at. */
uint32_t lxm_name; /* Absolute file name object was found in. */
uint32_t lxm_ld; /* Dynamic section of the shared object. */
uint32_t lxm_next; /* Chain of loaded objects. */
} lx_link_map_t;
typedef struct lx_r_debug {
int r_version; /* Version number for this protocol. */
uint32_t r_map; /* Head of the chain of loaded objects. */
/*
* This is the address of a function internal to the run-time linker,
* that will always be called when the linker begins to map in a
* library or unmap it, and again when the mapping change is complete.
* The debugger can set a breakpoint at this address if it wants to
* notice shared object mapping changes.
*/
uint32_t r_brk;
r_state_e r_state; /* defined the same way between lx/solaris */
uint32_t r_ldbase; /* Base address the linker is loaded at. */
} lx_r_debug_t;
static uint32_t
lx_ldb_getauxval32(struct ps_prochandle *php, int type)
{
const auxv_t *auxvp = NULL;
if (ps_pauxv(php, &auxvp) != PS_OK)
return ((uint32_t)-1);
while (auxvp->a_type != AT_NULL) {
if (auxvp->a_type == type)
return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr);
auxvp++;
}
return ((uint32_t)-1);
}
/*
* A key difference between the linux linker and ours' is that the linux
* linker adds the base address of segments to certain values in the
* segments' ELF header. As an example, look at the address of the
* DT_HASH hash table in a Solaris section - it is a relative address
* which locates the start of the hash table, relative to the beginning
* of the ELF file. However, when the linux linker loads a section, it
* modifies the in-memory ELF image by changing address of the hash
* table to be an absolute address. This is only done for libraries - not for
* executables.
*
* Solaris tools expect the relative address to remain relative, so
* here we will modify the in-memory ELF image so that it once again
* contains relative addresses.
*
* To accomplish this, we walk through all sections in the target.
* Linux sections are identified by pointing to the linux linker or libc in the
* DT_NEEDED section. For all matching sections, we subtract the segment
* base address to get back to relative addresses.
*/
static rd_err_e
lx_ldb_get_dyns32(rd_helper_data_t rhd,
psaddr_t addr, void **dynpp, size_t *dynpp_sz)
{
lx_rd_t *lx_rd = (lx_rd_t *)rhd;
rd_agent_t *rap = lx_rd->lr_rap;
Elf32_Ehdr ehdr;
Elf32_Dyn *dynp = NULL;
size_t dynp_sz;
uint_t ndyns;
int i;
ps_plog("lx_ldb_get_dyns: invoked for object at 0x%p", addr);
/* Read in a copy of the ehdr */
if (_rd_get_ehdr32(rap, addr, &ehdr, NULL) != RD_OK) {
ps_plog("lx_ldb_get_dyns: _rd_get_ehdr() failed");
return (RD_ERR);
}
/* read out the PT_DYNAMIC elements for this object */
if (_rd_get_dyns32(rap, addr, &dynp, &dynp_sz) != RD_OK) {
ps_plog("lx_ldb_get_dyns: _rd_get_dyns() failed");
return (RD_ERR);
}
/*
* From here on out if we encounter an error we'll just return
* success and pass back the unmolested dynamic elements that
* we've already obtained.
*/
*dynpp = dynp;
*dynpp_sz = dynp_sz;
ndyns = dynp_sz / sizeof (Elf32_Dyn);
/* If this isn't a dynamic object, there's nothing left todo */
if (ehdr.e_type != ET_DYN) {
ps_plog("lx_ldb_get_dyns: done: not a shared object");
return (RD_OK);
}
/*
* Before we blindly start changing dynamic section addresses
* we need to figure out if the current object that we're looking
* at is a linux object or a solaris object. To do this first
* we need to find the string tab dynamic section element.
*/
for (i = 0; i < ndyns; i++) {
if (dynp[i].d_tag == DT_STRTAB)
break;
}
if (i == ndyns) {
ps_plog("lx_ldb_get_dyns: "
"failed to find string tab in the dynamic section");
return (RD_OK);
}
/*
* Check if the strtab value looks like an offset or an address.
* It's an offset if the value is less then the base address that
* the object is loaded at, or if the value is less than the offset
* of the section headers in the same elf object. This check isn't
* perfect, but in practice it's good enough.
*/
if ((dynp[i].d_un.d_ptr < addr) ||
(dynp[i].d_un.d_ptr < ehdr.e_shoff)) {
ps_plog("lx_ldb_get_dyns: "
"doesn't appear to be an lx object");
return (RD_OK);
}
/*
* This seems to be a a linux object, so we'll patch up the dynamic
* section addresses
*/
ps_plog("lx_ldb_get_dyns: "
"patching up lx object dynamic section addresses");
for (i = 0; i < ndyns; i++) {
switch (dynp[i].d_tag) {
case DT_PLTGOT:
case DT_HASH:
case DT_STRTAB:
case DT_SYMTAB:
case DT_RELA:
case DT_REL:
case DT_DEBUG:
case DT_JMPREL:
case DT_VERSYM:
if (dynp[i].d_un.d_val > addr) {
dynp[i].d_un.d_ptr -= addr;
}
break;
default:
break;
}
}
return (RD_OK);
}
static void
lx_ldb_fini32(rd_helper_data_t rhd)
{
lx_rd_t *lx_rd = (lx_rd_t *)rhd;
ps_plog("lx_ldb_fini: cleaning up lx helper");
free(lx_rd);
}
/*
* The linux linker has an r_debug structure somewhere in its data section that
* contains the address of the head of the link map list. To find this, we will
* use the DT_DEBUG token in the executable's dynamic section. The linux linker
* wrote the address of its r_debug structure to the DT_DEBUG dynamic entry. We
* get the address of the executable's program headers from the
* AT_SUN_BRAND_LX_PHDR aux vector entry. From there, we calculate the
* address of the Elf header, and from there we can easily get to the DT_DEBUG
* entry.
*/
static rd_helper_data_t
lx_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
{
lx_rd_t *lx_rd;
uint32_t addr, phdr_addr, dyn_addr;
Elf32_Dyn *dyn;
Elf32_Phdr phdr, *ph, *phdrs;
Elf32_Ehdr ehdr;
int i, dyn_count;
lx_rd = calloc(sizeof (lx_rd_t), 1);
if (lx_rd == NULL) {
ps_plog("lx_ldb_init: cannot allocate memory");
return (NULL);
}
lx_rd->lr_rap = rap;
lx_rd->lr_php = php;
phdr_addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_PHDR);
if (phdr_addr == (uint32_t)-1) {
ps_plog("lx_ldb_init: no LX_PHDR found in aux vector");
return (NULL);
}
ps_plog("lx_ldb_init: found LX_PHDR auxv phdr at: 0x%p",
phdr_addr);
if (ps_pread(php, phdr_addr, &phdr, sizeof (phdr)) != PS_OK) {
ps_plog("lx_ldb_init: couldn't read phdr at 0x%p",
phdr_addr);
free(lx_rd);
return (NULL);
}
/* The ELF headher should be before the program header in memory */
lx_rd->lr_exec = addr = phdr_addr - phdr.p_offset;
if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
ps_plog("lx_ldb_init: couldn't read ehdr at 0x%p",
lx_rd->lr_exec);
free(lx_rd);
return (NULL);
}
ps_plog("lx_ldb_init: read ehdr at: 0x%p", addr);
if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
ps_plog("lx_ldb_init: couldn't alloc phdrs memory");
free(lx_rd);
return (NULL);
}
if (ps_pread(php, phdr_addr, phdrs, ehdr.e_phnum * ehdr.e_phentsize) !=
PS_OK) {
ps_plog("lx_ldb_init: couldn't read phdrs at 0x%p",
phdr_addr);
free(lx_rd);
free(phdrs);
return (NULL);
}
ps_plog("lx_ldb_init: read %d phdrs at: 0x%p",
ehdr.e_phnum, phdr_addr);
for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
/*LINTED */
ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
if (ph->p_type == PT_DYNAMIC)
break;
}
if (i == ehdr.e_phnum) {
ps_plog("lx_ldb_init: no PT_DYNAMIC in executable");
free(lx_rd);
free(phdrs);
return (NULL);
}
ps_plog("lx_ldb_init: found PT_DYNAMIC phdr[%d] at: 0x%p",
i, (phdr_addr + ((char *)ph - (char *)phdrs)));
if ((dyn = malloc(ph->p_filesz)) == NULL) {
ps_plog("lx_ldb_init: couldn't alloc for PT_DYNAMIC");
free(lx_rd);
free(phdrs);
return (NULL);
}
dyn_addr = addr + ph->p_offset;
dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p",
dyn_addr);
free(lx_rd);
free(phdrs);
free(dyn);
return (NULL);
}
ps_plog("lx_ldb_init: read %d dynamic headers at: 0x%p",
dyn_count, dyn_addr);
for (i = 0; i < dyn_count; i++) {
if (dyn[i].d_tag == DT_DEBUG) {
lx_rd->lr_rdebug = dyn[i].d_un.d_ptr;
break;
}
}
free(phdrs);
free(dyn);
if (lx_rd->lr_rdebug == 0) {
ps_plog("lx_ldb_init: no DT_DEBUG found in exe");
free(lx_rd);
return (NULL);
}
ps_plog("lx_ldb_init: found DT_DEBUG: 0x%p", lx_rd->lr_rdebug);
return ((rd_helper_data_t)lx_rd);
}
/*
* Given the address of an ELF object in the target, return its size and
* the proper link map ID.
*/
static size_t
lx_elf_props32(struct ps_prochandle *php, uint32_t addr, psaddr_t *data_addr)
{
Elf32_Ehdr ehdr;
Elf32_Phdr *phdrs, *ph;
int i;
uint32_t min = (uint32_t)-1;
uint32_t max = 0;
size_t sz;
if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
ps_plog("lx_elf_props: Couldn't read ELF header at 0x%p",
addr);
return (0);
}
if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL)
return (0);
if (ps_pread(php, addr + ehdr.e_phoff, phdrs, ehdr.e_phnum *
ehdr.e_phentsize) != PS_OK) {
ps_plog("lx_elf_props: Couldn't read program headers at 0x%p",
addr + ehdr.e_phoff);
return (0);
}
for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
/*LINTED */
ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
if (ph->p_type != PT_LOAD)
continue;
if ((ph->p_flags & (PF_W | PF_R)) == (PF_W | PF_R)) {
*data_addr = ph->p_vaddr;
if (ehdr.e_type == ET_DYN)
*data_addr += addr;
if (*data_addr & (ph->p_align - 1))
*data_addr = *data_addr & (~(ph->p_align -1));
}
if (ph->p_vaddr < min)
min = ph->p_vaddr;
if (ph->p_vaddr > max) {
max = ph->p_vaddr;
sz = ph->p_memsz + max - min;
if (sz & (ph->p_align - 1))
sz = (sz & (~(ph->p_align - 1))) + ph->p_align;
}
}
free(phdrs);
return (sz);
}
static int
lx_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
{
lx_rd_t *lx_rd = (lx_rd_t *)rhd;
struct ps_prochandle *php = lx_rd->lr_php;
lx_r_debug_t r_debug;
lx_link_map_t map;
uint32_t p = NULL;
int rc;
rd_loadobj_t exec;
if ((rc = ps_pread(php, (psaddr_t)lx_rd->lr_rdebug, &r_debug,
sizeof (r_debug))) != PS_OK) {
ps_plog("lx_ldb_loadobj_iter: "
"Couldn't read linux r_debug at 0x%p", lx_rd->lr_rdebug);
return (rc);
}
p = r_debug.r_map;
/*
* The first item on the link map list is for the executable, but it
* doesn't give us any useful information about it. We need to
* synthesize a rd_loadobj_t for the client.
*
* Linux doesn't give us the executable name, so we'll get it from
* the AT_EXECNAME entry instead.
*/
if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != PS_OK) {
ps_plog("lx_ldb_loadobj_iter: "
"Couldn't read linux link map at 0x%p", p);
return (rc);
}
bzero(&exec, sizeof (exec));
exec.rl_base = lx_rd->lr_exec;
exec.rl_dynamic = map.lxm_ld;
exec.rl_nameaddr = lx_ldb_getauxval32(php, AT_SUN_EXECNAME);
exec.rl_lmident = LM_ID_BASE;
exec.rl_bend = exec.rl_base +
lx_elf_props32(php, lx_rd->lr_exec, &exec.rl_data_base);
if ((*cb)(&exec, client_data) == 0) {
ps_plog("lx_ldb_loadobj_iter: "
"client callb failed for executable");
return (PS_ERR);
}
for (p = map.lxm_next; p != NULL; p = map.lxm_next) {
rd_loadobj_t obj;
if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) !=
PS_OK) {
ps_plog("lx_ldb_loadobj_iter: "
"Couldn't read lk map at %p", p);
return (rc);
}
/*
* The linux link map has less information than the Solaris one.
* We need to go fetch the missing information from the ELF
* headers.
*/
obj.rl_nameaddr = (psaddr_t)map.lxm_name;
obj.rl_base = map.lxm_addr;
obj.rl_refnameaddr = (psaddr_t)map.lxm_name;
obj.rl_plt_base = NULL;
obj.rl_plt_size = 0;
obj.rl_lmident = LM_ID_BASE;
/*
* Ugh - we have to walk the ELF stuff, find the PT_LOAD
* sections, and calculate the end of the file's mappings
* ourselves.
*/
obj.rl_bend = map.lxm_addr +
lx_elf_props32(php, map.lxm_addr, &obj.rl_data_base);
obj.rl_padstart = obj.rl_base;
obj.rl_padend = obj.rl_bend;
obj.rl_dynamic = map.lxm_ld;
obj.rl_tlsmodid = 0;
ps_plog("lx_ldb_loadobj_iter: 0x%p to 0x%p",
obj.rl_base, obj.rl_bend);
if ((*cb)(&obj, client_data) == 0) {
ps_plog("lx_ldb_loadobj_iter: "
"Client callback failed on %s", map.lxm_name);
return (rc);
}
}
return (RD_OK);
}
/*
* Librtld_db plugin linkage struct.
*
* When we get loaded by librtld_db, it will look for the symbol below
* to find our plugin entry points.
*/
rd_helper_ops_t RTLD_DB_BRAND_OPS = {
LM_ID_BRAND,
lx_ldb_init32,
lx_ldb_fini32,
lx_ldb_loadobj_iter32,
lx_ldb_get_dyns32
};