/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "Pcontrol.h" #include "Pstack.h" 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_XNumber; 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_pri; 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_pri; 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 */ static boolean_t argcount_ctf(struct ps_prochandle *P, uint32_t pc, uint_t *countp) { GElf_Sym sym; ctf_file_t *ctfp; ctf_funcinfo_t finfo; prsyminfo_t si = { 0 }; if (Pxlookup_by_addr(P, pc, NULL, 0, &sym, &si) != 0) return (B_FALSE); if ((ctfp = Paddr_to_ctf(P, pc)) == NULL) return (B_FALSE); if (ctf_func_info(ctfp, si.prs_id, &finfo) == CTF_ERR) return (B_FALSE); *countp = finfo.ctc_argc; return (B_TRUE); } /* * 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, ctf_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]; ctf_pc = 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); if (argcount_ctf(P, ctf_pc, &argc)) { argc = MIN(argc, 32); } else { argc = argcount(P, (uint32_t)frame.pc, sz); } } else argc = 3; /* sighandler(signo, sip, ucp) */ } else { (void) memset(&frame, 0, sizeof (frame)); argc = 0; } ctf_pc = frame.pc; 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)); } /* * Read arguments from the frame indicated by regs into args, return the * number of arguments successfully read */ static int read_args(struct ps_prochandle *P, uintptr_t fp, uintptr_t pc, prgreg_t *args, size_t argsize) { GElf_Sym sym; ctf_file_t *ctfp = NULL; ctf_funcinfo_t finfo; prsyminfo_t si = {0}; uint8_t ins[SAVEARGS_INSN_SEQ_LEN]; size_t insnsize; int argc = 0; int rettype = 0; int start_index = 0; int args_style = 0; int i; ctf_id_t args_types[5]; if (Pxlookup_by_addr(P, pc, NULL, 0, &sym, &si) != 0) return (0); if ((ctfp = Paddr_to_ctf(P, pc)) == NULL) return (0); if (ctf_func_info(ctfp, si.prs_id, &finfo) == CTF_ERR) return (0); argc = finfo.ctc_argc; if (argc == 0) return (0); rettype = ctf_type_kind(ctfp, finfo.ctc_return); /* * If the function returns a structure or union greater than 16 bytes * in size %rdi contains the address in which to store the return * value rather than for an argument. */ if (((rettype == CTF_K_STRUCT) || (rettype == CTF_K_UNION)) && ctf_type_size(ctfp, finfo.ctc_return) > 16) start_index = 1; else start_index = 0; /* * If any of the first 5 arguments are a structure less than 16 bytes * in size, it will be passed spread across two argument registers, * and we will not cope. */ if (ctf_func_args(ctfp, si.prs_id, 5, args_types) == CTF_ERR) return (0); for (i = 0; i < MIN(5, finfo.ctc_argc); i++) { int t = ctf_type_kind(ctfp, args_types[i]); if (((t == CTF_K_STRUCT) || (t == CTF_K_UNION)) && ctf_type_size(ctfp, args_types[i]) <= 16) return (0); } /* * The number of instructions to search for argument saving is limited * such that only instructions prior to %pc are considered and we * never read arguments from a function where the saving code has not * in fact yet executed. */ insnsize = MIN(MIN(sym.st_size, SAVEARGS_INSN_SEQ_LEN), pc - sym.st_value); if (Pread(P, ins, insnsize, sym.st_value) != insnsize) return (0); if ((argc != 0) && ((args_style = saveargs_has_args(ins, insnsize, argc, start_index)) != SAVEARGS_NO_ARGS)) { int regargs = MIN((6 - start_index), argc); size_t size = regargs * sizeof (long); int i; /* * If Studio pushed a structure return address as an argument, * we need to read one more argument than actually exists (the * addr) to make everything line up. */ if (args_style == SAVEARGS_STRUCT_ARGS) size += sizeof (long); if (Pread(P, args, size, (fp - size)) != size) return (0); for (i = 0; i < (regargs / 2); i++) { prgreg_t t = args[i]; args[i] = args[regargs - i - 1]; args[regargs - i - 1] = t; } if (argc > regargs) { size = MIN((argc - regargs) * sizeof (long), argsize - (regargs * sizeof (long))); if (Pread(P, &args[regargs], size, fp + (sizeof (uintptr_t) * 2)) != size) return (6); } return (argc); } else { return (0); } } 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] = {0}; 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) { 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 { argc = read_args(P, fp, pc, args, sizeof (args)); } } 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); }