summaryrefslogtreecommitdiff
path: root/usr/src/lib/libproc/common/Psymtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libproc/common/Psymtab.c')
-rw-r--r--usr/src/lib/libproc/common/Psymtab.c3434
1 files changed, 3434 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c
new file mode 100644
index 0000000000..05dd4d570c
--- /dev/null
+++ b/usr/src/lib/libproc/common/Psymtab.c
@@ -0,0 +1,3434 @@
+/*
+ * 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);
+}