Psymtab.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 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 <stddef.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <memory.h>
#include <errno.h>
#include <dirent.h>
#include <signal.h>
#include <limits.h>
#include <libgen.h>
#include <zone.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/systeminfo.h>
#include <sys/sysmacros.h>
#include "libproc.h"
#include "Pcontrol.h"
#include "Putil.h"
static file_info_t *build_map_symtab(struct ps_prochandle *, map_info_t *);
static map_info_t *exec_map(struct ps_prochandle *);
static map_info_t *object_to_map(struct ps_prochandle *, Lmid_t, const char *);
static map_info_t *object_name_to_map(struct ps_prochandle *,
Lmid_t, const char *);
static GElf_Sym *sym_by_name(sym_tbl_t *, const char *, GElf_Sym *, uint_t *);
static int read_ehdr32(struct ps_prochandle *, Elf32_Ehdr *, uintptr_t);
#ifdef _LP64
static int read_ehdr64(struct ps_prochandle *, Elf64_Ehdr *, uintptr_t);
#endif
#define DATA_TYPES \
((1 << STT_OBJECT) | (1 << STT_FUNC) | \
(1 << STT_COMMON) | (1 << STT_TLS))
#define IS_DATA_TYPE(tp) (((1 << (tp)) & DATA_TYPES) != 0)
#define MA_RWX (MA_READ | MA_WRITE | MA_EXEC)
typedef enum {
PRO_NATURAL,
PRO_BYADDR,
PRO_BYNAME
} pr_order_t;
static int
addr_cmp(const void *aa, const void *bb)
{
uintptr_t a = *((uintptr_t *)aa);
uintptr_t b = *((uintptr_t *)bb);
if (a > b)
return (1);
if (a < b)
return (-1);
return (0);
}
/*
* Allocation function for a new file_info_t
*/
static file_info_t *
file_info_new(struct ps_prochandle *P, map_info_t *mptr)
{
file_info_t *fptr;
map_info_t *mp;
uintptr_t a, addr, *addrs, last = 0;
uint_t i, j, naddrs = 0, unordered = 0;
if ((fptr = calloc(1, sizeof (file_info_t))) == NULL)
return (NULL);
list_link(fptr, &P->file_head);
(void) strcpy(fptr->file_pname, mptr->map_pmap.pr_mapname);
mptr->map_file = fptr;
fptr->file_ref = 1;
fptr->file_fd = -1;
P->num_files++;
/*
* To figure out which map_info_t instances correspond to the mappings
* for this load object, we look at the in-memory ELF image in the
* base mapping (usually the program text). We examine the program
* headers to find the addresses at the beginning and end of each
* section and store them in a list which we then sort. Finally, we
* walk down the list of addresses and the list of map_info_t
* instances in lock step to correctly find the mappings that
* correspond to this load object.
*/
if (P->status.pr_dmodel == PR_MODEL_ILP32) {
Elf32_Ehdr ehdr;
Elf32_Phdr phdr;
if (read_ehdr32(P, &ehdr, mptr->map_pmap.pr_vaddr) != 0)
return (fptr);
addrs = malloc(sizeof (uintptr_t) * ehdr.e_phnum * 2);
a = mptr->map_pmap.pr_vaddr + ehdr.e_phoff;
for (i = 0; i < ehdr.e_phnum; i++, a += ehdr.e_phentsize) {
if (Pread(P, &phdr, sizeof (phdr), a) != sizeof (phdr))
goto out;
if (phdr.p_type != PT_LOAD || phdr.p_memsz == 0)
continue;
addr = phdr.p_vaddr;
if (ehdr.e_type == ET_DYN)
addr += mptr->map_pmap.pr_vaddr;
if (last > addr)
unordered = 1;
addrs[naddrs++] = addr;
addrs[naddrs++] = last = addr + phdr.p_memsz - 1;
}
#ifdef _LP64
} else {
Elf64_Ehdr ehdr;
Elf64_Phdr phdr;
if (read_ehdr64(P, &ehdr, mptr->map_pmap.pr_vaddr) != 0)
return (fptr);
addrs = malloc(sizeof (uintptr_t) * ehdr.e_phnum * 2);
a = mptr->map_pmap.pr_vaddr + ehdr.e_phoff;
for (i = 0; i < ehdr.e_phnum; i++, a += ehdr.e_phentsize) {
if (Pread(P, &phdr, sizeof (phdr), a) != sizeof (phdr))
goto out;
if (phdr.p_type != PT_LOAD || phdr.p_memsz == 0)
continue;
addr = phdr.p_vaddr;
if (ehdr.e_type == ET_DYN)
addr += mptr->map_pmap.pr_vaddr;
if (last > addr)
unordered = 1;
addrs[naddrs++] = addr;
addrs[naddrs++] = last = addr + phdr.p_memsz - 1;
}
#endif
}
if (unordered)
qsort(addrs, naddrs, sizeof (uintptr_t), addr_cmp);
i = j = 0;
mp = P->mappings;
while (j < P->map_count && i < naddrs) {
addr = addrs[i];
if (addr >= mp->map_pmap.pr_vaddr &&
addr < mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size &&
mp->map_file == NULL) {
mp->map_file = fptr;
fptr->file_ref++;
}
if (addr < mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size) {
i++;
} else {
mp++;
j++;
}
}
out:
free(addrs);
return (fptr);
}
/*
* Deallocation function for a file_info_t
*/
static void
file_info_free(struct ps_prochandle *P, file_info_t *fptr)
{
if (--fptr->file_ref == 0) {
list_unlink(fptr);
if (fptr->file_symtab.sym_elf) {
(void) elf_end(fptr->file_symtab.sym_elf);
free(fptr->file_symtab.sym_elfmem);
}
if (fptr->file_symtab.sym_byname)
free(fptr->file_symtab.sym_byname);
if (fptr->file_symtab.sym_byaddr)
free(fptr->file_symtab.sym_byaddr);
if (fptr->file_dynsym.sym_elf) {
(void) elf_end(fptr->file_dynsym.sym_elf);
free(fptr->file_dynsym.sym_elfmem);
}
if (fptr->file_dynsym.sym_byname)
free(fptr->file_dynsym.sym_byname);
if (fptr->file_dynsym.sym_byaddr)
free(fptr->file_dynsym.sym_byaddr);
if (fptr->file_lo)
free(fptr->file_lo);
if (fptr->file_lname)
free(fptr->file_lname);
if (fptr->file_elf)
(void) elf_end(fptr->file_elf);
if (fptr->file_elfmem != NULL)
free(fptr->file_elfmem);
if (fptr->file_fd >= 0)
(void) close(fptr->file_fd);
if (fptr->file_ctfp) {
ctf_close(fptr->file_ctfp);
free(fptr->file_ctf_buf);
}
free(fptr);
P->num_files--;
}
}
/*
* Deallocation function for a map_info_t
*/
static void
map_info_free(struct ps_prochandle *P, map_info_t *mptr)
{
file_info_t *fptr;
if ((fptr = mptr->map_file) != NULL) {
if (fptr->file_map == mptr)
fptr->file_map = NULL;
file_info_free(P, fptr);
}
if (P->execname && mptr == P->map_exec) {
free(P->execname);
P->execname = NULL;
}
if (P->auxv && (mptr == P->map_exec || mptr == P->map_ldso)) {
free(P->auxv);
P->auxv = NULL;
P->nauxv = 0;
}
if (mptr == P->map_exec)
P->map_exec = NULL;
if (mptr == P->map_ldso)
P->map_ldso = NULL;
}
/*
* Call-back function for librtld_db to iterate through all of its shared
* libraries. We use this to get the load object names for the mappings.
*/
static int
map_iter(const rd_loadobj_t *lop, void *cd)
{
char buf[PATH_MAX];
struct ps_prochandle *P = cd;
map_info_t *mptr;
file_info_t *fptr;
dprintf("encountered rd object at %p\n", (void *)lop->rl_base);
if ((mptr = Paddr2mptr(P, lop->rl_base)) == NULL)
return (1); /* Base address does not match any mapping */
if ((fptr = mptr->map_file) == NULL &&
(fptr = file_info_new(P, mptr)) == NULL)
return (1); /* Failed to allocate a new file_info_t */
if ((fptr->file_lo == NULL) &&
(fptr->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) {
file_info_free(P, fptr);
return (1); /* Failed to allocate rd_loadobj_t */
}
fptr->file_map = mptr;
*fptr->file_lo = *lop;
fptr->file_lo->rl_plt_base = fptr->file_plt_base;
fptr->file_lo->rl_plt_size = fptr->file_plt_size;
if (fptr->file_lname) {
free(fptr->file_lname);
fptr->file_lname = NULL;
}
if (Pread_string(P, buf, sizeof (buf), lop->rl_nameaddr) > 0) {
if ((fptr->file_lname = strdup(buf)) != NULL)
fptr->file_lbase = basename(fptr->file_lname);
}
dprintf("loaded rd object %s lmid %lx\n",
fptr->file_lname ? fptr->file_lname : "<NULL>", lop->rl_lmident);
return (1);
}
static void
map_set(struct ps_prochandle *P, map_info_t *mptr, const char *lname)
{
file_info_t *fptr;
if ((fptr = mptr->map_file) == NULL &&
(fptr = file_info_new(P, mptr)) == NULL)
return; /* Failed to allocate a new file_info_t */
fptr->file_map = mptr;
if ((fptr->file_lo == NULL) &&
(fptr->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) {
file_info_free(P, fptr);
return; /* Failed to allocate rd_loadobj_t */
}
(void) memset(fptr->file_lo, 0, sizeof (rd_loadobj_t));
fptr->file_lo->rl_base = mptr->map_pmap.pr_vaddr;
fptr->file_lo->rl_bend =
mptr->map_pmap.pr_vaddr + mptr->map_pmap.pr_size;
fptr->file_lo->rl_plt_base = fptr->file_plt_base;
fptr->file_lo->rl_plt_size = fptr->file_plt_size;
if (fptr->file_lname) {
free(fptr->file_lname);
fptr->file_lname = NULL;
}
if ((fptr->file_lname = strdup(lname)) != NULL)
fptr->file_lbase = basename(fptr->file_lname);
}
static void
load_static_maps(struct ps_prochandle *P)
{
map_info_t *mptr;
/*
* Construct the map for the a.out.
*/
if ((mptr = object_name_to_map(P, PR_LMID_EVERY, PR_OBJ_EXEC)) != NULL)
map_set(P, mptr, "a.out");
/*
* If the dynamic linker exists for this process,
* construct the map for it.
*/
if (Pgetauxval(P, AT_BASE) != -1L &&
(mptr = object_name_to_map(P, PR_LMID_EVERY, PR_OBJ_LDSO)) != NULL)
map_set(P, mptr, "ld.so.1");
}
/*
* Go through all the address space mappings, validating or updating
* the information already gathered, or gathering new information.
*
* This function is only called when we suspect that the mappings have changed
* because this is the first time we're calling it or because of rtld activity.
*/
void
Pupdate_maps(struct ps_prochandle *P)
{
char mapfile[64];
int mapfd;
struct stat statb;
prmap_t *Pmap = NULL;
prmap_t *pmap;
ssize_t nmap;
int i;
uint_t oldmapcount;
map_info_t *newmap, *newp;
map_info_t *mptr;
if (P->info_valid)
return;
Preadauxvec(P);
(void) sprintf(mapfile, "/proc/%d/map", (int)P->pid);
if ((mapfd = open(mapfile, O_RDONLY)) < 0 ||
fstat(mapfd, &statb) != 0 ||
statb.st_size < sizeof (prmap_t) ||
(Pmap = malloc(statb.st_size)) == NULL ||
(nmap = pread(mapfd, Pmap, statb.st_size, 0L)) <= 0 ||
(nmap /= sizeof (prmap_t)) == 0) {
if (Pmap != NULL)
free(Pmap);
if (mapfd >= 0)
(void) close(mapfd);
Preset_maps(P); /* utter failure; destroy tables */
return;
}
(void) close(mapfd);
if ((newmap = calloc(1, nmap * sizeof (map_info_t))) == NULL)
return;
/*
* We try to merge any file information we may have for existing
* mappings, to avoid having to rebuild the file info.
*/
mptr = P->mappings;
pmap = Pmap;
newp = newmap;
oldmapcount = P->map_count;
for (i = 0; i < nmap; i++, pmap++, newp++) {
if (oldmapcount == 0) {
/*
* We've exhausted all the old mappings. Every new
* mapping should be added.
*/
newp->map_pmap = *pmap;
} else if (pmap->pr_vaddr == mptr->map_pmap.pr_vaddr &&
pmap->pr_size == mptr->map_pmap.pr_size &&
pmap->pr_offset == mptr->map_pmap.pr_offset &&
(pmap->pr_mflags & ~(MA_BREAK | MA_STACK)) ==
(mptr->map_pmap.pr_mflags & ~(MA_BREAK | MA_STACK)) &&
pmap->pr_pagesize == mptr->map_pmap.pr_pagesize &&
pmap->pr_shmid == mptr->map_pmap.pr_shmid &&
strcmp(pmap->pr_mapname, mptr->map_pmap.pr_mapname) == 0) {
/*
* This mapping matches exactly. Copy over the old
* mapping, taking care to get the latest flags.
* Make sure the associated file_info_t is updated
* appropriately.
*/
*newp = *mptr;
if (P->map_exec == mptr)
P->map_exec = newp;
if (P->map_ldso == mptr)
P->map_ldso = newp;
newp->map_pmap.pr_mflags = pmap->pr_mflags;
if (mptr->map_file != NULL &&
mptr->map_file->file_map == mptr)
mptr->map_file->file_map = newp;
oldmapcount--;
mptr++;
} else if (pmap->pr_vaddr + pmap->pr_size >
mptr->map_pmap.pr_vaddr) {
/*
* The old mapping doesn't exist any more, remove it
* from the list.
*/
map_info_free(P, mptr);
oldmapcount--;
i--;
newp--;
pmap--;
mptr++;
} else {
/*
* This is a new mapping, add it directly.
*/
newp->map_pmap = *pmap;
}
}
/*
* Free any old maps
*/
while (oldmapcount) {
map_info_free(P, mptr);
oldmapcount--;
mptr++;
}
free(Pmap);
if (P->mappings != NULL)
free(P->mappings);
P->mappings = newmap;
P->map_count = P->map_alloc = nmap;
P->info_valid = 1;
/*
* Consult librtld_db to get the load object
* names for all of the shared libraries.
*/
if (P->rap != NULL)
(void) rd_loadobj_iter(P->rap, map_iter, P);
}
/*
* Update all of the mappings and rtld_db as if by Pupdate_maps(), and then
* forcibly cache all of the symbol tables associated with all object files.
*/
void
Pupdate_syms(struct ps_prochandle *P)
{
file_info_t *fptr = list_next(&P->file_head);
int i;
Pupdate_maps(P);
for (i = 0; i < P->num_files; i++, fptr = list_next(fptr)) {
Pbuild_file_symtab(P, fptr);
(void) Pbuild_file_ctf(P, fptr);
}
}
/*
* Return the librtld_db agent handle for the victim process.
* The handle will become invalid at the next successful exec() and the
* client (caller of proc_rd_agent()) must not use it beyond that point.
* If the process is already dead, we've already tried our best to
* create the agent during core file initialization.
*/
rd_agent_t *
Prd_agent(struct ps_prochandle *P)
{
if (P->rap == NULL && P->state != PS_DEAD && P->state != PS_IDLE) {
Pupdate_maps(P);
if (P->num_files == 0)
load_static_maps(P);
rd_log(_libproc_debug);
if ((P->rap = rd_new(P)) != NULL)
(void) rd_loadobj_iter(P->rap, map_iter, P);
}
return (P->rap);
}
/*
* Return the prmap_t structure containing 'addr', but only if it
* is in the dynamic linker's link map and is the text section.
*/
const prmap_t *
Paddr_to_text_map(struct ps_prochandle *P, uintptr_t addr)
{
map_info_t *mptr;
if (!P->info_valid)
Pupdate_maps(P);
if ((mptr = Paddr2mptr(P, addr)) != NULL) {
file_info_t *fptr = build_map_symtab(P, mptr);
const prmap_t *pmp = &mptr->map_pmap;
if (fptr != NULL && fptr->file_lo != NULL &&
fptr->file_lo->rl_base >= pmp->pr_vaddr &&
fptr->file_lo->rl_base < pmp->pr_vaddr + pmp->pr_size)
return (pmp);
}
return (NULL);
}
/*
* Return the prmap_t structure containing 'addr' (no restrictions on
* the type of mapping).
*/
const prmap_t *
Paddr_to_map(struct ps_prochandle *P, uintptr_t addr)
{
map_info_t *mptr;
if (!P->info_valid)
Pupdate_maps(P);
if ((mptr = Paddr2mptr(P, addr)) != NULL)
return (&mptr->map_pmap);
return (NULL);
}
/*
* Convert a full or partial load object name to the prmap_t for its
* corresponding primary text mapping.
*/
const prmap_t *
Plmid_to_map(struct ps_prochandle *P, Lmid_t lmid, const char *name)
{
map_info_t *mptr;
if (name == PR_OBJ_EVERY)
return (NULL); /* A reasonable mistake */
if ((mptr = object_name_to_map(P, lmid, name)) != NULL)
return (&mptr->map_pmap);
return (NULL);
}
const prmap_t *
Pname_to_map(struct ps_prochandle *P, const char *name)
{
return (Plmid_to_map(P, PR_LMID_EVERY, name));
}
const rd_loadobj_t *
Paddr_to_loadobj(struct ps_prochandle *P, uintptr_t addr)
{
map_info_t *mptr;
if (!P->info_valid)
Pupdate_maps(P);
if ((mptr = Paddr2mptr(P, addr)) == NULL)
return (NULL);
/*
* By building the symbol table, we implicitly bring the PLT
* information up to date in the load object.
*/
(void) build_map_symtab(P, mptr);
return (mptr->map_file->file_lo);
}
const rd_loadobj_t *
Plmid_to_loadobj(struct ps_prochandle *P, Lmid_t lmid, const char *name)
{
map_info_t *mptr;
if (name == PR_OBJ_EVERY)
return (NULL);
if ((mptr = object_name_to_map(P, lmid, name)) == NULL)
return (NULL);
/*
* By building the symbol table, we implicitly bring the PLT
* information up to date in the load object.
*/
(void) build_map_symtab(P, mptr);
return (mptr->map_file->file_lo);
}
const rd_loadobj_t *
Pname_to_loadobj(struct ps_prochandle *P, const char *name)
{
return (Plmid_to_loadobj(P, PR_LMID_EVERY, name));
}
ctf_file_t *
Pbuild_file_ctf(struct ps_prochandle *P, file_info_t *fptr)
{
ctf_sect_t ctdata, symtab, strtab;
sym_tbl_t *symp;
int err;
if (fptr->file_ctfp != NULL)
return (fptr->file_ctfp);
Pbuild_file_symtab(P, fptr);
if (fptr->file_ctf_size == 0)
return (NULL);
symp = fptr->file_ctf_dyn ? &fptr->file_dynsym : &fptr->file_symtab;
if (symp->sym_data == NULL)
return (NULL);
/*
* The buffer may alread be allocated if this is a core file that
* contained CTF data for this file.
*/
if (fptr->file_ctf_buf == NULL) {
fptr->file_ctf_buf = malloc(fptr->file_ctf_size);
if (fptr->file_ctf_buf == NULL) {
dprintf("failed to allocate ctf buffer\n");
return (NULL);
}
if (pread(fptr->file_fd, fptr->file_ctf_buf,
fptr->file_ctf_size, fptr->file_ctf_off) !=
fptr->file_ctf_size) {
free(fptr->file_ctf_buf);
fptr->file_ctf_buf = NULL;
dprintf("failed to read ctf data\n");
return (NULL);
}
}
ctdata.cts_name = ".SUNW_ctf";
ctdata.cts_type = SHT_PROGBITS;
ctdata.cts_flags = 0;
ctdata.cts_data = fptr->file_ctf_buf;
ctdata.cts_size = fptr->file_ctf_size;
ctdata.cts_entsize = 1;
ctdata.cts_offset = 0;
symtab.cts_name = fptr->file_ctf_dyn ? ".dynsym" : ".symtab";
symtab.cts_type = symp->sym_hdr.sh_type;
symtab.cts_flags = symp->sym_hdr.sh_flags;
symtab.cts_data = symp->sym_data->d_buf;
symtab.cts_size = symp->sym_hdr.sh_size;
symtab.cts_entsize = symp->sym_hdr.sh_entsize;
symtab.cts_offset = symp->sym_hdr.sh_offset;
strtab.cts_name = fptr->file_ctf_dyn ? ".dynstr" : ".strtab";
strtab.cts_type = symp->sym_strhdr.sh_type;
strtab.cts_flags = symp->sym_strhdr.sh_flags;
strtab.cts_data = symp->sym_strs;
strtab.cts_size = symp->sym_strhdr.sh_size;
strtab.cts_entsize = symp->sym_strhdr.sh_entsize;
strtab.cts_offset = symp->sym_strhdr.sh_offset;
fptr->file_ctfp = ctf_bufopen(&ctdata, &symtab, &strtab, &err);
if (fptr->file_ctfp == NULL) {
free(fptr->file_ctf_buf);
fptr->file_ctf_buf = NULL;
return (NULL);
}
dprintf("loaded %lu bytes of CTF data for %s\n",
(ulong_t)fptr->file_ctf_size, fptr->file_pname);
return (fptr->file_ctfp);
}
ctf_file_t *
Paddr_to_ctf(struct ps_prochandle *P, uintptr_t addr)
{
map_info_t *mptr;
file_info_t *fptr;
if (!P->info_valid)
Pupdate_maps(P);
if ((mptr = Paddr2mptr(P, addr)) == NULL ||
(fptr = mptr->map_file) == NULL)
return (NULL);
return (Pbuild_file_ctf(P, fptr));
}
ctf_file_t *
Plmid_to_ctf(struct ps_prochandle *P, Lmid_t lmid, const char *name)
{
map_info_t *mptr;
file_info_t *fptr;
if (name == PR_OBJ_EVERY)
return (NULL);
if ((mptr = object_name_to_map(P, lmid, name)) == NULL ||
(fptr = mptr->map_file) == NULL)
return (NULL);
return (Pbuild_file_ctf(P, fptr));
}
ctf_file_t *
Pname_to_ctf(struct ps_prochandle *P, const char *name)
{
return (Plmid_to_ctf(P, PR_LMID_EVERY, name));
}
/*
* If we're not a core file, re-read the /proc/<pid>/auxv file and store
* its contents in P->auxv. In the case of a core file, we either
* initialized P->auxv in Pcore() from the NT_AUXV, or we don't have an
* auxv because the note was missing.
*/
void
Preadauxvec(struct ps_prochandle *P)
{
char auxfile[64];
struct stat statb;
ssize_t naux;
int fd;
if (P->state == PS_DEAD)
return; /* Already read during Pgrab_core() */
if (P->state == PS_IDLE)
return; /* No aux vec for Pgrab_file() */
if (P->auxv != NULL) {
free(P->auxv);
P->auxv = NULL;
P->nauxv = 0;
}
(void) sprintf(auxfile, "/proc/%d/auxv", (int)P->pid);
if ((fd = open(auxfile, O_RDONLY)) < 0)
return;
if (fstat(fd, &statb) == 0 &&
statb.st_size >= sizeof (auxv_t) &&
(P->auxv = malloc(statb.st_size + sizeof (auxv_t))) != NULL) {
if ((naux = read(fd, P->auxv, statb.st_size)) < 0 ||
(naux /= sizeof (auxv_t)) < 1) {
free(P->auxv);
P->auxv = NULL;
} else {
P->auxv[naux].a_type = AT_NULL;
P->auxv[naux].a_un.a_val = 0L;
P->nauxv = (int)naux;
}
}
(void) close(fd);
}
/*
* Return a requested element from the process's aux vector.
* Return -1 on failure (this is adequate for our purposes).
*/
long
Pgetauxval(struct ps_prochandle *P, int type)
{
auxv_t *auxv;
if (P->auxv == NULL)
Preadauxvec(P);
if (P->auxv == NULL)
return (-1);
for (auxv = P->auxv; auxv->a_type != AT_NULL; auxv++) {
if (auxv->a_type == type)
return (auxv->a_un.a_val);
}
return (-1);
}
/*
* Return a pointer to our internal copy of the process's aux vector.
* The caller should not hold on to this pointer across any libproc calls.
*/
const auxv_t *
Pgetauxvec(struct ps_prochandle *P)
{
static const auxv_t empty = { AT_NULL, 0L };
if (P->auxv == NULL)
Preadauxvec(P);
if (P->auxv == NULL)
return (&empty);
return (P->auxv);
}
/*
* Find or build the symbol table for the given mapping.
*/
static file_info_t *
build_map_symtab(struct ps_prochandle *P, map_info_t *mptr)
{
prmap_t *pmap = &mptr->map_pmap;
file_info_t *fptr;
rd_loadobj_t *lop;
uint_t i;
if ((fptr = mptr->map_file) != NULL) {
Pbuild_file_symtab(P, fptr);
return (fptr);
}
if (pmap->pr_mapname[0] == '\0')
return (NULL);
/*
* Attempt to find a matching file.
* (A file can be mapped at several different addresses.)
*/
for (i = 0, fptr = list_next(&P->file_head); i < P->num_files;
i++, fptr = list_next(fptr)) {
if (strcmp(fptr->file_pname, pmap->pr_mapname) == 0 &&
(lop = fptr->file_lo) != NULL &&
((pmap->pr_vaddr <= lop->rl_base &&
lop->rl_base < pmap->pr_vaddr + pmap->pr_size) ||
(pmap->pr_vaddr <= lop->rl_data_base &&
lop->rl_data_base < pmap->pr_vaddr + pmap->pr_size))) {
mptr->map_file = fptr;
fptr->file_ref++;
Pbuild_file_symtab(P, fptr);
return (fptr);
}
}
/*
* If we need to create a new file_info structure, iterate
* through the load objects in order to attempt to connect
* this new file with its primary text mapping. We again
* need to handle ld.so as a special case because we need
* to be able to bootstrap librtld_db.
*/
if ((fptr = file_info_new(P, mptr)) == NULL)
return (NULL);
if (P->map_ldso != mptr) {
if (P->rap != NULL)
(void) rd_loadobj_iter(P->rap, map_iter, P);
else
(void) Prd_agent(P);
} else {
fptr->file_map = mptr;
}
/*
* If librtld_db wasn't able to help us connect the file to a primary
* text mapping, set file_map to the current mapping because we require
* fptr->file_map to be set in Pbuild_file_symtab. librtld_db may be
* unaware of what's going on in the rare case that a legitimate ELF
* file has been mmap(2)ed into the process address space *without*
* the use of dlopen(3x). Why would this happen? See pwdx ... :)
*/
if (fptr->file_map == NULL)
fptr->file_map = mptr;
Pbuild_file_symtab(P, fptr);
return (fptr);
}
static int
read_ehdr32(struct ps_prochandle *P, Elf32_Ehdr *ehdr, uintptr_t addr)
{
if (Pread(P, ehdr, sizeof (*ehdr), addr) != sizeof (*ehdr))
return (-1);
if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
#ifdef _BIG_ENDIAN
ehdr->e_ident[EI_DATA] != ELFDATA2MSB ||
#else
ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
#endif
ehdr->e_ident[EI_VERSION] != EV_CURRENT)
return (-1);
return (0);
}
static int
read_dynamic_phdr32(struct ps_prochandle *P, const Elf32_Ehdr *ehdr,
Elf32_Phdr *phdr, uintptr_t addr)
{
uint_t i;
for (i = 0; i < ehdr->e_phnum; i++) {
uintptr_t a = addr + ehdr->e_phoff + i * ehdr->e_phentsize;
if (Pread(P, phdr, sizeof (*phdr), a) != sizeof (*phdr))
return (-1);
if (phdr->p_type == PT_DYNAMIC)
return (0);
}
return (-1);
}
#ifdef _LP64
static int
read_ehdr64(struct ps_prochandle *P, Elf64_Ehdr *ehdr, uintptr_t addr)
{
if (Pread(P, ehdr, sizeof (Elf64_Ehdr), addr) != sizeof (Elf64_Ehdr))
return (-1);
if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
#ifdef _BIG_ENDIAN
ehdr->e_ident[EI_DATA] != ELFDATA2MSB ||
#else
ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
#endif
ehdr->e_ident[EI_VERSION] != EV_CURRENT)
return (-1);
return (0);
}
static int
read_dynamic_phdr64(struct ps_prochandle *P, const Elf64_Ehdr *ehdr,
Elf64_Phdr *phdr, uintptr_t addr)
{
uint_t i;
for (i = 0; i < ehdr->e_phnum; i++) {
uintptr_t a = addr + ehdr->e_phoff + i * ehdr->e_phentsize;
if (Pread(P, phdr, sizeof (*phdr), a) != sizeof (*phdr))
return (-1);
if (phdr->p_type == PT_DYNAMIC)
return (0);
}
return (-1);
}
#endif /* _LP64 */
/*
* The text segment for each load object contains the elf header and
* program headers. We can use this information to determine if the
* file that corresponds to the load object is the same file that
* was loaded into the process's address space. There can be a discrepency
* if a file is recompiled after the process is started or if the target
* represents a core file from a differently configured system -- two
* common examples. The DT_CHECKSUM entry in the dynamic section
* provides an easy method of comparison. It is important to note that
* the dynamic section usually lives in the data segment, but the meta
* data we use to find the dynamic section lives in the text segment so
* if either of those segments is absent we can't proceed.
*
* We're looking through the elf file for several items: the symbol tables
* (both dynsym and symtab), the procedure linkage table (PLT) base,
* size, and relocation base, and the CTF information. Most of this can
* be recovered from the loaded image of the file itself, the exceptions
* being the symtab and CTF data.
*
* First we try to open the file that we think corresponds to the load
* object, if the DT_CHECKSUM values match, we're all set, and can simply
* recover all the information we need from the file. If the values of
* DT_CHECKSUM don't match, or if we can't access the file for whatever
* reasaon, we fake up a elf file to use in its stead. If we can't read
* the elf data in the process's address space, we fall back to using
* the file even though it may give inaccurate information.
*
* The elf file that we fake up has to consist of sections for the
* dynsym, the PLT and the dynamic section. Note that in the case of a
* core file, we'll get the CTF data in the file_info_t later on from
* a section embedded the core file (if it's present).
*
* file_differs() conservatively looks for mismatched files, identifying
* a match when there is any ambiguity (since that's the legacy behavior).
*/
static int
file_differs(struct ps_prochandle *P, Elf *elf, file_info_t *fptr)
{
Elf_Scn *scn;
GElf_Shdr shdr;
GElf_Dyn dyn;
Elf_Data *data;
uint_t i, ndyn;
GElf_Xword cksum;
uintptr_t addr;
if (fptr->file_map == NULL)
return (0);
if ((Pcontent(P) & (CC_CONTENT_TEXT | CC_CONTENT_DATA)) !=
(CC_CONTENT_TEXT | CC_CONTENT_DATA))
return (0);
/*
* First, we find the checksum value in the elf file.
*/
scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != NULL &&
shdr.sh_type == SHT_DYNAMIC)
goto found_shdr;
}
return (0);
found_shdr:
if ((data = elf_getdata(scn, NULL)) == NULL)
return (0);
if (P->status.pr_dmodel == PR_MODEL_ILP32)
ndyn = shdr.sh_size / sizeof (Elf32_Dyn);
#ifdef _LP64
else if (P->status.pr_dmodel == PR_MODEL_LP64)
ndyn = shdr.sh_size / sizeof (Elf64_Dyn);
#endif
else
return (0);
for (i = 0; i < ndyn; i++) {
if (gelf_getdyn(data, i, &dyn) != NULL &&
dyn.d_tag == DT_CHECKSUM)
goto found_cksum;
}
return (0);
found_cksum:
cksum = dyn.d_un.d_val;
dprintf("elf cksum value is %llx\n", (u_longlong_t)cksum);
/*
* Get the base of the text mapping that corresponds to this file.
*/
addr = fptr->file_map->map_pmap.pr_vaddr;
if (P->status.pr_dmodel == PR_MODEL_ILP32) {
Elf32_Ehdr ehdr;
Elf32_Phdr phdr;
Elf32_Dyn dync, *dynp;
uint_t i;
if (read_ehdr32(P, &ehdr, addr) != 0 ||
read_dynamic_phdr32(P, &ehdr, &phdr, addr) != 0)
return (0);
if (ehdr.e_type == ET_DYN)
phdr.p_vaddr += addr;
if ((dynp = malloc(phdr.p_filesz)) == NULL)
return (0);
dync.d_tag = DT_NULL;
if (Pread(P, dynp, phdr.p_filesz, phdr.p_vaddr) !=
phdr.p_filesz) {
free(dynp);
return (0);
}
for (i = 0; i < phdr.p_filesz / sizeof (Elf32_Dyn); i++) {
if (dynp[i].d_tag == DT_CHECKSUM)
dync = dynp[i];
}
free(dynp);
if (dync.d_tag != DT_CHECKSUM)
return (0);
dprintf("image cksum value is %llx\n",
(u_longlong_t)dync.d_un.d_val);
return (dync.d_un.d_val != cksum);
#ifdef _LP64
} else if (P->status.pr_dmodel == PR_MODEL_LP64) {
Elf64_Ehdr ehdr;
Elf64_Phdr phdr;
Elf64_Dyn dync, *dynp;
uint_t i;
if (read_ehdr64(P, &ehdr, addr) != 0 ||
read_dynamic_phdr64(P, &ehdr, &phdr, addr) != 0)
return (0);
if (ehdr.e_type == ET_DYN)
phdr.p_vaddr += addr;
if ((dynp = malloc(phdr.p_filesz)) == NULL)
return (0);
dync.d_tag = DT_NULL;
if (Pread(P, dynp, phdr.p_filesz, phdr.p_vaddr) !=
phdr.p_filesz) {
free(dynp);
return (0);
}
for (i = 0; i < phdr.p_filesz / sizeof (Elf64_Dyn); i++) {
if (dynp[i].d_tag == DT_CHECKSUM)
dync = dynp[i];
}
free(dynp);
if (dync.d_tag != DT_CHECKSUM)
return (0);
dprintf("image cksum value is %llx\n",
(u_longlong_t)dync.d_un.d_val);
return (dync.d_un.d_val != cksum);
#endif /* _LP64 */
}
return (0);
}
static Elf *
fake_elf(struct ps_prochandle *P, file_info_t *fptr)
{
enum {
DI_PLTGOT = 0,
DI_JMPREL,
DI_PLTRELSZ,
DI_PLTREL,
DI_SYMTAB,
DI_HASH,
DI_SYMENT,
DI_STRTAB,
DI_STRSZ,
DI_NENT
};
uintptr_t addr;
size_t size = 0;
caddr_t elfdata = NULL;
Elf *elf;
Elf32_Word nchain;
static char shstr[] = ".shstrtab\0.dynsym\0.dynstr\0.dynamic\0.plt";
if (fptr->file_map == NULL)
return (NULL);
if ((Pcontent(P) & (CC_CONTENT_TEXT | CC_CONTENT_DATA)) !=
(CC_CONTENT_TEXT | CC_CONTENT_DATA))
return (NULL);
addr = fptr->file_map->map_pmap.pr_vaddr;
/*
* We're building a in memory elf file that will let us use libelf
* for most of the work we need to later (e.g. symbol table lookups).
* We need sections for the dynsym, dynstr, and plt, and we need
* the program headers from the text section. The former is used in
* Pbuild_file_symtab(); the latter is used in several functions in
* Pcore.c to reconstruct the origin of each mapping from the load
* object that spawned it.
*
* Here are some useful pieces of elf trivia that will help
* to elucidate this code.
*
* All the information we need about the dynstr can be found in these
* two entries in the dynamic section:
*
* DT_STRTAB base of dynstr
* DT_STRSZ size of dynstr
*
* So deciphering the dynstr is pretty straightforward.
*
* The dynsym is a little trickier.
*
* DT_SYMTAB base of dynsym
* DT_SYMENT size of a dynstr entry (Elf{32,64}_Sym)
* DT_HASH base of hash table for dynamic lookups
*
* The DT_SYMTAB entry gives us any easy way of getting to the base
* of the dynsym, but getting the size involves rooting around in the
* dynamic lookup hash table. Here's the layout of the hash table:
*
* +-------------------+
* | nbucket | All values are of type
* +-------------------+ Elf32_Word
* | nchain |
* +-------------------+
* | bucket[0] |
* | . . . |
* | bucket[nbucket-1] |
* +-------------------+
* | chain[0] |
* | . . . |
* | chain[nchain-1] |
* +-------------------+
* (figure 5-12 from the SYS V Generic ABI)
*
* Symbols names are hashed into a particular bucket which contains
* an index into the symbol table. Each entry in the symbol table
* has a corresponding entry in the chain table which tells the
* consumer where the next entry in the hash chain is. We can use
* the nchain field to find out the size of the dynsym.
*
* We can figure out the size of the .plt section, but it takes some
* doing. We need to use the following information:
*
* DT_PLTGOT base of the PLT
* DT_JMPREL base of the PLT's relocation section
* DT_PLTRELSZ size of the PLT's relocation section
* DT_PLTREL type of the PLT's relocation section
*
* We can use the relocation section to figure out the address of the
* last entry and subtract off the value of DT_PLTGOT to calculate
* the size of the PLT.
*
* For more information, check out the System V Generic ABI.
*/
if (P->status.pr_dmodel == PR_MODEL_ILP32) {
Elf32_Ehdr ehdr, *ep;
Elf32_Phdr phdr;
Elf32_Shdr *sp;
Elf32_Dyn *dp;
Elf32_Dyn *d[DI_NENT] = { 0 };
uint_t i, dcount = 0;
uint32_t off;
size_t pltsz = 0, pltentsz;
if (read_ehdr32(P, &ehdr, addr) != 0 ||
read_dynamic_phdr32(P, &ehdr, &phdr, addr) != 0)
return (NULL);
if (ehdr.e_type == ET_DYN)
phdr.p_vaddr += addr;
if ((dp = malloc(phdr.p_filesz)) == NULL)
return (NULL);
if (Pread(P, dp, phdr.p_filesz, phdr.p_vaddr) !=
phdr.p_filesz) {
free(dp);
return (NULL);
}
for (i = 0; i < phdr.p_filesz / sizeof (Elf32_Dyn); i++) {
switch (dp[i].d_tag) {
/*
* For the .plt section.
*/
case DT_PLTGOT:
d[DI_PLTGOT] = &dp[i];
continue;
case DT_JMPREL:
d[DI_JMPREL] = &dp[i];
continue;
case DT_PLTRELSZ:
d[DI_PLTRELSZ] = &dp[i];
continue;
case DT_PLTREL:
d[DI_PLTREL] = &dp[i];
continue;
default:
continue;
/*
* For the .dynsym section.
*/
case DT_SYMTAB:
d[DI_SYMTAB] = &dp[i];
break;
case DT_HASH:
d[DI_HASH] = &dp[i];
break;
case DT_SYMENT:
d[DI_SYMENT] = &dp[i];
break;
/*
* For the .dynstr section.
*/
case DT_STRTAB:
d[DI_STRTAB] = &dp[i];
break;
case DT_STRSZ:
d[DI_STRSZ] = &dp[i];
break;
}
dcount++;
}
/*
* We need all of those dynamic entries in order to put
* together a complete set of elf sections, but we'll
* let the PLT section slide if need be. The dynsym- and
* dynstr-related dynamic entries are mandatory in both
* executables and shared objects so if one of those is
* missing, we're in some trouble and should abort.
*/
if (dcount + 4 != DI_NENT) {
dprintf("text section missing required dynamic "
"entries\n");
return (NULL);
}
if (ehdr.e_type == ET_DYN) {
if (d[DI_PLTGOT] != NULL)
d[DI_PLTGOT]->d_un.d_ptr += addr;
if (d[DI_JMPREL] != NULL)
d[DI_JMPREL]->d_un.d_ptr += addr;
d[DI_SYMTAB]->d_un.d_ptr += addr;
d[DI_HASH]->d_un.d_ptr += addr;
d[DI_STRTAB]->d_un.d_ptr += addr;
}
/* elf header */
size = sizeof (Elf32_Ehdr);
/* program headers from in-core elf fragment */
size += ehdr.e_phnum * ehdr.e_phentsize;
/* unused shdr, and .shstrtab section */
size += sizeof (Elf32_Shdr);
size += sizeof (Elf32_Shdr);
size += roundup(sizeof (shstr), 4);
/* .dynsym section */
size += sizeof (Elf32_Shdr);
if (Pread(P, &nchain, sizeof (nchain),
d[DI_HASH]->d_un.d_ptr + 4) != sizeof (nchain))
goto bad32;
size += sizeof (Elf32_Sym) * nchain;
/* .dynstr section */
size += sizeof (Elf32_Shdr);
size += roundup(d[DI_STRSZ]->d_un.d_val, 4);
/* .dynamic section */
size += sizeof (Elf32_Shdr);
size += roundup(phdr.p_filesz, 4);
/* .plt section */
if (d[DI_PLTGOT] != NULL && d[DI_JMPREL] != NULL &&
d[DI_PLTRELSZ] != NULL && d[DI_PLTREL] != NULL) {
uintptr_t penult, ult;
uintptr_t jmprel = d[DI_JMPREL]->d_un.d_ptr;
size_t pltrelsz = d[DI_PLTRELSZ]->d_un.d_val;
if (d[DI_PLTREL]->d_un.d_val == DT_RELA) {
uint_t ndx = pltrelsz / sizeof (Elf32_Rela) - 2;
Elf32_Rela r[2];
if (Pread(P, r, sizeof (r), jmprel +
sizeof (r[0]) * ndx) != sizeof (r))
goto bad32;
penult = r[0].r_offset;
ult = r[1].r_offset;
} else if (d[DI_PLTREL]->d_un.d_val == DT_REL) {
uint_t ndx = pltrelsz / sizeof (Elf32_Rel) - 2;
Elf32_Rel r[2];
if (Pread(P, r, sizeof (r), jmprel +
sizeof (r[0]) * ndx) != sizeof (r))
goto bad32;
penult = r[0].r_offset;
ult = r[1].r_offset;
} else {
goto bad32;
}
pltentsz = ult - penult;
if (ehdr.e_type == ET_DYN)
ult += addr;
pltsz = ult - d[DI_PLTGOT]->d_un.d_ptr + pltentsz;
size += sizeof (Elf32_Shdr);
size += roundup(pltsz, 4);
}
if ((elfdata = calloc(1, size)) == NULL)
goto bad32;
/* LINTED - alignment */
ep = (Elf32_Ehdr *)elfdata;
(void) memcpy(ep, &ehdr, offsetof(Elf32_Ehdr, e_phoff));
ep->e_ehsize = sizeof (Elf32_Ehdr);
ep->e_phoff = sizeof (Elf32_Ehdr);
ep->e_phentsize = ehdr.e_phentsize;
ep->e_phnum = ehdr.e_phnum;
ep->e_shoff = ep->e_phoff + ep->e_phnum * ep->e_phentsize;
ep->e_shentsize = sizeof (Elf32_Shdr);
ep->e_shnum = (pltsz == 0) ? 5 : 6;
ep->e_shstrndx = 1;
/* LINTED - alignment */
sp = (Elf32_Shdr *)(elfdata + ep->e_shoff);
off = ep->e_shoff + ep->e_shentsize * ep->e_shnum;
/*
* Copying the program headers directly from the process's
* address space is a little suspect, but since we only
* use them for their address and size values, this is fine.
*/
if (Pread(P, &elfdata[ep->e_phoff],
ep->e_phnum * ep->e_phentsize, addr + ehdr.e_phoff) !=
ep->e_phnum * ep->e_phentsize) {
free(elfdata);
goto bad32;
}
/*
* The first elf section is always skipped.
*/
sp++;
/*
* Section Header[1] sh_name: .shstrtab
*/
sp->sh_name = 0;
sp->sh_type = SHT_STRTAB;
sp->sh_flags = SHF_STRINGS;
sp->sh_addr = 0;
sp->sh_offset = off;
sp->sh_size = sizeof (shstr);
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 1;
sp->sh_entsize = 0;
(void) memcpy(&elfdata[off], shstr, sizeof (shstr));
off += roundup(sp->sh_size, 4);
sp++;
/*
* Section Header[2] sh_name: .dynsym
*/
sp->sh_name = 10;
sp->sh_type = SHT_DYNSYM;
sp->sh_flags = SHF_ALLOC;
sp->sh_addr = d[DI_SYMTAB]->d_un.d_ptr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = nchain * sizeof (Elf32_Sym);
sp->sh_link = 3;
sp->sh_info = 1;
sp->sh_addralign = 4;
sp->sh_entsize = sizeof (Elf32_Sym);
if (Pread(P, &elfdata[off], sp->sh_size,
d[DI_SYMTAB]->d_un.d_ptr) != sp->sh_size) {
free(elfdata);
goto bad32;
}
off += roundup(sp->sh_size, 4);
sp++;
/*
* Section Header[3] sh_name: .dynstr
*/
sp->sh_name = 18;
sp->sh_type = SHT_STRTAB;
sp->sh_flags = SHF_ALLOC | SHF_STRINGS;
sp->sh_addr = d[DI_STRTAB]->d_un.d_ptr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = d[DI_STRSZ]->d_un.d_val;
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 1;
sp->sh_entsize = 0;
if (Pread(P, &elfdata[off], sp->sh_size,
d[DI_STRTAB]->d_un.d_ptr) != sp->sh_size) {
free(elfdata);
goto bad32;
}
off += roundup(sp->sh_size, 4);
sp++;
/*
* Section Header[4] sh_name: .dynamic
*/
sp->sh_name = 26;
sp->sh_type = SHT_DYNAMIC;
sp->sh_flags = SHF_WRITE | SHF_ALLOC;
sp->sh_addr = phdr.p_vaddr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = phdr.p_filesz;
sp->sh_link = 3;
sp->sh_info = 0;
sp->sh_addralign = 4;
sp->sh_entsize = sizeof (Elf32_Dyn);
(void) memcpy(&elfdata[off], dp, sp->sh_size);
off += roundup(sp->sh_size, 4);
sp++;
/*
* Section Header[5] sh_name: .plt
*/
if (pltsz != 0) {
sp->sh_name = 35;
sp->sh_type = SHT_PROGBITS;
sp->sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR;
sp->sh_addr = d[DI_PLTGOT]->d_un.d_ptr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = pltsz;
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 4;
sp->sh_entsize = pltentsz;
if (Pread(P, &elfdata[off], sp->sh_size,
d[DI_PLTGOT]->d_un.d_ptr) != sp->sh_size) {
free(elfdata);
goto bad32;
}
off += roundup(sp->sh_size, 4);
sp++;
}
free(dp);
goto good;
bad32:
free(dp);
return (NULL);
#ifdef _LP64
} else if (P->status.pr_dmodel == PR_MODEL_LP64) {
Elf64_Ehdr ehdr, *ep;
Elf64_Phdr phdr;
Elf64_Shdr *sp;
Elf64_Dyn *dp;
Elf64_Dyn *d[DI_NENT] = { 0 };
uint_t i, dcount = 0;
uint64_t off;
size_t pltsz = 0, pltentsz;
if (read_ehdr64(P, &ehdr, addr) != 0 ||
read_dynamic_phdr64(P, &ehdr, &phdr, addr) != 0)
return (NULL);
if (ehdr.e_type == ET_DYN)
phdr.p_vaddr += addr;
if ((dp = malloc(phdr.p_filesz)) == NULL)
return (NULL);
if (Pread(P, dp, phdr.p_filesz, phdr.p_vaddr) !=
phdr.p_filesz) {
free(dp);
return (NULL);
}
for (i = 0; i < phdr.p_filesz / sizeof (Elf64_Dyn); i++) {
switch (dp[i].d_tag) {
/*
* For the .plt section.
*/
case DT_PLTGOT:
d[DI_PLTGOT] = &dp[i];
continue;
case DT_JMPREL:
d[DI_JMPREL] = &dp[i];
continue;
case DT_PLTRELSZ:
d[DI_PLTRELSZ] = &dp[i];
continue;
case DT_PLTREL:
d[DI_PLTREL] = &dp[i];
continue;
default:
continue;
/*
* For the .dynsym section.
*/
case DT_SYMTAB:
d[DI_SYMTAB] = &dp[i];
break;
case DT_HASH:
d[DI_HASH] = &dp[i];
break;
case DT_SYMENT:
d[DI_SYMENT] = &dp[i];
break;
/*
* For the .dynstr section.
*/
case DT_STRTAB:
d[DI_STRTAB] = &dp[i];
break;
case DT_STRSZ:
d[DI_STRSZ] = &dp[i];
break;
}
dcount++;
}
/*
* We need all of those dynamic entries in order to put
* together a complete set of elf sections, but we'll
* let the PLT section slide if need be. The dynsym- and
* dynstr-related dynamic entries are mandatory in both
* executables and shared objects so if one of those is
* missing, we're in some trouble and should abort.
*/
if (dcount + 4 != DI_NENT) {
dprintf("text section missing required dynamic "
"entries\n");
return (NULL);
}
if (ehdr.e_type == ET_DYN) {
if (d[DI_PLTGOT] != NULL)
d[DI_PLTGOT]->d_un.d_ptr += addr;
if (d[DI_JMPREL] != NULL)
d[DI_JMPREL]->d_un.d_ptr += addr;
d[DI_SYMTAB]->d_un.d_ptr += addr;
d[DI_HASH]->d_un.d_ptr += addr;
d[DI_STRTAB]->d_un.d_ptr += addr;
}
/* elf header */
size = sizeof (Elf64_Ehdr);
/* program headers from in-core elf fragment */
size += ehdr.e_phnum * ehdr.e_phentsize;
/* unused shdr, and .shstrtab section */
size += sizeof (Elf64_Shdr);
size += sizeof (Elf64_Shdr);
size += roundup(sizeof (shstr), 8);
/* .dynsym section */
size += sizeof (Elf64_Shdr);
if (Pread(P, &nchain, sizeof (nchain),
d[DI_HASH]->d_un.d_ptr + 4) != sizeof (nchain))
goto bad64;
size += sizeof (Elf64_Sym) * nchain;
/* .dynstr section */
size += sizeof (Elf64_Shdr);
size += roundup(d[DI_STRSZ]->d_un.d_val, 8);
/* .dynamic section */
size += sizeof (Elf64_Shdr);
size += roundup(phdr.p_filesz, 8);
/* .plt section */
if (d[DI_PLTGOT] != NULL && d[DI_JMPREL] != NULL &&
d[DI_PLTRELSZ] != NULL && d[DI_PLTREL] != NULL) {
uintptr_t penult, ult;
uintptr_t jmprel = d[DI_JMPREL]->d_un.d_ptr;
size_t pltrelsz = d[DI_PLTRELSZ]->d_un.d_val;
if (d[DI_PLTREL]->d_un.d_val == DT_RELA) {
uint_t ndx = pltrelsz / sizeof (Elf64_Rela) - 2;
Elf64_Rela r[2];
if (Pread(P, r, sizeof (r), jmprel +
sizeof (r[0]) * ndx) != sizeof (r))
goto bad64;
penult = r[0].r_offset;
ult = r[1].r_offset;
} else if (d[DI_PLTREL]->d_un.d_val == DT_REL) {
uint_t ndx = pltrelsz / sizeof (Elf64_Rel) - 2;
Elf64_Rel r[2];
if (Pread(P, r, sizeof (r), jmprel +
sizeof (r[0]) * ndx) != sizeof (r))
goto bad64;
penult = r[0].r_offset;
ult = r[1].r_offset;
} else {
goto bad64;
}
pltentsz = ult - penult;
if (ehdr.e_type == ET_DYN)
ult += addr;
pltsz = ult - d[DI_PLTGOT]->d_un.d_ptr + pltentsz;
size += sizeof (Elf64_Shdr);
size += roundup(pltsz, 8);
}
if ((elfdata = calloc(1, size)) == NULL)
goto bad64;
/* LINTED - alignment */
ep = (Elf64_Ehdr *)elfdata;
(void) memcpy(ep, &ehdr, offsetof(Elf64_Ehdr, e_phoff));
ep->e_ehsize = sizeof (Elf64_Ehdr);
ep->e_phoff = sizeof (Elf64_Ehdr);
ep->e_phentsize = ehdr.e_phentsize;
ep->e_phnum = ehdr.e_phnum;
ep->e_shoff = ep->e_phoff + ep->e_phnum * ep->e_phentsize;
ep->e_shentsize = sizeof (Elf64_Shdr);
ep->e_shnum = (pltsz == 0) ? 5 : 6;
ep->e_shstrndx = 1;
/* LINTED - alignment */
sp = (Elf64_Shdr *)(elfdata + ep->e_shoff);
off = ep->e_shoff + ep->e_shentsize * ep->e_shnum;
/*
* Copying the program headers directly from the process's
* address space is a little suspect, but since we only
* use them for their address and size values, this is fine.
*/
if (Pread(P, &elfdata[ep->e_phoff],
ep->e_phnum * ep->e_phentsize, addr + ehdr.e_phoff) !=
ep->e_phnum * ep->e_phentsize) {
free(elfdata);
goto bad64;
}
/*
* The first elf section is always skipped.
*/
sp++;
/*
* Section Header[1] sh_name: .shstrtab
*/
sp->sh_name = 0;
sp->sh_type = SHT_STRTAB;
sp->sh_flags = SHF_STRINGS;
sp->sh_addr = 0;
sp->sh_offset = off;
sp->sh_size = sizeof (shstr);
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 1;
sp->sh_entsize = 0;
(void) memcpy(&elfdata[off], shstr, sizeof (shstr));
off += roundup(sp->sh_size, 8);
sp++;
/*
* Section Header[2] sh_name: .dynsym
*/
sp->sh_name = 10;
sp->sh_type = SHT_DYNSYM;
sp->sh_flags = SHF_ALLOC;
sp->sh_addr = d[DI_SYMTAB]->d_un.d_ptr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = nchain * sizeof (Elf64_Sym);
sp->sh_link = 3;
sp->sh_info = 1;
sp->sh_addralign = 8;
sp->sh_entsize = sizeof (Elf64_Sym);
if (Pread(P, &elfdata[off], sp->sh_size,
d[DI_SYMTAB]->d_un.d_ptr) != sp->sh_size) {
free(elfdata);
goto bad64;
}
off += roundup(sp->sh_size, 8);
sp++;
/*
* Section Header[3] sh_name: .dynstr
*/
sp->sh_name = 18;
sp->sh_type = SHT_STRTAB;
sp->sh_flags = SHF_ALLOC | SHF_STRINGS;
sp->sh_addr = d[DI_STRTAB]->d_un.d_ptr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = d[DI_STRSZ]->d_un.d_val;
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 1;
sp->sh_entsize = 0;
if (Pread(P, &elfdata[off], sp->sh_size,
d[DI_STRTAB]->d_un.d_ptr) != sp->sh_size) {
free(elfdata);
goto bad64;
}
off += roundup(sp->sh_size, 8);
sp++;
/*
* Section Header[4] sh_name: .dynamic
*/
sp->sh_name = 26;
sp->sh_type = SHT_DYNAMIC;
sp->sh_flags = SHF_WRITE | SHF_ALLOC;
sp->sh_addr = phdr.p_vaddr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = phdr.p_filesz;
sp->sh_link = 3;
sp->sh_info = 0;
sp->sh_addralign = 8;
sp->sh_entsize = sizeof (Elf64_Dyn);
(void) memcpy(&elfdata[off], dp, sp->sh_size);
off += roundup(sp->sh_size, 8);
sp++;
/*
* Section Header[5] sh_name: .plt
*/
if (pltsz != 0) {
sp->sh_name = 35;
sp->sh_type = SHT_PROGBITS;
sp->sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR;
sp->sh_addr = d[DI_PLTGOT]->d_un.d_ptr;
if (ehdr.e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = pltsz;
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 8;
sp->sh_entsize = pltentsz;
if (Pread(P, &elfdata[off], sp->sh_size,
d[DI_PLTGOT]->d_un.d_ptr) != sp->sh_size) {
free(elfdata);
goto bad64;
}
off += roundup(sp->sh_size, 8);
sp++;
}
free(dp);
goto good;
bad64:
free(dp);
return (NULL);
#endif /* _LP64 */
}
good:
if ((elf = elf_memory(elfdata, size)) == NULL) {
free(elfdata);
return (NULL);
}
fptr->file_elfmem = elfdata;
return (elf);
}
/*
* We wouldn't need these if qsort(3C) took an argument for the callback...
*/
static mutex_t sort_mtx = DEFAULTMUTEX;
static char *sort_strs;
static GElf_Sym *sort_syms;
int
byaddr_cmp_common(GElf_Sym *a, char *aname, GElf_Sym *b, char *bname)
{
if (a->st_value < b->st_value)
return (-1);
if (a->st_value > b->st_value)
return (1);
/*
* Prefer the function to the non-function.
*/
if (GELF_ST_TYPE(a->st_info) != GELF_ST_TYPE(b->st_info)) {
if (GELF_ST_TYPE(a->st_info) == STT_FUNC)
return (-1);
if (GELF_ST_TYPE(b->st_info) == STT_FUNC)
return (1);
}
/*
* Prefer the weak or strong global symbol to the local symbol.
*/
if (GELF_ST_BIND(a->st_info) != GELF_ST_BIND(b->st_info)) {
if (GELF_ST_BIND(b->st_info) == STB_LOCAL)
return (-1);
if (GELF_ST_BIND(a->st_info) == STB_LOCAL)
return (1);
}
/*
* Prefer the name with fewer leading underscores in the name.
*/
while (*aname == '_' && *bname == '_') {
aname++;
bname++;
}
if (*bname == '_')
return (-1);
if (*aname == '_')
return (1);
/*
* Prefer the symbol with the smaller size.
*/
if (a->st_size < b->st_size)
return (-1);
if (a->st_size > b->st_size)
return (1);
/*
* All other factors being equal, fall back to lexicographic order.
*/
return (strcmp(aname, bname));
}
static int
byaddr_cmp(const void *aa, const void *bb)
{
GElf_Sym *a = &sort_syms[*(uint_t *)aa];
GElf_Sym *b = &sort_syms[*(uint_t *)bb];
char *aname = sort_strs + a->st_name;
char *bname = sort_strs + b->st_name;
return (byaddr_cmp_common(a, aname, b, bname));
}
static int
byname_cmp(const void *aa, const void *bb)
{
GElf_Sym *a = &sort_syms[*(uint_t *)aa];
GElf_Sym *b = &sort_syms[*(uint_t *)bb];
char *aname = sort_strs + a->st_name;
char *bname = sort_strs + b->st_name;
return (strcmp(aname, bname));
}
void
optimize_symtab(sym_tbl_t *symtab)
{
GElf_Sym *symp, *syms;
uint_t i, *indexa, *indexb;
Elf_Data *data;
size_t symn, strsz, count;
if (symtab == NULL || symtab->sym_data == NULL ||
symtab->sym_byaddr != NULL)
return;
data = symtab->sym_data;
symn = symtab->sym_symn;
strsz = symtab->sym_strsz;
symp = syms = malloc(sizeof (GElf_Sym) * symn);
/*
* First record all the symbols into a table and count up the ones
* that we're interested in. We mark symbols as invalid by setting
* the st_name to an illegal value.
*/
for (i = 0, count = 0; i < symn; i++, symp++) {
if (gelf_getsym(data, i, symp) != NULL &&
symp->st_name < strsz &&
IS_DATA_TYPE(GELF_ST_TYPE(symp->st_info)))
count++;
else
symp->st_name = strsz;
}
/*
* Allocate sufficient space for both tables and populate them
* with the same symbols we just counted.
*/
symtab->sym_count = count;
indexa = symtab->sym_byaddr = calloc(sizeof (uint_t), count);
indexb = symtab->sym_byname = calloc(sizeof (uint_t), count);
for (i = 0, symp = syms; i < symn; i++, symp++) {
if (symp->st_name < strsz)
*indexa++ = *indexb++ = i;
}
/*
* Sort the two tables according to the appropriate criteria.
*/
(void) mutex_lock(&sort_mtx);
sort_strs = symtab->sym_strs;
sort_syms = syms;
qsort(symtab->sym_byaddr, count, sizeof (uint_t), byaddr_cmp);
qsort(symtab->sym_byname, count, sizeof (uint_t), byname_cmp);
sort_strs = NULL;
sort_syms = NULL;
(void) mutex_unlock(&sort_mtx);
free(syms);
}
/*
* Build the symbol table for the given mapped file.
*/
void
Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr)
{
char objectfile[PATH_MAX];
uint_t i;
GElf_Ehdr ehdr;
GElf_Sym s;
Elf_Data *shdata;
Elf_Scn *scn;
Elf *elf;
struct {
GElf_Shdr c_shdr;
Elf_Data *c_data;
const char *c_name;
} *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL;
if (fptr->file_init)
return; /* We've already processed this file */
/*
* Mark the file_info struct as having the symbol table initialized
* even if we fail below. We tried once; we don't try again.
*/
fptr->file_init = 1;
if (elf_version(EV_CURRENT) == EV_NONE) {
dprintf("libproc ELF version is more recent than libelf\n");
return;
}
if (P->state == PS_DEAD || P->state == PS_IDLE) {
/*
* If we're a not live, we can't open files from the /proc
* object directory; we have only the mapping and file names
* to guide us. We prefer the file_lname, but need to handle
* the case of it being NULL in order to bootstrap: we first
* come here during rd_new() when the only information we have
* is interpreter name associated with the AT_BASE mapping.
*/
(void) snprintf(objectfile, sizeof (objectfile), "%s",
fptr->file_lname ? fptr->file_lname : fptr->file_pname);
} else {
(void) snprintf(objectfile, sizeof (objectfile),
"/proc/%d/object/%s", (int)P->pid, fptr->file_pname);
}
/*
* Open the object file, create the elf file, and then get the elf
* header and .shstrtab data buffer so we can process sections by
* name. If anything goes wrong try to fake up an elf file from
* the in-core elf image.
*/
if ((fptr->file_fd = open(objectfile, O_RDONLY)) < 0) {
dprintf("Pbuild_file_symtab: failed to open %s: %s\n",
objectfile, strerror(errno));
if ((elf = fake_elf(P, fptr)) == NULL ||
elf_kind(elf) != ELF_K_ELF ||
gelf_getehdr(elf, &ehdr) == NULL ||
(scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL ||
(shdata = elf_getdata(scn, NULL)) == NULL) {
dprintf("failed to fake up ELF file\n");
return;
}
} else if ((elf = elf_begin(fptr->file_fd, ELF_C_READ, NULL)) == NULL ||
elf_kind(elf) != ELF_K_ELF ||
gelf_getehdr(elf, &ehdr) == NULL ||
(scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL ||
(shdata = elf_getdata(scn, NULL)) == NULL) {
dprintf("failed to process ELF file %s: %s\n",
objectfile, elf_errmsg(elf_errno()));
if ((elf = fake_elf(P, fptr)) == NULL ||
elf_kind(elf) != ELF_K_ELF ||
gelf_getehdr(elf, &ehdr) == NULL ||
(scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL ||
(shdata = elf_getdata(scn, NULL)) == NULL) {
dprintf("failed to fake up ELF file\n");
goto bad;
}
} else if (file_differs(P, elf, fptr)) {
Elf *newelf;
/*
* Before we get too excited about this elf file, we'll check
* its checksum value against the value we have in memory. If
* they don't agree, we try to fake up a new elf file and
* proceed with that instead.
*/
dprintf("ELF file %s (%lx) doesn't match in-core image\n",
fptr->file_pname,
(ulong_t)fptr->file_map->map_pmap.pr_vaddr);
if ((newelf = fake_elf(P, fptr)) == NULL ||
elf_kind(newelf) != ELF_K_ELF ||
gelf_getehdr(newelf, &ehdr) == NULL ||
(scn = elf_getscn(newelf, ehdr.e_shstrndx)) == NULL ||
(shdata = elf_getdata(scn, NULL)) == NULL) {
dprintf("failed to fake up ELF file\n");
} else {
(void) elf_end(elf);
elf = newelf;
dprintf("switched to faked up ELF file\n");
}
}
if ((cache = malloc(ehdr.e_shnum * sizeof (*cache))) == NULL) {
dprintf("failed to malloc section cache for %s\n", objectfile);
goto bad;
}
dprintf("processing ELF file %s\n", objectfile);
fptr->file_class = ehdr.e_ident[EI_CLASS];
fptr->file_etype = ehdr.e_type;
fptr->file_elf = elf;
/*
* Iterate through each section, caching its section header, data
* pointer, and name. We use this for handling sh_link values below.
*/
for (cp = cache + 1, scn = NULL; scn = elf_nextscn(elf, scn); cp++) {
if (gelf_getshdr(scn, &cp->c_shdr) == NULL)
goto bad; /* Failed to get section header */
if ((cp->c_data = elf_getdata(scn, NULL)) == NULL)
goto bad; /* Failed to get section data */
if (cp->c_shdr.sh_name >= shdata->d_size)
goto bad; /* Corrupt section name */
cp->c_name = (const char *)shdata->d_buf + cp->c_shdr.sh_name;
}
/*
* Now iterate through the section cache in order to locate info
* for the .symtab, .dynsym, .dynamic, .plt, and .SUNW_ctf sections:
*/
for (i = 1, cp = cache + 1; i < ehdr.e_shnum; i++, cp++) {
GElf_Shdr *shp = &cp->c_shdr;
if (shp->sh_type == SHT_SYMTAB || shp->sh_type == SHT_DYNSYM) {
sym_tbl_t *symp = shp->sh_type == SHT_SYMTAB ?
&fptr->file_symtab : &fptr->file_dynsym;
/*
* It's possible that the we already got the symbol
* table from the core file itself. Either the file
* differs in which case our faked up elf file will
* only contain the dynsym (not the symtab) or the
* file matches in which case we'll just be replacing
* the symbol table we pulled out of the core file
* with an equivalent one. In either case, this
* check isn't essential, but it's a good idea.
*/
if (symp->sym_data == NULL) {
symp->sym_data = cp->c_data;
symp->sym_symn = shp->sh_size / shp->sh_entsize;
symp->sym_strs =
cache[shp->sh_link].c_data->d_buf;
symp->sym_strsz =
cache[shp->sh_link].c_data->d_size;
symp->sym_hdr = cp->c_shdr;
symp->sym_strhdr = cache[shp->sh_link].c_shdr;
}
} else if (shp->sh_type == SHT_DYNAMIC) {
dyn = cp;
} else if (strcmp(cp->c_name, ".plt") == 0) {
plt = cp;
} else if (strcmp(cp->c_name, ".SUNW_ctf") == 0) {
/*
* Skip over bogus CTF sections so they don't come back
* to haunt us later.
*/
if (shp->sh_link == 0 ||
shp->sh_link > ehdr.e_shnum ||
(cache[shp->sh_link].c_shdr.sh_type != SHT_DYNSYM &&
cache[shp->sh_link].c_shdr.sh_type != SHT_SYMTAB)) {
dprintf("Bad sh_link %d for "
"CTF\n", shp->sh_link);
continue;
}
ctf = cp;
}
}
/*
* At this point, we've found all the symbol tables we're ever going
* to find: the ones in the loop above and possibly the symtab that
* was included in the core file. Before we perform any lookups, we
* create sorted versions to optimize for lookups.
*/
optimize_symtab(&fptr->file_symtab);
optimize_symtab(&fptr->file_dynsym);
/*
* Fill in the base address of the text mapping for shared libraries.
* This allows us to translate symbols before librtld_db is ready.
*/
if (fptr->file_etype == ET_DYN) {
fptr->file_dyn_base = fptr->file_map->map_pmap.pr_vaddr -
fptr->file_map->map_pmap.pr_offset;
dprintf("setting file_dyn_base for %s to %p\n",
objectfile, (void *)fptr->file_dyn_base);
}
/*
* Record the CTF section information in the file info structure.
*/
if (ctf != NULL) {
fptr->file_ctf_off = ctf->c_shdr.sh_offset;
fptr->file_ctf_size = ctf->c_shdr.sh_size;
if (ctf->c_shdr.sh_link != 0 &&
cache[ctf->c_shdr.sh_link].c_shdr.sh_type == SHT_DYNSYM)
fptr->file_ctf_dyn = 1;
}
if (fptr->file_lo == NULL)
goto done; /* Nothing else to do if no load object info */
/*
* If the object is a shared library and we have a different rl_base
* value, reset file_dyn_base according to librtld_db's information.
*/
if (fptr->file_etype == ET_DYN &&
fptr->file_lo->rl_base != fptr->file_dyn_base) {
dprintf("resetting file_dyn_base for %s to %p\n",
objectfile, (void *)fptr->file_lo->rl_base);
fptr->file_dyn_base = fptr->file_lo->rl_base;
}
/*
* Fill in the PLT information for this file if a PLT symbol is found.
*/
if (sym_by_name(&fptr->file_dynsym, "_PROCEDURE_LINKAGE_TABLE_", &s,
NULL) != NULL) {
fptr->file_plt_base = s.st_value + fptr->file_dyn_base;
fptr->file_plt_size = (plt != NULL) ? plt->c_shdr.sh_size : 0;
/*
* Bring the load object up to date; it is the only way the
* user has to access the PLT data. The PLT information in the
* rd_loadobj_t is not set in the call to map_iter() (the
* callback for rd_loadobj_iter) where we set file_lo.
*/
fptr->file_lo->rl_plt_base = fptr->file_plt_base;
fptr->file_lo->rl_plt_size = fptr->file_plt_size;
dprintf("PLT found at %p, size = %lu\n",
(void *)fptr->file_plt_base, (ulong_t)fptr->file_plt_size);
}
/*
* Fill in the PLT information.
*/
if (dyn != NULL) {
uintptr_t dynaddr = dyn->c_shdr.sh_addr + fptr->file_dyn_base;
size_t ndyn = dyn->c_shdr.sh_size / dyn->c_shdr.sh_entsize;
GElf_Dyn d;
for (i = 0; i < ndyn; i++) {
if (gelf_getdyn(dyn->c_data, i, &d) != NULL &&
d.d_tag == DT_JMPREL) {
fptr->file_jmp_rel =
d.d_un.d_ptr + fptr->file_dyn_base;
break;
}
}
dprintf("_DYNAMIC found at %p, %lu entries, DT_JMPREL = %p\n",
(void *)dynaddr, (ulong_t)ndyn, (void *)fptr->file_jmp_rel);
}
done:
free(cache);
return;
bad:
if (cache != NULL)
free(cache);
(void) elf_end(elf);
fptr->file_elf = NULL;
if (fptr->file_elfmem != NULL) {
free(fptr->file_elfmem);
fptr->file_elfmem = NULL;
}
(void) close(fptr->file_fd);
fptr->file_fd = -1;
}
/*
* Given a process virtual address, return the map_info_t containing it.
* If none found, return NULL.
*/
map_info_t *
Paddr2mptr(struct ps_prochandle *P, uintptr_t addr)
{
int lo = 0;
int hi = P->map_count - 1;
int mid;
map_info_t *mp;
while (lo <= hi) {
mid = (lo + hi) / 2;
mp = &P->mappings[mid];
/* check that addr is in [vaddr, vaddr + size) */
if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size)
return (mp);
if (addr < mp->map_pmap.pr_vaddr)
hi = mid - 1;
else
lo = mid + 1;
}
return (NULL);
}
/*
* Return the map_info_t for the executable file.
* If not found, return NULL.
*/
static map_info_t *
exec_map(struct ps_prochandle *P)
{
uint_t i;
map_info_t *mptr;
map_info_t *mold = NULL;
file_info_t *fptr;
uintptr_t base;
for (i = 0, mptr = P->mappings; i < P->map_count; i++, mptr++) {
if (mptr->map_pmap.pr_mapname[0] == '\0')
continue;
if (strcmp(mptr->map_pmap.pr_mapname, "a.out") == 0) {
if ((fptr = mptr->map_file) != NULL &&
fptr->file_lo != NULL) {
base = fptr->file_lo->rl_base;
if (base >= mptr->map_pmap.pr_vaddr &&
base < mptr->map_pmap.pr_vaddr +
mptr->map_pmap.pr_size) /* text space */
return (mptr);
mold = mptr; /* must be the data */
continue;
}
/* This is a poor way to test for text space */
if (!(mptr->map_pmap.pr_mflags & MA_EXEC) ||
(mptr->map_pmap.pr_mflags & MA_WRITE)) {
mold = mptr;
continue;
}
return (mptr);
}
}
return (mold);
}
/*
* Given a shared object name, return the map_info_t for it. If no matching
* object is found, return NULL. Normally, the link maps contain the full
* object pathname, e.g. /usr/lib/libc.so.1. We allow the object name to
* take one of the following forms:
*
* 1. An exact match (i.e. a full pathname): "/usr/lib/libc.so.1"
* 2. An exact basename match: "libc.so.1"
* 3. An initial basename match up to a '.' suffix: "libc.so" or "libc"
* 4. The literal string "a.out" is an alias for the executable mapping
*
* The third case is a convenience for callers and may not be necessary.
*
* As the exact same object name may be loaded on different link maps (see
* dlmopen(3DL)), we also allow the caller to resolve the object name by
* specifying a particular link map id. If lmid is PR_LMID_EVERY, the
* first matching name will be returned, regardless of the link map id.
*/
static map_info_t *
object_to_map(struct ps_prochandle *P, Lmid_t lmid, const char *objname)
{
map_info_t *mp;
file_info_t *fp;
size_t objlen;
uint_t i;
/*
* First pass: look for exact matches of the entire pathname or
* basename (cases 1 and 2 above):
*/
for (i = 0, mp = P->mappings; i < P->map_count; i++, mp++) {
if (mp->map_pmap.pr_mapname[0] == '\0' ||
(fp = mp->map_file) == NULL || fp->file_lname == NULL)
continue;
if (lmid != PR_LMID_EVERY &&
(fp->file_lo == NULL || lmid != fp->file_lo->rl_lmident))
continue;
/*
* If we match, return the primary text mapping; otherwise
* just return the mapping we matched.
*/
if (strcmp(fp->file_lname, objname) == 0 ||
strcmp(fp->file_lbase, objname) == 0)
return (fp->file_map ? fp->file_map : mp);
}
objlen = strlen(objname);
/*
* Second pass: look for partial matches (case 3 above):
*/
for (i = 0, mp = P->mappings; i < P->map_count; i++, mp++) {
if (mp->map_pmap.pr_mapname[0] == '\0' ||
(fp = mp->map_file) == NULL || fp->file_lname == NULL)
continue;
if (lmid != PR_LMID_EVERY &&
(fp->file_lo == NULL || lmid != fp->file_lo->rl_lmident))
continue;
/*
* If we match, return the primary text mapping; otherwise
* just return the mapping we matched.
*/
if (strncmp(fp->file_lbase, objname, objlen) == 0 &&
fp->file_lbase[objlen] == '.')
return (fp->file_map ? fp->file_map : mp);
}
/*
* One last check: we allow "a.out" to always alias the executable,
* assuming this name was not in use for something else.
*/
if ((lmid == PR_LMID_EVERY || lmid == LM_ID_BASE) &&
(strcmp(objname, "a.out") == 0))
return (P->map_exec);
return (NULL);
}
static map_info_t *
object_name_to_map(struct ps_prochandle *P, Lmid_t lmid, const char *name)
{
map_info_t *mptr;
if (!P->info_valid)
Pupdate_maps(P);
if (P->map_exec == NULL && ((mptr = Paddr2mptr(P,
Pgetauxval(P, AT_ENTRY))) != NULL || (mptr = exec_map(P)) != NULL))
P->map_exec = mptr;
if (P->map_ldso == NULL && (mptr = Paddr2mptr(P,
Pgetauxval(P, AT_BASE))) != NULL)
P->map_ldso = mptr;
if (name == PR_OBJ_EXEC)
mptr = P->map_exec;
else if (name == PR_OBJ_LDSO)
mptr = P->map_ldso;
else if (Prd_agent(P) != NULL || P->state == PS_IDLE)
mptr = object_to_map(P, lmid, name);
else
mptr = NULL;
return (mptr);
}
/*
* When two symbols are found by address, decide which one is to be preferred.
*/
static GElf_Sym *
sym_prefer(GElf_Sym *sym1, char *name1, GElf_Sym *sym2, char *name2)
{
/*
* Prefer the non-NULL symbol.
*/
if (sym1 == NULL)
return (sym2);
if (sym2 == NULL)
return (sym1);
/*
* Defer to the sort ordering...
*/
return (byaddr_cmp_common(sym1, name1, sym2, name2) <= 0 ? sym1 : sym2);
}
/*
* Look up a symbol by address in the specified symbol table.
* Adjustment to 'addr' must already have been made for the
* offset of the symbol if this is a dynamic library symbol table.
*/
static GElf_Sym *
sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
{
Elf_Data *data = symtab->sym_data;
GElf_Sym sym, osym;
uint_t i, oid, *byaddr = symtab->sym_byaddr;
int min, max, mid, omid, found = 0;
if (data == NULL)
return (NULL);
min = 0;
max = symtab->sym_count - 1;
osym.st_value = 0;
/*
* We can't return when we've found a match, we have to continue
* searching for the closest matching symbol.
*/
while (min <= max) {
mid = (max + min) / 2;
i = byaddr[mid];
(void) gelf_getsym(data, i, &sym);
if (addr >= sym.st_value &&
addr < sym.st_value + sym.st_size &&
(!found || sym.st_value > osym.st_value)) {
osym = sym;
omid = mid;
oid = i;
found = 1;
}
if (addr < sym.st_value)
max = mid - 1;
else
min = mid + 1;
}
if (!found)
return (NULL);
/*
* There may be many symbols with identical values so we walk
* backward in the byaddr table to find the best match.
*/
do {
sym = osym;
i = oid;
if (omid == 0)
break;
oid = byaddr[--omid];
(void) gelf_getsym(data, oid, &osym);
} while (addr >= osym.st_value &&
addr < sym.st_value + osym.st_size &&
osym.st_value == sym.st_value);
*symp = sym;
if (idp != NULL)
*idp = i;
return (symp);
}
/*
* Look up a symbol by name in the specified symbol table.
*/
static GElf_Sym *
sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
{
Elf_Data *data = symtab->sym_data;
char *strs = symtab->sym_strs;
uint_t i, *byname = symtab->sym_byname;
int min, mid, max, cmp;
if (data == NULL || strs == NULL)
return (NULL);
min = 0;
max = symtab->sym_count - 1;
while (min <= max) {
mid = (max + min) / 2;
i = byname[mid];
(void) gelf_getsym(data, i, symp);
if ((cmp = strcmp(name, strs + symp->st_name)) == 0) {
if (idp != NULL)
*idp = i;
return (symp);
}
if (cmp < 0)
max = mid - 1;
else
min = mid + 1;
}
return (NULL);
}
/*
* Search the process symbol tables looking for a symbol whose
* value to value+size contain the address specified by addr.
* Return values are:
* sym_name_buffer containing the symbol name
* GElf_Sym symbol table entry
* prsyminfo_t ancillary symbol information
* Returns 0 on success, -1 on failure.
*/
int
Pxlookup_by_addr(
struct ps_prochandle *P,
uintptr_t addr, /* process address being sought */
char *sym_name_buffer, /* buffer for the symbol name */
size_t bufsize, /* size of sym_name_buffer */
GElf_Sym *symbolp, /* returned symbol table entry */
prsyminfo_t *sip) /* returned symbol info */
{
GElf_Sym *symp;
char *name;
GElf_Sym sym1, *sym1p = NULL;
GElf_Sym sym2, *sym2p = NULL;
char *name1 = NULL;
char *name2 = NULL;
uint_t i1;
uint_t i2;
map_info_t *mptr;
file_info_t *fptr;
(void) Prd_agent(P);
if ((mptr = Paddr2mptr(P, addr)) == NULL || /* no such address */
(fptr = build_map_symtab(P, mptr)) == NULL || /* no mapped file */
fptr->file_elf == NULL) /* not an ELF file */
return (-1);
/*
* Adjust the address by the load object base address in
* case the address turns out to be in a shared library.
*/
addr -= fptr->file_dyn_base;
/*
* Search both symbol tables, symtab first, then dynsym.
*/
if ((sym1p = sym_by_addr(&fptr->file_symtab, addr, &sym1, &i1)) != NULL)
name1 = fptr->file_symtab.sym_strs + sym1.st_name;
if ((sym2p = sym_by_addr(&fptr->file_dynsym, addr, &sym2, &i2)) != NULL)
name2 = fptr->file_dynsym.sym_strs + sym2.st_name;
if ((symp = sym_prefer(sym1p, name1, sym2p, name2)) == NULL)
return (-1);
name = (symp == sym1p) ? name1 : name2;
if (bufsize > 0) {
(void) strncpy(sym_name_buffer, name, bufsize);
sym_name_buffer[bufsize - 1] = '\0';
}
*symbolp = *symp;
if (sip != NULL) {
sip->prs_name = bufsize == 0 ? NULL : sym_name_buffer;
sip->prs_object = fptr->file_lbase;
sip->prs_id = (symp == sym1p) ? i1 : i2;
sip->prs_table = (symp == sym1p) ? PR_SYMTAB : PR_DYNSYM;
sip->prs_lmid = (fptr->file_lo == NULL) ? LM_ID_BASE :
fptr->file_lo->rl_lmident;
}
if (GELF_ST_TYPE(symbolp->st_info) != STT_TLS)
symbolp->st_value += fptr->file_dyn_base;
return (0);
}
int
Plookup_by_addr(struct ps_prochandle *P, uintptr_t addr, char *buf, size_t size,
GElf_Sym *symp)
{
return (Pxlookup_by_addr(P, addr, buf, size, symp, NULL));
}
/*
* Search the process symbol tables looking for a symbol whose name matches the
* specified name and whose object and link map optionally match the specified
* parameters. On success, the function returns 0 and fills in the GElf_Sym
* symbol table entry. On failure, -1 is returned.
*/
int
Pxlookup_by_name(
struct ps_prochandle *P,
Lmid_t lmid, /* link map to match, or -1 for any */
const char *oname, /* load object name */
const char *sname, /* symbol name */
GElf_Sym *symp, /* returned symbol table entry */
prsyminfo_t *sip) /* returned symbol info */
{
map_info_t *mptr;
file_info_t *fptr;
int cnt;
GElf_Sym sym;
prsyminfo_t si;
int rv = -1;
uint_t id;
if (oname == PR_OBJ_EVERY) {
/* create all the file_info_t's for all the mappings */
(void) Prd_agent(P);
cnt = P->num_files;
fptr = list_next(&P->file_head);
} else {
cnt = 1;
if ((mptr = object_name_to_map(P, lmid, oname)) == NULL ||
(fptr = build_map_symtab(P, mptr)) == NULL)
return (-1);
}
/*
* Iterate through the loaded object files and look for the symbol
* name in the .symtab and .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.
*/
for (; cnt > 0; cnt--, fptr = list_next(fptr)) {
Pbuild_file_symtab(P, fptr);
if (fptr->file_elf == NULL)
continue;
if (lmid != PR_LMID_EVERY && fptr->file_lo != NULL &&
lmid != fptr->file_lo->rl_lmident)
continue;
if (fptr->file_symtab.sym_data != NULL &&
sym_by_name(&fptr->file_symtab, sname, symp, &id)) {
if (sip != NULL) {
sip->prs_id = id;
sip->prs_table = PR_SYMTAB;
sip->prs_object = oname;
sip->prs_name = sname;
sip->prs_lmid = fptr->file_lo == NULL ?
LM_ID_BASE : fptr->file_lo->rl_lmident;
}
} else if (fptr->file_dynsym.sym_data != NULL &&
sym_by_name(&fptr->file_dynsym, sname, symp, &id)) {
if (sip != NULL) {
sip->prs_id = id;
sip->prs_table = PR_DYNSYM;
sip->prs_object = oname;
sip->prs_name = sname;
sip->prs_lmid = fptr->file_lo == NULL ?
LM_ID_BASE : fptr->file_lo->rl_lmident;
}
} else {
continue;
}
if (GELF_ST_TYPE(symp->st_info) != STT_TLS)
symp->st_value += fptr->file_dyn_base;
if (symp->st_shndx != SHN_UNDEF)
return (0);
if (rv != 0) {
if (sip != NULL)
si = *sip;
sym = *symp;
rv = 0;
}
}
if (rv == 0) {
if (sip != NULL)
*sip = si;
*symp = sym;
}
return (rv);
}
/*
* Search the process symbol tables looking for a symbol whose name matches the
* specified name, but without any restriction on the link map id.
*/
int
Plookup_by_name(struct ps_prochandle *P, const char *object,
const char *symbol, GElf_Sym *symp)
{
return (Pxlookup_by_name(P, PR_LMID_EVERY, object, symbol, symp, NULL));
}
/*
* Iterate over the process's address space mappings.
*/
int
Pmapping_iter(struct ps_prochandle *P, proc_map_f *func, void *cd)
{
map_info_t *mptr;
file_info_t *fptr;
char *object_name;
int rc = 0;
int i;
/* create all the file_info_t's for all the mappings */
(void) Prd_agent(P);
for (i = 0, mptr = P->mappings; i < P->map_count; i++, mptr++) {
if ((fptr = mptr->map_file) == NULL)
object_name = NULL;
else
object_name = fptr->file_lname;
if ((rc = func(cd, &mptr->map_pmap, object_name)) != 0)
return (rc);
}
return (0);
}
/*
* Iterate over the process's mapped objects.
*/
int
Pobject_iter(struct ps_prochandle *P, proc_map_f *func, void *cd)
{
map_info_t *mptr;
file_info_t *fptr;
uint_t cnt;
int rc = 0;
(void) Prd_agent(P); /* create file_info_t's for all the mappings */
Pupdate_maps(P);
for (cnt = P->num_files, fptr = list_next(&P->file_head);
cnt; cnt--, fptr = list_next(fptr)) {
const char *lname = fptr->file_lname ? fptr->file_lname : "";
if ((mptr = fptr->file_map) == NULL)
continue;
if ((rc = func(cd, &mptr->map_pmap, lname)) != 0)
return (rc);
}
return (0);
}
/*
* Given a virtual address, return the name of the underlying
* mapped object (file), as provided by the dynamic linker.
* Return NULL on failure (no underlying shared library).
*/
char *
Pobjname(struct ps_prochandle *P, uintptr_t addr,
char *buffer, size_t bufsize)
{
map_info_t *mptr;
file_info_t *fptr;
/* create all the file_info_t's for all the mappings */
(void) Prd_agent(P);
if ((mptr = Paddr2mptr(P, addr)) != NULL &&
(fptr = mptr->map_file) != NULL &&
fptr->file_lname != NULL) {
(void) strncpy(buffer, fptr->file_lname, bufsize);
if (strlen(fptr->file_lname) >= bufsize)
buffer[bufsize-1] = '\0';
return (buffer);
}
return (NULL);
}
/*
* Given a virtual address, return the link map id of the underlying mapped
* object (file), as provided by the dynamic linker. Return -1 on failure.
*/
int
Plmid(struct ps_prochandle *P, uintptr_t addr, Lmid_t *lmidp)
{
map_info_t *mptr;
file_info_t *fptr;
/* create all the file_info_t's for all the mappings */
(void) Prd_agent(P);
if ((mptr = Paddr2mptr(P, addr)) != NULL &&
(fptr = mptr->map_file) != NULL && fptr->file_lo != NULL) {
*lmidp = fptr->file_lo->rl_lmident;
return (0);
}
return (-1);
}
/*
* Given an object name and optional lmid, iterate over the object's symbols.
* If which == PR_SYMTAB, search the normal symbol table.
* If which == PR_DYNSYM, search the dynamic symbol table.
*/
static int
Psymbol_iter_com(struct ps_prochandle *P, Lmid_t lmid, const char *object_name,
int which, int mask, pr_order_t order, proc_xsym_f *func, void *cd)
{
GElf_Sym sym;
map_info_t *mptr;
file_info_t *fptr;
sym_tbl_t *symtab;
Elf_Data *data;
size_t symn;
const char *strs;
size_t strsz;
prsyminfo_t si;
int rv;
uint_t *map, i, count, ndx;
if ((mptr = object_name_to_map(P, lmid, object_name)) == NULL)
return (-1);
if ((fptr = build_map_symtab(P, mptr)) == NULL || /* no mapped file */
fptr->file_elf == NULL) /* not an ELF file */
return (-1);
/*
* Search the specified symbol table.
*/
switch (which) {
case PR_SYMTAB:
symtab = &fptr->file_symtab;
si.prs_table = PR_SYMTAB;
break;
case PR_DYNSYM:
symtab = &fptr->file_dynsym;
si.prs_table = PR_DYNSYM;
break;
default:
return (-1);
}
si.prs_object = object_name;
si.prs_lmid = fptr->file_lo == NULL ?
LM_ID_BASE : fptr->file_lo->rl_lmident;
data = symtab->sym_data;
symn = symtab->sym_symn;
strs = symtab->sym_strs;
strsz = symtab->sym_strsz;
if (data == NULL || strs == NULL)
return (-1);
switch (order) {
case PRO_NATURAL:
map = NULL;
count = symn;
break;
case PRO_BYNAME:
map = symtab->sym_byname;
count = symtab->sym_count;
break;
case PRO_BYADDR:
map = symtab->sym_byaddr;
count = symtab->sym_count;
break;
default:
return (-1);
}
rv = 0;
for (i = 0; i < count; i++) {
ndx = map == NULL ? i : map[i];
if (gelf_getsym(data, ndx, &sym) != NULL) {
uint_t s_bind, s_type, type;
if (sym.st_name >= strsz) /* invalid st_name */
continue;
s_bind = GELF_ST_BIND(sym.st_info);
s_type = GELF_ST_TYPE(sym.st_info);
/*
* In case you haven't already guessed, this relies on
* the bitmask used in <libproc.h> for encoding symbol
* type and binding matching the order of STB and STT
* constants in <sys/elf.h>. ELF can't change without
* breaking binary compatibility, so I think this is
* reasonably fair game.
*/
if (s_bind < STB_NUM && s_type < STT_NUM) {
type = (1 << (s_type + 8)) | (1 << s_bind);
if ((type & ~mask) != 0)
continue;
} else
continue; /* Invalid type or binding */
if (GELF_ST_TYPE(sym.st_info) != STT_TLS)
sym.st_value += fptr->file_dyn_base;
si.prs_name = strs + sym.st_name;
si.prs_id = ndx;
if ((rv = func(cd, &sym, strs + sym.st_name, &si)) != 0)
break;
}
}
return (rv);
}
int
Pxsymbol_iter(struct ps_prochandle *P, Lmid_t lmid, const char *object_name,
int which, int mask, proc_xsym_f *func, void *cd)
{
return (Psymbol_iter_com(P, lmid, object_name, which, mask,
PRO_NATURAL, func, cd));
}
int
Psymbol_iter_by_lmid(struct ps_prochandle *P, Lmid_t lmid,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, lmid, object_name, which, mask,
PRO_NATURAL, (proc_xsym_f *)func, cd));
}
int
Psymbol_iter(struct ps_prochandle *P,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, PR_LMID_EVERY, object_name, which, mask,
PRO_NATURAL, (proc_xsym_f *)func, cd));
}
int
Psymbol_iter_by_addr(struct ps_prochandle *P,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, PR_LMID_EVERY, object_name, which, mask,
PRO_BYADDR, (proc_xsym_f *)func, cd));
}
int
Psymbol_iter_by_name(struct ps_prochandle *P,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, PR_LMID_EVERY, object_name, which, mask,
PRO_BYNAME, (proc_xsym_f *)func, cd));
}
/*
* Get the platform string from the core file if we have it;
* just perform the system call for the caller if this is a live process.
*/
char *
Pplatform(struct ps_prochandle *P, char *s, size_t n)
{
if (P->state == PS_IDLE) {
errno = ENODATA;
return (NULL);
}
if (P->state == PS_DEAD) {
if (P->core->core_platform == NULL) {
errno = ENODATA;
return (NULL);
}
(void) strncpy(s, P->core->core_platform, n - 1);
s[n - 1] = '\0';
} else if (sysinfo(SI_PLATFORM, s, n) == -1)
return (NULL);
return (s);
}
/*
* Get the uname(2) information from the core file if we have it;
* just perform the system call for the caller if this is a live process.
*/
int
Puname(struct ps_prochandle *P, struct utsname *u)
{
if (P->state == PS_IDLE) {
errno = ENODATA;
return (-1);
}
if (P->state == PS_DEAD) {
if (P->core->core_uts == NULL) {
errno = ENODATA;
return (-1);
}
(void) memcpy(u, P->core->core_uts, sizeof (struct utsname));
return (0);
}
return (uname(u));
}
/*
* Get the zone name from the core file if we have it; look up the
* name based on the zone id if this is a live process.
*/
char *
Pzonename(struct ps_prochandle *P, char *s, size_t n)
{
if (P->state == PS_IDLE) {
errno = ENODATA;
return (NULL);
}
if (P->state == PS_DEAD) {
if (P->core->core_zonename == NULL) {
errno = ENODATA;
return (NULL);
}
(void) strlcpy(s, P->core->core_zonename, n);
} else {
if (getzonenamebyid(P->status.pr_zoneid, s, n) < 0)
return (NULL);
s[n - 1] = '\0';
}
return (s);
}
/*
* Called from Pcreate(), Pgrab(), and Pfgrab_core() to initialize
* the symbol table heads in the new ps_prochandle.
*/
void
Pinitsym(struct ps_prochandle *P)
{
P->num_files = 0;
list_link(&P->file_head, NULL);
}
/*
* Called from Prelease() to destroy the symbol tables.
* Must be called by the client after an exec() in the victim process.
*/
void
Preset_maps(struct ps_prochandle *P)
{
int i;
if (P->rap != NULL) {
rd_delete(P->rap);
P->rap = NULL;
}
if (P->execname != NULL) {
free(P->execname);
P->execname = NULL;
}
if (P->auxv != NULL) {
free(P->auxv);
P->auxv = NULL;
P->nauxv = 0;
}
for (i = 0; i < P->map_count; i++)
map_info_free(P, &P->mappings[i]);
if (P->mappings != NULL) {
free(P->mappings);
P->mappings = NULL;
}
P->map_count = P->map_alloc = 0;
P->info_valid = 0;
}
typedef struct getenv_data {
char *buf;
size_t bufsize;
const char *search;
size_t searchlen;
} getenv_data_t;
/*ARGSUSED*/
static int
getenv_func(void *data, struct ps_prochandle *P, uintptr_t addr,
const char *nameval)
{
getenv_data_t *d = data;
size_t len;
if (nameval == NULL)
return (0);
if (d->searchlen < strlen(nameval) &&
strncmp(nameval, d->search, d->searchlen) == 0 &&
nameval[d->searchlen] == '=') {
len = MIN(strlen(nameval), d->bufsize - 1);
(void) strncpy(d->buf, nameval, len);
d->buf[len] = '\0';
return (1);
}
return (0);
}
char *
Pgetenv(struct ps_prochandle *P, const char *name, char *buf, size_t buflen)
{
getenv_data_t d;
d.buf = buf;
d.bufsize = buflen;
d.search = name;
d.searchlen = strlen(name);
if (Penv_iter(P, getenv_func, &d) == 1) {
char *equals = strchr(d.buf, '=');
if (equals != NULL) {
(void) memmove(d.buf, equals + 1,
d.buf + buflen - equals - 1);
d.buf[d.buf + buflen - equals] = '\0';
return (buf);
}
}
return (NULL);
}
/* number of argument or environment pointers to read all at once */
#define NARG 100
int
Penv_iter(struct ps_prochandle *P, proc_env_f *func, void *data)
{
const psinfo_t *psp;
uintptr_t envpoff;
GElf_Sym sym;
int ret;
char *buf, *nameval;
size_t buflen;
int nenv = NARG;
long envp[NARG];
/*
* Attempt to find the "_environ" variable in the process.
* Failing that, use the original value provided by Ppsinfo().
*/
if ((psp = Ppsinfo(P)) == NULL)
return (-1);
envpoff = psp->pr_envp; /* Default if no _environ found */
if (Plookup_by_name(P, PR_OBJ_EXEC, "_environ", &sym) == 0) {
if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
if (Pread(P, &envpoff, sizeof (envpoff),
sym.st_value) != sizeof (envpoff))
envpoff = psp->pr_envp;
} else if (P->status.pr_dmodel == PR_MODEL_ILP32) {
uint32_t envpoff32;
if (Pread(P, &envpoff32, sizeof (envpoff32),
sym.st_value) != sizeof (envpoff32))
envpoff = psp->pr_envp;
else
envpoff = envpoff32;
}
}
buflen = 128;
buf = malloc(buflen);
ret = 0;
for (;;) {
uintptr_t envoff;
if (nenv == NARG) {
(void) memset(envp, 0, sizeof (envp));
if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
if (Pread(P, envp,
sizeof (envp), envpoff) <= 0) {
ret = -1;
break;
}
} else if (P->status.pr_dmodel == PR_MODEL_ILP32) {
uint32_t e32[NARG];
int i;
(void) memset(e32, 0, sizeof (e32));
if (Pread(P, e32, sizeof (e32), envpoff) <= 0) {
ret = -1;
break;
}
for (i = 0; i < NARG; i++)
envp[i] = e32[i];
}
nenv = 0;
}
if ((envoff = envp[nenv++]) == NULL)
break;
/*
* Attempt to read the string from the process.
*/
again:
ret = Pread_string(P, buf, buflen, envoff);
if (ret <= 0) {
nameval = NULL;
} else if (ret == buflen - 1) {
free(buf);
/*
* Bail if we have a corrupted environment
*/
if (buflen >= ARG_MAX)
return (-1);
buflen *= 2;
buf = malloc(buflen);
goto again;
} else {
nameval = buf;
}
if ((ret = func(data, P, envoff, nameval)) != 0)
break;
envpoff += (P->status.pr_dmodel == PR_MODEL_LP64)? 8 : 4;
}
free(buf);
return (ret);
}