2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <sys/stack.h>
2N/A#include <sys/regset.h>
2N/A#include <sys/frame.h>
2N/A#include <sys/sysmacros.h>
2N/A#include <sys/trap.h>
2N/A#include <sys/machelf.h>
2N/A
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <sys/types.h>
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A
2N/A#include "Pcontrol.h"
2N/A#include "Pstack.h"
2N/A
2N/Astatic uchar_t int_syscall_instr[] = { 0xCD, T_SYSCALLINT };
2N/Astatic uchar_t syscall_instr[] = { 0x0f, 0x05 };
2N/A
2N/Aconst char *
2N/APpltdest(struct ps_prochandle *P, uintptr_t pltaddr)
2N/A{
2N/A map_info_t *mp = Paddr2mptr(P, pltaddr);
2N/A file_info_t *fp;
2N/A size_t i;
2N/A uintptr_t r_addr;
2N/A
2N/A if (mp == NULL || (fp = mp->map_file) == NULL ||
2N/A fp->file_plt_base == 0 ||
2N/A pltaddr - fp->file_plt_base >= fp->file_plt_size) {
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A i = (pltaddr - fp->file_plt_base) / M_PLT_ENTSIZE - M_PLT_XNumber;
2N/A
2N/A if (P->status.pr_dmodel == PR_MODEL_LP64) {
2N/A Elf64_Rela r;
2N/A
2N/A r_addr = fp->file_jmp_rel + i * sizeof (r);
2N/A
2N/A if (Pread(P, &r, sizeof (r), r_addr) == sizeof (r) &&
2N/A (i = ELF64_R_SYM(r.r_info)) < fp->file_dynsym.sym_symn) {
2N/A Elf_Data *data = fp->file_dynsym.sym_data_pri;
2N/A Elf64_Sym *symp = &(((Elf64_Sym *)data->d_buf)[i]);
2N/A
2N/A return (fp->file_dynsym.sym_strs + symp->st_name);
2N/A }
2N/A } else {
2N/A Elf32_Rel r;
2N/A
2N/A r_addr = fp->file_jmp_rel + i * sizeof (r);
2N/A
2N/A if (Pread(P, &r, sizeof (r), r_addr) == sizeof (r) &&
2N/A (i = ELF32_R_SYM(r.r_info)) < fp->file_dynsym.sym_symn) {
2N/A Elf_Data *data = fp->file_dynsym.sym_data_pri;
2N/A Elf32_Sym *symp = &(((Elf32_Sym *)data->d_buf)[i]);
2N/A
2N/A return (fp->file_dynsym.sym_strs + symp->st_name);
2N/A }
2N/A }
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/Aint
2N/APissyscall(struct ps_prochandle *P, uintptr_t addr)
2N/A{
2N/A uchar_t instr[16];
2N/A
2N/A if (P->status.pr_dmodel == PR_MODEL_LP64) {
2N/A if (Pread(P, instr, sizeof (syscall_instr), addr) !=
2N/A sizeof (syscall_instr) ||
2N/A memcmp(instr, syscall_instr, sizeof (syscall_instr)) != 0)
2N/A return (0);
2N/A else
2N/A return (1);
2N/A }
2N/A
2N/A if (Pread(P, instr, sizeof (int_syscall_instr), addr) !=
2N/A sizeof (int_syscall_instr))
2N/A return (0);
2N/A
2N/A if (memcmp(instr, int_syscall_instr, sizeof (int_syscall_instr)) == 0)
2N/A return (1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/APissyscall_prev(struct ps_prochandle *P, uintptr_t addr, uintptr_t *dst)
2N/A{
2N/A int ret;
2N/A
2N/A if (P->status.pr_dmodel == PR_MODEL_LP64) {
2N/A if (Pissyscall(P, addr - sizeof (syscall_instr))) {
2N/A if (dst)
2N/A *dst = addr - sizeof (syscall_instr);
2N/A return (1);
2N/A }
2N/A return (0);
2N/A }
2N/A
2N/A if ((ret = Pissyscall(P, addr - sizeof (int_syscall_instr))) != 0) {
2N/A if (dst)
2N/A *dst = addr - sizeof (int_syscall_instr);
2N/A return (ret);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/APissyscall_text(struct ps_prochandle *P, const void *buf, size_t buflen)
2N/A{
2N/A if (P->status.pr_dmodel == PR_MODEL_LP64) {
2N/A if (buflen >= sizeof (syscall_instr) &&
2N/A memcmp(buf, syscall_instr, sizeof (syscall_instr)) == 0)
2N/A return (1);
2N/A else
2N/A return (0);
2N/A }
2N/A
2N/A if (buflen < sizeof (int_syscall_instr))
2N/A return (0);
2N/A
2N/A if (memcmp(buf, int_syscall_instr, sizeof (int_syscall_instr)) == 0)
2N/A return (1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A#define TR_ARG_MAX 6 /* Max args to print, same as SPARC */
2N/A
2N/A/*
2N/A * Given a return address, determine the likely number of arguments
2N/A * that were pushed on the stack prior to its execution. We do this by
2N/A * expecting that a typical call sequence consists of pushing arguments on
2N/A * the stack, executing a call instruction, and then performing an add
2N/A * on %esp to restore it to the value prior to pushing the arguments for
2N/A * the call. We attempt to detect such an add, and divide the addend
2N/A * by the size of a word to determine the number of pushed arguments.
2N/A *
2N/A * If we do not find such an add, this does not necessarily imply that the
2N/A * function took no arguments. It is not possible to reliably detect such a
2N/A * void function because hand-coded assembler does not always perform an add
2N/A * to %esp immediately after the "call" instruction (eg. _sys_call()).
2N/A * Because of this, we default to returning MIN(sz, TR_ARG_MAX) instead of 0
2N/A * in the absence of an add to %esp.
2N/A */
2N/Astatic ulong_t
2N/Aargcount(struct ps_prochandle *P, uint32_t pc, ssize_t sz)
2N/A{
2N/A uchar_t instr[6];
2N/A ulong_t count, max;
2N/A
2N/A max = MIN(sz / sizeof (uint32_t), TR_ARG_MAX);
2N/A
2N/A /*
2N/A * Read the instruction at the return location.
2N/A */
2N/A if (Pread(P, instr, sizeof (instr), (uintptr_t)pc) != sizeof (instr))
2N/A return (max);
2N/A
2N/A if (instr[1] != 0xc4)
2N/A return (max);
2N/A
2N/A switch (instr[0]) {
2N/A case 0x81: /* count is a longword */
2N/A count = instr[2]+(instr[3]<<8)+(instr[4]<<16)+(instr[5]<<24);
2N/A break;
2N/A case 0x83: /* count is a byte */
2N/A count = instr[2];
2N/A break;
2N/A default:
2N/A return (max);
2N/A }
2N/A
2N/A count /= sizeof (uint32_t);
2N/A return (MIN(count, max));
2N/A}
2N/A
2N/Astatic void
2N/Aucontext_32_to_prgregs(const ucontext32_t *uc, prgregset_t dst)
2N/A{
2N/A const greg32_t *src = &uc->uc_mcontext.gregs[0];
2N/A
2N/A dst[REG_DS] = (uint16_t)src[DS];
2N/A dst[REG_ES] = (uint16_t)src[ES];
2N/A
2N/A dst[REG_GS] = (uint16_t)src[GS];
2N/A dst[REG_FS] = (uint16_t)src[FS];
2N/A dst[REG_SS] = (uint16_t)src[SS];
2N/A dst[REG_RSP] = (uint32_t)src[UESP];
2N/A dst[REG_RFL] = src[EFL];
2N/A dst[REG_CS] = (uint16_t)src[CS];
2N/A dst[REG_RIP] = (uint32_t)src[EIP];
2N/A dst[REG_ERR] = (uint32_t)src[ERR];
2N/A dst[REG_TRAPNO] = (uint32_t)src[TRAPNO];
2N/A dst[REG_RAX] = (uint32_t)src[EAX];
2N/A dst[REG_RCX] = (uint32_t)src[ECX];
2N/A dst[REG_RDX] = (uint32_t)src[EDX];
2N/A dst[REG_RBX] = (uint32_t)src[EBX];
2N/A dst[REG_RBP] = (uint32_t)src[EBP];
2N/A dst[REG_RSI] = (uint32_t)src[ESI];
2N/A dst[REG_RDI] = (uint32_t)src[EDI];
2N/A}
2N/A
2N/Astatic int
2N/APstack_iter32(struct ps_prochandle *P, const prgregset_t regs,
2N/A proc_stack_f *func, void *arg)
2N/A{
2N/A prgreg_t *prevfp = NULL;
2N/A uint_t pfpsize = 0;
2N/A int nfp = 0;
2N/A struct {
2N/A prgreg32_t fp;
2N/A prgreg32_t pc;
2N/A prgreg32_t args[32];
2N/A } frame;
2N/A uint_t argc;
2N/A ssize_t sz;
2N/A prgregset_t gregs;
2N/A uint32_t fp, pc;
2N/A long args[32];
2N/A int rv;
2N/A int i;
2N/A int frame_flags = 0;
2N/A int sig; /* ignored unless (frame_flags & PR_FOUND_SIGNAL) */
2N/A
2N/A /*
2N/A * Type definition for a structure corresponding to an IA32
2N/A * signal frame. Refer to the comments in Pstack.c for more info
2N/A */
2N/A typedef struct {
2N/A prgreg32_t fp;
2N/A prgreg32_t pc;
2N/A int signo;
2N/A caddr32_t ucp;
2N/A caddr32_t sip;
2N/A } sf_t;
2N/A
2N/A uclist_t ucl;
2N/A ucontext32_t uc;
2N/A uintptr_t uc_addr;
2N/A
2N/A init_uclist(&ucl, P);
2N/A (void) memcpy(gregs, regs, sizeof (gregs));
2N/A
2N/A fp = regs[R_FP];
2N/A pc = regs[R_PC];
2N/A
2N/A while (fp != 0 || pc != 0) {
2N/A if (stack_loop(fp, &prevfp, &nfp, &pfpsize))
2N/A break;
2N/A
2N/A if (fp != 0 &&
2N/A (sz = Pread(P, &frame, sizeof (frame), (uintptr_t)fp)
2N/A >= (ssize_t)(2* sizeof (uint32_t)))) {
2N/A
2N/A /*
2N/A * A return PC of -1 on x86 denotes a signal frame. We
2N/A * continue to look for a ucontext as a modest
2N/A * precaution against stack corruption.
2N/A */
2N/A if (frame.pc == -1 &&
2N/A find_uclink(&ucl, fp + sizeof (sf_t))) {
2N/A uc_addr = fp + sizeof (sf_t);
2N/A frame_flags = PR_SIGNAL_FRAME;
2N/A if (Pread(P, &sig, sizeof (int),
2N/A (uintptr_t)(fp + sizeof (struct frame32)))
2N/A == sizeof (int))
2N/A frame_flags |= PR_FOUND_SIGNAL;
2N/A /*
2N/A * The bogus return PC prevents us from calling
2N/A * argcount() but we know that the signal
2N/A * handler takes three arguments (the signal
2N/A * number and pointers to a siginfo_t and a
2N/A * ucontext_t)/
2N/A */
2N/A argc = 3;
2N/A } else {
2N/A uc_addr = NULL;
2N/A sz -= 2* sizeof (long);
2N/A argc = argcount(P, (long)frame.pc, sz);
2N/A }
2N/A } else {
2N/A (void) memset(&frame, 0, sizeof (frame));
2N/A uc_addr = NULL;
2N/A argc = 0;
2N/A }
2N/A
2N/A gregs[R_FP] = fp;
2N/A gregs[R_PC] = pc;
2N/A
2N/A for (i = 0; i < argc; i++)
2N/A args[i] = (uint32_t)frame.args[i];
2N/A
2N/A if ((rv = func(arg, gregs, argc, args, frame_flags, sig)) != 0)
2N/A break;
2N/A
2N/A if (frame_flags & PR_SIGNAL_FRAME)
2N/A frame_flags = 0;
2N/A
2N/A /* Locate the next frame. */
2N/A if (gregs[R_FP] != fp || gregs[R_PC] != pc) {
2N/A /*
2N/A * In order to allow iteration over java frames (which
2N/A * can have their own frame pointers), we allow the
2N/A * iterator to change the contents of gregs. If we
2N/A * detect a change, then we assume that the new values
2N/A * point to the next frame.
2N/A */
2N/A fp = gregs[R_FP];
2N/A pc = gregs[R_PC];
2N/A } else if (uc_addr != NULL &&
2N/A Pread(P, &uc, sizeof (uc), uc_addr) == sizeof (uc)) {
2N/A /*
2N/A * If this is a signal frame then we extract the new
2N/A * registers from the saved context, thereby allowing us
2N/A * to display the interrupted frame.
2N/A */
2N/A ucontext_32_to_prgregs(&uc, gregs);
2N/A fp = gregs[R_FP];
2N/A pc = gregs[R_PC];
2N/A } else {
2N/A fp = frame.fp;
2N/A pc = frame.pc;
2N/A }
2N/A }
2N/A
2N/A if (prevfp)
2N/A free(prevfp);
2N/A
2N/A free_uclist(&ucl);
2N/A return (rv);
2N/A}
2N/A
2N/Astatic void
2N/Aucontext_n_to_prgregs(const ucontext_t *src, prgregset_t dst)
2N/A{
2N/A (void) memcpy(dst, src->uc_mcontext.gregs, sizeof (gregset_t));
2N/A}
2N/A
2N/A
2N/Aint
2N/APstack_iter(struct ps_prochandle *P, const prgregset_t regs,
2N/A proc_stack_f *func, void *arg)
2N/A{
2N/A struct {
2N/A uintptr_t fp;
2N/A uintptr_t pc;
2N/A } frame;
2N/A
2N/A uint_t pfpsize = 0;
2N/A prgreg_t *prevfp = NULL;
2N/A prgreg_t fp;
2N/A prgreg_t pc;
2N/A
2N/A prgregset_t gregs;
2N/A int nfp = 0;
2N/A
2N/A uclist_t ucl;
2N/A int rv = 0;
2N/A int argc;
2N/A
2N/A uintptr_t uc_addr;
2N/A ucontext_t uc;
2N/A
2N/A int frame_flags = 0;
2N/A int sig; /* ignored unless (frame_flags & PR_FOUND_SIGNAL) */
2N/A
2N/A /*
2N/A * Type definition for a structure corresponding to an IA32
2N/A * signal frame. Refer to the comments in Pstack.c for more info
2N/A */
2N/A typedef struct {
2N/A prgreg_t fp;
2N/A prgreg_t pc;
2N/A prgreg_t signo;
2N/A siginfo_t *sip;
2N/A } sigframe_t;
2N/A prgreg_t args[32];
2N/A
2N/A if (P->status.pr_dmodel != PR_MODEL_LP64)
2N/A return (Pstack_iter32(P, regs, func, arg));
2N/A
2N/A init_uclist(&ucl, P);
2N/A (void) memcpy(gregs, regs, sizeof (gregs));
2N/A
2N/A fp = gregs[R_FP];
2N/A pc = gregs[R_PC];
2N/A
2N/A while (fp != 0 || pc != 0) {
2N/A
2N/A if (stack_loop(fp, &prevfp, &nfp, &pfpsize))
2N/A break;
2N/A
2N/A uc_addr = NULL;
2N/A argc = 0;
2N/A if (fp != 0 &&
2N/A Pread(P, &frame, sizeof (frame), (uintptr_t)fp) ==
2N/A sizeof (frame)) {
2N/A
2N/A /*
2N/A * A return PC of -1 on x86 denotes a signal frame. We
2N/A * continue to look for a ucontext as a modest
2N/A * precaution against stack corruption.
2N/A */
2N/A if (frame.pc == -1L &&
2N/A find_uclink(&ucl, fp + sizeof (sigframe_t))) {
2N/A uc_addr = fp + sizeof (sigframe_t);
2N/A frame_flags = PR_SIGNAL_FRAME;
2N/A /*
2N/A * Ordinarily, function arguments are
2N/A * unavailable on amd64 without extensive DWARF
2N/A * processing. However, we may derive them for
2N/A * the special case of the signal handler since
2N/A * we know that its signature is
2N/A *
2N/A * sighandler(signo, sip, ucp)
2N/A *
2N/A * and that these data will be present in the
2N/A * signal frame.
2N/A */
2N/A if (Pread(P, &args, 2 * sizeof (prgreg_t),
2N/A fp + 2 * sizeof (prgreg_t)) ==
2N/A 2 * sizeof (prgreg_t)) {
2N/A args[2] = fp + sizeof (sigframe_t);
2N/A argc = 3;
2N/A frame_flags |= PR_FOUND_SIGNAL;
2N/A sig = args[0];
2N/A }
2N/A }
2N/A } else {
2N/A (void) memset(&frame, 0, sizeof (frame));
2N/A }
2N/A
2N/A gregs[R_FP] = fp;
2N/A gregs[R_PC] = pc;
2N/A
2N/A if ((rv = func(arg, gregs, argc, args, frame_flags, sig)) != 0)
2N/A break;
2N/A
2N/A if (frame_flags & PR_SIGNAL_FRAME)
2N/A frame_flags = 0;
2N/A
2N/A /* Locate the next frame. */
2N/A if (uc_addr &&
2N/A Pread(P, &uc, sizeof (uc), uc_addr) == sizeof (uc)) {
2N/A /*
2N/A * If this is a signal frame then we extract the new
2N/A * registers from the saved context, thereby allowing us
2N/A * to display the interrupted frame.
2N/A */
2N/A ucontext_n_to_prgregs(&uc, gregs);
2N/A fp = gregs[R_FP];
2N/A pc = gregs[R_PC];
2N/A } else {
2N/A fp = frame.fp;
2N/A pc = frame.pc;
2N/A }
2N/A }
2N/A
2N/A if (prevfp)
2N/A free(prevfp);
2N/A
2N/A free_uclist(&ucl);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Auintptr_t
2N/APsyscall_setup(struct ps_prochandle *P, int nargs, int sysindex, uintptr_t sp)
2N/A{
2N/A if (P->status.pr_dmodel == PR_MODEL_ILP32) {
2N/A sp -= sizeof (int) * (nargs+2);
2N/A
2N/A P->status.pr_lwp.pr_reg[REG_RAX] = sysindex;
2N/A P->status.pr_lwp.pr_reg[REG_RSP] = sp;
2N/A P->status.pr_lwp.pr_reg[REG_RIP] = P->sysaddr;
2N/A } else {
2N/A int pusharg = (nargs > 6) ? nargs - 6: 0;
2N/A
2N/A sp -= sizeof (int64_t) * (pusharg+2);
2N/A
2N/A P->status.pr_lwp.pr_reg[REG_RAX] = sysindex;
2N/A P->status.pr_lwp.pr_reg[REG_RSP] = sp;
2N/A P->status.pr_lwp.pr_reg[REG_RIP] = P->sysaddr;
2N/A }
2N/A
2N/A return (sp);
2N/A}
2N/A
2N/Aint
2N/APsyscall_copyinargs(struct ps_prochandle *P, int nargs, argdes_t *argp,
2N/A uintptr_t ap)
2N/A{
2N/A if (P->status.pr_dmodel == PR_MODEL_ILP32) {
2N/A int32_t arglist[MAXARGS+2];
2N/A int i;
2N/A argdes_t *adp;
2N/A
2N/A for (i = 0, adp = argp; i < nargs; i++, adp++)
2N/A arglist[1 + i] = (int32_t)adp->arg_value;
2N/A
2N/A arglist[0] = P->status.pr_lwp.pr_reg[REG_RIP];
2N/A if (Pwrite(P, &arglist[0], sizeof (int) * (nargs+1),
2N/A (uintptr_t)ap) != sizeof (int) * (nargs+1))
2N/A return (-1);
2N/A } else {
2N/A int64_t arglist[MAXARGS+2];
2N/A int i;
2N/A argdes_t *adp;
2N/A int pusharg = (nargs > 6) ? nargs - 6: 0;
2N/A
2N/A for (i = 0, adp = argp; i < nargs; i++, adp++) {
2N/A switch (i) {
2N/A case 0:
2N/A (void) Pputareg(P, REG_RDI, adp->arg_value);
2N/A break;
2N/A case 1:
2N/A (void) Pputareg(P, REG_RSI, adp->arg_value);
2N/A break;
2N/A case 2:
2N/A (void) Pputareg(P, REG_RDX, adp->arg_value);
2N/A break;
2N/A case 3:
2N/A (void) Pputareg(P, REG_RCX, adp->arg_value);
2N/A break;
2N/A case 4:
2N/A (void) Pputareg(P, REG_R8, adp->arg_value);
2N/A break;
2N/A case 5:
2N/A (void) Pputareg(P, REG_R9, adp->arg_value);
2N/A break;
2N/A default:
2N/A arglist[i - 5] = (uint64_t)adp->arg_value;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A arglist[0] = P->status.pr_lwp.pr_reg[REG_RIP];
2N/A
2N/A if (Pwrite(P, &arglist[0],
2N/A sizeof (int64_t) * (pusharg + 1), ap) !=
2N/A sizeof (int64_t) * (pusharg + 1))
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/APsyscall_copyoutargs(struct ps_prochandle *P, int nargs, argdes_t *argp,
2N/A uintptr_t ap)
2N/A{
2N/A if (P->status.pr_dmodel == PR_MODEL_ILP32) {
2N/A uint32_t arglist[MAXARGS + 2];
2N/A int i;
2N/A argdes_t *adp;
2N/A
2N/A if (Pread(P, &arglist[0], sizeof (int) * (nargs+1),
2N/A (uintptr_t)ap) != sizeof (int) * (nargs+1))
2N/A return (-1);
2N/A
2N/A for (i = 0, adp = argp; i < nargs; i++, adp++)
2N/A adp->arg_value = arglist[i];
2N/A } else {
2N/A int pusharg = (nargs > 6) ? nargs - 6: 0;
2N/A int64_t arglist[MAXARGS+2];
2N/A int i;
2N/A argdes_t *adp;
2N/A
2N/A if (pusharg > 0 &&
2N/A Pread(P, &arglist[0], sizeof (int64_t) * (pusharg + 1),
2N/A ap) != sizeof (int64_t) * (pusharg + 1))
2N/A return (-1);
2N/A
2N/A for (i = 0, adp = argp; i < nargs; i++, adp++) {
2N/A switch (i) {
2N/A case 0:
2N/A adp->arg_value =
2N/A P->status.pr_lwp.pr_reg[REG_RDI];
2N/A break;
2N/A case 1:
2N/A adp->arg_value =
2N/A P->status.pr_lwp.pr_reg[REG_RSI];
2N/A break;
2N/A case 2:
2N/A adp->arg_value =
2N/A P->status.pr_lwp.pr_reg[REG_RDX];
2N/A break;
2N/A case 3:
2N/A adp->arg_value =
2N/A P->status.pr_lwp.pr_reg[REG_RCX];
2N/A break;
2N/A case 4:
2N/A adp->arg_value =
2N/A P->status.pr_lwp.pr_reg[REG_R8];
2N/A break;
2N/A case 5:
2N/A adp->arg_value =
2N/A P->status.pr_lwp.pr_reg[REG_R9];
2N/A break;
2N/A default:
2N/A adp->arg_value = arglist[i - 6];
2N/A break;
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A return (0);
2N/A}