#include <assert.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/crc32.h>
#include "libproc.h"
#include "Pcontrol.h"
#include "Putil.h"
#include "Psymtab_machelf.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 *, uint_t *,
uintptr_t);
#ifdef _LP64
static int read_ehdr64(struct ps_prochandle *, Elf64_Ehdr *, uint_t *,
uintptr_t);
#endif
static uint32_t psym_crc32[] = { CRC32_TABLE };
#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)
#define MINBUILDID 2
#define MAXBUILDID 64
#define BUILDID_STRLEN (3*MAXBUILDID)
#define BUILDID_NAME ".note.gnu.build-id"
#define DBGLINK_NAME ".gnu_debuglink"
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);
}
static uintptr_t *
get_saddrs(struct ps_prochandle *P, uintptr_t ehdr_start, uint_t *n)
{
uintptr_t a, addr, *addrs, last = 0;
uint_t i, naddrs = 0, unordered = 0;
if (P->status.pr_dmodel == PR_MODEL_ILP32) {
Elf32_Ehdr ehdr;
Elf32_Phdr phdr;
uint_t phnum;
if (read_ehdr32(P, &ehdr, &phnum, ehdr_start) != 0)
return (NULL);
addrs = malloc(sizeof (uintptr_t) * phnum * 2);
a = ehdr_start + ehdr.e_phoff;
for (i = 0; i < phnum; i++, a += ehdr.e_phentsize) {
if (Pread(P, &phdr, sizeof (phdr), a) !=
sizeof (phdr)) {
free(addrs);
return (NULL);
}
if (phdr.p_type != PT_LOAD || phdr.p_memsz == 0)
continue;
addr = phdr.p_vaddr;
if (ehdr.e_type == ET_DYN)
addr += ehdr_start;
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;
uint_t phnum;
if (read_ehdr64(P, &ehdr, &phnum, ehdr_start) != 0)
return (NULL);
addrs = malloc(sizeof (uintptr_t) * phnum * 2);
a = ehdr_start + ehdr.e_phoff;
for (i = 0; i < phnum; i++, a += ehdr.e_phentsize) {
if (Pread(P, &phdr, sizeof (phdr), a) !=
sizeof (phdr)) {
free(addrs);
return (NULL);
}
if (phdr.p_type != PT_LOAD || phdr.p_memsz == 0)
continue;
addr = phdr.p_vaddr;
if (ehdr.e_type == ET_DYN)
addr += ehdr_start;
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);
*n = naddrs;
return (addrs);
}
file_info_t *
file_info_new(struct ps_prochandle *P, map_info_t *mptr)
{
file_info_t *fptr;
map_info_t *mp;
uintptr_t mstart, mend, sstart, send;
uint_t i;
if ((fptr = calloc(1, sizeof (file_info_t))) == NULL)
return (NULL);
list_insert_tail(&P->file_head, fptr);
(void) strcpy(fptr->file_pname, mptr->map_pmap.pr_mapname);
mptr->map_file = fptr;
fptr->file_ref = 1;
fptr->file_fd = -1;
fptr->file_dbgfile = -1;
P->num_files++;
if ((fptr->file_saddrs = get_saddrs(P, mptr->map_pmap.pr_vaddr,
&fptr->file_nsaddrs)) == NULL)
return (fptr);
mp = P->mappings;
i = 0;
while (mp < P->mappings + P->map_count && i < fptr->file_nsaddrs) {
mstart = mp->map_pmap.pr_vaddr;
mend = mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size;
sstart = fptr->file_saddrs[i];
send = fptr->file_saddrs[i + 1];
if (mend <= sstart) {
mp++;
} else if (mstart >= send) {
i += 2;
} else {
if (mp->map_file == NULL) {
Pdprintf("file_info_new: associating "
"segment at %p\n",
(void *)mp->map_pmap.pr_vaddr);
mp->map_file = fptr;
fptr->file_ref++;
} else {
Pdprintf("file_info_new: segment at %p "
"already associated with %s\n",
(void *)mp->map_pmap.pr_vaddr,
(mp == mptr ? "this file" :
mp->map_file->file_pname));
}
mp++;
}
}
return (fptr);
}
static void
file_info_free(struct ps_prochandle *P, file_info_t *fptr)
{
if (--fptr->file_ref == 0) {
list_remove(&P->file_head, 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_rname)
free(fptr->file_rname);
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_dbgelf)
(void) elf_end(fptr->file_dbgelf);
if (fptr->file_dbgfile >= 0)
(void) close(fptr->file_dbgfile);
ctf_close(fptr->file_ctfp);
free(fptr->file_ctf_buf);
if (fptr->file_saddrs)
free(fptr->file_saddrs);
free(fptr);
P->num_files--;
}
}
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;
}
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;
Pdprintf("encountered rd object at %p\n", (void *)lop->rl_base);
if ((mptr = Paddr2mptr(P, lop->rl_base)) == NULL) {
Pdprintf("map_iter: base address doesn't match any mapping\n");
return (1);
}
if ((fptr = mptr->map_file) == NULL &&
(fptr = file_info_new(P, mptr)) == NULL) {
Pdprintf("map_iter: failed to allocate a new file_info_t\n");
return (1);
}
if ((fptr->file_lo == NULL) &&
(fptr->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) {
Pdprintf("map_iter: failed to allocate rd_loadobj_t\n");
file_info_free(P, fptr);
return (1);
}
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;
fptr->file_lbase = NULL;
}
if (fptr->file_rname) {
free(fptr->file_rname);
fptr->file_rname = NULL;
fptr->file_rbase = 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);
} else {
Pdprintf("map_iter: failed to read string at %p\n",
(void *)lop->rl_nameaddr);
}
if ((Pfindmap(P, mptr, buf, sizeof (buf)) != NULL) &&
((fptr->file_rname = strdup(buf)) != NULL))
fptr->file_rbase = basename(fptr->file_rname);
Pdprintf("loaded rd object %s lmid %lx\n",
fptr->file_lname ? buf : "<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;
char buf[PATH_MAX];
if ((fptr = mptr->map_file) == NULL &&
(fptr = file_info_new(P, mptr)) == NULL)
return;
fptr->file_map = mptr;
if ((fptr->file_lo == NULL) &&
(fptr->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) {
file_info_free(P, fptr);
return;
}
(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 == NULL) &&
(fptr->file_lname = strdup(lname)) != NULL)
fptr->file_lbase = basename(fptr->file_lname);
if ((Pfindmap(P, mptr, buf, sizeof (buf)) != NULL) &&
((fptr->file_rname = strdup(buf)) != NULL))
fptr->file_rbase = basename(fptr->file_rname);
}
static void
load_static_maps(struct ps_prochandle *P)
{
map_info_t *mptr;
if ((mptr = object_name_to_map(P, PR_LMID_EVERY, PR_OBJ_EXEC)) != NULL)
map_set(P, mptr, "a.out");
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");
}
int
Preadmaps(struct ps_prochandle *P, prmap_t **Pmapp, ssize_t *nmapp)
{
return (P->ops.pop_read_maps(P, Pmapp, nmapp, P->data));
}
void
Pupdate_maps(struct ps_prochandle *P)
{
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 || P->state == PS_UNDEAD)
return;
Preadauxvec(P);
if (Preadmaps(P, &Pmap, &nmap) != 0)
return;
if ((newmap = calloc(1, nmap * sizeof (map_info_t))) == NULL)
return;
mptr = P->mappings;
pmap = Pmap;
newp = newmap;
oldmapcount = P->map_count;
for (i = 0; i < nmap; i++, pmap++, newp++) {
if (oldmapcount == 0) {
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) {
*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) {
map_info_free(P, mptr);
oldmapcount--;
i--;
newp--;
pmap--;
mptr++;
} else {
newp->map_pmap = *pmap;
}
}
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;
if (P->rap != NULL)
(void) rd_loadobj_iter(P->rap, map_iter, P);
}
void
Pupdate_syms(struct ps_prochandle *P)
{
file_info_t *fptr;
Pupdate_maps(P);
for (fptr = list_head(&P->file_head); fptr != NULL;
fptr = list_next(&P->file_head, fptr)) {
Pbuild_file_symtab(P, fptr);
(void) Pbuild_file_ctf(P, fptr);
}
}
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);
}
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_data_base == (uintptr_t)NULL ||
pmp->pr_vaddr + pmp->pr_size <=
fptr->file_lo->rl_data_base))
return (pmp);
}
return (NULL);
}
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);
}
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);
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);
(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);
(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_pri == NULL)
return (NULL);
if (fptr->file_ctf_buf == NULL) {
fptr->file_ctf_buf = malloc(fptr->file_ctf_size);
if (fptr->file_ctf_buf == NULL) {
Pdprintf("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;
Pdprintf("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_pri.sh_type;
symtab.cts_flags = symp->sym_hdr_pri.sh_flags;
symtab.cts_data = symp->sym_data_pri->d_buf;
symtab.cts_size = symp->sym_hdr_pri.sh_size;
symtab.cts_entsize = symp->sym_hdr_pri.sh_entsize;
symtab.cts_offset = symp->sym_hdr_pri.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) {
Pdprintf("ctf_bufopen() failed, error code %d\n", err);
free(fptr->file_ctf_buf);
fptr->file_ctf_buf = NULL;
return (NULL);
}
Pdprintf("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 = NULL;
if (name == PR_OBJ_EVERY)
return (NULL);
if (P->state == PS_IDLE && name == PR_OBJ_EXEC && P->info_valid == 1 &&
P->num_files == 1 && P->mappings == NULL) {
fptr = list_head(&P->file_head);
}
if (fptr == 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));
}
void
Preadauxvec(struct ps_prochandle *P)
{
if (P->auxv != NULL) {
free(P->auxv);
P->auxv = NULL;
P->nauxv = 0;
}
P->ops.pop_read_aux(P, &P->auxv, &P->nauxv, P->data);
}
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);
}
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);
}
static int
is_mapping_in_file(struct ps_prochandle *P, map_info_t *mptr, file_info_t *fptr)
{
prmap_t *pmap = &mptr->map_pmap;
rd_loadobj_t *lop = fptr->file_lo;
uint_t i;
uintptr_t mstart, mend, sstart, send;
if ((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))
return (1);
if (fptr->file_saddrs == NULL &&
(fptr->file_saddrs = get_saddrs(P,
fptr->file_map->map_pmap.pr_vaddr, &fptr->file_nsaddrs)) == NULL)
return (0);
mstart = mptr->map_pmap.pr_vaddr;
mend = mptr->map_pmap.pr_vaddr + mptr->map_pmap.pr_size;
for (i = 0; i < fptr->file_nsaddrs; i += 2) {
sstart = fptr->file_saddrs[i];
send = fptr->file_saddrs[i + 1];
if (!(mend <= sstart || mstart >= send))
return (1);
}
return (0);
}
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;
if ((fptr = mptr->map_file) != NULL) {
Pbuild_file_symtab(P, fptr);
return (fptr);
}
if (pmap->pr_mapname[0] == '\0')
return (NULL);
for (fptr = list_head(&P->file_head); fptr != NULL;
fptr = list_next(&P->file_head, fptr)) {
if (strcmp(fptr->file_pname, pmap->pr_mapname) == 0 &&
fptr->file_lo && is_mapping_in_file(P, mptr, fptr)) {
mptr->map_file = fptr;
fptr->file_ref++;
Pbuild_file_symtab(P, fptr);
return (fptr);
}
}
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 (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, uint_t *phnum,
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);
if ((*phnum = ehdr->e_phnum) == PN_XNUM) {
Elf32_Shdr shdr0;
if (ehdr->e_shoff == 0 || ehdr->e_shentsize < sizeof (shdr0) ||
Pread(P, &shdr0, sizeof (shdr0), addr + ehdr->e_shoff) !=
sizeof (shdr0))
return (-1);
if (shdr0.sh_info != 0)
*phnum = shdr0.sh_info;
}
return (0);
}
static int
read_dynamic_phdr32(struct ps_prochandle *P, const Elf32_Ehdr *ehdr,
uint_t phnum, Elf32_Phdr *phdr, uintptr_t addr)
{
uint_t i;
for (i = 0; i < 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, uint_t *phnum,
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);
if ((*phnum = ehdr->e_phnum) == PN_XNUM) {
Elf64_Shdr shdr0;
if (ehdr->e_shoff == 0 || ehdr->e_shentsize < sizeof (shdr0) ||
Pread(P, &shdr0, sizeof (shdr0), addr + ehdr->e_shoff) !=
sizeof (shdr0))
return (-1);
if (shdr0.sh_info != 0)
*phnum = shdr0.sh_info;
}
return (0);
}
static int
read_dynamic_phdr64(struct ps_prochandle *P, const Elf64_Ehdr *ehdr,
uint_t phnum, Elf64_Phdr *phdr, uintptr_t addr)
{
uint_t i;
for (i = 0; i < 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
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);
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;
Pdprintf("elf cksum value is %llx\n", (u_longlong_t)cksum);
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 phnum, i;
if (read_ehdr32(P, &ehdr, &phnum, addr) != 0 ||
read_dynamic_phdr32(P, &ehdr, phnum, &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);
Pdprintf("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 phnum, i;
if (read_ehdr64(P, &ehdr, &phnum, addr) != 0 ||
read_dynamic_phdr64(P, &ehdr, phnum, &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);
Pdprintf("image cksum value is %llx\n",
(u_longlong_t)dync.d_un.d_val);
return (dync.d_un.d_val != cksum);
#endif
}
return (0);
}
static Elf *
fake_elf(struct ps_prochandle *P, file_info_t *fptr)
{
Elf *elf;
uintptr_t addr;
uint_t phnum;
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;
if (P->status.pr_dmodel == PR_MODEL_ILP32) {
Elf32_Ehdr ehdr;
Elf32_Phdr phdr;
if ((read_ehdr32(P, &ehdr, &phnum, addr) != 0) ||
read_dynamic_phdr32(P, &ehdr, phnum, &phdr, addr) != 0)
return (NULL);
elf = fake_elf32(P, fptr, addr, &ehdr, phnum, &phdr);
#ifdef _LP64
} else {
Elf64_Ehdr ehdr;
Elf64_Phdr phdr;
if (read_ehdr64(P, &ehdr, &phnum, addr) != 0 ||
read_dynamic_phdr64(P, &ehdr, phnum, &phdr, addr) != 0)
return (NULL);
elf = fake_elf64(P, fptr, addr, &ehdr, phnum, &phdr);
#endif
}
return (elf);
}
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);
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);
}
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);
}
if (*bname == '$')
return (-1);
if (*aname == '$')
return (1);
while (*aname == '_' && *bname == '_') {
aname++;
bname++;
}
if (*bname == '_')
return (-1);
if (*aname == '_')
return (1);
if (a->st_size < b->st_size)
return (-1);
if (a->st_size > b->st_size)
return (1);
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));
}
static GElf_Sym *
symtab_getsym(sym_tbl_t *symtab, int ndx, GElf_Sym *dst)
{
if (ndx >= symtab->sym_symn_aux) {
return (gelf_getsym(symtab->sym_data_pri,
ndx - symtab->sym_symn_aux, dst));
}
return (gelf_getsym(symtab->sym_data_aux, ndx, dst));
}
void
optimize_symtab(sym_tbl_t *symtab)
{
GElf_Sym *symp, *syms;
uint_t i, *indexa, *indexb;
size_t symn, strsz, count;
if (symtab == NULL || symtab->sym_data_pri == NULL ||
symtab->sym_byaddr != NULL)
return;
symn = symtab->sym_symn;
strsz = symtab->sym_strsz;
symp = syms = malloc(sizeof (GElf_Sym) * symn);
if (symp == NULL) {
Pdprintf("optimize_symtab: failed to malloc symbol array");
return;
}
for (i = 0, count = 0; i < symn; i++, symp++) {
if (symtab_getsym(symtab, i, symp) != NULL &&
symp->st_name < strsz &&
IS_DATA_TYPE(GELF_ST_TYPE(symp->st_info)))
count++;
else
symp->st_name = strsz;
}
symtab->sym_count = count;
indexa = symtab->sym_byaddr = calloc(sizeof (uint_t), count);
indexb = symtab->sym_byname = calloc(sizeof (uint_t), count);
if (indexa == NULL || indexb == NULL) {
Pdprintf(
"optimize_symtab: failed to malloc symbol index arrays");
symtab->sym_count = 0;
if (indexa != NULL) {
free(indexa);
symtab->sym_byaddr = NULL;
}
free(syms);
return;
}
for (i = 0, symp = syms; i < symn; i++, symp++) {
if (symp->st_name < strsz)
*indexa++ = *indexb++ = i;
}
if (!_libproc_no_qsort) {
(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);
}
static Elf *
build_fake_elf(struct ps_prochandle *P, file_info_t *fptr, GElf_Ehdr *ehdr,
size_t *nshdrs, Elf_Data **shdata)
{
size_t shstrndx;
Elf_Scn *scn;
Elf *elf;
if ((elf = fake_elf(P, fptr)) == NULL ||
elf_kind(elf) != ELF_K_ELF ||
gelf_getehdr(elf, ehdr) == NULL ||
elf_getshdrnum(elf, nshdrs) == -1 ||
elf_getshdrstrndx(elf, &shstrndx) == -1 ||
(scn = elf_getscn(elf, shstrndx)) == NULL ||
(*shdata = elf_getdata(scn, NULL)) == NULL) {
if (elf != NULL)
(void) elf_end(elf);
Pdprintf("failed to fake up ELF file\n");
return (NULL);
}
return (elf);
}
static boolean_t
build_alt_debug(file_info_t *fptr, const char *path, uint32_t crc,
Elf_Data *data)
{
int fd;
struct stat st;
Elf *elf;
Elf_Scn *scn;
GElf_Shdr symshdr, strshdr;
Elf_Data *symdata, *strdata;
boolean_t valid;
uint32_t c = -1U;
if ((fd = open(path, O_RDONLY)) < 0)
return (B_FALSE);
if (fstat(fd, &st) != 0) {
(void) close(fd);
return (B_FALSE);
}
if (S_ISREG(st.st_mode) == 0) {
(void) close(fd);
return (B_FALSE);
}
if (data == NULL) {
for (;;) {
char buf[4096];
ssize_t ret = read(fd, buf, sizeof (buf));
if (ret == -1) {
if (ret == EINTR)
continue;
(void) close(fd);
return (B_FALSE);
}
if (ret == 0) {
c = ~c;
if (c != crc) {
Pdprintf("crc mismatch, found: 0x%x "
"expected 0x%x\n", c, crc);
(void) close(fd);
return (B_FALSE);
}
break;
}
CRC32(c, buf, ret, c, psym_crc32);
}
}
elf = elf_begin(fd, ELF_C_READ, NULL);
if (elf == NULL) {
(void) close(fd);
return (B_FALSE);
}
if (elf_kind(elf) != ELF_K_ELF) {
goto fail;
}
scn = NULL;
valid = B_FALSE;
for (scn = elf_nextscn(elf, scn); data != NULL && scn != NULL;
scn = elf_nextscn(elf, scn)) {
GElf_Shdr hdr;
Elf_Data *ntdata;
if (gelf_getshdr(scn, &hdr) == NULL)
goto fail;
if (hdr.sh_type != SHT_NOTE)
continue;
if ((ntdata = elf_getdata(scn, NULL)) == NULL)
goto fail;
if (data->d_size != ntdata->d_size)
continue;
Pdprintf("found corresponding section in alternate file\n");
if (bcmp(ntdata->d_buf, data->d_buf, data->d_size) != 0)
goto fail;
valid = B_TRUE;
break;
}
if (data != NULL && valid == B_FALSE) {
Pdprintf("failed to find a matching %s section in %s\n",
BUILDID_NAME, path);
goto fail;
}
scn = NULL;
for (scn = elf_nextscn(elf, scn); scn != NULL;
scn = elf_nextscn(elf, scn)) {
if (gelf_getshdr(scn, &symshdr) == NULL)
goto fail;
if (symshdr.sh_type != SHT_SYMTAB)
continue;
if ((symdata = elf_getdata(scn, NULL)) == NULL)
goto fail;
break;
}
if (scn == NULL)
goto fail;
if ((scn = elf_getscn(elf, symshdr.sh_link)) == NULL)
goto fail;
if (gelf_getshdr(scn, &strshdr) == NULL)
goto fail;
if ((strdata = elf_getdata(scn, NULL)) == NULL)
goto fail;
fptr->file_symtab.sym_data_pri = symdata;
fptr->file_symtab.sym_symn += symshdr.sh_size / symshdr.sh_entsize;
fptr->file_symtab.sym_strs = strdata->d_buf;
fptr->file_symtab.sym_strsz = strdata->d_size;
fptr->file_symtab.sym_hdr_pri = symshdr;
fptr->file_symtab.sym_strhdr = strshdr;
Pdprintf(
"successfully loaded additional debug symbols for %s from %s\n",
fptr->file_rname, path);
fptr->file_dbgfile = fd;
fptr->file_dbgelf = elf;
return (B_TRUE);
fail:
(void) elf_end(elf);
(void) close(fd);
return (B_FALSE);
}
static void
find_alt_debuglink(file_info_t *fptr, const char *name, uint32_t crc)
{
boolean_t r;
char *dup = NULL, *path = NULL, *dname;
Pdprintf("find_alt_debug: looking for %s, crc 0x%x\n", name, crc);
if (fptr->file_rname == NULL) {
Pdprintf("find_alt_debug: encountered null file_rname\n");
return;
}
dup = strdup(fptr->file_rname);
if (dup == NULL)
return;
dname = dirname(dup);
if (asprintf(&path, "%s/.debug/%s", dname, name) != -1) {
Pdprintf("attempting to load alternate debug information "
"from %s\n", path);
r = build_alt_debug(fptr, path, crc, NULL);
free(path);
if (r == B_TRUE)
goto out;
}
if (asprintf(&path, "/usr/lib/debug/%s/%s", dname, name) != -1) {
Pdprintf("attempting to load alternate debug information "
"from %s\n", path);
r = build_alt_debug(fptr, path, crc, NULL);
free(path);
if (r == B_TRUE)
goto out;
}
out:
free(dup);
}
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;
size_t nshdrs, shstrndx;
struct {
GElf_Shdr c_shdr;
Elf_Data *c_data;
const char *c_name;
} *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL,
*dbglink = NULL, *buildid = NULL;
if (fptr->file_init)
return;
fptr->file_init = 1;
if (elf_version(EV_CURRENT) == EV_NONE) {
Pdprintf("libproc ELF version is more recent than libelf\n");
return;
}
if (P->state == PS_DEAD || P->state == PS_IDLE) {
char *name;
if (fptr->file_rname != NULL)
name = fptr->file_rname;
else if (fptr->file_lname != NULL)
name = fptr->file_lname;
else
name = fptr->file_pname;
(void) strlcpy(objectfile, name, sizeof (objectfile));
} else {
(void) snprintf(objectfile, sizeof (objectfile),
"%s/%d/object/%s",
procfs_path, (int)P->pid, fptr->file_pname);
}
if (_libproc_incore_elf || (P->flags & INCORE)) {
Pdprintf("Pbuild_file_symtab: using in-core data for: %s\n",
fptr->file_pname);
if ((elf = build_fake_elf(P, fptr, &ehdr, &nshdrs, &shdata)) ==
NULL)
return;
} else if ((fptr->file_fd = open(objectfile, O_RDONLY)) < 0) {
Pdprintf("Pbuild_file_symtab: failed to open %s: %s\n",
objectfile, strerror(errno));
if ((elf = build_fake_elf(P, fptr, &ehdr, &nshdrs, &shdata)) ==
NULL)
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 ||
elf_getshdrnum(elf, &nshdrs) == -1 ||
elf_getshdrstrndx(elf, &shstrndx) == -1 ||
(scn = elf_getscn(elf, shstrndx)) == NULL ||
(shdata = elf_getdata(scn, NULL)) == NULL) {
int err = elf_errno();
Pdprintf("failed to process ELF file %s: %s\n",
objectfile, (err == 0) ? "<null>" : elf_errmsg(err));
(void) elf_end(elf);
if ((elf = build_fake_elf(P, fptr, &ehdr, &nshdrs, &shdata)) ==
NULL)
return;
} else if (file_differs(P, elf, fptr)) {
Elf *newelf;
Pdprintf("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 = build_fake_elf(P, fptr, &ehdr, &nshdrs, &shdata))
!= NULL) {
(void) elf_end(elf);
elf = newelf;
Pdprintf("switched to faked up ELF file\n");
if (fptr->file_rname != NULL && P->execname != NULL &&
strcmp(fptr->file_rname, P->execname) == 0) {
Pdprintf("file/in-core image mismatch was "
"on P->execname; discarding\n");
free(P->execname);
P->execname = NULL;
}
}
}
if ((cache = malloc(nshdrs * sizeof (*cache))) == NULL) {
Pdprintf("failed to malloc section cache for %s\n", objectfile);
goto bad;
}
Pdprintf("processing ELF file %s\n", objectfile);
fptr->file_class = ehdr.e_ident[EI_CLASS];
fptr->file_etype = ehdr.e_type;
fptr->file_elf = elf;
fptr->file_shstrs = shdata->d_buf;
fptr->file_shstrsz = shdata->d_size;
for (cp = cache + 1, scn = NULL; scn = elf_nextscn(elf, scn); cp++) {
if (gelf_getshdr(scn, &cp->c_shdr) == NULL) {
Pdprintf("Pbuild_file_symtab: Failed to get section "
"header\n");
goto bad;
}
if ((cp->c_data = elf_getdata(scn, NULL)) == NULL) {
Pdprintf("Pbuild_file_symtab: Failed to get section "
"data\n");
goto bad;
}
if (cp->c_shdr.sh_name >= shdata->d_size) {
Pdprintf("Pbuild_file_symtab: corrupt section name");
goto bad;
}
cp->c_name = (const char *)shdata->d_buf + cp->c_shdr.sh_name;
}
for (i = 1, cp = cache + 1; i < nshdrs; 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;
if (symp->sym_data_pri == NULL) {
Pdprintf("Symbol table found for %s\n",
objectfile);
symp->sym_data_pri = 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_pri = cp->c_shdr;
symp->sym_strhdr = cache[shp->sh_link].c_shdr;
} else {
Pdprintf("Symbol table already there for %s\n",
objectfile);
}
} else if (shp->sh_type == SHT_SUNW_LDYNSYM) {
if (fptr->file_dynsym.sym_data_aux == NULL) {
Pdprintf(".SUNW_ldynsym symbol table"
" found for %s\n", objectfile);
fptr->file_dynsym.sym_data_aux = cp->c_data;
fptr->file_dynsym.sym_symn_aux =
shp->sh_size / shp->sh_entsize;
fptr->file_dynsym.sym_symn +=
fptr->file_dynsym.sym_symn_aux;
fptr->file_dynsym.sym_hdr_aux = cp->c_shdr;
} else {
Pdprintf(".SUNW_ldynsym symbol table already"
" there for %s\n", objectfile);
}
} 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) {
if (shp->sh_link == 0 ||
shp->sh_link >= nshdrs ||
(cache[shp->sh_link].c_shdr.sh_type != SHT_DYNSYM &&
cache[shp->sh_link].c_shdr.sh_type != SHT_SYMTAB)) {
Pdprintf("Bad sh_link %d for "
"CTF\n", shp->sh_link);
continue;
}
ctf = cp;
} else if (strcmp(cp->c_name, BUILDID_NAME) == 0) {
Pdprintf("Found a %s section for %s\n", BUILDID_NAME,
fptr->file_rname);
if (cp->c_shdr.sh_type == SHT_NOTE &&
cp->c_data->d_buf != NULL &&
cp->c_data->d_size >= sizeof (Elf32_Nhdr)) {
Elf32_Nhdr *hdr = cp->c_data->d_buf;
if (hdr->n_type != 3)
continue;
if (hdr->n_namesz != 4)
continue;
if (hdr->n_descsz < MINBUILDID)
continue;
if (hdr->n_descsz > MAXBUILDID) {
Pdprintf("Skipped %s as too large "
"(%ld)\n", BUILDID_NAME,
(unsigned long)hdr->n_descsz);
continue;
}
if (cp->c_data->d_size < sizeof (hdr) +
hdr->n_namesz + hdr->n_descsz)
continue;
buildid = cp;
}
} else if (strcmp(cp->c_name, DBGLINK_NAME) == 0) {
Pdprintf("found %s section for %s\n", DBGLINK_NAME,
fptr->file_rname);
if (cp->c_shdr.sh_type == SHT_PROGBITS &&
cp->c_data->d_buf != NULL &&
cp->c_data->d_size) {
dbglink = cp;
}
}
}
if (fptr->file_symtab.sym_data_pri == NULL && buildid != NULL) {
int i, bo;
uint8_t *dp;
char buf[BUILDID_STRLEN], *path;
Elf32_Nhdr *hdr = buildid->c_data->d_buf;
bzero(buf, sizeof (buf));
dp = (uint8_t *)((uintptr_t)hdr + sizeof (*hdr) +
hdr->n_namesz);
for (i = 0, bo = 0; i < hdr->n_descsz; i++, bo += 2, dp++) {
assert(sizeof (buf) - bo > 0);
if (bo == 2) {
buf[bo] = '/';
bo++;
}
(void) snprintf(buf + bo, sizeof (buf) - bo, "%2x",
*dp);
}
if (asprintf(&path, "/usr/lib/debug/.build-id/%s.debug",
buf) != -1) {
boolean_t r;
Pdprintf("attempting to find build id alternate debug "
"file at %s\n", path);
r = build_alt_debug(fptr, path, 0, buildid->c_data);
Pdprintf("attempt %s\n", r == B_TRUE ?
"succeeded" : "failed");
free(path);
} else {
Pdprintf("failed to construct build id path: %s\n",
strerror(errno));
}
}
if (fptr->file_symtab.sym_data_pri == NULL && dbglink != NULL) {
char *c = dbglink->c_data->d_buf;
size_t i;
boolean_t found = B_FALSE;
Elf_Data *ed = dbglink->c_data;
uint32_t crc;
for (i = 0; i < ed->d_size; i++) {
if (c[i] == '\0') {
uintptr_t off;
Pdprintf("got .gnu_debuglink terminator at "
"offset %lu\n", (unsigned long)i);
if ((i % sizeof (uint32_t)) == 0) {
i += 4;
} else {
i += sizeof (uint32_t) -
(i % sizeof (uint32_t));
}
if (i + sizeof (uint32_t) ==
dbglink->c_data->d_size) {
found = B_TRUE;
off = (uintptr_t)ed->d_buf + i;
crc = *(uint32_t *)off;
} else {
Pdprintf(".gnu_debuglink size mismatch,"
" expected: %lu, found: %lu\n",
(unsigned long)i,
(unsigned long)ed->d_size);
}
break;
}
}
if (found == B_TRUE)
find_alt_debuglink(fptr, dbglink->c_data->d_buf, crc);
}
optimize_symtab(&fptr->file_symtab);
optimize_symtab(&fptr->file_dynsym);
if (fptr->file_etype == ET_DYN) {
fptr->file_dyn_base = fptr->file_map->map_pmap.pr_vaddr -
fptr->file_map->map_pmap.pr_offset;
Pdprintf("setting file_dyn_base for %s to %lx\n",
objectfile, (long)fptr->file_dyn_base);
}
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;
if (fptr->file_etype == ET_DYN &&
fptr->file_lo->rl_base != fptr->file_dyn_base) {
Pdprintf("resetting file_dyn_base for %s to %lx\n",
objectfile, (long)fptr->file_lo->rl_base);
fptr->file_dyn_base = fptr->file_lo->rl_base;
}
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;
fptr->file_lo->rl_plt_base = fptr->file_plt_base;
fptr->file_lo->rl_plt_size = fptr->file_plt_size;
Pdprintf("PLT found at %p, size = %lu\n",
(void *)fptr->file_plt_base, (ulong_t)fptr->file_plt_size);
}
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)
continue;
switch (d.d_tag) {
case DT_JMPREL:
Pdprintf("DT_JMPREL is %p\n",
(void *)(uintptr_t)d.d_un.d_ptr);
fptr->file_jmp_rel =
d.d_un.d_ptr + fptr->file_dyn_base;
break;
case DT_STRTAB:
Pdprintf("DT_STRTAB is %p\n",
(void *)(uintptr_t)d.d_un.d_ptr);
break;
case DT_PLTGOT:
Pdprintf("DT_PLTGOT is %p\n",
(void *)(uintptr_t)d.d_un.d_ptr);
break;
case DT_SUNW_SYMTAB:
Pdprintf("DT_SUNW_SYMTAB is %p\n",
(void *)(uintptr_t)d.d_un.d_ptr);
break;
case DT_SYMTAB:
Pdprintf("DT_SYMTAB is %p\n",
(void *)(uintptr_t)d.d_un.d_ptr);
break;
case DT_HASH:
Pdprintf("DT_HASH is %p\n",
(void *)(uintptr_t)d.d_un.d_ptr);
break;
}
}
Pdprintf("_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);
if (fptr->file_dbgelf != NULL)
(void) elf_end(fptr->file_dbgelf);
fptr->file_dbgelf = NULL;
if (fptr->file_dbgfile >= 0)
(void) close(fptr->file_dbgfile);
fptr->file_fd = -1;
fptr->file_dbgfile = -1;
}
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];
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);
}
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)
return (mptr);
mold = mptr;
continue;
}
if (!(mptr->map_pmap.pr_mflags & MA_EXEC) ||
(mptr->map_pmap.pr_mflags & MA_WRITE)) {
mold = mptr;
continue;
}
return (mptr);
}
}
return (mold);
}
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;
if (P->rap == NULL)
lmid = PR_LMID_EVERY;
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) && (fp->file_rname == NULL)))
continue;
if (lmid != PR_LMID_EVERY &&
(fp->file_lo == NULL || lmid != fp->file_lo->rl_lmident))
continue;
if ((fp->file_lbase && strcmp(fp->file_lbase, objname) == 0) ||
(fp->file_rbase && strcmp(fp->file_rbase, objname) == 0) ||
(fp->file_lname && strcmp(fp->file_lname, objname) == 0) ||
(fp->file_rname && strcmp(fp->file_rname, objname) == 0))
return (fp->file_map ? fp->file_map : mp);
}
objlen = strlen(objname);
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) && (fp->file_rname == NULL)))
continue;
if (lmid != PR_LMID_EVERY &&
(fp->file_lo == NULL || lmid != fp->file_lo->rl_lmident))
continue;
if ((fp->file_lbase != NULL) &&
(strncmp(fp->file_lbase, objname, objlen) == 0) &&
(fp->file_lbase[objlen] == '.'))
return (fp->file_map ? fp->file_map : mp);
if ((fp->file_rbase != NULL) &&
(strncmp(fp->file_rbase, objname, objlen) == 0) &&
(fp->file_rbase[objlen] == '.'))
return (fp->file_map ? fp->file_map : mp);
}
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);
}
static GElf_Sym *
sym_prefer(GElf_Sym *sym1, char *name1, GElf_Sym *sym2, char *name2)
{
if (sym1 == NULL)
return (sym2);
if (sym2 == NULL)
return (sym1);
return (byaddr_cmp_common(sym1, name1, sym2, name2) <= 0 ? sym1 : sym2);
}
static GElf_Sym *
sym_by_addr_binary(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp,
uint_t *idp)
{
GElf_Sym sym, osym;
uint_t i, oid, *byaddr = symtab->sym_byaddr;
int min, max, mid, omid, found = 0;
if (symtab->sym_data_pri == NULL || symtab->sym_count == 0)
return (NULL);
min = 0;
max = symtab->sym_count - 1;
osym.st_value = 0;
while (min <= max) {
mid = (max + min) / 2;
i = byaddr[mid];
(void) symtab_getsym(symtab, 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);
do {
sym = osym;
i = oid;
if (omid == 0)
break;
oid = byaddr[--omid];
(void) symtab_getsym(symtab, 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);
}
static GElf_Sym *
sym_by_addr_linear(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symbolp,
uint_t *idp)
{
size_t symn = symtab->sym_symn;
char *strs = symtab->sym_strs;
GElf_Sym sym, *symp = NULL;
GElf_Sym osym, *osymp = NULL;
int i, id;
if (symtab->sym_data_pri == NULL || symn == 0 || strs == NULL)
return (NULL);
for (i = 0; i < symn; i++) {
if ((symp = symtab_getsym(symtab, i, &sym)) != NULL) {
if (addr >= sym.st_value &&
addr < sym.st_value + sym.st_size) {
if (osymp)
symp = sym_prefer(
symp, strs + symp->st_name,
osymp, strs + osymp->st_name);
if (symp != osymp) {
osym = sym;
osymp = &osym;
id = i;
}
}
}
}
if (osymp) {
*symbolp = osym;
if (idp)
*idp = id;
return (symbolp);
}
return (NULL);
}
static GElf_Sym *
sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
{
if (_libproc_no_qsort) {
return (sym_by_addr_linear(symtab, addr, symp, idp));
} else {
return (sym_by_addr_binary(symtab, addr, symp, idp));
}
}
static GElf_Sym *
sym_by_name_binary(sym_tbl_t *symtab, const char *name, GElf_Sym *symp,
uint_t *idp)
{
char *strs = symtab->sym_strs;
uint_t i, *byname = symtab->sym_byname;
int min, mid, max, cmp;
if (symtab->sym_data_pri == NULL || strs == NULL ||
symtab->sym_count == 0)
return (NULL);
min = 0;
max = symtab->sym_count - 1;
while (min <= max) {
mid = (max + min) / 2;
i = byname[mid];
(void) symtab_getsym(symtab, 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);
}
static GElf_Sym *
sym_by_name_linear(sym_tbl_t *symtab, const char *name, GElf_Sym *symp,
uint_t *idp)
{
size_t symn = symtab->sym_symn;
char *strs = symtab->sym_strs;
int i;
if (symtab->sym_data_pri == NULL || symn == 0 || strs == NULL)
return (NULL);
for (i = 0; i < symn; i++) {
if (symtab_getsym(symtab, i, symp) &&
strcmp(name, strs + symp->st_name) == 0) {
if (idp)
*idp = i;
return (symp);
}
}
return (NULL);
}
static GElf_Sym *
sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
{
if (_libproc_no_qsort) {
return (sym_by_name_linear(symtab, name, symp, idp));
} else {
return (sym_by_name_binary(symtab, name, symp, idp));
}
}
static int
i_Pxlookup_by_addr(
struct ps_prochandle *P,
int lmresolve,
uintptr_t addr,
char *sym_name_buffer,
size_t bufsize,
GElf_Sym *symbolp,
prsyminfo_t *sip)
{
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 ||
(fptr = build_map_symtab(P, mptr)) == NULL ||
fptr->file_elf == NULL)
return (-1);
addr -= fptr->file_dyn_base;
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;
if (lmresolve && (fptr->file_rname != NULL))
sip->prs_object = fptr->file_rbase;
else
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
Pxlookup_by_addr(struct ps_prochandle *P, uintptr_t addr, char *buf,
size_t bufsize, GElf_Sym *symp, prsyminfo_t *sip)
{
return (i_Pxlookup_by_addr(P, B_FALSE, addr, buf, bufsize, symp, sip));
}
int
Pxlookup_by_addr_resolved(struct ps_prochandle *P, uintptr_t addr, char *buf,
size_t bufsize, GElf_Sym *symp, prsyminfo_t *sip)
{
return (i_Pxlookup_by_addr(P, B_TRUE, addr, buf, bufsize, symp, sip));
}
int
Plookup_by_addr(struct ps_prochandle *P, uintptr_t addr, char *buf,
size_t size, GElf_Sym *symp)
{
return (i_Pxlookup_by_addr(P, B_FALSE, addr, buf, size, symp, NULL));
}
int
Pxlookup_by_name(
struct ps_prochandle *P,
Lmid_t lmid,
const char *oname,
const char *sname,
GElf_Sym *symp,
prsyminfo_t *sip)
{
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) {
(void) Prd_agent(P);
cnt = P->num_files;
fptr = list_head(&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);
}
for (; cnt > 0; cnt--, fptr = list_next(&P->file_head, 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_pri != 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_pri != 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);
}
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));
}
static int
i_Pmapping_iter(struct ps_prochandle *P, boolean_t lmresolve,
proc_map_f *func, void *cd)
{
map_info_t *mptr;
file_info_t *fptr;
char *object_name;
int rc = 0;
int i;
(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 if (lmresolve && (fptr->file_rname != NULL))
object_name = fptr->file_rname;
else
object_name = fptr->file_lname;
if ((rc = func(cd, &mptr->map_pmap, object_name)) != 0)
return (rc);
}
return (0);
}
int
Pmapping_iter(struct ps_prochandle *P, proc_map_f *func, void *cd)
{
return (i_Pmapping_iter(P, B_FALSE, func, cd));
}
int
Pmapping_iter_resolved(struct ps_prochandle *P, proc_map_f *func, void *cd)
{
return (i_Pmapping_iter(P, B_TRUE, func, cd));
}
static int
i_Pobject_iter(struct ps_prochandle *P, boolean_t lmresolve,
proc_map_f *func, void *cd)
{
map_info_t *mptr;
file_info_t *fptr;
int rc = 0;
(void) Prd_agent(P);
Pupdate_maps(P);
for (fptr = list_head(&P->file_head); fptr != NULL;
fptr = list_next(&P->file_head, fptr)) {
const char *lname;
if (lmresolve && (fptr->file_rname != NULL))
lname = fptr->file_rname;
else if (fptr->file_lname != NULL)
lname = fptr->file_lname;
else
lname = "";
if ((mptr = fptr->file_map) == NULL)
continue;
if ((rc = func(cd, &mptr->map_pmap, lname)) != 0)
return (rc);
if (!P->info_valid)
Pupdate_maps(P);
}
return (0);
}
int
Pobject_iter(struct ps_prochandle *P, proc_map_f *func, void *cd)
{
return (i_Pobject_iter(P, B_FALSE, func, cd));
}
int
Pobject_iter_resolved(struct ps_prochandle *P, proc_map_f *func, void *cd)
{
return (i_Pobject_iter(P, B_TRUE, func, cd));
}
static char *
i_Pobjname(struct ps_prochandle *P, boolean_t lmresolve, uintptr_t addr,
char *buffer, size_t bufsize)
{
map_info_t *mptr;
file_info_t *fptr;
(void) Prd_agent(P);
if ((mptr = Paddr2mptr(P, addr)) == NULL)
return (NULL);
if (!lmresolve) {
if (((fptr = mptr->map_file) == NULL) ||
(fptr->file_lname == NULL))
return (NULL);
(void) strlcpy(buffer, fptr->file_lname, bufsize);
return (buffer);
}
if (Pfindmap(P, mptr, buffer, bufsize) != NULL)
return (buffer);
return (NULL);
}
char *
Pobjname(struct ps_prochandle *P, uintptr_t addr,
char *buffer, size_t bufsize)
{
return (i_Pobjname(P, B_FALSE, addr, buffer, bufsize));
}
char *
Pobjname_resolved(struct ps_prochandle *P, uintptr_t addr,
char *buffer, size_t bufsize)
{
return (i_Pobjname(P, B_TRUE, addr, buffer, bufsize));
}
int
Plmid(struct ps_prochandle *P, uintptr_t addr, Lmid_t *lmidp)
{
map_info_t *mptr;
file_info_t *fptr;
(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);
}
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)
{
#if STT_NUM != (STT_TLS + 1)
#error "STT_NUM has grown. update Psymbol_iter_com()"
#endif
GElf_Sym sym;
GElf_Shdr shdr;
map_info_t *mptr;
file_info_t *fptr;
sym_tbl_t *symtab;
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 ||
fptr->file_elf == NULL)
return (-1);
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;
symn = symtab->sym_symn;
strs = symtab->sym_strs;
strsz = symtab->sym_strsz;
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);
}
if (symtab->sym_data_pri == NULL || strs == NULL || count == 0)
return (-1);
rv = 0;
for (i = 0; i < count; i++) {
ndx = map == NULL ? i : map[i];
if (symtab_getsym(symtab, ndx, &sym) != NULL) {
uint_t s_bind, s_type, type;
if (sym.st_name >= strsz)
continue;
s_bind = GELF_ST_BIND(sym.st_info);
s_type = GELF_ST_TYPE(sym.st_info);
if (s_bind < STB_NUM && s_type < STT_NUM) {
type = (1 << (s_type + 8)) | (1 << s_bind);
if ((type & ~mask) != 0)
continue;
} else
continue;
if (GELF_ST_TYPE(sym.st_info) != STT_TLS)
sym.st_value += fptr->file_dyn_base;
si.prs_name = strs + sym.st_name;
if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
fptr->file_shstrs != NULL &&
gelf_getshdr(elf_getscn(fptr->file_elf,
sym.st_shndx), &shdr) != NULL &&
shdr.sh_name != 0 &&
shdr.sh_name < fptr->file_shstrsz)
si.prs_name = fptr->file_shstrs + shdr.sh_name;
si.prs_id = ndx;
if ((rv = func(cd, &sym, si.prs_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 *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)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 *)(uintptr_t)func, cd));
}
char *
Pplatform(struct ps_prochandle *P, char *s, size_t n)
{
return (P->ops.pop_platform(P, s, n, P->data));
}
int
Puname(struct ps_prochandle *P, struct utsname *u)
{
return (P->ops.pop_uname(P, u, P->data));
}
void
Pinitsym(struct ps_prochandle *P)
{
P->num_files = 0;
list_create(&P->file_head, sizeof (file_info_t),
offsetof(file_info_t, file_list));
}
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;
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);
}
#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];
if ((psp = Ppsinfo(P)) == NULL)
return (-1);
envpoff = psp->pr_envp;
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;
}
if (envpoff == 0) {
envpoff = psp->pr_envp;
}
}
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++]) == (uintptr_t)NULL)
break;
again:
ret = Pread_string(P, buf, buflen, envoff);
if (ret <= 0) {
nameval = NULL;
} else if (ret == buflen - 1) {
free(buf);
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);
}