summaryrefslogtreecommitdiff
path: root/usr/src/lib/libproc/amd64/Pisadep.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libproc/amd64/Pisadep.c')
-rw-r--r--usr/src/lib/libproc/amd64/Pisadep.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/amd64/Pisadep.c b/usr/src/lib/libproc/amd64/Pisadep.c
new file mode 100644
index 0000000000..2583474d58
--- /dev/null
+++ b/usr/src/lib/libproc/amd64/Pisadep.c
@@ -0,0 +1,601 @@
+/*
+ * 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 <sys/stack.h>
+#include <sys/regset.h>
+#include <sys/frame.h>
+#include <sys/sysmacros.h>
+#include <sys/trap.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+
+#include "Pcontrol.h"
+#include "Pstack.h"
+
+#define M_PLT_NRSV 1 /* reserved PLT entries */
+#define M_PLT_ENTSIZE 16 /* size of each PLT entry */
+
+static uchar_t int_syscall_instr[] = { 0xCD, T_SYSCALLINT };
+static uchar_t syscall_instr[] = { 0x0f, 0x05 };
+
+const char *
+Ppltdest(struct ps_prochandle *P, uintptr_t pltaddr)
+{
+ map_info_t *mp = Paddr2mptr(P, pltaddr);
+ file_info_t *fp;
+ size_t i;
+ uintptr_t r_addr;
+
+ if (mp == NULL || (fp = mp->map_file) == NULL ||
+ fp->file_plt_base == 0 ||
+ pltaddr - fp->file_plt_base >= fp->file_plt_size) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ i = (pltaddr - fp->file_plt_base) / M_PLT_ENTSIZE - M_PLT_NRSV;
+
+ if (P->status.pr_dmodel == PR_MODEL_LP64) {
+ Elf64_Rela r;
+
+ r_addr = fp->file_jmp_rel + i * sizeof (r);
+
+ if (Pread(P, &r, sizeof (r), r_addr) == sizeof (r) &&
+ (i = ELF64_R_SYM(r.r_info)) < fp->file_dynsym.sym_symn) {
+ Elf_Data *data = fp->file_dynsym.sym_data;
+ Elf64_Sym *symp = &(((Elf64_Sym *)data->d_buf)[i]);
+
+ return (fp->file_dynsym.sym_strs + symp->st_name);
+ }
+ } else {
+ Elf32_Rel r;
+
+ r_addr = fp->file_jmp_rel + i * sizeof (r);
+
+ if (Pread(P, &r, sizeof (r), r_addr) == sizeof (r) &&
+ (i = ELF32_R_SYM(r.r_info)) < fp->file_dynsym.sym_symn) {
+ Elf_Data *data = fp->file_dynsym.sym_data;
+ Elf32_Sym *symp = &(((Elf32_Sym *)data->d_buf)[i]);
+
+ return (fp->file_dynsym.sym_strs + symp->st_name);
+ }
+ }
+
+ return (NULL);
+}
+
+int
+Pissyscall(struct ps_prochandle *P, uintptr_t addr)
+{
+ uchar_t instr[16];
+
+ if (P->status.pr_dmodel == PR_MODEL_LP64) {
+ if (Pread(P, instr, sizeof (syscall_instr), addr) !=
+ sizeof (syscall_instr) ||
+ memcmp(instr, syscall_instr, sizeof (syscall_instr)) != 0)
+ return (0);
+ else
+ return (1);
+ }
+
+ if (Pread(P, instr, sizeof (int_syscall_instr), addr) !=
+ sizeof (int_syscall_instr))
+ return (0);
+
+ if (memcmp(instr, int_syscall_instr, sizeof (int_syscall_instr)) == 0)
+ return (1);
+
+ return (0);
+}
+
+int
+Pissyscall_prev(struct ps_prochandle *P, uintptr_t addr, uintptr_t *dst)
+{
+ int ret;
+
+ if (P->status.pr_dmodel == PR_MODEL_LP64) {
+ if (Pissyscall(P, addr - sizeof (syscall_instr))) {
+ if (dst)
+ *dst = addr - sizeof (syscall_instr);
+ return (1);
+ }
+ return (0);
+ }
+
+ if ((ret = Pissyscall(P, addr - sizeof (int_syscall_instr))) != 0) {
+ if (dst)
+ *dst = addr - sizeof (int_syscall_instr);
+ return (ret);
+ }
+
+ return (0);
+}
+
+int
+Pissyscall_text(struct ps_prochandle *P, const void *buf, size_t buflen)
+{
+ if (P->status.pr_dmodel == PR_MODEL_LP64) {
+ if (buflen >= sizeof (syscall_instr) &&
+ memcmp(buf, syscall_instr, sizeof (syscall_instr)) == 0)
+ return (1);
+ else
+ return (0);
+ }
+
+ if (buflen < sizeof (int_syscall_instr))
+ return (0);
+
+ if (memcmp(buf, int_syscall_instr, sizeof (int_syscall_instr)) == 0)
+ return (1);
+
+ return (0);
+}
+
+#define TR_ARG_MAX 6 /* Max args to print, same as SPARC */
+
+/*
+ * Given a return address, determine the likely number of arguments
+ * that were pushed on the stack prior to its execution. We do this by
+ * expecting that a typical call sequence consists of pushing arguments on
+ * the stack, executing a call instruction, and then performing an add
+ * on %esp to restore it to the value prior to pushing the arguments for
+ * the call. We attempt to detect such an add, and divide the addend
+ * by the size of a word to determine the number of pushed arguments.
+ *
+ * If we do not find such an add, this does not necessarily imply that the
+ * function took no arguments. It is not possible to reliably detect such a
+ * void function because hand-coded assembler does not always perform an add
+ * to %esp immediately after the "call" instruction (eg. _sys_call()).
+ * Because of this, we default to returning MIN(sz, TR_ARG_MAX) instead of 0
+ * in the absence of an add to %esp.
+ */
+static ulong_t
+argcount(struct ps_prochandle *P, uint32_t pc, ssize_t sz)
+{
+ uchar_t instr[6];
+ ulong_t count, max;
+
+ max = MIN(sz / sizeof (uint32_t), TR_ARG_MAX);
+
+ /*
+ * Read the instruction at the return location.
+ */
+ if (Pread(P, instr, sizeof (instr), (uintptr_t)pc) != sizeof (instr))
+ return (max);
+
+ if (instr[1] != 0xc4)
+ return (max);
+
+ switch (instr[0]) {
+ case 0x81: /* count is a longword */
+ count = instr[2]+(instr[3]<<8)+(instr[4]<<16)+(instr[5]<<24);
+ break;
+ case 0x83: /* count is a byte */
+ count = instr[2];
+ break;
+ default:
+ return (max);
+ }
+
+ count /= sizeof (uint32_t);
+ return (MIN(count, max));
+}
+
+static void
+ucontext_32_to_prgregs(const ucontext32_t *uc, prgregset_t dst)
+{
+ const greg32_t *src = &uc->uc_mcontext.gregs[0];
+
+ dst[REG_DS] = (uint16_t)src[DS];
+ dst[REG_ES] = (uint16_t)src[ES];
+
+ dst[REG_GS] = (uint16_t)src[GS];
+ dst[REG_FS] = (uint16_t)src[FS];
+ dst[REG_SS] = (uint16_t)src[SS];
+ dst[REG_RSP] = (uint32_t)src[UESP];
+ dst[REG_RFL] = src[EFL];
+ dst[REG_CS] = (uint16_t)src[CS];
+ dst[REG_RIP] = (uint32_t)src[EIP];
+ dst[REG_ERR] = (uint32_t)src[ERR];
+ dst[REG_TRAPNO] = (uint32_t)src[TRAPNO];
+ dst[REG_RAX] = (uint32_t)src[EAX];
+ dst[REG_RCX] = (uint32_t)src[ECX];
+ dst[REG_RDX] = (uint32_t)src[EDX];
+ dst[REG_RBX] = (uint32_t)src[EBX];
+ dst[REG_RBP] = (uint32_t)src[EBP];
+ dst[REG_RSI] = (uint32_t)src[ESI];
+ dst[REG_RDI] = (uint32_t)src[EDI];
+}
+
+static int
+Pstack_iter32(struct ps_prochandle *P, const prgregset_t regs,
+ proc_stack_f *func, void *arg)
+{
+ prgreg_t *prevfp = NULL;
+ uint_t pfpsize = 0;
+ int nfp = 0;
+ struct {
+ prgreg32_t fp;
+ prgreg32_t pc;
+ prgreg32_t args[32];
+ } frame;
+ uint_t argc;
+ ssize_t sz;
+ prgregset_t gregs;
+ uint32_t fp, pfp, pc;
+ long args[32];
+ int rv;
+ int i;
+
+ /*
+ * Type definition for a structure corresponding to an IA32
+ * signal frame. Refer to the comments in Pstack.c for more info
+ */
+ typedef struct {
+ prgreg32_t fp;
+ prgreg32_t pc;
+ int signo;
+ caddr32_t ucp;
+ caddr32_t sip;
+ } sf_t;
+
+ uclist_t ucl;
+ ucontext32_t uc;
+ uintptr_t uc_addr;
+
+ init_uclist(&ucl, P);
+ (void) memcpy(gregs, regs, sizeof (gregs));
+
+ fp = regs[R_FP];
+ pc = regs[R_PC];
+
+ while (fp != 0 || pc != 0) {
+ if (stack_loop(fp, &prevfp, &nfp, &pfpsize))
+ break;
+
+ if (fp != 0 &&
+ (sz = Pread(P, &frame, sizeof (frame), (uintptr_t)fp)
+ >= (ssize_t)(2* sizeof (uint32_t)))) {
+ /*
+ * One more trick for signal frames: the kernel sets
+ * the return pc of the signal frame to 0xffffffff on
+ * Intel IA32, so argcount won't work.
+ */
+ if (frame.pc != -1L) {
+ sz -= 2* sizeof (uint32_t);
+ argc = argcount(P, (uint32_t)frame.pc, sz);
+ } else
+ argc = 3; /* sighandler(signo, sip, ucp) */
+ } else {
+ (void) memset(&frame, 0, sizeof (frame));
+ argc = 0;
+ }
+
+ gregs[R_FP] = fp;
+ gregs[R_PC] = pc;
+
+ for (i = 0; i < argc; i++)
+ args[i] = (uint32_t)frame.args[i];
+
+ if ((rv = func(arg, gregs, argc, args)) != 0)
+ break;
+
+ /*
+ * In order to allow iteration over java frames (which can have
+ * their own frame pointers), we allow the iterator to change
+ * the contents of gregs. If we detect a change, then we assume
+ * that the new values point to the next frame.
+ */
+ if (gregs[R_FP] != fp || gregs[R_PC] != pc) {
+ fp = gregs[R_FP];
+ pc = gregs[R_PC];
+ continue;
+ }
+
+ pfp = fp;
+ fp = frame.fp;
+ pc = frame.pc;
+
+ if (find_uclink(&ucl, pfp + sizeof (sf_t)))
+ uc_addr = pfp + sizeof (sf_t);
+ else
+ uc_addr = NULL;
+
+ if (uc_addr != NULL &&
+ Pread(P, &uc, sizeof (uc), uc_addr) == sizeof (uc)) {
+ ucontext_32_to_prgregs(&uc, gregs);
+ fp = gregs[R_FP];
+ pc = gregs[R_PC];
+ }
+ }
+
+ if (prevfp)
+ free(prevfp);
+
+ free_uclist(&ucl);
+ return (rv);
+}
+
+static void
+ucontext_n_to_prgregs(const ucontext_t *src, prgregset_t dst)
+{
+ (void) memcpy(dst, src->uc_mcontext.gregs, sizeof (gregset_t));
+}
+
+
+int
+Pstack_iter(struct ps_prochandle *P, const prgregset_t regs,
+ proc_stack_f *func, void *arg)
+{
+ struct {
+ uintptr_t fp;
+ uintptr_t pc;
+ } frame;
+
+ uint_t pfpsize = 0;
+ prgreg_t *prevfp = NULL;
+ prgreg_t fp, pfp;
+ prgreg_t pc;
+
+ prgregset_t gregs;
+ int nfp = 0;
+
+ uclist_t ucl;
+ int rv = 0;
+ int argc;
+
+ uintptr_t uc_addr;
+ ucontext_t uc;
+
+ /*
+ * Type definition for a structure corresponding to an IA32
+ * signal frame. Refer to the comments in Pstack.c for more info
+ */
+ typedef struct {
+ prgreg_t fp;
+ prgreg_t pc;
+ prgreg_t signo;
+ siginfo_t *sip;
+ } sigframe_t;
+ prgreg_t args[32];
+
+ if (P->status.pr_dmodel != PR_MODEL_LP64)
+ return (Pstack_iter32(P, regs, func, arg));
+
+ init_uclist(&ucl, P);
+ (void) memcpy(gregs, regs, sizeof (gregs));
+
+ fp = gregs[R_FP];
+ pc = gregs[R_PC];
+
+ while (fp != 0 || pc != 0) {
+
+ if (stack_loop(fp, &prevfp, &nfp, &pfpsize))
+ break;
+
+ if (fp != 0 &&
+ Pread(P, &frame, sizeof (frame), (uintptr_t)fp) ==
+ sizeof (frame)) {
+
+ if (frame.pc != -1) {
+ /*
+ * Function arguments are not available on
+ * amd64 without extensive DWARF processing.
+ */
+ argc = 0;
+ } else {
+ argc = 3;
+ args[2] = fp + sizeof (sigframe_t);
+ if (Pread(P, &args, 2 * sizeof (prgreg_t),
+ fp + 2 * sizeof (prgreg_t)) !=
+ 2 * sizeof (prgreg_t))
+ argc = 0;
+ }
+ } else {
+ (void) memset(&frame, 0, sizeof (frame));
+ argc = 0;
+ }
+
+ gregs[R_FP] = fp;
+ gregs[R_PC] = pc;
+
+ if ((rv = func(arg, gregs, argc, args)) != 0)
+ break;
+
+ pfp = fp;
+ fp = frame.fp;
+ pc = frame.pc;
+
+ if (pc == -1 && find_uclink(&ucl, pfp + sizeof (sigframe_t))) {
+ uc_addr = pfp + sizeof (sigframe_t);
+
+ if (Pread(P, &uc, sizeof (uc), uc_addr)
+ == sizeof (uc)) {
+ ucontext_n_to_prgregs(&uc, gregs);
+ fp = gregs[R_FP];
+ pc = gregs[R_PC];
+ }
+ }
+ }
+
+ if (prevfp)
+ free(prevfp);
+
+ free_uclist(&ucl);
+
+ return (rv);
+}
+
+uintptr_t
+Psyscall_setup(struct ps_prochandle *P, int nargs, int sysindex, uintptr_t sp)
+{
+ if (P->status.pr_dmodel == PR_MODEL_ILP32) {
+ sp -= sizeof (int) * (nargs+2);
+
+ P->status.pr_lwp.pr_reg[REG_RAX] = sysindex;
+ P->status.pr_lwp.pr_reg[REG_RSP] = sp;
+ P->status.pr_lwp.pr_reg[REG_RIP] = P->sysaddr;
+ } else {
+ int pusharg = (nargs > 6) ? nargs - 6: 0;
+
+ sp -= sizeof (int64_t) * (pusharg+2);
+
+ P->status.pr_lwp.pr_reg[REG_RAX] = sysindex;
+ P->status.pr_lwp.pr_reg[REG_RSP] = sp;
+ P->status.pr_lwp.pr_reg[REG_RIP] = P->sysaddr;
+ }
+
+ return (sp);
+}
+
+int
+Psyscall_copyinargs(struct ps_prochandle *P, int nargs, argdes_t *argp,
+ uintptr_t ap)
+{
+ if (P->status.pr_dmodel == PR_MODEL_ILP32) {
+ int32_t arglist[MAXARGS+2];
+ int i;
+ argdes_t *adp;
+
+ for (i = 0, adp = argp; i < nargs; i++, adp++)
+ arglist[1 + i] = (int32_t)adp->arg_value;
+
+ arglist[0] = P->status.pr_lwp.pr_reg[REG_RIP];
+ if (Pwrite(P, &arglist[0], sizeof (int) * (nargs+1),
+ (uintptr_t)ap) != sizeof (int) * (nargs+1))
+ return (-1);
+ } else {
+ int64_t arglist[MAXARGS+2];
+ int i;
+ argdes_t *adp;
+ int pusharg = (nargs > 6) ? nargs - 6: 0;
+
+ for (i = 0, adp = argp; i < nargs; i++, adp++) {
+ switch (i) {
+ case 0:
+ (void) Pputareg(P, REG_RDI, adp->arg_value);
+ break;
+ case 1:
+ (void) Pputareg(P, REG_RSI, adp->arg_value);
+ break;
+ case 2:
+ (void) Pputareg(P, REG_RDX, adp->arg_value);
+ break;
+ case 3:
+ (void) Pputareg(P, REG_RCX, adp->arg_value);
+ break;
+ case 4:
+ (void) Pputareg(P, REG_R8, adp->arg_value);
+ break;
+ case 5:
+ (void) Pputareg(P, REG_R9, adp->arg_value);
+ break;
+ default:
+ arglist[i - 5] = (uint64_t)adp->arg_value;
+ break;
+ }
+ }
+
+ arglist[0] = P->status.pr_lwp.pr_reg[REG_RIP];
+
+ if (Pwrite(P, &arglist[0],
+ sizeof (int64_t) * (pusharg + 1), ap) !=
+ sizeof (int64_t) * (pusharg + 1))
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+Psyscall_copyoutargs(struct ps_prochandle *P, int nargs, argdes_t *argp,
+ uintptr_t ap)
+{
+ if (P->status.pr_dmodel == PR_MODEL_ILP32) {
+ uint32_t arglist[MAXARGS + 2];
+ int i;
+ argdes_t *adp;
+
+ if (Pread(P, &arglist[0], sizeof (int) * (nargs+1),
+ (uintptr_t)ap) != sizeof (int) * (nargs+1))
+ return (-1);
+
+ for (i = 0, adp = argp; i < nargs; i++, adp++)
+ adp->arg_value = arglist[i];
+ } else {
+ int pusharg = (nargs > 6) ? nargs - 6: 0;
+ int64_t arglist[MAXARGS+2];
+ int i;
+ argdes_t *adp;
+
+ if (pusharg > 0 &&
+ Pread(P, &arglist[0], sizeof (int64_t) * (pusharg + 1),
+ ap) != sizeof (int64_t) * (pusharg + 1))
+ return (-1);
+
+ for (i = 0, adp = argp; i < nargs; i++, adp++) {
+ switch (i) {
+ case 0:
+ adp->arg_value =
+ P->status.pr_lwp.pr_reg[REG_RDI];
+ break;
+ case 1:
+ adp->arg_value =
+ P->status.pr_lwp.pr_reg[REG_RSI];
+ break;
+ case 2:
+ adp->arg_value =
+ P->status.pr_lwp.pr_reg[REG_RDX];
+ break;
+ case 3:
+ adp->arg_value =
+ P->status.pr_lwp.pr_reg[REG_RCX];
+ break;
+ case 4:
+ adp->arg_value =
+ P->status.pr_lwp.pr_reg[REG_R8];
+ break;
+ case 5:
+ adp->arg_value =
+ P->status.pr_lwp.pr_reg[REG_R9];
+ break;
+ default:
+ adp->arg_value = arglist[i - 6];
+ break;
+ }
+ }
+
+ return (0);
+ }
+
+ return (0);
+}