diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libproc/common | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libproc/common')
46 files changed, 20833 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/common/P32ton.c b/usr/src/lib/libproc/common/P32ton.c new file mode 100644 index 0000000000..90cdb41a41 --- /dev/null +++ b/usr/src/lib/libproc/common/P32ton.c @@ -0,0 +1,819 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/mkdev.h> +#include <sys/regset.h> +#include <string.h> + +#if defined(__amd64) +#include <sys/fp.h> +#include <ieeefp.h> +#endif + +#include "P32ton.h" + +dev_t +prexpldev(dev32_t d) +{ + if (d != (dev32_t)-1L) + return (makedev((d >> NBITSMINOR32) & MAXMAJ32, d & MAXMIN32)); + + return ((dev_t)PRNODEV); +} + + +dev32_t +prcmpldev(dev_t d) +{ +#ifdef _LP64 + if (d == PRNODEV) { + return (PRNODEV32); + } else { + major_t maj = major(d); + minor_t min = minor(d); + + if (maj == (major_t)PRNODEV || min == (minor_t)PRNODEV) + return (PRNODEV32); + + return ((dev32_t)((maj << NBITSMINOR32) | min)); + } +#else + return ((dev32_t)d); +#endif +} + +#ifdef _LP64 + +void +timestruc_32_to_n(const timestruc32_t *src, timestruc_t *dst) +{ + dst->tv_sec = (time_t)(uint32_t)src->tv_sec; + dst->tv_nsec = (long)(uint32_t)src->tv_nsec; +} + +void +stack_32_to_n(const stack32_t *src, stack_t *dst) +{ + dst->ss_sp = (caddr_t)(uintptr_t)src->ss_sp; + dst->ss_size = src->ss_size; + dst->ss_flags = src->ss_flags; +} + +void +sigaction_32_to_n(const struct sigaction32 *src, struct sigaction *dst) +{ + (void) memset(dst, 0, sizeof (struct sigaction)); + dst->sa_flags = src->sa_flags; + dst->sa_handler = (void (*)())(uintptr_t)src->sa_handler; + (void) memcpy(&dst->sa_mask, &src->sa_mask, sizeof (dst->sa_mask)); +} + +void +siginfo_32_to_n(const siginfo32_t *src, siginfo_t *dst) +{ + (void) memset(dst, 0, sizeof (siginfo_t)); + + /* + * The absolute minimum content is si_signo and si_code. + */ + dst->si_signo = src->si_signo; + if ((dst->si_code = src->si_code) == SI_NOINFO) + return; + + /* + * A siginfo generated by user level is structured + * differently from one generated by the kernel. + */ + if (SI_FROMUSER(src)) { + dst->si_pid = src->si_pid; + dst->si_ctid = src->si_ctid; + dst->si_zoneid = src->si_zoneid; + dst->si_uid = src->si_uid; + if (SI_CANQUEUE(src->si_code)) { + dst->si_value.sival_int = + (long)(uint32_t)src->si_value.sival_int; + } + return; + } + + dst->si_errno = src->si_errno; + + switch (src->si_signo) { + default: + dst->si_pid = src->si_pid; + dst->si_ctid = src->si_ctid; + dst->si_zoneid = src->si_zoneid; + dst->si_uid = src->si_uid; + dst->si_value.sival_int = + (long)(uint32_t)src->si_value.sival_int; + break; + case SIGCLD: + dst->si_pid = src->si_pid; + dst->si_ctid = src->si_ctid; + dst->si_zoneid = src->si_zoneid; + dst->si_status = src->si_status; + dst->si_stime = src->si_stime; + dst->si_utime = src->si_utime; + break; + case SIGSEGV: + case SIGBUS: + case SIGILL: + case SIGTRAP: + case SIGFPE: + case SIGEMT: + dst->si_addr = (void *)(uintptr_t)src->si_addr; + dst->si_trapno = src->si_trapno; + dst->si_pc = (void *)(uintptr_t)src->si_pc; + break; + case SIGPOLL: + case SIGXFSZ: + dst->si_fd = src->si_fd; + dst->si_band = src->si_band; + break; + case SIGPROF: + dst->si_faddr = (void *)(uintptr_t)src->si_faddr; + dst->si_tstamp.tv_sec = src->si_tstamp.tv_sec; + dst->si_tstamp.tv_nsec = src->si_tstamp.tv_nsec; + dst->si_syscall = src->si_syscall; + dst->si_nsysarg = src->si_nsysarg; + dst->si_fault = src->si_fault; + break; + } +} + +void +auxv_32_to_n(const auxv32_t *src, auxv_t *dst) +{ + /* + * This is a little sketchy: we have three types of values stored + * in an auxv (long, void *, and void (*)()) so the only sign-extension + * issue is with the long. We could case on all possible AT_* types, + * but this seems silly since currently none of the types which use + * a_un.a_val actually use negative numbers as a value. For this + * reason, it seems simpler to just do an unsigned expansion for now. + */ + dst->a_type = src->a_type; + dst->a_un.a_ptr = (void *)(uintptr_t)src->a_un.a_ptr; +} + +#if defined(__sparc) +void +rwindow_32_to_n(const struct rwindow32 *src, struct rwindow *dst) +{ + int i; + + for (i = 0; i < 8; i++) { + dst->rw_local[i] = (uint64_t)(uint32_t)src->rw_local[i]; + dst->rw_in[i] = (uint64_t)(uint32_t)src->rw_in[i]; + } +} + +void +gwindows_32_to_n(const gwindows32_t *src, gwindows_t *dst) +{ + int i; + + (void) memset(dst, 0, sizeof (gwindows_t)); + dst->wbcnt = src->wbcnt; + + for (i = 0; i < src->wbcnt; i++) { + if (src->spbuf[i] != 0) { + rwindow_32_to_n(&src->wbuf[i], &dst->wbuf[i]); + dst->spbuf[i] = (greg_t *)src->spbuf[i]; + } + } +} +#endif /* __sparc */ + +void +prgregset_32_to_n(const prgreg32_t *src, prgreg_t *dst) +{ +#ifdef __amd64 + (void) memset(dst, 0, NPRGREG * sizeof (prgreg_t)); + dst[REG_GS] = (uint32_t)src[GS]; + dst[REG_FS] = (uint32_t)src[FS]; + dst[REG_DS] = (uint32_t)src[DS]; + dst[REG_ES] = (uint32_t)src[ES]; + dst[REG_RDI] = (uint32_t)src[EDI]; + dst[REG_RSI] = (uint32_t)src[ESI]; + dst[REG_RBP] = (uint32_t)src[EBP]; + dst[REG_RBX] = (uint32_t)src[EBX]; + dst[REG_RDX] = (uint32_t)src[EDX]; + dst[REG_RCX] = (uint32_t)src[ECX]; + dst[REG_RAX] = (uint32_t)src[EAX]; + dst[REG_TRAPNO] = (uint32_t)src[TRAPNO]; + dst[REG_ERR] = (uint32_t)src[ERR]; + dst[REG_RIP] = (uint32_t)src[EIP]; + dst[REG_CS] = (uint32_t)src[CS]; + dst[REG_RFL] = (uint32_t)src[EFL]; + dst[REG_RSP] = (uint32_t)src[UESP]; + dst[REG_SS] = (uint32_t)src[SS]; +#else + int i; + + for (i = 0; i < NPRGREG; i++) + dst[i] = (prgreg_t)(uint32_t)src[i]; +#endif +} + +void +prfpregset_32_to_n(const prfpregset32_t *src, prfpregset_t *dst) +{ +#if defined(__sparc) + int i; + + (void) memset(dst, 0, sizeof (prfpregset_t)); + + for (i = 0; i < 32; i++) + dst->pr_fr.pr_regs[i] = src->pr_fr.pr_regs[i]; + + /* + * We deliberately do not convert pr_qcnt or pr_q because it is a long- + * standing /proc bug that this information is not exported, and another + * bug further caused these values to be returned as uninitialized data + * when the 64-bit kernel exported them for a 32-bit process with en=0. + */ + dst->pr_filler = src->pr_filler; + dst->pr_fsr = src->pr_fsr; + dst->pr_q_entrysize = src->pr_q_entrysize; + dst->pr_en = src->pr_en; + +#elif defined(__amd64) + + struct _fpstate32 *src32 = (struct _fpstate32 *)src; + struct fpchip_state *dst64 = (struct fpchip_state *)dst; + int i; + + (void) memcpy(dst64->st, src32->_st, sizeof (src32->_st)); + (void) memcpy(dst64->xmm, src32->xmm, sizeof (src32->xmm)); + (void) memset((caddr_t)dst64->xmm + sizeof (src32->xmm), 0, + sizeof (dst64->xmm) - sizeof (src32->xmm)); + dst64->cw = (uint16_t)src32->cw; + dst64->sw = (uint16_t)src32->sw; + dst64->fop = 0; + dst64->rip = src32->ipoff; + dst64->rdp = src32->dataoff; + dst64->mxcsr = src32->mxcsr; + dst64->mxcsr_mask = 0; + dst64->status = src32->status; + dst64->xstatus = src32->xstatus; + + /* + * Converting from the tag field to the compressed fctw is easy. + * If the two tag bits are 3, then the register is empty and we + * clear the bit in fctw. Otherwise we set the bit. + */ + + dst64->fctw = 0; + for (i = 0; i < 8; i++) + if (((src32->tag >> (i * 2)) & 3) != 3) + dst64->fctw |= 1 << i; +#else +#error "unrecognized ISA" +#endif +} + +void +lwpstatus_32_to_n(const lwpstatus32_t *src, lwpstatus_t *dst) +{ + int i; + + dst->pr_flags = src->pr_flags; + dst->pr_lwpid = src->pr_lwpid; + dst->pr_why = src->pr_why; + dst->pr_what = src->pr_what; + dst->pr_cursig = src->pr_cursig; + + siginfo_32_to_n(&src->pr_info, &dst->pr_info); + + dst->pr_lwppend = src->pr_lwppend; + dst->pr_lwphold = src->pr_lwphold; + + sigaction_32_to_n(&src->pr_action, &dst->pr_action); + stack_32_to_n(&src->pr_altstack, &dst->pr_altstack); + + dst->pr_oldcontext = src->pr_oldcontext; + dst->pr_syscall = src->pr_syscall; + dst->pr_nsysarg = src->pr_nsysarg; + dst->pr_errno = src->pr_errno; + + for (i = 0; i < PRSYSARGS; i++) + dst->pr_sysarg[i] = (long)(uint32_t)src->pr_sysarg[i]; + + dst->pr_rval1 = (long)(uint32_t)src->pr_rval1; + dst->pr_rval2 = (long)(uint32_t)src->pr_rval2; + + (void) memcpy(&dst->pr_clname[0], &src->pr_clname[0], PRCLSZ); + timestruc_32_to_n(&src->pr_tstamp, &dst->pr_tstamp); + + dst->pr_ustack = src->pr_ustack; + dst->pr_instr = src->pr_instr; + + prgregset_32_to_n(src->pr_reg, dst->pr_reg); + prfpregset_32_to_n(&src->pr_fpreg, &dst->pr_fpreg); +} + +void +pstatus_32_to_n(const pstatus32_t *src, pstatus_t *dst) +{ + dst->pr_flags = src->pr_flags; + dst->pr_nlwp = src->pr_nlwp; + dst->pr_nzomb = src->pr_nzomb; + dst->pr_pid = src->pr_pid; + dst->pr_ppid = src->pr_ppid; + dst->pr_pgid = src->pr_pgid; + dst->pr_sid = src->pr_sid; + dst->pr_taskid = src->pr_taskid; + dst->pr_projid = src->pr_projid; + dst->pr_zoneid = src->pr_zoneid; + dst->pr_aslwpid = src->pr_aslwpid; + dst->pr_agentid = src->pr_agentid; + dst->pr_sigpend = src->pr_sigpend; + dst->pr_brkbase = src->pr_brkbase; + dst->pr_brksize = src->pr_brksize; + dst->pr_stkbase = src->pr_stkbase; + dst->pr_stksize = src->pr_stksize; + + timestruc_32_to_n(&src->pr_utime, &dst->pr_utime); + timestruc_32_to_n(&src->pr_stime, &dst->pr_stime); + timestruc_32_to_n(&src->pr_cutime, &dst->pr_cutime); + timestruc_32_to_n(&src->pr_cstime, &dst->pr_cstime); + + dst->pr_sigtrace = src->pr_sigtrace; + dst->pr_flttrace = src->pr_flttrace; + dst->pr_sysentry = src->pr_sysentry; + dst->pr_sysexit = src->pr_sysexit; + dst->pr_dmodel = src->pr_dmodel; + + lwpstatus_32_to_n(&src->pr_lwp, &dst->pr_lwp); +} + +void +lwpsinfo_32_to_n(const lwpsinfo32_t *src, lwpsinfo_t *dst) +{ + dst->pr_flag = src->pr_flag; + dst->pr_lwpid = src->pr_lwpid; + dst->pr_addr = src->pr_addr; + dst->pr_wchan = src->pr_wchan; + dst->pr_stype = src->pr_stype; + dst->pr_state = src->pr_state; + dst->pr_sname = src->pr_sname; + dst->pr_nice = src->pr_nice; + dst->pr_syscall = src->pr_syscall; + dst->pr_oldpri = src->pr_oldpri; + dst->pr_cpu = src->pr_cpu; + dst->pr_pri = src->pr_pri; + dst->pr_pctcpu = src->pr_pctcpu; + + timestruc_32_to_n(&src->pr_start, &dst->pr_start); + timestruc_32_to_n(&src->pr_time, &dst->pr_time); + + (void) memcpy(&dst->pr_clname[0], &src->pr_clname[0], PRCLSZ); + (void) memcpy(&dst->pr_name[0], &src->pr_name[0], PRFNSZ); + + dst->pr_onpro = src->pr_onpro; + dst->pr_bindpro = src->pr_bindpro; + dst->pr_bindpset = src->pr_bindpset; +} + +void +psinfo_32_to_n(const psinfo32_t *src, psinfo_t *dst) +{ + dst->pr_flag = src->pr_flag; + dst->pr_nlwp = src->pr_nlwp; + dst->pr_nzomb = src->pr_nzomb; + dst->pr_pid = src->pr_pid; + dst->pr_pgid = src->pr_pgid; + dst->pr_sid = src->pr_sid; + dst->pr_taskid = src->pr_taskid; + dst->pr_projid = src->pr_projid; + dst->pr_zoneid = src->pr_zoneid; + dst->pr_uid = src->pr_uid; + dst->pr_euid = src->pr_euid; + dst->pr_gid = src->pr_gid; + dst->pr_egid = src->pr_egid; + dst->pr_addr = src->pr_addr; + dst->pr_size = src->pr_size; + dst->pr_rssize = src->pr_rssize; + + dst->pr_ttydev = prexpldev(src->pr_ttydev); + + dst->pr_pctcpu = src->pr_pctcpu; + dst->pr_pctmem = src->pr_pctmem; + + timestruc_32_to_n(&src->pr_start, &dst->pr_start); + timestruc_32_to_n(&src->pr_time, &dst->pr_time); + timestruc_32_to_n(&src->pr_ctime, &dst->pr_ctime); + + (void) memcpy(&dst->pr_fname[0], &src->pr_fname[0], PRFNSZ); + (void) memcpy(&dst->pr_psargs[0], &src->pr_psargs[0], PRARGSZ); + + dst->pr_wstat = src->pr_wstat; + dst->pr_argc = src->pr_argc; + dst->pr_argv = src->pr_argv; + dst->pr_envp = src->pr_envp; + dst->pr_dmodel = src->pr_dmodel; + + lwpsinfo_32_to_n(&src->pr_lwp, &dst->pr_lwp); +} + +void +timestruc_n_to_32(const timestruc_t *src, timestruc32_t *dst) +{ + dst->tv_sec = (time32_t)src->tv_sec; + dst->tv_nsec = (int32_t)src->tv_nsec; +} + +void +stack_n_to_32(const stack_t *src, stack32_t *dst) +{ + dst->ss_sp = (caddr32_t)(uintptr_t)src->ss_sp; + dst->ss_size = src->ss_size; + dst->ss_flags = src->ss_flags; +} + +void +sigaction_n_to_32(const struct sigaction *src, struct sigaction32 *dst) +{ + (void) memset(dst, 0, sizeof (struct sigaction32)); + dst->sa_flags = src->sa_flags; + dst->sa_handler = (caddr32_t)(uintptr_t)src->sa_handler; + (void) memcpy(&dst->sa_mask, &src->sa_mask, sizeof (dst->sa_mask)); +} + +void +siginfo_n_to_32(const siginfo_t *src, siginfo32_t *dst) +{ + (void) memset(dst, 0, sizeof (siginfo32_t)); + + /* + * The absolute minimum content is si_signo and si_code. + */ + dst->si_signo = src->si_signo; + if ((dst->si_code = src->si_code) == SI_NOINFO) + return; + + /* + * A siginfo generated by user level is structured + * differently from one generated by the kernel. + */ + if (SI_FROMUSER(src)) { + dst->si_pid = src->si_pid; + dst->si_ctid = src->si_ctid; + dst->si_zoneid = src->si_zoneid; + dst->si_uid = src->si_uid; + if (SI_CANQUEUE(src->si_code)) { + dst->si_value.sival_int = + (int32_t)src->si_value.sival_int; + } + return; + } + + dst->si_errno = src->si_errno; + + switch (src->si_signo) { + default: + dst->si_pid = src->si_pid; + dst->si_ctid = src->si_ctid; + dst->si_zoneid = src->si_zoneid; + dst->si_uid = src->si_uid; + dst->si_value.sival_int = + (int32_t)src->si_value.sival_int; + break; + case SIGCLD: + dst->si_pid = src->si_pid; + dst->si_ctid = src->si_ctid; + dst->si_zoneid = src->si_zoneid; + dst->si_status = src->si_status; + dst->si_stime = src->si_stime; + dst->si_utime = src->si_utime; + break; + case SIGSEGV: + case SIGBUS: + case SIGILL: + case SIGTRAP: + case SIGFPE: + case SIGEMT: + dst->si_addr = (caddr32_t)(uintptr_t)src->si_addr; + dst->si_trapno = src->si_trapno; + dst->si_pc = (caddr32_t)(uintptr_t)src->si_pc; + break; + case SIGPOLL: + case SIGXFSZ: + dst->si_fd = src->si_fd; + dst->si_band = src->si_band; + break; + case SIGPROF: + dst->si_faddr = (caddr32_t)(uintptr_t)src->si_faddr; + dst->si_tstamp.tv_sec = src->si_tstamp.tv_sec; + dst->si_tstamp.tv_nsec = src->si_tstamp.tv_nsec; + dst->si_syscall = src->si_syscall; + dst->si_nsysarg = src->si_nsysarg; + dst->si_fault = src->si_fault; + break; + } +} + +void +auxv_n_to_32(const auxv_t *src, auxv32_t *dst) +{ + dst->a_type = src->a_type; + dst->a_un.a_ptr = (caddr32_t)(uintptr_t)src->a_un.a_ptr; +} + +void +prgregset_n_to_32(const prgreg_t *src, prgreg32_t *dst) +{ +#ifdef __amd64 + (void) memset(dst, 0, NPRGREG32 * sizeof (prgreg32_t)); + dst[GS] = src[REG_GS]; + dst[FS] = src[REG_FS]; + dst[DS] = src[REG_DS]; + dst[ES] = src[REG_ES]; + dst[EDI] = src[REG_RDI]; + dst[ESI] = src[REG_RSI]; + dst[EBP] = src[REG_RBP]; + dst[EBX] = src[REG_RBX]; + dst[EDX] = src[REG_RDX]; + dst[ECX] = src[REG_RCX]; + dst[EAX] = src[REG_RAX]; + dst[TRAPNO] = src[REG_TRAPNO]; + dst[ERR] = src[REG_ERR]; + dst[EIP] = src[REG_RIP]; + dst[CS] = src[REG_CS]; + dst[EFL] = src[REG_RFL]; + dst[UESP] = src[REG_RSP]; + dst[SS] = src[REG_SS]; +#else + int i; + + for (i = 0; i < NPRGREG; i++) + dst[i] = (prgreg32_t)src[i]; +#endif +} + +void +prfpregset_n_to_32(const prfpregset_t *src, prfpregset32_t *dst) +{ +#if defined(__sparc) + int i; + + (void) memset(dst, 0, sizeof (prfpregset32_t)); + + for (i = 0; i < 32; i++) + dst->pr_fr.pr_regs[i] = src->pr_fr.pr_regs[i]; + + dst->pr_filler = src->pr_filler; + dst->pr_fsr = src->pr_fsr; + dst->pr_q_entrysize = src->pr_q_entrysize; + dst->pr_en = src->pr_en; + +#elif defined(__amd64) + + struct _fpstate32 *dst32 = (struct _fpstate32 *)dst; + struct fpchip_state *src64 = (struct fpchip_state *)src; + uint32_t top; + int i; + + (void) memcpy(dst32->_st, src64->st, sizeof (dst32->_st)); + (void) memcpy(dst32->xmm, src64->xmm, sizeof (dst32->xmm)); + dst32->cw = src64->cw; + dst32->sw = src64->sw; + dst32->ipoff = (unsigned int)src64->rip; + dst32->cssel = 0; + dst32->dataoff = (unsigned int)src64->rdp; + dst32->datasel = 0; + dst32->status = src64->status; + dst32->mxcsr = src64->mxcsr; + dst32->xstatus = src64->xstatus; + + /* + * AMD64 stores the tag in a compressed form. It is + * necessary to extract the original 2-bit tag value. + * See AMD64 Architecture Programmer's Manual Volume 2: + * System Programming, Chapter 11. + */ + + top = (src64->sw & FPS_TOP) >> 11; + dst32->tag = 0; + for (i = 0; i < 8; i++) { + /* + * Recall that we need to use the current TOP-of-stack value to + * associate the _st[] index back to a physical register number, + * since tag word indices are physical register numbers. Then + * to get the tag value, we shift over two bits for each tag + * index, and then grab the bottom two bits. + */ + uint_t tag_index = (i + top) & 7; + uint_t tag_fctw = (src64->fctw >> tag_index) & 1; + uint_t tag_value; + uint_t exp; + + /* + * Union for overlaying _fpreg structure on to quad-precision + * floating-point value (long double). + */ + union { + struct _fpreg reg; + long double ld; + } fpru; + + fpru.ld = src64->st[i].__fpr_pad._q; + exp = fpru.reg.exponent & 0x7fff; + + if (tag_fctw == 0) { + tag_value = 3; /* empty */ + } else if (exp == 0) { + if (fpru.reg.significand[0] == 0 && + fpru.reg.significand[1] == 0 && + fpru.reg.significand[2] == 0 && + fpru.reg.significand[3] == 0) + tag_value = 1; /* zero */ + else + tag_value = 2; /* special: denormal */ + } else if (exp == 0x7fff) { + tag_value = 2; /* special: infinity or NaN */ + } else if (fpru.reg.significand[3] & 0x8000) { + tag_value = 0; /* valid */ + } else { + tag_value = 2; /* special: unnormal */ + } + dst32->tag |= tag_value << (tag_index * 2); + } +#else +#error "unrecognized ISA" +#endif +} + +void +lwpstatus_n_to_32(const lwpstatus_t *src, lwpstatus32_t *dst) +{ + int i; + + dst->pr_flags = src->pr_flags; + dst->pr_lwpid = src->pr_lwpid; + dst->pr_why = src->pr_why; + dst->pr_what = src->pr_what; + dst->pr_cursig = src->pr_cursig; + + siginfo_n_to_32(&src->pr_info, &dst->pr_info); + + dst->pr_lwppend = src->pr_lwppend; + dst->pr_lwphold = src->pr_lwphold; + + sigaction_n_to_32(&src->pr_action, &dst->pr_action); + stack_n_to_32(&src->pr_altstack, &dst->pr_altstack); + + dst->pr_oldcontext = (caddr32_t)src->pr_oldcontext; + dst->pr_syscall = src->pr_syscall; + dst->pr_nsysarg = src->pr_nsysarg; + dst->pr_errno = src->pr_errno; + + for (i = 0; i < PRSYSARGS; i++) + dst->pr_sysarg[i] = (int32_t)src->pr_sysarg[i]; + + dst->pr_rval1 = (int32_t)src->pr_rval1; + dst->pr_rval2 = (int32_t)src->pr_rval2; + + (void) memcpy(&dst->pr_clname[0], &src->pr_clname[0], PRCLSZ); + timestruc_n_to_32(&src->pr_tstamp, &dst->pr_tstamp); + + dst->pr_ustack = (caddr32_t)src->pr_ustack; + dst->pr_instr = src->pr_instr; + + prgregset_n_to_32(src->pr_reg, dst->pr_reg); + prfpregset_n_to_32(&src->pr_fpreg, &dst->pr_fpreg); +} + +void +pstatus_n_to_32(const pstatus_t *src, pstatus32_t *dst) +{ + dst->pr_flags = src->pr_flags; + dst->pr_nlwp = src->pr_nlwp; + dst->pr_nzomb = src->pr_nzomb; + dst->pr_pid = (pid32_t)src->pr_pid; + dst->pr_ppid = (pid32_t)src->pr_ppid; + dst->pr_pgid = (pid32_t)src->pr_pgid; + dst->pr_sid = (pid32_t)src->pr_sid; + dst->pr_taskid = (id32_t)src->pr_taskid; + dst->pr_projid = (id32_t)src->pr_projid; + dst->pr_zoneid = (id32_t)src->pr_zoneid; + dst->pr_aslwpid = (id32_t)src->pr_aslwpid; + dst->pr_agentid = (id32_t)src->pr_agentid; + dst->pr_sigpend = src->pr_sigpend; + dst->pr_brkbase = (caddr32_t)src->pr_brkbase; + dst->pr_brksize = (size32_t)src->pr_brksize; + dst->pr_stkbase = (caddr32_t)src->pr_stkbase; + dst->pr_stksize = (size32_t)src->pr_stksize; + + timestruc_n_to_32(&src->pr_utime, &dst->pr_utime); + timestruc_n_to_32(&src->pr_stime, &dst->pr_stime); + timestruc_n_to_32(&src->pr_cutime, &dst->pr_cutime); + timestruc_n_to_32(&src->pr_cstime, &dst->pr_cstime); + + dst->pr_sigtrace = src->pr_sigtrace; + dst->pr_flttrace = src->pr_flttrace; + dst->pr_sysentry = src->pr_sysentry; + dst->pr_sysexit = src->pr_sysexit; + dst->pr_dmodel = src->pr_dmodel; + + lwpstatus_n_to_32(&src->pr_lwp, &dst->pr_lwp); +} + +void +lwpsinfo_n_to_32(const lwpsinfo_t *src, lwpsinfo32_t *dst) +{ + dst->pr_flag = src->pr_flag; + dst->pr_lwpid = (id32_t)src->pr_lwpid; + dst->pr_addr = (caddr32_t)src->pr_addr; + dst->pr_wchan = (caddr32_t)src->pr_wchan; + dst->pr_stype = src->pr_stype; + dst->pr_state = src->pr_state; + dst->pr_sname = src->pr_sname; + dst->pr_nice = src->pr_nice; + dst->pr_syscall = src->pr_syscall; + dst->pr_oldpri = src->pr_oldpri; + dst->pr_cpu = src->pr_cpu; + dst->pr_pri = src->pr_pri; + dst->pr_pctcpu = src->pr_pctcpu; + + timestruc_n_to_32(&src->pr_start, &dst->pr_start); + timestruc_n_to_32(&src->pr_time, &dst->pr_time); + + (void) memcpy(&dst->pr_clname[0], &src->pr_clname[0], PRCLSZ); + (void) memcpy(&dst->pr_name[0], &src->pr_name[0], PRFNSZ); + + dst->pr_onpro = src->pr_onpro; + dst->pr_bindpro = src->pr_bindpro; + dst->pr_bindpset = src->pr_bindpset; +} + +void +psinfo_n_to_32(const psinfo_t *src, psinfo32_t *dst) +{ + dst->pr_flag = src->pr_flag; + dst->pr_nlwp = src->pr_nlwp; + dst->pr_nzomb = src->pr_nzomb; + dst->pr_pid = (pid32_t)src->pr_pid; + dst->pr_pgid = (pid32_t)src->pr_pgid; + dst->pr_sid = (pid32_t)src->pr_sid; + dst->pr_taskid = (id32_t)src->pr_taskid; + dst->pr_projid = (id32_t)src->pr_projid; + dst->pr_zoneid = (id32_t)src->pr_zoneid; + dst->pr_uid = (uid32_t)src->pr_uid; + dst->pr_euid = (uid32_t)src->pr_euid; + dst->pr_gid = (gid32_t)src->pr_gid; + dst->pr_egid = (gid32_t)src->pr_egid; + dst->pr_addr = (caddr32_t)src->pr_addr; + dst->pr_size = (size32_t)src->pr_size; + dst->pr_rssize = (size32_t)src->pr_rssize; + + dst->pr_ttydev = prcmpldev(src->pr_ttydev); + + dst->pr_pctcpu = src->pr_pctcpu; + dst->pr_pctmem = src->pr_pctmem; + + timestruc_n_to_32(&src->pr_start, &dst->pr_start); + timestruc_n_to_32(&src->pr_time, &dst->pr_time); + timestruc_n_to_32(&src->pr_ctime, &dst->pr_ctime); + + (void) memcpy(&dst->pr_fname[0], &src->pr_fname[0], PRFNSZ); + (void) memcpy(&dst->pr_psargs[0], &src->pr_psargs[0], PRARGSZ); + + dst->pr_wstat = src->pr_wstat; + dst->pr_argc = src->pr_argc; + dst->pr_argv = (caddr32_t)src->pr_argv; + dst->pr_envp = (caddr32_t)src->pr_envp; + dst->pr_dmodel = src->pr_dmodel; + + lwpsinfo_n_to_32(&src->pr_lwp, &dst->pr_lwp); +} + + +#endif /* _LP64 */ diff --git a/usr/src/lib/libproc/common/P32ton.h b/usr/src/lib/libproc/common/P32ton.h new file mode 100644 index 0000000000..95b6d06fae --- /dev/null +++ b/usr/src/lib/libproc/common/P32ton.h @@ -0,0 +1,86 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _P32TON_H +#define _P32TON_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/types32.h> +#include <sys/time_impl.h> +#include <sys/regset.h> +#include <sys/signal.h> +#include <sys/auxv.h> +#include <procfs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern dev_t prexpldev(dev32_t); +extern dev32_t prcmpldev(dev_t); + +#ifdef _LP64 + +extern void timestruc_32_to_n(const timestruc32_t *, timestruc_t *); +extern void stack_32_to_n(const stack32_t *, stack_t *); +extern void sigaction_32_to_n(const struct sigaction32 *, struct sigaction *); +extern void siginfo_32_to_n(const siginfo32_t *, siginfo_t *); +extern void auxv_32_to_n(const auxv32_t *, auxv_t *); + +#if defined(__sparc) +extern void rwindow_32_to_n(const struct rwindow32 *, struct rwindow *); +extern void gwindows_32_to_n(const gwindows32_t *, gwindows_t *); +#endif + +extern void prgregset_32_to_n(const prgreg32_t *, prgreg_t *); +extern void prfpregset_32_to_n(const prfpregset32_t *, prfpregset_t *); +extern void lwpstatus_32_to_n(const lwpstatus32_t *, lwpstatus_t *); +extern void pstatus_32_to_n(const pstatus32_t *, pstatus_t *); +extern void lwpsinfo_32_to_n(const lwpsinfo32_t *, lwpsinfo_t *); +extern void psinfo_32_to_n(const psinfo32_t *, psinfo_t *); + +extern void timestruc_n_to_32(const timestruc_t *, timestruc32_t *); +extern void stack_n_to_32(const stack_t *, stack32_t *); +extern void sigaction_n_to_32(const struct sigaction *, struct sigaction32 *); +extern void siginfo_n_to_32(const siginfo_t *, siginfo32_t *); +extern void auxv_n_to_32(const auxv_t *, auxv32_t *); + +extern void prgregset_n_to_32(const prgreg_t *, prgreg32_t *); +extern void prfpregset_n_to_32(const prfpregset_t *, prfpregset32_t *); +extern void lwpstatus_n_to_32(const lwpstatus_t *, lwpstatus32_t *); +extern void pstatus_n_to_32(const pstatus_t *, pstatus32_t *); +extern void lwpsinfo_n_to_32(const lwpsinfo_t *, lwpsinfo32_t *); +extern void psinfo_n_to_32(const psinfo_t *, psinfo32_t *); + +#endif /* _LP64 */ + +#ifdef __cplusplus +} +#endif + +#endif /* _P32TON_H */ diff --git a/usr/src/lib/libproc/common/Pcontrol.c b/usr/src/lib/libproc/common/Pcontrol.c new file mode 100644 index 0000000000..a0db30858b --- /dev/null +++ b/usr/src/lib/libproc/common/Pcontrol.c @@ -0,0 +1,3693 @@ +/* + * 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 <unistd.h> +#include <ctype.h> +#include <fcntl.h> +#include <string.h> +#include <memory.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <sys/param.h> +#include <sys/stack.h> +#include <sys/fault.h> +#include <sys/syscall.h> +#include <sys/sysmacros.h> + +#include "libproc.h" +#include "Pcontrol.h" +#include "Putil.h" +#include "P32ton.h" + +int _libproc_debug; /* set non-zero to enable debugging printfs */ +sigset_t blockable_sigs; /* signals to block when we need to be safe */ +static int minfd; /* minimum file descriptor returned by dupfd(fd, 0) */ + +/* + * Function prototypes for static routines in this module. + */ +static void deadcheck(struct ps_prochandle *); +static void restore_tracing_flags(struct ps_prochandle *); +static void Lfree_internal(struct ps_prochandle *, struct ps_lwphandle *); + +/* + * Read/write interface for live processes: just pread/pwrite the + * /proc/<pid>/as file: + */ + +static ssize_t +Pread_live(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) +{ + return (pread(P->asfd, buf, n, (off_t)addr)); +} + +static ssize_t +Pwrite_live(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) +{ + return (pwrite(P->asfd, buf, n, (off_t)addr)); +} + +static const ps_rwops_t P_live_ops = { Pread_live, Pwrite_live }; + +/* + * This is the library's .init handler. + */ +#pragma init(_libproc_init) +void +_libproc_init(void) +{ + _libproc_debug = getenv("LIBPROC_DEBUG") != NULL; + + (void) sigfillset(&blockable_sigs); + (void) sigdelset(&blockable_sigs, SIGKILL); + (void) sigdelset(&blockable_sigs, SIGSTOP); +} + +/* + * Call set_minfd() once before calling dupfd() several times. + * We assume that the application will not reduce its current file + * descriptor limit lower than 512 once it has set at least that value. + */ +int +set_minfd(void) +{ + static mutex_t minfd_lock = DEFAULTMUTEX; + struct rlimit rlim; + int fd; + + if ((fd = minfd) < 256) { + (void) mutex_lock(&minfd_lock); + if ((fd = minfd) < 256) { + if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) + rlim.rlim_cur = rlim.rlim_max = 0; + if (rlim.rlim_cur >= 512) + fd = 256; + else if ((fd = rlim.rlim_cur / 2) < 3) + fd = 3; + minfd = fd; + } + (void) mutex_unlock(&minfd_lock); + } + return (fd); +} + +int +dupfd(int fd, int dfd) +{ + int mfd; + + /* + * Make fd be greater than 255 (the 32-bit stdio limit), + * or at least make it greater than 2 so that the + * program will work when spawned by init(1m). + * Also, if dfd is non-zero, dup the fd to be dfd. + */ + if ((mfd = minfd) == 0) + mfd = set_minfd(); + if (dfd > 0 || (0 <= fd && fd < mfd)) { + if (dfd <= 0) + dfd = mfd; + dfd = fcntl(fd, F_DUPFD, dfd); + (void) close(fd); + fd = dfd; + } + /* + * Mark it close-on-exec so any created process doesn't inherit it. + */ + if (fd >= 0) + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + return (fd); +} + +/* + * Create a new controlled process. + * Leave it stopped on successful exit from exec() or execve(). + * Return an opaque pointer to its process control structure. + * Return NULL if process cannot be created (fork()/exec() not successful). + */ +struct ps_prochandle * +Pxcreate(const char *file, /* executable file name */ + char *const *argv, /* argument vector */ + char *const *envp, /* environment */ + int *perr, /* pointer to error return code */ + char *path, /* if non-null, holds exec path name on return */ + size_t len) /* size of the path buffer */ +{ + char execpath[PATH_MAX]; + char procname[100]; + struct ps_prochandle *P; + pid_t pid; + int fd; + char *fname; + int rc; + int lasterrno = 0; + + if (len == 0) /* zero length, no path */ + path = NULL; + if (path != NULL) + *path = '\0'; + + if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) { + *perr = C_STRANGE; + return (NULL); + } + + if ((pid = fork1()) == -1) { + free(P); + *perr = C_FORK; + return (NULL); + } + + if (pid == 0) { /* child process */ + id_t id; + extern char **environ; + + /* + * If running setuid or setgid, reset credentials to normal. + */ + if ((id = getgid()) != getegid()) + (void) setgid(id); + if ((id = getuid()) != geteuid()) + (void) setuid(id); + + Pcreate_callback(P); /* execute callback (see below) */ + (void) pause(); /* wait for PRSABORT from parent */ + + /* + * This is ugly. There is no execvep() function that takes a + * path and an environment. We cheat here by replacing the + * global 'environ' variable right before we call this. + */ + if (envp) + environ = (char **)envp; + + (void) execvp(file, argv); /* execute the program */ + _exit(127); + } + + /* + * Initialize the process structure. + */ + (void) memset(P, 0, sizeof (*P)); + (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL); + P->flags |= CREATED; + P->state = PS_RUN; + P->pid = pid; + P->asfd = -1; + P->ctlfd = -1; + P->statfd = -1; + P->agentctlfd = -1; + P->agentstatfd = -1; + P->ops = &P_live_ops; + Pinitsym(P); + + /* + * Open the /proc/pid files. + */ + (void) sprintf(procname, "/proc/%d/", (int)pid); + fname = procname + strlen(procname); + (void) set_minfd(); + + /* + * Exclusive write open advises others not to interfere. + * There is no reason for any of these open()s to fail. + */ + (void) strcpy(fname, "as"); + if ((fd = open(procname, (O_RDWR|O_EXCL))) < 0 || + (fd = dupfd(fd, 0)) < 0) { + dprintf("Pcreate: failed to open %s: %s\n", + procname, strerror(errno)); + rc = C_STRANGE; + goto bad; + } + P->asfd = fd; + + (void) strcpy(fname, "status"); + if ((fd = open(procname, O_RDONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) { + dprintf("Pcreate: failed to open %s: %s\n", + procname, strerror(errno)); + rc = C_STRANGE; + goto bad; + } + P->statfd = fd; + + (void) strcpy(fname, "ctl"); + if ((fd = open(procname, O_WRONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) { + dprintf("Pcreate: failed to open %s: %s\n", + procname, strerror(errno)); + rc = C_STRANGE; + goto bad; + } + P->ctlfd = fd; + + (void) Pstop(P, 0); /* stop the controlled process */ + + /* + * Wait for process to sleep in pause(). + * If the process has already called pause(), then it should be + * stopped (PR_REQUESTED) while asleep in pause and we are done. + * Else we set up to catch entry/exit to pause() and set the process + * running again, expecting it to stop when it reaches pause(). + * There is no reason for this to fail other than an interrupt. + */ + (void) Psysentry(P, SYS_pause, 1); + (void) Psysexit(P, SYS_pause, 1); + for (;;) { + if (P->state == PS_STOP && + P->status.pr_lwp.pr_syscall == SYS_pause && + (P->status.pr_lwp.pr_why == PR_REQUESTED || + P->status.pr_lwp.pr_why == PR_SYSENTRY || + P->status.pr_lwp.pr_why == PR_SYSEXIT)) + break; + + if (P->state != PS_STOP || /* interrupt or process died */ + Psetrun(P, 0, 0) != 0) { /* can't restart */ + if (errno == EINTR || errno == ERESTART) + rc = C_INTR; + else { + dprintf("Pcreate: Psetrun failed: %s\n", + strerror(errno)); + rc = C_STRANGE; + } + goto bad; + } + + (void) Pwait(P, 0); + } + (void) Psysentry(P, SYS_pause, 0); + (void) Psysexit(P, SYS_pause, 0); + + /* + * Kick the process off the pause() and catch + * it again on entry to exec() or exit(). + */ + (void) Psysentry(P, SYS_exit, 1); + (void) Psysentry(P, SYS_exec, 1); + (void) Psysentry(P, SYS_execve, 1); + if (Psetrun(P, 0, PRSABORT) == -1) { + dprintf("Pcreate: Psetrun failed: %s\n", strerror(errno)); + rc = C_STRANGE; + goto bad; + } + (void) Pwait(P, 0); + if (P->state != PS_STOP) { + dprintf("Pcreate: Pwait failed: %s\n", strerror(errno)); + rc = C_STRANGE; + goto bad; + } + + /* + * Move the process through instances of failed exec()s + * to reach the point of stopped on successful exec(). + */ + (void) Psysexit(P, SYS_exec, TRUE); + (void) Psysexit(P, SYS_execve, TRUE); + + while (P->state == PS_STOP && + P->status.pr_lwp.pr_why == PR_SYSENTRY && + (P->status.pr_lwp.pr_what == SYS_execve || + P->status.pr_lwp.pr_what == SYS_exec)) { + /* + * Fetch the exec path name now, before we complete + * the exec(). We may lose the process and be unable + * to get the information later. + */ + (void) Pread_string(P, execpath, sizeof (execpath), + (off_t)P->status.pr_lwp.pr_sysarg[0]); + if (path != NULL) + (void) strncpy(path, execpath, len); + /* + * Set the process running and wait for + * it to stop on exit from the exec(). + */ + (void) Psetrun(P, 0, 0); + (void) Pwait(P, 0); + + if (P->state == PS_LOST && /* we lost control */ + Preopen(P) != 0) { /* and we can't get it back */ + rc = C_PERM; + goto bad; + } + + /* + * If the exec() failed, continue the loop, expecting + * there to be more attempts to exec(), based on PATH. + */ + if (P->state == PS_STOP && + P->status.pr_lwp.pr_why == PR_SYSEXIT && + (P->status.pr_lwp.pr_what == SYS_execve || + P->status.pr_lwp.pr_what == SYS_exec) && + (lasterrno = P->status.pr_lwp.pr_errno) != 0) { + /* + * The exec() failed. Set the process running and + * wait for it to stop on entry to the next exec(). + */ + (void) Psetrun(P, 0, 0); + (void) Pwait(P, 0); + + continue; + } + break; + } + + if (P->state == PS_STOP && + P->status.pr_lwp.pr_why == PR_SYSEXIT && + (P->status.pr_lwp.pr_what == SYS_execve || + P->status.pr_lwp.pr_what == SYS_exec) && + P->status.pr_lwp.pr_errno == 0) { + /* + * The process is stopped on successful exec() or execve(). + * Turn off all tracing flags and return success. + */ + restore_tracing_flags(P); +#ifndef _LP64 + /* We must be a 64-bit process to deal with a 64-bit process */ + if (P->status.pr_dmodel == PR_MODEL_LP64) { + rc = C_LP64; + goto bad; + } +#endif + /* + * Set run-on-last-close so the controlled process + * runs even if we die on a signal. + */ + (void) Psetflags(P, PR_RLC); + *perr = 0; + return (P); + } + + rc = lasterrno == ENOENT ? C_NOENT : C_NOEXEC; + +bad: + (void) kill(pid, SIGKILL); + if (path != NULL && rc != C_PERM && rc != C_LP64) + *path = '\0'; + Pfree(P); + *perr = rc; + return (NULL); +} + +struct ps_prochandle * +Pcreate( + const char *file, /* executable file name */ + char *const *argv, /* argument vector */ + int *perr, /* pointer to error return code */ + char *path, /* if non-null, holds exec path name on return */ + size_t len) /* size of the path buffer */ +{ + return (Pxcreate(file, argv, NULL, perr, path, len)); +} + +/* + * Return a printable string corresponding to a Pcreate() error return. + */ +const char * +Pcreate_error(int error) +{ + const char *str; + + switch (error) { + case C_FORK: + str = "cannot fork"; + break; + case C_PERM: + str = "file is set-id or unreadable"; + break; + case C_NOEXEC: + str = "cannot execute file"; + break; + case C_INTR: + str = "operation interrupted"; + break; + case C_LP64: + str = "program is _LP64, self is not"; + break; + case C_STRANGE: + str = "unanticipated system error"; + break; + case C_NOENT: + str = "cannot find executable file"; + break; + default: + str = "unknown error"; + break; + } + + return (str); +} + +/* + * Callback to execute in each child process created with Pcreate() after fork + * but before it execs the new process image. By default, we do nothing, but + * by calling this function we allow the client program to define its own + * version of the function which will interpose on our empty default. This + * may be useful for clients that need to modify signal dispositions, terminal + * attributes, or process group and session properties for each new victim. + */ +/*ARGSUSED*/ +void +Pcreate_callback(struct ps_prochandle *P) +{ + /* nothing to do here */ +} + +/* + * Grab an existing process. + * Return an opaque pointer to its process control structure. + * + * pid: UNIX process ID. + * flags: + * PGRAB_RETAIN Retain tracing flags (default clears all tracing flags). + * PGRAB_FORCE Grab regardless of whether process is already traced. + * PGRAB_RDONLY Open the address space file O_RDONLY instead of O_RDWR, + * and do not open the process control file. + * PGRAB_NOSTOP Open the process but do not force it to stop. + * perr: pointer to error return code. + */ +struct ps_prochandle * +Pgrab(pid_t pid, int flags, int *perr) +{ + struct ps_prochandle *P; + int fd, omode; + char procname[100]; + char *fname; + int rc = 0; + + /* + * PGRAB_RDONLY means that we do not open the /proc/<pid>/control file, + * and so it implies RETAIN and NOSTOP since both require control. + */ + if (flags & PGRAB_RDONLY) + flags |= PGRAB_RETAIN | PGRAB_NOSTOP; + + if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) { + *perr = G_STRANGE; + return (NULL); + } + + P->asfd = -1; + P->ctlfd = -1; + P->statfd = -1; + +again: /* Come back here if we lose it in the Window of Vulnerability */ + if (P->ctlfd >= 0) + (void) close(P->ctlfd); + if (P->asfd >= 0) + (void) close(P->asfd); + if (P->statfd >= 0) + (void) close(P->statfd); + (void) memset(P, 0, sizeof (*P)); + (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL); + P->ctlfd = -1; + P->asfd = -1; + P->statfd = -1; + P->agentctlfd = -1; + P->agentstatfd = -1; + P->ops = &P_live_ops; + Pinitsym(P); + + /* + * Open the /proc/pid files + */ + (void) sprintf(procname, "/proc/%d/", (int)pid); + fname = procname + strlen(procname); + (void) set_minfd(); + + /* + * Request exclusive open to avoid grabbing someone else's + * process and to prevent others from interfering afterwards. + * If this fails and the 'PGRAB_FORCE' flag is set, attempt to + * open non-exclusively. + */ + (void) strcpy(fname, "as"); + omode = (flags & PGRAB_RDONLY) ? O_RDONLY : O_RDWR; + + if (((fd = open(procname, omode | O_EXCL)) < 0 && + (fd = ((flags & PGRAB_FORCE)? open(procname, omode) : -1)) < 0) || + (fd = dupfd(fd, 0)) < 0) { + switch (errno) { + case ENOENT: + rc = G_NOPROC; + break; + case EACCES: + case EPERM: + rc = G_PERM; + break; + case EBUSY: + if (!(flags & PGRAB_FORCE) || geteuid() != 0) { + rc = G_BUSY; + break; + } + /* FALLTHROUGH */ + default: + dprintf("Pgrab: failed to open %s: %s\n", + procname, strerror(errno)); + rc = G_STRANGE; + break; + } + goto err; + } + P->asfd = fd; + + (void) strcpy(fname, "status"); + if ((fd = open(procname, O_RDONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) { + switch (errno) { + case ENOENT: + rc = G_NOPROC; + break; + default: + dprintf("Pgrab: failed to open %s: %s\n", + procname, strerror(errno)); + rc = G_STRANGE; + break; + } + goto err; + } + P->statfd = fd; + + if (!(flags & PGRAB_RDONLY)) { + (void) strcpy(fname, "ctl"); + if ((fd = open(procname, O_WRONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) { + switch (errno) { + case ENOENT: + rc = G_NOPROC; + break; + default: + dprintf("Pgrab: failed to open %s: %s\n", + procname, strerror(errno)); + rc = G_STRANGE; + break; + } + goto err; + } + P->ctlfd = fd; + } + + P->state = PS_RUN; + P->pid = pid; + + /* + * We are now in the Window of Vulnerability (WoV). The process may + * exec() a setuid/setgid or unreadable object file between the open() + * and the PCSTOP. We will get EAGAIN in this case and must start over. + * As Pstopstatus will trigger the first read() from a /proc file, + * we also need to handle EOVERFLOW here when 32-bit as an indicator + * that this process is 64-bit. Finally, if the process has become + * a zombie (PS_UNDEAD) while we were trying to grab it, just remain + * silent about this and pretend there was no process. + */ + if (Pstopstatus(P, PCNULL, 0) != 0) { +#ifndef _LP64 + if (errno == EOVERFLOW) { + rc = G_LP64; + goto err; + } +#endif + if (P->state == PS_LOST) { /* WoV */ + (void) mutex_destroy(&P->proc_lock); + goto again; + } + + if (P->state == PS_UNDEAD) + rc = G_NOPROC; + else + rc = G_STRANGE; + + goto err; + } + + /* + * If the process is a system process, we can't control it even as root + */ + if (P->status.pr_flags & PR_ISSYS) { + rc = G_SYS; + goto err; + } +#ifndef _LP64 + /* + * We must be a 64-bit process to deal with a 64-bit process + */ + if (P->status.pr_dmodel == PR_MODEL_LP64) { + rc = G_LP64; + goto err; + } +#endif + + /* + * Remember the status for use by Prelease(). + */ + P->orig_status = P->status; /* structure copy */ + + /* + * Before stopping the process, make sure we are not grabbing ourselves. + * If we are, make sure we are doing it PGRAB_RDONLY. + */ + if (pid == getpid()) { + /* + * Verify that the process is really ourself: + * Set a magic number, read it through the + * /proc file and see if the results match. + */ + uint32_t magic1 = 0; + uint32_t magic2 = 2; + + errno = 0; + + if (Pread(P, &magic2, sizeof (magic2), (uintptr_t)&magic1) + == sizeof (magic2) && + magic2 == 0 && + (magic1 = 0xfeedbeef) && + Pread(P, &magic2, sizeof (magic2), (uintptr_t)&magic1) + == sizeof (magic2) && + magic2 == 0xfeedbeef && + !(flags & PGRAB_RDONLY)) { + rc = G_SELF; + goto err; + } + } + + /* + * If the process is already stopped or has been directed + * to stop via /proc, do not set run-on-last-close. + */ + if (!(P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) && + !(flags & PGRAB_RDONLY)) { + /* + * Mark the process run-on-last-close so + * it runs even if we die from SIGKILL. + */ + if (Psetflags(P, PR_RLC) != 0) { + if (errno == EAGAIN) { /* WoV */ + (void) mutex_destroy(&P->proc_lock); + goto again; + } + if (errno == ENOENT) /* No complaint about zombies */ + rc = G_ZOMB; + else { + dprintf("Pgrab: failed to set RLC\n"); + rc = G_STRANGE; + } + goto err; + } + } + + /* + * If a stop directive is pending and the process has not yet stopped, + * then synchronously wait for the stop directive to take effect. + * Limit the time spent waiting for the process to stop by iterating + * at most 10 times. The time-out of 20 ms corresponds to the time + * between sending the stop directive and the process actually stopped + * as measured by DTrace on a slow, busy system. If the process doesn't + * stop voluntarily, clear the PR_DSTOP flag so that the code below + * forces the process to stop. + */ + if (!(flags & PGRAB_RDONLY)) { + int niter = 0; + while ((P->status.pr_lwp.pr_flags & (PR_STOPPED|PR_DSTOP)) == + PR_DSTOP && niter < 10 && + Pstopstatus(P, PCTWSTOP, 20) != 0) { + niter++; + if (flags & PGRAB_NOSTOP) + break; + } + if (niter == 10 && !(flags & PGRAB_NOSTOP)) { + /* Try it harder down below */ + P->status.pr_lwp.pr_flags &= ~PR_DSTOP; + } + } + + /* + * If the process is not already stopped or directed to stop + * and PGRAB_NOSTOP was not specified, stop the process now. + */ + if (!(P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) && + !(flags & PGRAB_NOSTOP)) { + /* + * Stop the process, get its status and signal/syscall masks. + */ + if (((P->status.pr_lwp.pr_flags & PR_STOPPED) && + Pstopstatus(P, PCDSTOP, 0) != 0) || + Pstopstatus(P, PCSTOP, 2000) != 0) { +#ifndef _LP64 + if (errno == EOVERFLOW) { + rc = G_LP64; + goto err; + } +#endif + if (P->state == PS_LOST) { /* WoV */ + (void) mutex_destroy(&P->proc_lock); + goto again; + } + if ((errno != EINTR && errno != ERESTART) || + (P->state != PS_STOP && + !(P->status.pr_flags & PR_DSTOP))) { + if (P->state != PS_RUN && errno != ENOENT) { + dprintf("Pgrab: failed to PCSTOP\n"); + rc = G_STRANGE; + } else { + rc = G_ZOMB; + } + goto err; + } + } + + /* + * Process should now either be stopped via /proc or there + * should be an outstanding stop directive. + */ + if (!(P->status.pr_flags & (PR_ISTOP|PR_DSTOP))) { + dprintf("Pgrab: process is not stopped\n"); + rc = G_STRANGE; + goto err; + } +#ifndef _LP64 + /* + * Test this again now because the 32-bit victim process may + * have exec'd a 64-bit process in the meantime. + */ + if (P->status.pr_dmodel == PR_MODEL_LP64) { + rc = G_LP64; + goto err; + } +#endif + } + + /* + * Cancel all tracing flags unless the PGRAB_RETAIN flag is set. + */ + if (!(flags & PGRAB_RETAIN)) { + (void) Psysentry(P, 0, FALSE); + (void) Psysexit(P, 0, FALSE); + (void) Psignal(P, 0, FALSE); + (void) Pfault(P, 0, FALSE); + Psync(P); + } + + *perr = 0; + return (P); + +err: + Pfree(P); + *perr = rc; + return (NULL); +} + +/* + * Return a printable string corresponding to a Pgrab() error return. + */ +const char * +Pgrab_error(int error) +{ + const char *str; + + switch (error) { + case G_NOPROC: + str = "no such process"; + break; + case G_NOCORE: + str = "no such core file"; + break; + case G_NOPROCORCORE: + str = "no such process or core file"; + break; + case G_NOEXEC: + str = "cannot find executable file"; + break; + case G_ZOMB: + str = "zombie process"; + break; + case G_PERM: + str = "permission denied"; + break; + case G_BUSY: + str = "process is traced"; + break; + case G_SYS: + str = "system process"; + break; + case G_SELF: + str = "attempt to grab self"; + break; + case G_INTR: + str = "operation interrupted"; + break; + case G_LP64: + str = "program is _LP64, self is not"; + break; + case G_FORMAT: + str = "file is not an ELF core file"; + break; + case G_ELF: + str = "libelf error"; + break; + case G_NOTE: + str = "core file is corrupt or missing required data"; + break; + case G_STRANGE: + str = "unanticipated system error"; + break; + case G_ISAINVAL: + str = "wrong ELF machine type"; + break; + case G_BADLWPS: + str = "bad lwp specification"; + break; + default: + str = "unknown error"; + break; + } + + return (str); +} + +/* + * Free a process control structure. + * Close the file descriptors but don't do the Prelease logic. + */ +void +Pfree(struct ps_prochandle *P) +{ + uint_t i; + + if (P->core != NULL) { + extern void __priv_free_info(void *); + lwp_info_t *nlwp, *lwp = list_next(&P->core->core_lwp_head); + + for (i = 0; i < P->core->core_nlwp; i++, lwp = nlwp) { + nlwp = list_next(lwp); +#ifdef __sparc + if (lwp->lwp_gwins != NULL) + free(lwp->lwp_gwins); + if (lwp->lwp_xregs != NULL) + free(lwp->lwp_xregs); + if (lwp->lwp_asrs != NULL) + free(lwp->lwp_asrs); +#endif + free(lwp); + } + + if (P->core->core_platform != NULL) + free(P->core->core_platform); + if (P->core->core_uts != NULL) + free(P->core->core_uts); + if (P->core->core_cred != NULL) + free(P->core->core_cred); + if (P->core->core_priv != NULL) + free(P->core->core_priv); + if (P->core->core_privinfo != NULL) + __priv_free_info(P->core->core_privinfo); + if (P->core->core_ppii != NULL) + free(P->core->core_ppii); + if (P->core->core_zonename != NULL) + free(P->core->core_zonename); +#if defined(__i386) || defined(__amd64) + if (P->core->core_ldt != NULL) + free(P->core->core_ldt); +#endif + + free(P->core); + } + + if (P->ucaddrs != NULL) { + free(P->ucaddrs); + P->ucaddrs = NULL; + P->ucnelems = 0; + } + + (void) mutex_lock(&P->proc_lock); + if (P->hashtab != NULL) { + struct ps_lwphandle *L; + for (i = 0; i < HASHSIZE; i++) { + while ((L = P->hashtab[i]) != NULL) + Lfree_internal(P, L); + } + free(P->hashtab); + } + (void) mutex_unlock(&P->proc_lock); + (void) mutex_destroy(&P->proc_lock); + + if (P->agentctlfd >= 0) + (void) close(P->agentctlfd); + if (P->agentstatfd >= 0) + (void) close(P->agentstatfd); + if (P->ctlfd >= 0) + (void) close(P->ctlfd); + if (P->asfd >= 0) + (void) close(P->asfd); + if (P->statfd >= 0) + (void) close(P->statfd); + Preset_maps(P); + + /* clear out the structure as a precaution against reuse */ + (void) memset(P, 0, sizeof (*P)); + P->ctlfd = -1; + P->asfd = -1; + P->statfd = -1; + P->agentctlfd = -1; + P->agentstatfd = -1; + + free(P); +} + +/* + * Return the state of the process, one of the PS_* values. + */ +int +Pstate(struct ps_prochandle *P) +{ + return (P->state); +} + +/* + * Return the open address space file descriptor for the process. + * Clients must not close this file descriptor, not use it + * after the process is freed. + */ +int +Pasfd(struct ps_prochandle *P) +{ + return (P->asfd); +} + +/* + * Return the open control file descriptor for the process. + * Clients must not close this file descriptor, not use it + * after the process is freed. + */ +int +Pctlfd(struct ps_prochandle *P) +{ + return (P->ctlfd); +} + +/* + * Return a pointer to the process psinfo structure. + * Clients should not hold on to this pointer indefinitely. + * It will become invalid on Prelease(). + */ +const psinfo_t * +Ppsinfo(struct ps_prochandle *P) +{ + if (P->state == PS_IDLE) { + errno = ENODATA; + return (NULL); + } + + if (P->state != PS_DEAD && proc_get_psinfo(P->pid, &P->psinfo) == -1) + return (NULL); + + return (&P->psinfo); +} + +/* + * Return a pointer to the process status structure. + * Clients should not hold on to this pointer indefinitely. + * It will become invalid on Prelease(). + */ +const pstatus_t * +Pstatus(struct ps_prochandle *P) +{ + return (&P->status); +} + +/* + * Fill in a pointer to a process credentials structure. The ngroups parameter + * is the number of supplementary group entries allocated in the caller's cred + * structure. It should equal zero or one unless extra space has been + * allocated for the group list by the caller. + */ +int +Pcred(struct ps_prochandle *P, prcred_t *pcrp, int ngroups) +{ + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) + return (proc_get_cred(P->pid, pcrp, ngroups)); + + if (P->core->core_cred != NULL) { + /* + * Avoid returning more supplementary group data than the + * caller has allocated in their buffer. We expect them to + * check pr_ngroups afterward and potentially call us again. + */ + ngroups = MIN(ngroups, P->core->core_cred->pr_ngroups); + + (void) memcpy(pcrp, P->core->core_cred, + sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t)); + + return (0); + } + + errno = ENODATA; + return (-1); +} + +#if defined(__i386) || defined(__amd64) +/* + * Fill in a pointer to a process LDT structure. + * The caller provides a buffer of size 'nldt * sizeof (struct ssd)'; + * If pldt == NULL or nldt == 0, we return the number of existing LDT entries. + * Otherwise we return the actual number of LDT entries fetched (<= nldt). + */ +int +Pldt(struct ps_prochandle *P, struct ssd *pldt, int nldt) +{ + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) + return (proc_get_ldt(P->pid, pldt, nldt)); + + if (pldt == NULL || nldt == 0) + return (P->core->core_nldt); + + if (P->core->core_ldt != NULL) { + nldt = MIN(nldt, P->core->core_nldt); + + (void) memcpy(pldt, P->core->core_ldt, + nldt * sizeof (struct ssd)); + + return (nldt); + } + + errno = ENODATA; + return (-1); +} +#endif /* __i386 */ + +/* + * Fill in a pointer to a process privilege structure. + */ +ssize_t +Ppriv(struct ps_prochandle *P, prpriv_t *pprv, size_t size) +{ + if (P->state != PS_DEAD) { + prpriv_t *pp = proc_get_priv(P->pid); + if (pp != NULL) { + size = MIN(size, PRIV_PRPRIV_SIZE(pp)); + (void) memcpy(pprv, pp, size); + free(pp); + return (size); + } + return (-1); + } + + if (P->core->core_priv != NULL) { + size = MIN(P->core->core_priv_size, size); + (void) memcpy(pprv, P->core->core_priv, size); + return (size); + } + errno = ENODATA; + return (-1); +} + +int +Psetpriv(struct ps_prochandle *P, prpriv_t *pprv) +{ + int rc; + long *ctl; + size_t sz; + + if (P->state == PS_DEAD) { + errno = EBADF; + return (-1); + } + + sz = PRIV_PRPRIV_SIZE(pprv) + sizeof (long); + + sz = ((sz - 1) / sizeof (long) + 1) * sizeof (long); + + ctl = malloc(sz); + if (ctl == NULL) + return (-1); + + ctl[0] = PCSPRIV; + + (void) memcpy(&ctl[1], pprv, PRIV_PRPRIV_SIZE(pprv)); + + if (write(P->ctlfd, ctl, sz) != sz) + rc = -1; + else + rc = 0; + + free(ctl); + + return (rc); +} + +void * +Pprivinfo(struct ps_prochandle *P) +{ + /* Use default from libc */ + if (P->state != PS_DEAD) + return (NULL); + + return (P->core->core_privinfo); +} + +/* + * Ensure that all cached state is written to the process. + * The cached state is the LWP's signal mask and registers + * and the process's tracing flags. + */ +void +Psync(struct ps_prochandle *P) +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + long cmd[6]; + iovec_t iov[12]; + int n = 0; + + if (P->flags & SETHOLD) { + cmd[0] = PCSHOLD; + iov[n].iov_base = (caddr_t)&cmd[0]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&P->status.pr_lwp.pr_lwphold; + iov[n++].iov_len = sizeof (P->status.pr_lwp.pr_lwphold); + } + if (P->flags & SETREGS) { + cmd[1] = PCSREG; +#ifdef __i386 + /* XX64 we should probably restore REG_GS after this */ + if (ctlfd == P->agentctlfd) + P->status.pr_lwp.pr_reg[GS] = 0; +#elif defined(__amd64) + /* XX64 */ +#endif + iov[n].iov_base = (caddr_t)&cmd[1]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&P->status.pr_lwp.pr_reg[0]; + iov[n++].iov_len = sizeof (P->status.pr_lwp.pr_reg); + } + if (P->flags & SETSIG) { + cmd[2] = PCSTRACE; + iov[n].iov_base = (caddr_t)&cmd[2]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&P->status.pr_sigtrace; + iov[n++].iov_len = sizeof (P->status.pr_sigtrace); + } + if (P->flags & SETFAULT) { + cmd[3] = PCSFAULT; + iov[n].iov_base = (caddr_t)&cmd[3]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&P->status.pr_flttrace; + iov[n++].iov_len = sizeof (P->status.pr_flttrace); + } + if (P->flags & SETENTRY) { + cmd[4] = PCSENTRY; + iov[n].iov_base = (caddr_t)&cmd[4]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&P->status.pr_sysentry; + iov[n++].iov_len = sizeof (P->status.pr_sysentry); + } + if (P->flags & SETEXIT) { + cmd[5] = PCSEXIT; + iov[n].iov_base = (caddr_t)&cmd[5]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&P->status.pr_sysexit; + iov[n++].iov_len = sizeof (P->status.pr_sysexit); + } + + if (n == 0 || writev(ctlfd, iov, n) < 0) + return; /* nothing to do or write failed */ + + P->flags &= ~(SETSIG|SETFAULT|SETENTRY|SETEXIT|SETHOLD|SETREGS); +} + +/* + * Reopen the /proc file (after PS_LOST). + */ +int +Preopen(struct ps_prochandle *P) +{ + int fd; + char procname[100]; + char *fname; + + if (P->state == PS_DEAD || P->state == PS_IDLE) + return (0); + + if (P->agentcnt > 0) { + P->agentcnt = 1; + Pdestroy_agent(P); + } + + (void) sprintf(procname, "/proc/%d/", (int)P->pid); + fname = procname + strlen(procname); + + (void) strcpy(fname, "as"); + if ((fd = open(procname, O_RDWR)) < 0 || + close(P->asfd) < 0 || + (fd = dupfd(fd, P->asfd)) != P->asfd) { + dprintf("Preopen: failed to open %s: %s\n", + procname, strerror(errno)); + if (fd >= 0) + (void) close(fd); + return (-1); + } + P->asfd = fd; + + (void) strcpy(fname, "status"); + if ((fd = open(procname, O_RDONLY)) < 0 || + close(P->statfd) < 0 || + (fd = dupfd(fd, P->statfd)) != P->statfd) { + dprintf("Preopen: failed to open %s: %s\n", + procname, strerror(errno)); + if (fd >= 0) + (void) close(fd); + return (-1); + } + P->statfd = fd; + + (void) strcpy(fname, "ctl"); + if ((fd = open(procname, O_WRONLY)) < 0 || + close(P->ctlfd) < 0 || + (fd = dupfd(fd, P->ctlfd)) != P->ctlfd) { + dprintf("Preopen: failed to open %s: %s\n", + procname, strerror(errno)); + if (fd >= 0) + (void) close(fd); + return (-1); + } + P->ctlfd = fd; + + /* + * Set the state to PS_RUN and wait for the process to stop so that + * we re-read the status from the new P->statfd. If this fails, Pwait + * will reset the state to PS_LOST and we fail the reopen. Before + * returning, we also forge a bit of P->status to allow the debugger to + * see that we are PS_LOST following a successful exec. + */ + P->state = PS_RUN; + if (Pwait(P, 0) == -1) { +#ifdef _ILP32 + if (errno == EOVERFLOW) + P->status.pr_dmodel = PR_MODEL_LP64; +#endif + P->status.pr_lwp.pr_why = PR_SYSEXIT; + P->status.pr_lwp.pr_what = SYS_execve; + P->status.pr_lwp.pr_errno = 0; + return (-1); + } + + /* + * The process should be stopped on exec (REQUESTED) + * or else should be stopped on exit from exec() (SYSEXIT) + */ + if (P->state == PS_STOP && + (P->status.pr_lwp.pr_why == PR_REQUESTED || + (P->status.pr_lwp.pr_why == PR_SYSEXIT && + (P->status.pr_lwp.pr_what == SYS_exec || + P->status.pr_lwp.pr_what == SYS_execve)))) { + /* fake up stop-on-exit-from-execve */ + if (P->status.pr_lwp.pr_why == PR_REQUESTED) { + P->status.pr_lwp.pr_why = PR_SYSEXIT; + P->status.pr_lwp.pr_what = SYS_execve; + P->status.pr_lwp.pr_errno = 0; + } + } else { + dprintf("Preopen: expected REQUESTED or " + "SYSEXIT(SYS_execve) stop\n"); + } + + return (0); +} + +/* + * Define all settable flags other than the microstate accounting flags. + */ +#define ALL_SETTABLE_FLAGS (PR_FORK|PR_RLC|PR_KLC|PR_ASYNC|PR_BPTADJ|PR_PTRACE) + +/* + * Restore /proc tracing flags to their original values + * in preparation for releasing the process. + * Also called by Pcreate() to clear all tracing flags. + */ +static void +restore_tracing_flags(struct ps_prochandle *P) +{ + long flags; + long cmd[4]; + iovec_t iov[8]; + + if (P->flags & CREATED) { + /* we created this process; clear all tracing flags */ + premptyset(&P->status.pr_sigtrace); + premptyset(&P->status.pr_flttrace); + premptyset(&P->status.pr_sysentry); + premptyset(&P->status.pr_sysexit); + if ((P->status.pr_flags & ALL_SETTABLE_FLAGS) != 0) + (void) Punsetflags(P, ALL_SETTABLE_FLAGS); + } else { + /* we grabbed the process; restore its tracing flags */ + P->status.pr_sigtrace = P->orig_status.pr_sigtrace; + P->status.pr_flttrace = P->orig_status.pr_flttrace; + P->status.pr_sysentry = P->orig_status.pr_sysentry; + P->status.pr_sysexit = P->orig_status.pr_sysexit; + if ((P->status.pr_flags & ALL_SETTABLE_FLAGS) != + (flags = (P->orig_status.pr_flags & ALL_SETTABLE_FLAGS))) { + (void) Punsetflags(P, ALL_SETTABLE_FLAGS); + if (flags) + (void) Psetflags(P, flags); + } + } + + cmd[0] = PCSTRACE; + iov[0].iov_base = (caddr_t)&cmd[0]; + iov[0].iov_len = sizeof (long); + iov[1].iov_base = (caddr_t)&P->status.pr_sigtrace; + iov[1].iov_len = sizeof (P->status.pr_sigtrace); + + cmd[1] = PCSFAULT; + iov[2].iov_base = (caddr_t)&cmd[1]; + iov[2].iov_len = sizeof (long); + iov[3].iov_base = (caddr_t)&P->status.pr_flttrace; + iov[3].iov_len = sizeof (P->status.pr_flttrace); + + cmd[2] = PCSENTRY; + iov[4].iov_base = (caddr_t)&cmd[2]; + iov[4].iov_len = sizeof (long); + iov[5].iov_base = (caddr_t)&P->status.pr_sysentry; + iov[5].iov_len = sizeof (P->status.pr_sysentry); + + cmd[3] = PCSEXIT; + iov[6].iov_base = (caddr_t)&cmd[3]; + iov[6].iov_len = sizeof (long); + iov[7].iov_base = (caddr_t)&P->status.pr_sysexit; + iov[7].iov_len = sizeof (P->status.pr_sysexit); + + (void) writev(P->ctlfd, iov, 8); + + P->flags &= ~(SETSIG|SETFAULT|SETENTRY|SETEXIT); +} + +/* + * Release the process. Frees the process control structure. + * flags: + * PRELEASE_CLEAR Clear all tracing flags. + * PRELEASE_RETAIN Retain current tracing flags. + * PRELEASE_HANG Leave the process stopped and abandoned. + * PRELEASE_KILL Terminate the process with SIGKILL. + */ +void +Prelease(struct ps_prochandle *P, int flags) +{ + if (P->state == PS_DEAD) { + dprintf("Prelease: releasing handle %p PS_DEAD of pid %d\n", + (void *)P, (int)P->pid); + Pfree(P); + return; + } + + if (P->state == PS_IDLE) { + file_info_t *fptr = list_next(&P->file_head); + dprintf("Prelease: releasing handle %p PS_IDLE of file %s\n", + (void *)P, fptr->file_pname); + Pfree(P); + return; + } + + dprintf("Prelease: releasing handle %p pid %d\n", + (void *)P, (int)P->pid); + + if (P->ctlfd == -1) { + Pfree(P); + return; + } + + if (P->agentcnt > 0) { + P->agentcnt = 1; + Pdestroy_agent(P); + } + + /* + * Attempt to stop the process. + */ + P->state = PS_RUN; + (void) Pstop(P, 1000); + + if (flags & PRELEASE_KILL) { + if (P->state == PS_STOP) + (void) Psetrun(P, SIGKILL, 0); + (void) kill(P->pid, SIGKILL); + Pfree(P); + return; + } + + /* + * If we lost control, all we can do now is close the files. + * In this case, the last close sets the process running. + */ + if (P->state != PS_STOP && + (P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) == 0) { + Pfree(P); + return; + } + + /* + * We didn't lose control; we do more. + */ + Psync(P); + + if (flags & PRELEASE_CLEAR) + P->flags |= CREATED; + + if (!(flags & PRELEASE_RETAIN)) + restore_tracing_flags(P); + + if (flags & PRELEASE_HANG) { + /* Leave the process stopped and abandoned */ + (void) Punsetflags(P, PR_RLC|PR_KLC); + Pfree(P); + return; + } + + /* + * Set the process running if we created it or if it was + * not originally stopped or directed to stop via /proc + * or if we were given the PRELEASE_CLEAR flag. + */ + if ((P->flags & CREATED) || + (P->orig_status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) == 0) { + (void) Psetflags(P, PR_RLC); + /* + * We do this repeatedly because the process may have + * more than one LWP stopped on an event of interest. + * This makes sure all of them are set running. + */ + do { + if (Psetrun(P, 0, 0) == -1 && errno == EBUSY) + break; /* Agent LWP may be stuck */ + } while (Pstopstatus(P, PCNULL, 0) == 0 && + P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)); + + if (P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) + dprintf("Prelease: failed to set process running\n"); + } + + Pfree(P); +} + +/* debugging */ +void +prldump(const char *caller, lwpstatus_t *lsp) +{ + char name[32]; + uint32_t bits; + + switch (lsp->pr_why) { + case PR_REQUESTED: + dprintf("%s: REQUESTED\n", caller); + break; + case PR_SIGNALLED: + dprintf("%s: SIGNALLED %s\n", caller, + proc_signame(lsp->pr_what, name, sizeof (name))); + break; + case PR_FAULTED: + dprintf("%s: FAULTED %s\n", caller, + proc_fltname(lsp->pr_what, name, sizeof (name))); + break; + case PR_SYSENTRY: + dprintf("%s: SYSENTRY %s\n", caller, + proc_sysname(lsp->pr_what, name, sizeof (name))); + break; + case PR_SYSEXIT: + dprintf("%s: SYSEXIT %s\n", caller, + proc_sysname(lsp->pr_what, name, sizeof (name))); + break; + case PR_JOBCONTROL: + dprintf("%s: JOBCONTROL %s\n", caller, + proc_signame(lsp->pr_what, name, sizeof (name))); + break; + case PR_SUSPENDED: + dprintf("%s: SUSPENDED\n", caller); + break; + default: + dprintf("%s: Unknown\n", caller); + break; + } + + if (lsp->pr_cursig) + dprintf("%s: p_cursig = %d\n", caller, lsp->pr_cursig); + + bits = *((uint32_t *)&lsp->pr_lwppend); + if (bits) + dprintf("%s: pr_lwppend = 0x%.8X\n", caller, bits); +} + +/* debugging */ +static void +prdump(struct ps_prochandle *P) +{ + uint32_t bits; + + prldump("Pstopstatus", &P->status.pr_lwp); + + bits = *((uint32_t *)&P->status.pr_sigpend); + if (bits) + dprintf("Pstopstatus: pr_sigpend = 0x%.8X\n", bits); +} + +/* + * Wait for the specified process to stop or terminate. + * Or, just get the current status (PCNULL). + * Or, direct it to stop and get the current status (PCDSTOP). + * If the agent LWP exists, do these things to the agent, + * else do these things to the process as a whole. + */ +int +Pstopstatus(struct ps_prochandle *P, + long request, /* PCNULL, PCDSTOP, PCSTOP, PCWSTOP */ + uint_t msec) /* if non-zero, timeout in milliseconds */ +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + long ctl[3]; + ssize_t rc; + int err; + int old_state = P->state; + + switch (P->state) { + case PS_RUN: + break; + case PS_STOP: + if (request != PCNULL && request != PCDSTOP) + return (0); + break; + case PS_LOST: + if (request != PCNULL) { + errno = EAGAIN; + return (-1); + } + break; + case PS_UNDEAD: + case PS_DEAD: + case PS_IDLE: + if (request != PCNULL) { + errno = ENOENT; + return (-1); + } + break; + default: /* corrupted state */ + dprintf("Pstopstatus: corrupted state: %d\n", P->state); + errno = EINVAL; + return (-1); + } + + ctl[0] = PCDSTOP; + ctl[1] = PCTWSTOP; + ctl[2] = (long)msec; + rc = 0; + switch (request) { + case PCSTOP: + rc = write(ctlfd, &ctl[0], 3*sizeof (long)); + break; + case PCWSTOP: + rc = write(ctlfd, &ctl[1], 2*sizeof (long)); + break; + case PCDSTOP: + rc = write(ctlfd, &ctl[0], 1*sizeof (long)); + break; + case PCNULL: + if (P->state == PS_DEAD || P->state == PS_IDLE) + return (0); + break; + default: /* programming error */ + errno = EINVAL; + return (-1); + } + err = (rc < 0)? errno : 0; + Psync(P); + + if (P->agentstatfd < 0) { + if (pread(P->statfd, &P->status, + sizeof (P->status), (off_t)0) < 0) + err = errno; + } else { + if (pread(P->agentstatfd, &P->status.pr_lwp, + sizeof (P->status.pr_lwp), (off_t)0) < 0) + err = errno; + P->status.pr_flags = P->status.pr_lwp.pr_flags; + } + + if (err) { + switch (err) { + case EINTR: /* user typed ctl-C */ + case ERESTART: + dprintf("Pstopstatus: EINTR\n"); + break; + case EAGAIN: /* we lost control of the the process */ + case EOVERFLOW: + dprintf("Pstopstatus: PS_LOST, errno=%d\n", err); + P->state = PS_LOST; + break; + default: /* check for dead process */ + if (_libproc_debug) { + const char *errstr; + + switch (request) { + case PCNULL: + errstr = "Pstopstatus PCNULL"; break; + case PCSTOP: + errstr = "Pstopstatus PCSTOP"; break; + case PCDSTOP: + errstr = "Pstopstatus PCDSTOP"; break; + case PCWSTOP: + errstr = "Pstopstatus PCWSTOP"; break; + default: + errstr = "Pstopstatus PC???"; break; + } + dprintf("%s: %s\n", errstr, strerror(err)); + } + deadcheck(P); + break; + } + if (err != EINTR && err != ERESTART) { + errno = err; + return (-1); + } + } + + if (!(P->status.pr_flags & PR_STOPPED)) { + P->state = PS_RUN; + if (request == PCNULL || request == PCDSTOP || msec != 0) + return (0); + dprintf("Pstopstatus: process is not stopped\n"); + errno = EPROTO; + return (-1); + } + + P->state = PS_STOP; + + if (_libproc_debug) /* debugging */ + prdump(P); + + /* + * If the process was already stopped coming into Pstopstatus(), + * then don't use its PC to set P->sysaddr since it may have been + * changed since the time the process originally stopped. + */ + if (old_state == PS_STOP) + return (0); + + switch (P->status.pr_lwp.pr_why) { + case PR_SYSENTRY: + case PR_SYSEXIT: + if (Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], + &P->sysaddr) == 0) + P->sysaddr = P->status.pr_lwp.pr_reg[R_PC]; + break; + case PR_REQUESTED: + case PR_SIGNALLED: + case PR_FAULTED: + case PR_JOBCONTROL: + case PR_SUSPENDED: + break; + default: + errno = EPROTO; + return (-1); + } + + return (0); +} + +/* + * Wait for the process to stop for any reason. + */ +int +Pwait(struct ps_prochandle *P, uint_t msec) +{ + return (Pstopstatus(P, PCWSTOP, msec)); +} + +/* + * Direct the process to stop; wait for it to stop. + */ +int +Pstop(struct ps_prochandle *P, uint_t msec) +{ + return (Pstopstatus(P, PCSTOP, msec)); +} + +/* + * Direct the process to stop; don't wait. + */ +int +Pdstop(struct ps_prochandle *P) +{ + return (Pstopstatus(P, PCDSTOP, 0)); +} + +static void +deadcheck(struct ps_prochandle *P) +{ + int fd; + void *buf; + size_t size; + + if (P->statfd < 0) + P->state = PS_UNDEAD; + else { + if (P->agentstatfd < 0) { + fd = P->statfd; + buf = &P->status; + size = sizeof (P->status); + } else { + fd = P->agentstatfd; + buf = &P->status.pr_lwp; + size = sizeof (P->status.pr_lwp); + } + while (pread(fd, buf, size, (off_t)0) != size) { + switch (errno) { + default: + P->state = PS_UNDEAD; + break; + case EINTR: + case ERESTART: + continue; + case EAGAIN: + P->state = PS_LOST; + break; + } + break; + } + P->status.pr_flags = P->status.pr_lwp.pr_flags; + } +} + +/* + * Get the value of one register from stopped process. + */ +int +Pgetareg(struct ps_prochandle *P, int regno, prgreg_t *preg) +{ + if (regno < 0 || regno >= NPRGREG) { + errno = EINVAL; + return (-1); + } + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_STOP && P->state != PS_DEAD) { + errno = EBUSY; + return (-1); + } + + *preg = P->status.pr_lwp.pr_reg[regno]; + return (0); +} + +/* + * Put value of one register into stopped process. + */ +int +Pputareg(struct ps_prochandle *P, int regno, prgreg_t reg) +{ + if (regno < 0 || regno >= NPRGREG) { + errno = EINVAL; + return (-1); + } + + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + P->status.pr_lwp.pr_reg[regno] = reg; + P->flags |= SETREGS; /* set registers before continuing */ + return (0); +} + +int +Psetrun(struct ps_prochandle *P, + int sig, /* signal to pass to process */ + int flags) /* PRSTEP|PRSABORT|PRSTOP|PRCSIG|PRCFAULT */ +{ + int ctlfd = (P->agentctlfd >= 0) ? P->agentctlfd : P->ctlfd; + int sbits = (PR_DSTOP | PR_ISTOP | PR_ASLEEP); + + long ctl[1 + /* PCCFAULT */ + 1 + sizeof (siginfo_t)/sizeof (long) + /* PCSSIG/PCCSIG */ + 2 ]; /* PCRUN */ + + long *ctlp = ctl; + size_t size; + + if (P->state != PS_STOP && (P->status.pr_lwp.pr_flags & sbits) == 0) { + errno = EBUSY; + return (-1); + } + + Psync(P); /* flush tracing flags and registers */ + + if (flags & PRCFAULT) { /* clear current fault */ + *ctlp++ = PCCFAULT; + flags &= ~PRCFAULT; + } + + if (flags & PRCSIG) { /* clear current signal */ + *ctlp++ = PCCSIG; + flags &= ~PRCSIG; + } else if (sig && sig != P->status.pr_lwp.pr_cursig) { + /* make current signal */ + siginfo_t *infop; + + *ctlp++ = PCSSIG; + infop = (siginfo_t *)ctlp; + (void) memset(infop, 0, sizeof (*infop)); + infop->si_signo = sig; + ctlp += sizeof (siginfo_t) / sizeof (long); + } + + *ctlp++ = PCRUN; + *ctlp++ = flags; + size = (char *)ctlp - (char *)ctl; + + P->info_valid = 0; /* will need to update map and file info */ + + /* + * If we've cached ucontext-list information while we were stopped, + * free it now. + */ + if (P->ucaddrs != NULL) { + free(P->ucaddrs); + P->ucaddrs = NULL; + P->ucnelems = 0; + } + + if (write(ctlfd, ctl, size) != size) { + /* If it is dead or lost, return the real status, not PS_RUN */ + if (errno == ENOENT || errno == EAGAIN) { + (void) Pstopstatus(P, PCNULL, 0); + return (0); + } + /* If it is not in a jobcontrol stop, issue an error message */ + if (errno != EBUSY || + P->status.pr_lwp.pr_why != PR_JOBCONTROL) { + dprintf("Psetrun: %s\n", strerror(errno)); + return (-1); + } + /* Otherwise pretend that the job-stopped process is running */ + } + + P->state = PS_RUN; + return (0); +} + +ssize_t +Pread(struct ps_prochandle *P, + void *buf, /* caller's buffer */ + size_t nbyte, /* number of bytes to read */ + uintptr_t address) /* address in process */ +{ + return (P->ops->p_pread(P, buf, nbyte, address)); +} + +ssize_t +Pread_string(struct ps_prochandle *P, + char *buf, /* caller's buffer */ + size_t size, /* upper limit on bytes to read */ + uintptr_t addr) /* address in process */ +{ + enum { STRSZ = 40 }; + char string[STRSZ + 1]; + ssize_t leng = 0; + int nbyte; + + if (size < 2) { + errno = EINVAL; + return (-1); + } + + size--; /* ensure trailing null fits in buffer */ + + *buf = '\0'; + string[STRSZ] = '\0'; + + for (nbyte = STRSZ; nbyte == STRSZ && leng < size; addr += STRSZ) { + if ((nbyte = P->ops->p_pread(P, string, STRSZ, addr)) <= 0) { + buf[leng] = '\0'; + return (leng ? leng : -1); + } + if ((nbyte = strlen(string)) > 0) { + if (leng + nbyte > size) + nbyte = size - leng; + (void) strncpy(buf + leng, string, nbyte); + leng += nbyte; + } + } + buf[leng] = '\0'; + return (leng); +} + +ssize_t +Pwrite(struct ps_prochandle *P, + const void *buf, /* caller's buffer */ + size_t nbyte, /* number of bytes to write */ + uintptr_t address) /* address in process */ +{ + return (P->ops->p_pwrite(P, buf, nbyte, address)); +} + +int +Pclearsig(struct ps_prochandle *P) +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + long ctl = PCCSIG; + + if (write(ctlfd, &ctl, sizeof (ctl)) != sizeof (ctl)) + return (-1); + P->status.pr_lwp.pr_cursig = 0; + return (0); +} + +int +Pclearfault(struct ps_prochandle *P) +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + long ctl = PCCFAULT; + + if (write(ctlfd, &ctl, sizeof (ctl)) != sizeof (ctl)) + return (-1); + return (0); +} + +/* + * Set a breakpoint trap, return original instruction. + */ +int +Psetbkpt(struct ps_prochandle *P, uintptr_t address, ulong_t *saved) +{ + long ctl[1 + sizeof (priovec_t) / sizeof (long) + /* PCREAD */ + 1 + sizeof (priovec_t) / sizeof (long)]; /* PCWRITE */ + long *ctlp = ctl; + size_t size; + priovec_t *iovp; + instr_t bpt = BPT; + instr_t old; + + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + /* fetch the old instruction */ + *ctlp++ = PCREAD; + iovp = (priovec_t *)ctlp; + iovp->pio_base = &old; + iovp->pio_len = sizeof (old); + iovp->pio_offset = address; + ctlp += sizeof (priovec_t) / sizeof (long); + + /* write the BPT instruction */ + *ctlp++ = PCWRITE; + iovp = (priovec_t *)ctlp; + iovp->pio_base = &bpt; + iovp->pio_len = sizeof (bpt); + iovp->pio_offset = address; + ctlp += sizeof (priovec_t) / sizeof (long); + + size = (char *)ctlp - (char *)ctl; + if (write(P->ctlfd, ctl, size) != size) + return (-1); + + /* + * Fail if there was already a breakpoint there from another debugger + * or DTrace's user-level tracing on x86. + */ + if (old == BPT) + return (EBUSY); + + *saved = (ulong_t)old; + return (0); +} + +/* + * Restore original instruction where a breakpoint was set. + */ +int +Pdelbkpt(struct ps_prochandle *P, uintptr_t address, ulong_t saved) +{ + instr_t old = (instr_t)saved; + instr_t cur; + + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + /* + * If the breakpoint instruction we had placed has been overwritten + * with a new instruction, then don't try to replace it with the + * old instruction. Doing do can cause problems with self-modifying + * code -- PLTs for example. If the Pread() fails, we assume that we + * should proceed though most likely the Pwrite() will also fail. + */ + if (Pread(P, &cur, sizeof (cur), address) == sizeof (cur) && + cur != BPT) + return (0); + + if (Pwrite(P, &old, sizeof (old), address) != sizeof (old)) + return (-1); + + return (0); +} + +/* + * Common code for Pxecbkpt() and Lxecbkpt(). + * Develop the array of requests that will do the job, then + * write them to the specified control file descriptor. + * Return the non-zero errno if the write fails. + */ +static int +execute_bkpt( + int ctlfd, /* process or LWP control file descriptor */ + const fltset_t *faultset, /* current set of traced faults */ + const sigset_t *sigmask, /* current signal mask */ + uintptr_t address, /* address of breakpint */ + ulong_t saved) /* the saved instruction */ +{ + long ctl[ + 1 + sizeof (sigset_t) / sizeof (long) + /* PCSHOLD */ + 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ + 1 + sizeof (priovec_t) / sizeof (long) + /* PCWRITE */ + 2 + /* PCRUN */ + 1 + /* PCWSTOP */ + 1 + /* PCCFAULT */ + 1 + sizeof (priovec_t) / sizeof (long) + /* PCWRITE */ + 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ + 1 + sizeof (sigset_t) / sizeof (long)]; /* PCSHOLD */ + long *ctlp = ctl; + sigset_t unblock; + size_t size; + ssize_t ssize; + priovec_t *iovp; + sigset_t *holdp; + fltset_t *faultp; + instr_t old = (instr_t)saved; + instr_t bpt = BPT; + int error = 0; + + /* block our signals for the duration */ + (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock); + + /* hold posted signals */ + *ctlp++ = PCSHOLD; + holdp = (sigset_t *)ctlp; + prfillset(holdp); + prdelset(holdp, SIGKILL); + prdelset(holdp, SIGSTOP); + ctlp += sizeof (sigset_t) / sizeof (long); + + /* force tracing of FLTTRACE */ + if (!(prismember(faultset, FLTTRACE))) { + *ctlp++ = PCSFAULT; + faultp = (fltset_t *)ctlp; + *faultp = *faultset; + praddset(faultp, FLTTRACE); + ctlp += sizeof (fltset_t) / sizeof (long); + } + + /* restore the old instruction */ + *ctlp++ = PCWRITE; + iovp = (priovec_t *)ctlp; + iovp->pio_base = &old; + iovp->pio_len = sizeof (old); + iovp->pio_offset = address; + ctlp += sizeof (priovec_t) / sizeof (long); + + /* clear current signal and fault; set running w/ single-step */ + *ctlp++ = PCRUN; + *ctlp++ = PRCSIG | PRCFAULT | PRSTEP; + + /* wait for stop, cancel the fault */ + *ctlp++ = PCWSTOP; + *ctlp++ = PCCFAULT; + + /* restore the breakpoint trap */ + *ctlp++ = PCWRITE; + iovp = (priovec_t *)ctlp; + iovp->pio_base = &bpt; + iovp->pio_len = sizeof (bpt); + iovp->pio_offset = address; + ctlp += sizeof (priovec_t) / sizeof (long); + + /* restore fault tracing set */ + if (!(prismember(faultset, FLTTRACE))) { + *ctlp++ = PCSFAULT; + *(fltset_t *)ctlp = *faultset; + ctlp += sizeof (fltset_t) / sizeof (long); + } + + /* restore the hold mask */ + *ctlp++ = PCSHOLD; + *(sigset_t *)ctlp = *sigmask; + ctlp += sizeof (sigset_t) / sizeof (long); + + size = (char *)ctlp - (char *)ctl; + if ((ssize = write(ctlfd, ctl, size)) != size) + error = (ssize == -1)? errno : EINTR; + (void) sigprocmask(SIG_SETMASK, &unblock, NULL); + return (error); +} + +/* + * Step over a breakpoint, i.e., execute the instruction that + * really belongs at the breakpoint location (the current %pc) + * and leave the process stopped at the next instruction. + */ +int +Pxecbkpt(struct ps_prochandle *P, ulong_t saved) +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + int rv, error; + + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + Psync(P); + + error = execute_bkpt(ctlfd, + &P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold, + P->status.pr_lwp.pr_reg[R_PC], saved); + rv = Pstopstatus(P, PCNULL, 0); + + if (error != 0) { + if (P->status.pr_lwp.pr_why == PR_JOBCONTROL && + error == EBUSY) { /* jobcontrol stop -- back off */ + P->state = PS_RUN; + return (0); + } + if (error == ENOENT) + return (0); + errno = error; + return (-1); + } + + return (rv); +} + +/* + * Install the watchpoint described by wp. + */ +int +Psetwapt(struct ps_prochandle *P, const prwatch_t *wp) +{ + long ctl[1 + sizeof (prwatch_t) / sizeof (long)]; + prwatch_t *cwp = (prwatch_t *)&ctl[1]; + + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + ctl[0] = PCWATCH; + cwp->pr_vaddr = wp->pr_vaddr; + cwp->pr_size = wp->pr_size; + cwp->pr_wflags = wp->pr_wflags; + + if (write(P->ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) + return (-1); + + return (0); +} + +/* + * Remove the watchpoint described by wp. + */ +int +Pdelwapt(struct ps_prochandle *P, const prwatch_t *wp) +{ + long ctl[1 + sizeof (prwatch_t) / sizeof (long)]; + prwatch_t *cwp = (prwatch_t *)&ctl[1]; + + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + ctl[0] = PCWATCH; + cwp->pr_vaddr = wp->pr_vaddr; + cwp->pr_size = wp->pr_size; + cwp->pr_wflags = 0; + + if (write(P->ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) + return (-1); + + return (0); +} + +/* + * Common code for Pxecwapt() and Lxecwapt(). Develop the array of requests + * that will do the job, then write them to the specified control file + * descriptor. Return the non-zero errno if the write fails. + */ +static int +execute_wapt( + int ctlfd, /* process or LWP control file descriptor */ + const fltset_t *faultset, /* current set of traced faults */ + const sigset_t *sigmask, /* current signal mask */ + const prwatch_t *wp) /* watchpoint descriptor */ +{ + long ctl[ + 1 + sizeof (sigset_t) / sizeof (long) + /* PCSHOLD */ + 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ + 1 + sizeof (prwatch_t) / sizeof (long) + /* PCWATCH */ + 2 + /* PCRUN */ + 1 + /* PCWSTOP */ + 1 + /* PCCFAULT */ + 1 + sizeof (prwatch_t) / sizeof (long) + /* PCWATCH */ + 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ + 1 + sizeof (sigset_t) / sizeof (long)]; /* PCSHOLD */ + + long *ctlp = ctl; + int error = 0; + + sigset_t unblock; + sigset_t *holdp; + fltset_t *faultp; + prwatch_t *prw; + ssize_t ssize; + size_t size; + + (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock); + + /* + * Hold all posted signals in the victim process prior to stepping. + */ + *ctlp++ = PCSHOLD; + holdp = (sigset_t *)ctlp; + prfillset(holdp); + prdelset(holdp, SIGKILL); + prdelset(holdp, SIGSTOP); + ctlp += sizeof (sigset_t) / sizeof (long); + + /* + * Force tracing of FLTTRACE since we need to single step. + */ + if (!(prismember(faultset, FLTTRACE))) { + *ctlp++ = PCSFAULT; + faultp = (fltset_t *)ctlp; + *faultp = *faultset; + praddset(faultp, FLTTRACE); + ctlp += sizeof (fltset_t) / sizeof (long); + } + + /* + * Clear only the current watchpoint by setting pr_wflags to zero. + */ + *ctlp++ = PCWATCH; + prw = (prwatch_t *)ctlp; + prw->pr_vaddr = wp->pr_vaddr; + prw->pr_size = wp->pr_size; + prw->pr_wflags = 0; + ctlp += sizeof (prwatch_t) / sizeof (long); + + /* + * Clear the current signal and fault; set running with single-step. + * Then wait for the victim to stop and cancel the FLTTRACE. + */ + *ctlp++ = PCRUN; + *ctlp++ = PRCSIG | PRCFAULT | PRSTEP; + *ctlp++ = PCWSTOP; + *ctlp++ = PCCFAULT; + + /* + * Restore the current watchpoint. + */ + *ctlp++ = PCWATCH; + (void) memcpy(ctlp, wp, sizeof (prwatch_t)); + ctlp += sizeof (prwatch_t) / sizeof (long); + + /* + * Restore fault tracing set if we modified it. + */ + if (!(prismember(faultset, FLTTRACE))) { + *ctlp++ = PCSFAULT; + *(fltset_t *)ctlp = *faultset; + ctlp += sizeof (fltset_t) / sizeof (long); + } + + /* + * Restore the hold mask to the current hold mask (i.e. the one + * before we executed any of the previous operations). + */ + *ctlp++ = PCSHOLD; + *(sigset_t *)ctlp = *sigmask; + ctlp += sizeof (sigset_t) / sizeof (long); + + size = (char *)ctlp - (char *)ctl; + if ((ssize = write(ctlfd, ctl, size)) != size) + error = (ssize == -1)? errno : EINTR; + (void) sigprocmask(SIG_SETMASK, &unblock, NULL); + return (error); +} + +/* + * Step over a watchpoint, i.e., execute the instruction that was stopped by + * the watchpoint, and then leave the LWP stopped at the next instruction. + */ +int +Pxecwapt(struct ps_prochandle *P, const prwatch_t *wp) +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + int rv, error; + + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + Psync(P); + error = execute_wapt(ctlfd, + &P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold, wp); + rv = Pstopstatus(P, PCNULL, 0); + + if (error != 0) { + if (P->status.pr_lwp.pr_why == PR_JOBCONTROL && + error == EBUSY) { /* jobcontrol stop -- back off */ + P->state = PS_RUN; + return (0); + } + if (error == ENOENT) + return (0); + errno = error; + return (-1); + } + + return (rv); +} + +int +Psetflags(struct ps_prochandle *P, long flags) +{ + int rc; + long ctl[2]; + + ctl[0] = PCSET; + ctl[1] = flags; + + if (write(P->ctlfd, ctl, 2*sizeof (long)) != 2*sizeof (long)) { + rc = -1; + } else { + P->status.pr_flags |= flags; + P->status.pr_lwp.pr_flags |= flags; + rc = 0; + } + + return (rc); +} + +int +Punsetflags(struct ps_prochandle *P, long flags) +{ + int rc; + long ctl[2]; + + ctl[0] = PCUNSET; + ctl[1] = flags; + + if (write(P->ctlfd, ctl, 2*sizeof (long)) != 2*sizeof (long)) { + rc = -1; + } else { + P->status.pr_flags &= ~flags; + P->status.pr_lwp.pr_flags &= ~flags; + rc = 0; + } + + return (rc); +} + +/* + * Common function to allow clients to manipulate the action to be taken + * on receipt of a signal, receipt of machine fault, entry to a system call, + * or exit from a system call. We make use of our private prset_* functions + * in order to make this code be common. The 'which' parameter identifies + * the code for the event of interest (0 means change the entire set), and + * the 'stop' parameter is a boolean indicating whether the process should + * stop when the event of interest occurs. The previous value is returned + * to the caller; -1 is returned if an error occurred. + */ +static int +Psetaction(struct ps_prochandle *P, void *sp, size_t size, + uint_t flag, int max, int which, int stop) +{ + int oldval; + + if (which < 0 || which > max) { + errno = EINVAL; + return (-1); + } + + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + oldval = prset_ismember(sp, size, which) ? TRUE : FALSE; + + if (stop) { + if (which == 0) { + prset_fill(sp, size); + P->flags |= flag; + } else if (!oldval) { + prset_add(sp, size, which); + P->flags |= flag; + } + } else { + if (which == 0) { + prset_empty(sp, size); + P->flags |= flag; + } else if (oldval) { + prset_del(sp, size, which); + P->flags |= flag; + } + } + + if (P->state == PS_RUN) + Psync(P); + + return (oldval); +} + +/* + * Set action on specified signal. + */ +int +Psignal(struct ps_prochandle *P, int which, int stop) +{ + int oldval; + + if (which == SIGKILL && stop != 0) { + errno = EINVAL; + return (-1); + } + + oldval = Psetaction(P, &P->status.pr_sigtrace, sizeof (sigset_t), + SETSIG, PRMAXSIG, which, stop); + + if (oldval != -1 && which == 0 && stop != 0) + prdelset(&P->status.pr_sigtrace, SIGKILL); + + return (oldval); +} + +/* + * Set all signal tracing flags. + */ +void +Psetsignal(struct ps_prochandle *P, const sigset_t *set) +{ + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) + return; + + P->status.pr_sigtrace = *set; + P->flags |= SETSIG; + + if (P->state == PS_RUN) + Psync(P); +} + +/* + * Set action on specified fault. + */ +int +Pfault(struct ps_prochandle *P, int which, int stop) +{ + return (Psetaction(P, &P->status.pr_flttrace, sizeof (fltset_t), + SETFAULT, PRMAXFAULT, which, stop)); +} + +/* + * Set all machine fault tracing flags. + */ +void +Psetfault(struct ps_prochandle *P, const fltset_t *set) +{ + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) + return; + + P->status.pr_flttrace = *set; + P->flags |= SETFAULT; + + if (P->state == PS_RUN) + Psync(P); +} + +/* + * Set action on specified system call entry. + */ +int +Psysentry(struct ps_prochandle *P, int which, int stop) +{ + return (Psetaction(P, &P->status.pr_sysentry, sizeof (sysset_t), + SETENTRY, PRMAXSYS, which, stop)); +} + +/* + * Set all system call entry tracing flags. + */ +void +Psetsysentry(struct ps_prochandle *P, const sysset_t *set) +{ + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) + return; + + P->status.pr_sysentry = *set; + P->flags |= SETENTRY; + + if (P->state == PS_RUN) + Psync(P); +} + +/* + * Set action on specified system call exit. + */ +int +Psysexit(struct ps_prochandle *P, int which, int stop) +{ + return (Psetaction(P, &P->status.pr_sysexit, sizeof (sysset_t), + SETEXIT, PRMAXSYS, which, stop)); +} + +/* + * Set all system call exit tracing flags. + */ +void +Psetsysexit(struct ps_prochandle *P, const sysset_t *set) +{ + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) + return; + + P->status.pr_sysexit = *set; + P->flags |= SETEXIT; + + if (P->state == PS_RUN) + Psync(P); +} + +/* + * Utility function to read the contents of a file that contains a + * prheader_t at the start (/proc/pid/lstatus or /proc/pid/lpsinfo). + * Returns a malloc()d buffer or NULL on failure. + */ +static prheader_t * +read_lfile(struct ps_prochandle *P, const char *lname) +{ + prheader_t *Lhp; + char lpath[64]; + struct stat64 statb; + int fd; + size_t size; + ssize_t rval; + + (void) snprintf(lpath, sizeof (lpath), "/proc/%d/%s", + (int)P->status.pr_pid, lname); + if ((fd = open(lpath, O_RDONLY)) < 0 || fstat64(fd, &statb) != 0) { + if (fd >= 0) + (void) close(fd); + return (NULL); + } + + /* + * 'size' is just the initial guess at the buffer size. + * It will have to grow if the number of lwps increases + * while we are looking at the process. + * 'size' must be larger than the actual file size. + */ + size = statb.st_size + 32; + + for (;;) { + if ((Lhp = malloc(size)) == NULL) + break; + if ((rval = pread(fd, Lhp, size, 0)) < 0 || + rval <= sizeof (prheader_t)) { + free(Lhp); + Lhp = NULL; + break; + } + if (rval < size) + break; + /* need a bigger buffer */ + free(Lhp); + size *= 2; + } + + (void) close(fd); + return (Lhp); +} + +/* + * LWP iteration interface. + */ +int +Plwp_iter(struct ps_prochandle *P, proc_lwp_f *func, void *cd) +{ + prheader_t *Lhp; + lwpstatus_t *Lsp; + long nlwp; + int rv; + + switch (P->state) { + case PS_RUN: + (void) Pstopstatus(P, PCNULL, 0); + break; + + case PS_STOP: + Psync(P); + break; + + case PS_IDLE: + errno = ENODATA; + return (-1); + } + + /* + * For either live processes or cores, the single LWP case is easy: + * the pstatus_t contains the lwpstatus_t for the only LWP. + */ + if (P->status.pr_nlwp <= 1) + return (func(cd, &P->status.pr_lwp)); + + /* + * For the core file multi-LWP case, we just iterate through the + * list of LWP structs we read in from the core file. + */ + if (P->state == PS_DEAD) { + lwp_info_t *lwp = list_prev(&P->core->core_lwp_head); + uint_t i; + + for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) { + if (lwp->lwp_psinfo.pr_sname != 'Z' && + (rv = func(cd, &lwp->lwp_status)) != 0) + break; + } + + return (rv); + } + + /* + * For the live process multi-LWP case, we have to work a little + * harder: the /proc/pid/lstatus file has the array of LWP structs. + */ + if ((Lhp = read_lfile(P, "lstatus")) == NULL) + return (-1); + + for (nlwp = Lhp->pr_nent, Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); + nlwp > 0; + nlwp--, Lsp = (lwpstatus_t *)((uintptr_t)Lsp + Lhp->pr_entsize)) { + if ((rv = func(cd, Lsp)) != 0) + break; + } + + free(Lhp); + return (rv); +} + +/* + * Extended LWP iteration interface. + * Iterate over all LWPs, active and zombie. + */ +int +Plwp_iter_all(struct ps_prochandle *P, proc_lwp_all_f *func, void *cd) +{ + prheader_t *Lhp = NULL; + lwpstatus_t *Lsp; + lwpstatus_t *sp; + prheader_t *Lphp = NULL; + lwpsinfo_t *Lpsp; + long nstat; + long ninfo; + int rv; + +retry: + if (Lhp != NULL) + free(Lhp); + if (Lphp != NULL) + free(Lphp); + if (P->state == PS_RUN) + (void) Pstopstatus(P, PCNULL, 0); + (void) Ppsinfo(P); + + if (P->state == PS_STOP) + Psync(P); + + /* + * For either live processes or cores, the single LWP case is easy: + * the pstatus_t contains the lwpstatus_t for the only LWP and + * the psinfo_t contains the lwpsinfo_t for the only LWP. + */ + if (P->status.pr_nlwp + P->status.pr_nzomb <= 1) + return (func(cd, &P->status.pr_lwp, &P->psinfo.pr_lwp)); + + /* + * For the core file multi-LWP case, we just iterate through the + * list of LWP structs we read in from the core file. + */ + if (P->state == PS_DEAD) { + lwp_info_t *lwp = list_prev(&P->core->core_lwp_head); + uint_t i; + + for (i = 0; i < P->core->core_nlwp; i++, lwp = list_prev(lwp)) { + sp = (lwp->lwp_psinfo.pr_sname == 'Z')? NULL : + &lwp->lwp_status; + if ((rv = func(cd, sp, &lwp->lwp_psinfo)) != 0) + break; + } + + return (rv); + } + + /* + * For the live process multi-LWP case, we have to work a little + * harder: the /proc/pid/lstatus file has the array of lwpstatus_t's + * and the /proc/pid/lpsinfo file has the array of lwpsinfo_t's. + */ + if ((Lhp = read_lfile(P, "lstatus")) == NULL) + return (-1); + if ((Lphp = read_lfile(P, "lpsinfo")) == NULL) { + free(Lhp); + return (-1); + } + + /* + * If we are looking at a running process, or one we do not control, + * the active and zombie lwps in the process may have changed since + * we read the process status structure. If so, just start over. + */ + if (Lhp->pr_nent != P->status.pr_nlwp || + Lphp->pr_nent != P->status.pr_nlwp + P->status.pr_nzomb) + goto retry; + + /* + * To be perfectly safe, prescan the two arrays, checking consistency. + * We rely on /proc giving us lwpstatus_t's and lwpsinfo_t's in the + * same order (the lwp directory order) in their respective files. + * We also rely on there being (possibly) more lwpsinfo_t's than + * lwpstatus_t's (the extra lwpsinfo_t's are for zombie lwps). + */ + Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); + Lpsp = (lwpsinfo_t *)(uintptr_t)(Lphp + 1); + nstat = Lhp->pr_nent; + for (ninfo = Lphp->pr_nent; ninfo != 0; ninfo--) { + if (Lpsp->pr_sname != 'Z') { + /* + * Not a zombie lwp; check for matching lwpids. + */ + if (nstat == 0 || Lsp->pr_lwpid != Lpsp->pr_lwpid) + goto retry; + Lsp = (lwpstatus_t *)((uintptr_t)Lsp + Lhp->pr_entsize); + nstat--; + } + Lpsp = (lwpsinfo_t *)((uintptr_t)Lpsp + Lphp->pr_entsize); + } + if (nstat != 0) + goto retry; + + /* + * Rescan, this time for real. + */ + Lsp = (lwpstatus_t *)(uintptr_t)(Lhp + 1); + Lpsp = (lwpsinfo_t *)(uintptr_t)(Lphp + 1); + for (ninfo = Lphp->pr_nent; ninfo != 0; ninfo--) { + if (Lpsp->pr_sname != 'Z') { + sp = Lsp; + Lsp = (lwpstatus_t *)((uintptr_t)Lsp + Lhp->pr_entsize); + } else { + sp = NULL; + } + if ((rv = func(cd, sp, Lpsp)) != 0) + break; + Lpsp = (lwpsinfo_t *)((uintptr_t)Lpsp + Lphp->pr_entsize); + } + + free(Lhp); + free(Lphp); + return (rv); +} + +core_content_t +Pcontent(struct ps_prochandle *P) +{ + if (P->state == PS_DEAD) + return (P->core->core_content); + if (P->state == PS_IDLE) + return (CC_CONTENT_TEXT | CC_CONTENT_DATA | CC_CONTENT_CTF); + + return (CC_CONTENT_ALL); +} + +/* + * ================================================================= + * The remainder of the functions in this file are for the + * control of individual LWPs in the controlled process. + * ================================================================= + */ + +/* + * Find an entry in the process hash table for the specified lwpid. + * The entry will either point to an existing struct ps_lwphandle + * or it will point to an empty slot for a new struct ps_lwphandle. + */ +static struct ps_lwphandle ** +Lfind(struct ps_prochandle *P, lwpid_t lwpid) +{ + struct ps_lwphandle **Lp; + struct ps_lwphandle *L; + + for (Lp = &P->hashtab[lwpid % (HASHSIZE - 1)]; + (L = *Lp) != NULL; Lp = &L->lwp_hash) + if (L->lwp_id == lwpid) + break; + return (Lp); +} + +/* + * Grab an LWP contained within the controlled process. + * Return an opaque pointer to its LWP control structure. + * perr: pointer to error return code. + */ +struct ps_lwphandle * +Lgrab(struct ps_prochandle *P, lwpid_t lwpid, int *perr) +{ + struct ps_lwphandle **Lp; + struct ps_lwphandle *L; + int fd; + char procname[100]; + char *fname; + int rc = 0; + + (void) mutex_lock(&P->proc_lock); + + if (P->state == PS_UNDEAD || P->state == PS_IDLE) + rc = G_NOPROC; + else if (P->hashtab == NULL && + (P->hashtab = calloc(HASHSIZE, sizeof (struct ps_lwphandle *))) + == NULL) + rc = G_STRANGE; + else if (*(Lp = Lfind(P, lwpid)) != NULL) + rc = G_BUSY; + else if ((L = malloc(sizeof (struct ps_lwphandle))) == NULL) + rc = G_STRANGE; + if (rc) { + *perr = rc; + (void) mutex_unlock(&P->proc_lock); + return (NULL); + } + + (void) memset(L, 0, sizeof (*L)); + L->lwp_ctlfd = -1; + L->lwp_statfd = -1; + L->lwp_proc = P; + L->lwp_id = lwpid; + *Lp = L; /* insert into the hash table */ + + if (P->state == PS_DEAD) { /* core file */ + if (getlwpstatus(P, lwpid, &L->lwp_status) == -1) { + rc = G_NOPROC; + goto err; + } + L->lwp_state = PS_DEAD; + *perr = 0; + (void) mutex_unlock(&P->proc_lock); + return (L); + } + + /* + * Open the /proc/<pid>/lwp/<lwpid> files + */ + (void) sprintf(procname, "/proc/%d/lwp/%d/", (int)P->pid, (int)lwpid); + fname = procname + strlen(procname); + (void) set_minfd(); + + (void) strcpy(fname, "lwpstatus"); + if ((fd = open(procname, O_RDONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) { + switch (errno) { + case ENOENT: + rc = G_NOPROC; + break; + default: + dprintf("Lgrab: failed to open %s: %s\n", + procname, strerror(errno)); + rc = G_STRANGE; + break; + } + goto err; + } + L->lwp_statfd = fd; + + if (pread(fd, &L->lwp_status, sizeof (L->lwp_status), (off_t)0) < 0) { + switch (errno) { + case ENOENT: + rc = G_NOPROC; + break; + default: + dprintf("Lgrab: failed to read %s: %s\n", + procname, strerror(errno)); + rc = G_STRANGE; + break; + } + goto err; + } + + (void) strcpy(fname, "lwpctl"); + if ((fd = open(procname, O_WRONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) { + switch (errno) { + case ENOENT: + rc = G_NOPROC; + break; + default: + dprintf("Lgrab: failed to open %s: %s\n", + procname, strerror(errno)); + rc = G_STRANGE; + break; + } + goto err; + } + L->lwp_ctlfd = fd; + + L->lwp_state = + ((L->lwp_status.pr_flags & (PR_STOPPED|PR_ISTOP)) + == (PR_STOPPED|PR_ISTOP))? + PS_STOP : PS_RUN; + + *perr = 0; + (void) mutex_unlock(&P->proc_lock); + return (L); + +err: + Lfree_internal(P, L); + *perr = rc; + (void) mutex_unlock(&P->proc_lock); + return (NULL); +} + +/* + * Return a printable string corresponding to an Lgrab() error return. + */ +const char * +Lgrab_error(int error) +{ + const char *str; + + switch (error) { + case G_NOPROC: + str = "no such LWP"; + break; + case G_BUSY: + str = "LWP already grabbed"; + break; + case G_STRANGE: + str = "unanticipated system error"; + break; + default: + str = "unknown error"; + break; + } + + return (str); +} + +/* + * Free an LWP control structure. + */ +void +Lfree(struct ps_lwphandle *L) +{ + struct ps_prochandle *P = L->lwp_proc; + + (void) mutex_lock(&P->proc_lock); + Lfree_internal(P, L); + (void) mutex_unlock(&P->proc_lock); +} + +static void +Lfree_internal(struct ps_prochandle *P, struct ps_lwphandle *L) +{ + *Lfind(P, L->lwp_id) = L->lwp_hash; /* delete from hash table */ + if (L->lwp_ctlfd >= 0) + (void) close(L->lwp_ctlfd); + if (L->lwp_statfd >= 0) + (void) close(L->lwp_statfd); + + /* clear out the structure as a precaution against reuse */ + (void) memset(L, 0, sizeof (*L)); + L->lwp_ctlfd = -1; + L->lwp_statfd = -1; + + free(L); +} + +/* + * Return the state of the process, one of the PS_* values. + */ +int +Lstate(struct ps_lwphandle *L) +{ + return (L->lwp_state); +} + +/* + * Return the open control file descriptor for the LWP. + * Clients must not close this file descriptor, nor use it + * after the LWP is freed. + */ +int +Lctlfd(struct ps_lwphandle *L) +{ + return (L->lwp_ctlfd); +} + +/* + * Return a pointer to the LWP lwpsinfo structure. + * Clients should not hold on to this pointer indefinitely. + * It will become invalid on Lfree(). + */ +const lwpsinfo_t * +Lpsinfo(struct ps_lwphandle *L) +{ + if (Plwp_getpsinfo(L->lwp_proc, L->lwp_id, &L->lwp_psinfo) == -1) + return (NULL); + + return (&L->lwp_psinfo); +} + +/* + * Return a pointer to the LWP status structure. + * Clients should not hold on to this pointer indefinitely. + * It will become invalid on Lfree(). + */ +const lwpstatus_t * +Lstatus(struct ps_lwphandle *L) +{ + return (&L->lwp_status); +} + +/* + * Given an LWP handle, return the process handle. + */ +struct ps_prochandle * +Lprochandle(struct ps_lwphandle *L) +{ + return (L->lwp_proc); +} + +/* + * Ensure that all cached state is written to the LWP. + * The cached state is the LWP's signal mask and registers. + */ +void +Lsync(struct ps_lwphandle *L) +{ + int ctlfd = L->lwp_ctlfd; + long cmd[2]; + iovec_t iov[4]; + int n = 0; + + if (L->lwp_flags & SETHOLD) { + cmd[0] = PCSHOLD; + iov[n].iov_base = (caddr_t)&cmd[0]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&L->lwp_status.pr_lwphold; + iov[n++].iov_len = sizeof (L->lwp_status.pr_lwphold); + } + if (L->lwp_flags & SETREGS) { + cmd[1] = PCSREG; + iov[n].iov_base = (caddr_t)&cmd[1]; + iov[n++].iov_len = sizeof (long); + iov[n].iov_base = (caddr_t)&L->lwp_status.pr_reg[0]; + iov[n++].iov_len = sizeof (L->lwp_status.pr_reg); + } + + if (n == 0 || writev(ctlfd, iov, n) < 0) + return; /* nothing to do or write failed */ + + L->lwp_flags &= ~(SETHOLD|SETREGS); +} + +/* + * Wait for the specified LWP to stop or terminate. + * Or, just get the current status (PCNULL). + * Or, direct it to stop and get the current status (PCDSTOP). + */ +static int +Lstopstatus(struct ps_lwphandle *L, + long request, /* PCNULL, PCDSTOP, PCSTOP, PCWSTOP */ + uint_t msec) /* if non-zero, timeout in milliseconds */ +{ + int ctlfd = L->lwp_ctlfd; + long ctl[3]; + ssize_t rc; + int err; + + switch (L->lwp_state) { + case PS_RUN: + break; + case PS_STOP: + if (request != PCNULL && request != PCDSTOP) + return (0); + break; + case PS_LOST: + if (request != PCNULL) { + errno = EAGAIN; + return (-1); + } + break; + case PS_UNDEAD: + case PS_DEAD: + if (request != PCNULL) { + errno = ENOENT; + return (-1); + } + break; + default: /* corrupted state */ + dprintf("Lstopstatus: corrupted state: %d\n", L->lwp_state); + errno = EINVAL; + return (-1); + } + + ctl[0] = PCDSTOP; + ctl[1] = PCTWSTOP; + ctl[2] = (long)msec; + rc = 0; + switch (request) { + case PCSTOP: + rc = write(ctlfd, &ctl[0], 3*sizeof (long)); + break; + case PCWSTOP: + rc = write(ctlfd, &ctl[1], 2*sizeof (long)); + break; + case PCDSTOP: + rc = write(ctlfd, &ctl[0], 1*sizeof (long)); + break; + case PCNULL: + if (L->lwp_state == PS_DEAD) + return (0); /* Nothing else to do for cores */ + break; + default: /* programming error */ + errno = EINVAL; + return (-1); + } + err = (rc < 0)? errno : 0; + Lsync(L); + + if (pread(L->lwp_statfd, &L->lwp_status, + sizeof (L->lwp_status), (off_t)0) < 0) + err = errno; + + if (err) { + switch (err) { + case EINTR: /* user typed ctl-C */ + case ERESTART: + dprintf("Lstopstatus: EINTR\n"); + break; + case EAGAIN: /* we lost control of the the process */ + dprintf("Lstopstatus: EAGAIN\n"); + L->lwp_state = PS_LOST; + errno = err; + return (-1); + default: + if (_libproc_debug) { + const char *errstr; + + switch (request) { + case PCNULL: + errstr = "Lstopstatus PCNULL"; break; + case PCSTOP: + errstr = "Lstopstatus PCSTOP"; break; + case PCDSTOP: + errstr = "Lstopstatus PCDSTOP"; break; + case PCWSTOP: + errstr = "Lstopstatus PCWSTOP"; break; + default: + errstr = "Lstopstatus PC???"; break; + } + dprintf("%s: %s\n", errstr, strerror(err)); + } + L->lwp_state = PS_UNDEAD; + errno = err; + return (-1); + } + } + + if ((L->lwp_status.pr_flags & (PR_STOPPED|PR_ISTOP)) + != (PR_STOPPED|PR_ISTOP)) { + L->lwp_state = PS_RUN; + if (request == PCNULL || request == PCDSTOP || msec != 0) + return (0); + dprintf("Lstopstatus: LWP is not stopped\n"); + errno = EPROTO; + return (-1); + } + + L->lwp_state = PS_STOP; + + if (_libproc_debug) /* debugging */ + prldump("Lstopstatus", &L->lwp_status); + + switch (L->lwp_status.pr_why) { + case PR_SYSENTRY: + case PR_SYSEXIT: + case PR_REQUESTED: + case PR_SIGNALLED: + case PR_FAULTED: + case PR_JOBCONTROL: + case PR_SUSPENDED: + break; + default: + errno = EPROTO; + return (-1); + } + + return (0); +} + +/* + * Wait for the LWP to stop for any reason. + */ +int +Lwait(struct ps_lwphandle *L, uint_t msec) +{ + return (Lstopstatus(L, PCWSTOP, msec)); +} + +/* + * Direct the LWP to stop; wait for it to stop. + */ +int +Lstop(struct ps_lwphandle *L, uint_t msec) +{ + return (Lstopstatus(L, PCSTOP, msec)); +} + +/* + * Direct the LWP to stop; don't wait. + */ +int +Ldstop(struct ps_lwphandle *L) +{ + return (Lstopstatus(L, PCDSTOP, 0)); +} + +/* + * Get the value of one register from stopped LWP. + */ +int +Lgetareg(struct ps_lwphandle *L, int regno, prgreg_t *preg) +{ + if (regno < 0 || regno >= NPRGREG) { + errno = EINVAL; + return (-1); + } + + if (L->lwp_state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + *preg = L->lwp_status.pr_reg[regno]; + return (0); +} + +/* + * Put value of one register into stopped LWP. + */ +int +Lputareg(struct ps_lwphandle *L, int regno, prgreg_t reg) +{ + if (regno < 0 || regno >= NPRGREG) { + errno = EINVAL; + return (-1); + } + + if (L->lwp_state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + L->lwp_status.pr_reg[regno] = reg; + L->lwp_flags |= SETREGS; /* set registers before continuing */ + return (0); +} + +int +Lsetrun(struct ps_lwphandle *L, + int sig, /* signal to pass to LWP */ + int flags) /* PRSTEP|PRSABORT|PRSTOP|PRCSIG|PRCFAULT */ +{ + int ctlfd = L->lwp_ctlfd; + int sbits = (PR_DSTOP | PR_ISTOP | PR_ASLEEP); + + long ctl[1 + /* PCCFAULT */ + 1 + sizeof (siginfo_t)/sizeof (long) + /* PCSSIG/PCCSIG */ + 2 ]; /* PCRUN */ + + long *ctlp = ctl; + size_t size; + + if (L->lwp_state != PS_STOP && + (L->lwp_status.pr_flags & sbits) == 0) { + errno = EBUSY; + return (-1); + } + + Lsync(L); /* flush registers */ + + if (flags & PRCFAULT) { /* clear current fault */ + *ctlp++ = PCCFAULT; + flags &= ~PRCFAULT; + } + + if (flags & PRCSIG) { /* clear current signal */ + *ctlp++ = PCCSIG; + flags &= ~PRCSIG; + } else if (sig && sig != L->lwp_status.pr_cursig) { + /* make current signal */ + siginfo_t *infop; + + *ctlp++ = PCSSIG; + infop = (siginfo_t *)ctlp; + (void) memset(infop, 0, sizeof (*infop)); + infop->si_signo = sig; + ctlp += sizeof (siginfo_t) / sizeof (long); + } + + *ctlp++ = PCRUN; + *ctlp++ = flags; + size = (char *)ctlp - (char *)ctl; + + L->lwp_proc->info_valid = 0; /* will need to update map and file info */ + L->lwp_proc->state = PS_RUN; + L->lwp_state = PS_RUN; + + if (write(ctlfd, ctl, size) != size) { + /* Pretend that a job-stopped LWP is running */ + if (errno != EBUSY || L->lwp_status.pr_why != PR_JOBCONTROL) + return (Lstopstatus(L, PCNULL, 0)); + } + + return (0); +} + +int +Lclearsig(struct ps_lwphandle *L) +{ + int ctlfd = L->lwp_ctlfd; + long ctl = PCCSIG; + + if (write(ctlfd, &ctl, sizeof (ctl)) != sizeof (ctl)) + return (-1); + L->lwp_status.pr_cursig = 0; + return (0); +} + +int +Lclearfault(struct ps_lwphandle *L) +{ + int ctlfd = L->lwp_ctlfd; + long ctl = PCCFAULT; + + if (write(ctlfd, &ctl, sizeof (ctl)) != sizeof (ctl)) + return (-1); + return (0); +} + +/* + * Step over a breakpoint, i.e., execute the instruction that + * really belongs at the breakpoint location (the current %pc) + * and leave the LWP stopped at the next instruction. + */ +int +Lxecbkpt(struct ps_lwphandle *L, ulong_t saved) +{ + struct ps_prochandle *P = L->lwp_proc; + int rv, error; + + if (L->lwp_state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + Lsync(L); + error = execute_bkpt(L->lwp_ctlfd, + &P->status.pr_flttrace, &L->lwp_status.pr_lwphold, + L->lwp_status.pr_reg[R_PC], saved); + rv = Lstopstatus(L, PCNULL, 0); + + if (error != 0) { + if (L->lwp_status.pr_why == PR_JOBCONTROL && + error == EBUSY) { /* jobcontrol stop -- back off */ + L->lwp_state = PS_RUN; + return (0); + } + if (error == ENOENT) + return (0); + errno = error; + return (-1); + } + + return (rv); +} + +/* + * Step over a watchpoint, i.e., execute the instruction that was stopped by + * the watchpoint, and then leave the LWP stopped at the next instruction. + */ +int +Lxecwapt(struct ps_lwphandle *L, const prwatch_t *wp) +{ + struct ps_prochandle *P = L->lwp_proc; + int rv, error; + + if (L->lwp_state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + Lsync(L); + error = execute_wapt(L->lwp_ctlfd, + &P->status.pr_flttrace, &L->lwp_status.pr_lwphold, wp); + rv = Lstopstatus(L, PCNULL, 0); + + if (error != 0) { + if (L->lwp_status.pr_why == PR_JOBCONTROL && + error == EBUSY) { /* jobcontrol stop -- back off */ + L->lwp_state = PS_RUN; + return (0); + } + if (error == ENOENT) + return (0); + errno = error; + return (-1); + } + + return (rv); +} + +int +Lstack(struct ps_lwphandle *L, stack_t *stkp) +{ + struct ps_prochandle *P = L->lwp_proc; + uintptr_t addr = L->lwp_status.pr_ustack; + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp)) + return (-1); +#ifdef _LP64 + } else { + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32)) + return (-1); + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); +} + +int +Lmain_stack(struct ps_lwphandle *L, stack_t *stkp) +{ + struct ps_prochandle *P = L->lwp_proc; + + if (Lstack(L, stkp) != 0) + return (-1); + + /* + * If the SS_ONSTACK flag is set then this LWP is operating on the + * alternate signal stack. We can recover the original stack from + * pr_oldcontext. + */ + if (!(stkp->ss_flags & SS_ONSTACK)) + return (0); + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + ucontext_t *ctxp = (void *)L->lwp_status.pr_oldcontext; + + if (Pread(P, stkp, sizeof (*stkp), + (uintptr_t)&ctxp->uc_stack) != sizeof (*stkp)) + return (-1); +#ifdef _LP64 + } else { + ucontext32_t *ctxp = (void *)L->lwp_status.pr_oldcontext; + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), + (uintptr_t)&ctxp->uc_stack) != sizeof (stk32)) + return (-1); + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); +} + +int +Lalt_stack(struct ps_lwphandle *L, stack_t *stkp) +{ + if (L->lwp_status.pr_altstack.ss_flags & SS_DISABLE) { + errno = ENODATA; + return (-1); + } + + *stkp = L->lwp_status.pr_altstack; + + return (0); +} + +/* + * Add a mapping to the given proc handle. Resizes the array as appropriate and + * manages reference counts on the given file_info_t. + * + * The 'map_relocate' member is used to tell Psort_mappings() that the + * associated file_map pointer needs to be relocated after the mappings have + * been sorted. It is only set for the first mapping, and has no meaning + * outside these two functions. + */ +int +Padd_mapping(struct ps_prochandle *P, off64_t off, file_info_t *fp, + prmap_t *pmap) +{ + map_info_t *mp; + + if (P->map_count == P->map_alloc) { + size_t next = P->map_alloc ? P->map_alloc * 2 : 16; + + if ((P->mappings = realloc(P->mappings, + next * sizeof (map_info_t))) == NULL) + return (-1); + + P->map_alloc = next; + } + + mp = &P->mappings[P->map_count++]; + + mp->map_offset = off; + mp->map_pmap = *pmap; + mp->map_relocate = 0; + if ((mp->map_file = fp) != NULL) { + if (fp->file_map == NULL) { + fp->file_map = mp; + mp->map_relocate = 1; + } + fp->file_ref++; + } + + return (0); +} + +static int +map_sort(const void *a, const void *b) +{ + const map_info_t *ap = a, *bp = b; + + if (ap->map_pmap.pr_vaddr < bp->map_pmap.pr_vaddr) + return (-1); + else if (ap->map_pmap.pr_vaddr > bp->map_pmap.pr_vaddr) + return (1); + else + return (0); +} + +/* + * Sort the current set of mappings. Should be called during target + * initialization after all calls to Padd_mapping() have been made. + */ +void +Psort_mappings(struct ps_prochandle *P) +{ + int i; + map_info_t *mp; + + qsort(P->mappings, P->map_count, sizeof (map_info_t), map_sort); + + /* + * Update all the file_map pointers to refer to the new locations. + */ + for (i = 0; i < P->map_count; i++) { + mp = &P->mappings[i]; + if (mp->map_relocate) + mp->map_file->file_map = mp; + mp->map_relocate = 0; + } +} diff --git a/usr/src/lib/libproc/common/Pcontrol.h b/usr/src/lib/libproc/common/Pcontrol.h new file mode 100644 index 0000000000..5d3797d16f --- /dev/null +++ b/usr/src/lib/libproc/common/Pcontrol.h @@ -0,0 +1,250 @@ +/* + * 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. + */ + +#ifndef _PCONTROL_H +#define _PCONTROL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Implemention-specific include file for libproc process management. + * This is not to be seen by the clients of libproc. + */ + +#include <stdio.h> +#include <gelf.h> +#include <synch.h> +#include <procfs.h> +#include <rtld_db.h> +#include <libproc.h> +#include <libctf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "Putil.h" + +/* + * Definitions of the process control structures, internal to libproc. + * These may change without affecting clients of libproc. + */ + +typedef struct { /* symbol table */ + Elf_Data *sym_data; /* start of table */ + size_t sym_symn; /* number of entries */ + char *sym_strs; /* ptr to strings */ + size_t sym_strsz; /* size of string table */ + GElf_Shdr sym_hdr; /* symbol table section header */ + GElf_Shdr sym_strhdr; /* string table section header */ + Elf *sym_elf; /* faked-up elf handle from core file */ + void *sym_elfmem; /* data for faked-up elf handle */ + uint_t *sym_byname; /* symbols sorted by name */ + uint_t *sym_byaddr; /* symbols sorted by addr */ + size_t sym_count; /* number of symbols in each sorted list */ +} sym_tbl_t; + +typedef struct file_info { /* symbol information for a mapped file */ + list_t file_list; /* linked list */ + char file_pname[PRMAPSZ]; /* name from prmap_t */ + struct map_info *file_map; /* primary (text) mapping */ + int file_ref; /* references from map_info_t structures */ + int file_fd; /* file descriptor for the mapped file */ + int file_init; /* 0: initialization yet to be performed */ + GElf_Half file_etype; /* ELF e_type from ehdr */ + GElf_Half file_class; /* ELF e_ident[EI_CLASS] from ehdr */ + rd_loadobj_t *file_lo; /* load object structure from rtld_db */ + char *file_lname; /* load object name from rtld_db */ + char *file_lbase; /* pointer to basename of file_lname */ + Elf *file_elf; /* elf handle so we can close */ + void *file_elfmem; /* data for faked-up elf handle */ + sym_tbl_t file_symtab; /* symbol table */ + sym_tbl_t file_dynsym; /* dynamic symbol table */ + uintptr_t file_dyn_base; /* load address for ET_DYN files */ + uintptr_t file_plt_base; /* base address for PLT */ + size_t file_plt_size; /* size of PLT region */ + uintptr_t file_jmp_rel; /* base address of PLT relocations */ + uintptr_t file_ctf_off; /* offset of CTF data in object file */ + size_t file_ctf_size; /* size of CTF data in object file */ + int file_ctf_dyn; /* does the CTF data reference the dynsym */ + void *file_ctf_buf; /* CTF data for this file */ + ctf_file_t *file_ctfp; /* CTF container for this file */ +} file_info_t; + +typedef struct map_info { /* description of an address space mapping */ + prmap_t map_pmap; /* /proc description of this mapping */ + file_info_t *map_file; /* pointer into list of mapped files */ + off64_t map_offset; /* offset into core file (if core) */ + int map_relocate; /* associated file_map needs to be relocated */ +} map_info_t; + +typedef struct lwp_info { /* per-lwp information from core file */ + list_t lwp_list; /* linked list */ + lwpid_t lwp_id; /* lwp identifier */ + lwpsinfo_t lwp_psinfo; /* /proc/<pid>/lwp/<lwpid>/lwpsinfo data */ + lwpstatus_t lwp_status; /* /proc/<pid>/lwp/<lwpid>/lwpstatus data */ +#if defined(sparc) || defined(__sparc) + gwindows_t *lwp_gwins; /* /proc/<pid>/lwp/<lwpid>/gwindows data */ + prxregset_t *lwp_xregs; /* /proc/<pid>/lwp/<lwpid>/xregs data */ + int64_t *lwp_asrs; /* /proc/<pid>/lwp/<lwpid>/asrs data */ +#endif +} lwp_info_t; + +typedef struct core_info { /* information specific to core files */ + char core_dmodel; /* data model for core file */ + int core_errno; /* error during initialization if != 0 */ + list_t core_lwp_head; /* head of list of lwp info */ + lwp_info_t *core_lwp; /* current lwp information */ + uint_t core_nlwp; /* number of lwp's in list */ + off64_t core_size; /* size of core file in bytes */ + char *core_platform; /* platform string from core file */ + struct utsname *core_uts; /* uname(2) data from core file */ + prcred_t *core_cred; /* process credential from core file */ + core_content_t core_content; /* content dumped to core file */ + prpriv_t *core_priv; /* process privileges from core file */ + size_t core_priv_size; /* size of the privileges */ + void *core_privinfo; /* system privileges info from core file */ + priv_impl_info_t *core_ppii; /* NOTE entry for core_privinfo */ + char *core_zonename; /* zone name from core file */ +#if defined(__i386) || defined(__amd64) + struct ssd *core_ldt; /* LDT entries from core file */ + uint_t core_nldt; /* number of LDT entries in core file */ +#endif +} core_info_t; + +typedef struct elf_file { /* convenience for managing ELF files */ + GElf_Ehdr e_hdr; /* ELF file header information */ + Elf *e_elf; /* ELF library handle */ + int e_fd; /* file descriptor */ +} elf_file_t; + +typedef struct ps_rwops { /* ops vector for Pread() and Pwrite() */ + ssize_t (*p_pread)(struct ps_prochandle *, + void *, size_t, uintptr_t); + ssize_t (*p_pwrite)(struct ps_prochandle *, + const void *, size_t, uintptr_t); +} ps_rwops_t; + +#define HASHSIZE 1024 /* hash table size, power of 2 */ + +struct ps_prochandle { + struct ps_lwphandle **hashtab; /* hash table for LWPs (Lgrab()) */ + mutex_t proc_lock; /* protects hash table; serializes Lgrab() */ + pstatus_t orig_status; /* remembered status on Pgrab() */ + pstatus_t status; /* status when stopped */ + psinfo_t psinfo; /* psinfo_t from last Ppsinfo() request */ + uintptr_t sysaddr; /* address of most recent syscall instruction */ + pid_t pid; /* process-ID */ + int state; /* state of the process, see "libproc.h" */ + uint_t flags; /* see defines below */ + uint_t agentcnt; /* Pcreate_agent()/Pdestroy_agent() ref count */ + int asfd; /* /proc/<pid>/as filedescriptor */ + int ctlfd; /* /proc/<pid>/ctl filedescriptor */ + int statfd; /* /proc/<pid>/status filedescriptor */ + int agentctlfd; /* /proc/<pid>/lwp/agent/ctl */ + int agentstatfd; /* /proc/<pid>/lwp/agent/status */ + int info_valid; /* if zero, map and file info need updating */ + map_info_t *mappings; /* cached process mappings */ + size_t map_count; /* number of mappings */ + size_t map_alloc; /* number of mappings allocated */ + uint_t num_files; /* number of file elements in file_info */ + list_t file_head; /* head of mapped files w/ symbol table info */ + char *execname; /* name of the executable file */ + auxv_t *auxv; /* the process's aux vector */ + int nauxv; /* number of aux vector entries */ + rd_agent_t *rap; /* cookie for rtld_db */ + map_info_t *map_exec; /* the mapping for the executable file */ + map_info_t *map_ldso; /* the mapping for ld.so.1 */ + const ps_rwops_t *ops; /* pointer to ops-vector for read and write */ + core_info_t *core; /* information specific to core (if PS_DEAD) */ + uintptr_t *ucaddrs; /* ucontext-list addresses */ + uint_t ucnelems; /* number of elements in the ucaddrs list */ +}; + +/* flags */ +#define CREATED 0x01 /* process was created by Pcreate() */ +#define SETSIG 0x02 /* set signal trace mask before continuing */ +#define SETFAULT 0x04 /* set fault trace mask before continuing */ +#define SETENTRY 0x08 /* set sysentry trace mask before continuing */ +#define SETEXIT 0x10 /* set sysexit trace mask before continuing */ +#define SETHOLD 0x20 /* set signal hold mask before continuing */ +#define SETREGS 0x40 /* set registers before continuing */ + +struct ps_lwphandle { + struct ps_prochandle *lwp_proc; /* process to which this lwp belongs */ + struct ps_lwphandle *lwp_hash; /* hash table linked list */ + lwpstatus_t lwp_status; /* status when stopped */ + lwpsinfo_t lwp_psinfo; /* lwpsinfo_t from last Lpsinfo() */ + lwpid_t lwp_id; /* lwp identifier */ + int lwp_state; /* state of the lwp, see "libproc.h" */ + uint_t lwp_flags; /* SETHOLD and/or SETREGS */ + int lwp_ctlfd; /* /proc/<pid>/lwp/<lwpid>/lwpctl */ + int lwp_statfd; /* /proc/<pid>/lwp/<lwpid>/lwpstatus */ +}; + +/* + * Implementation functions in the process control library. + * These are not exported to clients of the library. + */ +extern void prldump(const char *, lwpstatus_t *); +extern int dupfd(int, int); +extern int set_minfd(void); +extern int Pscantext(struct ps_prochandle *); +extern void Pinitsym(struct ps_prochandle *); +extern void Preadauxvec(struct ps_prochandle *); +extern void optimize_symtab(sym_tbl_t *); +extern void Pbuild_file_symtab(struct ps_prochandle *, file_info_t *); +extern ctf_file_t *Pbuild_file_ctf(struct ps_prochandle *, file_info_t *); +extern map_info_t *Paddr2mptr(struct ps_prochandle *, uintptr_t); +extern char *Pfindexec(struct ps_prochandle *, const char *, + int (*)(const char *, void *), void *); +extern int getlwpstatus(struct ps_prochandle *, lwpid_t, lwpstatus_t *); +int Pstopstatus(struct ps_prochandle *, long, uint32_t); + +extern int Padd_mapping(struct ps_prochandle *, off64_t, file_info_t *, + prmap_t *); +extern void Psort_mappings(struct ps_prochandle *); + +/* + * Architecture-dependent definition of the breakpoint instruction. + */ +#if defined(sparc) || defined(__sparc) +#define BPT ((instr_t)0x91d02001) +#elif defined(__i386) || defined(__amd64) +#define BPT ((instr_t)0xcc) +#endif + +/* + * Simple convenience. + */ +#define TRUE 1 +#define FALSE 0 + +#ifdef __cplusplus +} +#endif + +#endif /* _PCONTROL_H */ diff --git a/usr/src/lib/libproc/common/Pcore.c b/usr/src/lib/libproc/common/Pcore.c new file mode 100644 index 0000000000..99feb87ac4 --- /dev/null +++ b/usr/src/lib/libproc/common/Pcore.c @@ -0,0 +1,2003 @@ +/* + * 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/types.h> +#include <sys/utsname.h> +#include <sys/sysmacros.h> + +#include <alloca.h> +#include <rtld_db.h> +#include <libgen.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <gelf.h> +#include <stddef.h> + +#include "Pcontrol.h" +#include "P32ton.h" +#include "Putil.h" + +/* + * Pcore.c - Code to initialize a ps_prochandle from a core dump. We + * allocate an additional structure to hold information from the core + * file, and attach this to the standard ps_prochandle in place of the + * ability to examine /proc/<pid>/ files. + */ + +/* + * Basic i/o function for reading and writing from the process address space + * stored in the core file and associated shared libraries. We compute the + * appropriate fd and offsets, and let the provided prw function do the rest. + */ +static ssize_t +core_rw(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, + ssize_t (*prw)(int, void *, size_t, off64_t)) +{ + ssize_t resid = n; + + while (resid != 0) { + map_info_t *mp = Paddr2mptr(P, addr); + + uintptr_t mapoff; + ssize_t len; + off64_t off; + int fd; + + if (mp == NULL) + break; /* No mapping for this address */ + + if (mp->map_pmap.pr_mflags & MA_RESERVED1) { + if (mp->map_file == NULL || mp->map_file->file_fd < 0) + break; /* No file or file not open */ + + fd = mp->map_file->file_fd; + } else + fd = P->asfd; + + mapoff = addr - mp->map_pmap.pr_vaddr; + len = MIN(resid, mp->map_pmap.pr_size - mapoff); + off = mp->map_offset + mapoff; + + if ((len = prw(fd, buf, len, off)) <= 0) + break; + + resid -= len; + addr += len; + buf = (char *)buf + len; + } + + /* + * Important: Be consistent with the behavior of i/o on the as file: + * writing to an invalid address yields EIO; reading from an invalid + * address falls through to returning success and zero bytes. + */ + if (resid == n && n != 0 && prw != pread64) { + errno = EIO; + return (-1); + } + + return (n - resid); +} + +static ssize_t +Pread_core(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) +{ + return (core_rw(P, buf, n, addr, pread64)); +} + +static ssize_t +Pwrite_core(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) +{ + return (core_rw(P, (void *)buf, n, addr, + (ssize_t (*)(int, void *, size_t, off64_t)) pwrite64)); +} + +static const ps_rwops_t P_core_ops = { Pread_core, Pwrite_core }; + +/* + * Return the lwp_info_t for the given lwpid. If no such lwpid has been + * encountered yet, allocate a new structure and return a pointer to it. + * Create a list of lwp_info_t structures sorted in decreasing lwp_id order. + */ +static lwp_info_t * +lwpid2info(struct ps_prochandle *P, lwpid_t id) +{ + lwp_info_t *lwp = list_next(&P->core->core_lwp_head); + lwp_info_t *next; + uint_t i; + + for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { + if (lwp->lwp_id == id) { + P->core->core_lwp = lwp; + return (lwp); + } + if (lwp->lwp_id < id) { + break; + } + } + + next = lwp; + if ((lwp = calloc(1, sizeof (lwp_info_t))) == NULL) + return (NULL); + + list_link(lwp, next); + lwp->lwp_id = id; + + P->core->core_lwp = lwp; + P->core->core_nlwp++; + + return (lwp); +} + +/* + * The core file itself contains a series of NOTE segments containing saved + * structures from /proc at the time the process died. For each note we + * comprehend, we define a function to read it in from the core file, + * convert it to our native data model if necessary, and store it inside + * the ps_prochandle. Each function is invoked by Pfgrab_core() with the + * seek pointer on P->asfd positioned appropriately. We populate a table + * of pointers to these note functions below. + */ + +static int +note_pstatus(struct ps_prochandle *P, size_t nbytes) +{ +#ifdef _LP64 + if (P->core->core_dmodel == PR_MODEL_ILP32) { + pstatus32_t ps32; + + if (nbytes < sizeof (pstatus32_t) || + read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32)) + goto err; + + pstatus_32_to_n(&ps32, &P->status); + + } else +#endif + if (nbytes < sizeof (pstatus_t) || + read(P->asfd, &P->status, sizeof (pstatus_t)) != sizeof (pstatus_t)) + goto err; + + P->orig_status = P->status; + P->pid = P->status.pr_pid; + + return (0); + +err: + dprintf("Pgrab_core: failed to read NT_PSTATUS\n"); + return (-1); +} + +static int +note_lwpstatus(struct ps_prochandle *P, size_t nbytes) +{ + lwp_info_t *lwp; + lwpstatus_t lps; + +#ifdef _LP64 + if (P->core->core_dmodel == PR_MODEL_ILP32) { + lwpstatus32_t l32; + + if (nbytes < sizeof (lwpstatus32_t) || + read(P->asfd, &l32, sizeof (l32)) != sizeof (l32)) + goto err; + + lwpstatus_32_to_n(&l32, &lps); + } else +#endif + if (nbytes < sizeof (lwpstatus_t) || + read(P->asfd, &lps, sizeof (lps)) != sizeof (lps)) + goto err; + + if ((lwp = lwpid2info(P, lps.pr_lwpid)) == NULL) { + dprintf("Pgrab_core: failed to add NT_LWPSTATUS\n"); + return (-1); + } + + /* + * Erase a useless and confusing artifact of the kernel implementation: + * the lwps which did *not* create the core will show SIGKILL. We can + * be assured this is bogus because SIGKILL can't produce core files. + */ + if (lps.pr_cursig == SIGKILL) + lps.pr_cursig = 0; + + (void) memcpy(&lwp->lwp_status, &lps, sizeof (lps)); + return (0); + +err: + dprintf("Pgrab_core: failed to read NT_LWPSTATUS\n"); + return (-1); +} + +static int +note_psinfo(struct ps_prochandle *P, size_t nbytes) +{ +#ifdef _LP64 + if (P->core->core_dmodel == PR_MODEL_ILP32) { + psinfo32_t ps32; + + if (nbytes < sizeof (psinfo32_t) || + read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32)) + goto err; + + psinfo_32_to_n(&ps32, &P->psinfo); + } else +#endif + if (nbytes < sizeof (psinfo_t) || + read(P->asfd, &P->psinfo, sizeof (psinfo_t)) != sizeof (psinfo_t)) + goto err; + + dprintf("pr_fname = <%s>\n", P->psinfo.pr_fname); + dprintf("pr_psargs = <%s>\n", P->psinfo.pr_psargs); + dprintf("pr_wstat = 0x%x\n", P->psinfo.pr_wstat); + + return (0); + +err: + dprintf("Pgrab_core: failed to read NT_PSINFO\n"); + return (-1); +} + +static int +note_lwpsinfo(struct ps_prochandle *P, size_t nbytes) +{ + lwp_info_t *lwp; + lwpsinfo_t lps; + +#ifdef _LP64 + if (P->core->core_dmodel == PR_MODEL_ILP32) { + lwpsinfo32_t l32; + + if (nbytes < sizeof (lwpsinfo32_t) || + read(P->asfd, &l32, sizeof (l32)) != sizeof (l32)) + goto err; + + lwpsinfo_32_to_n(&l32, &lps); + } else +#endif + if (nbytes < sizeof (lwpsinfo_t) || + read(P->asfd, &lps, sizeof (lps)) != sizeof (lps)) + goto err; + + if ((lwp = lwpid2info(P, lps.pr_lwpid)) == NULL) { + dprintf("Pgrab_core: failed to add NT_LWPSINFO\n"); + return (-1); + } + + (void) memcpy(&lwp->lwp_psinfo, &lps, sizeof (lps)); + return (0); + +err: + dprintf("Pgrab_core: failed to read NT_LWPSINFO\n"); + return (-1); +} + +static int +note_platform(struct ps_prochandle *P, size_t nbytes) +{ + char *plat; + + if (P->core->core_platform != NULL) + return (0); /* Already seen */ + + if (nbytes != 0 && ((plat = malloc(nbytes + 1)) != NULL)) { + if (read(P->asfd, plat, nbytes) != nbytes) { + dprintf("Pgrab_core: failed to read NT_PLATFORM\n"); + free(plat); + return (-1); + } + plat[nbytes - 1] = '\0'; + P->core->core_platform = plat; + } + + return (0); +} + +static int +note_utsname(struct ps_prochandle *P, size_t nbytes) +{ + size_t ubytes = sizeof (struct utsname); + struct utsname *utsp; + + if (P->core->core_uts != NULL || nbytes < ubytes) + return (0); /* Already seen or bad size */ + + if ((utsp = malloc(ubytes)) == NULL) + return (-1); + + if (read(P->asfd, utsp, ubytes) != ubytes) { + dprintf("Pgrab_core: failed to read NT_UTSNAME\n"); + free(utsp); + return (-1); + } + + if (_libproc_debug) { + dprintf("uts.sysname = \"%s\"\n", utsp->sysname); + dprintf("uts.nodename = \"%s\"\n", utsp->nodename); + dprintf("uts.release = \"%s\"\n", utsp->release); + dprintf("uts.version = \"%s\"\n", utsp->version); + dprintf("uts.machine = \"%s\"\n", utsp->machine); + } + + P->core->core_uts = utsp; + return (0); +} + +static int +note_content(struct ps_prochandle *P, size_t nbytes) +{ + core_content_t content; + + if (sizeof (P->core->core_content) != nbytes) + return (-1); + + if (read(P->asfd, &content, sizeof (content)) != sizeof (content)) + return (-1); + + P->core->core_content = content; + + dprintf("core content = %llx\n", content); + + return (0); +} + +static int +note_cred(struct ps_prochandle *P, size_t nbytes) +{ + prcred_t *pcrp; + int ngroups; + const size_t min_size = sizeof (prcred_t) - sizeof (gid_t); + + /* + * We allow for prcred_t notes that are actually smaller than a + * prcred_t since the last member isn't essential if there are + * no group memberships. This allows for more flexibility when it + * comes to slightly malformed -- but still valid -- notes. + */ + if (P->core->core_cred != NULL || nbytes < min_size) + return (0); /* Already seen or bad size */ + + ngroups = (nbytes - min_size) / sizeof (gid_t); + nbytes = sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t); + + if ((pcrp = malloc(nbytes)) == NULL) + return (-1); + + if (read(P->asfd, pcrp, nbytes) != nbytes) { + dprintf("Pgrab_core: failed to read NT_PRCRED\n"); + free(pcrp); + return (-1); + } + + if (pcrp->pr_ngroups > ngroups) { + dprintf("pr_ngroups = %d; resetting to %d based on note size\n", + pcrp->pr_ngroups, ngroups); + pcrp->pr_ngroups = ngroups; + } + + P->core->core_cred = pcrp; + return (0); +} + +#if defined(__i386) || defined(__amd64) +static int +note_ldt(struct ps_prochandle *P, size_t nbytes) +{ + struct ssd *pldt; + uint_t nldt; + + if (P->core->core_ldt != NULL || nbytes < sizeof (struct ssd)) + return (0); /* Already seen or bad size */ + + nldt = nbytes / sizeof (struct ssd); + nbytes = nldt * sizeof (struct ssd); + + if ((pldt = malloc(nbytes)) == NULL) + return (-1); + + if (read(P->asfd, pldt, nbytes) != nbytes) { + dprintf("Pgrab_core: failed to read NT_LDT\n"); + free(pldt); + return (-1); + } + + P->core->core_ldt = pldt; + P->core->core_nldt = nldt; + return (0); +} +#endif /* __i386 */ + +static int +note_priv(struct ps_prochandle *P, size_t nbytes) +{ + prpriv_t *pprvp; + + if (P->core->core_priv != NULL || nbytes < sizeof (prpriv_t)) + return (0); /* Already seen or bad size */ + + if ((pprvp = malloc(nbytes)) == NULL) + return (-1); + + if (read(P->asfd, pprvp, nbytes) != nbytes) { + dprintf("Pgrab_core: failed to read NT_PRPRIV\n"); + free(pprvp); + return (-1); + } + + P->core->core_priv = pprvp; + P->core->core_priv_size = nbytes; + return (0); +} + +static int +note_priv_info(struct ps_prochandle *P, size_t nbytes) +{ + extern void *__priv_parse_info(); + priv_impl_info_t *ppii; + + if (P->core->core_privinfo != NULL || + nbytes < sizeof (priv_impl_info_t)) + return (0); /* Already seen or bad size */ + + if ((ppii = malloc(nbytes)) == NULL) + return (-1); + + if (read(P->asfd, ppii, nbytes) != nbytes || + PRIV_IMPL_INFO_SIZE(ppii) != nbytes) { + dprintf("Pgrab_core: failed to read NT_PRPRIVINFO\n"); + free(ppii); + return (-1); + } + + P->core->core_privinfo = __priv_parse_info(ppii); + P->core->core_ppii = ppii; + return (0); +} + +static int +note_zonename(struct ps_prochandle *P, size_t nbytes) +{ + char *zonename; + + if (P->core->core_zonename != NULL) + return (0); /* Already seen */ + + if (nbytes != 0) { + if ((zonename = malloc(nbytes)) == NULL) + return (-1); + if (read(P->asfd, zonename, nbytes) != nbytes) { + dprintf("Pgrab_core: failed to read NT_ZONENAME\n"); + free(zonename); + return (-1); + } + zonename[nbytes - 1] = '\0'; + P->core->core_zonename = zonename; + } + + return (0); +} + +static int +note_auxv(struct ps_prochandle *P, size_t nbytes) +{ + size_t n, i; + +#ifdef _LP64 + if (P->core->core_dmodel == PR_MODEL_ILP32) { + auxv32_t *a32; + + n = nbytes / sizeof (auxv32_t); + nbytes = n * sizeof (auxv32_t); + a32 = alloca(nbytes); + + if (read(P->asfd, a32, nbytes) != nbytes) { + dprintf("Pgrab_core: failed to read NT_AUXV\n"); + return (-1); + } + + if ((P->auxv = malloc(sizeof (auxv_t) * (n + 1))) == NULL) + return (-1); + + for (i = 0; i < n; i++) + auxv_32_to_n(&a32[i], &P->auxv[i]); + + } else { +#endif + n = nbytes / sizeof (auxv_t); + nbytes = n * sizeof (auxv_t); + + if ((P->auxv = malloc(nbytes + sizeof (auxv_t))) == NULL) + return (-1); + + if (read(P->asfd, P->auxv, nbytes) != nbytes) { + free(P->auxv); + P->auxv = NULL; + return (-1); + } +#ifdef _LP64 + } +#endif + + if (_libproc_debug) { + for (i = 0; i < n; i++) { + dprintf("P->auxv[%lu] = ( %d, 0x%lx )\n", (ulong_t)i, + P->auxv[i].a_type, P->auxv[i].a_un.a_val); + } + } + + /* + * Defensive coding for loops which depend upon the auxv array being + * terminated by an AT_NULL element; in each case, we've allocated + * P->auxv to have an additional element which we force to be AT_NULL. + */ + P->auxv[n].a_type = AT_NULL; + P->auxv[n].a_un.a_val = 0L; + P->nauxv = (int)n; + + return (0); +} + +#ifdef __sparc +static int +note_xreg(struct ps_prochandle *P, size_t nbytes) +{ + lwp_info_t *lwp = P->core->core_lwp; + size_t xbytes = sizeof (prxregset_t); + prxregset_t *xregs; + + if (lwp == NULL || lwp->lwp_xregs != NULL || nbytes < xbytes) + return (0); /* No lwp yet, already seen, or bad size */ + + if ((xregs = malloc(xbytes)) == NULL) + return (-1); + + if (read(P->asfd, xregs, xbytes) != xbytes) { + dprintf("Pgrab_core: failed to read NT_PRXREG\n"); + free(xregs); + return (-1); + } + + lwp->lwp_xregs = xregs; + return (0); +} + +static int +note_gwindows(struct ps_prochandle *P, size_t nbytes) +{ + lwp_info_t *lwp = P->core->core_lwp; + + if (lwp == NULL || lwp->lwp_gwins != NULL || nbytes == 0) + return (0); /* No lwp yet or already seen or no data */ + + if ((lwp->lwp_gwins = malloc(sizeof (gwindows_t))) == NULL) + return (-1); + + /* + * Since the amount of gwindows data varies with how many windows were + * actually saved, we just read up to the minimum of the note size + * and the size of the gwindows_t type. It doesn't matter if the read + * fails since we have to zero out gwindows first anyway. + */ +#ifdef _LP64 + if (P->core->core_dmodel == PR_MODEL_ILP32) { + gwindows32_t g32; + + (void) memset(&g32, 0, sizeof (g32)); + (void) read(P->asfd, &g32, MIN(nbytes, sizeof (g32))); + gwindows_32_to_n(&g32, lwp->lwp_gwins); + + } else { +#endif + (void) memset(lwp->lwp_gwins, 0, sizeof (gwindows_t)); + (void) read(P->asfd, lwp->lwp_gwins, + MIN(nbytes, sizeof (gwindows_t))); +#ifdef _LP64 + } +#endif + return (0); +} + +#ifdef __sparcv9 +static int +note_asrs(struct ps_prochandle *P, size_t nbytes) +{ + lwp_info_t *lwp = P->core->core_lwp; + int64_t *asrs; + + if (lwp == NULL || lwp->lwp_asrs != NULL || nbytes < sizeof (asrset_t)) + return (0); /* No lwp yet, already seen, or bad size */ + + if ((asrs = malloc(sizeof (asrset_t))) == NULL) + return (-1); + + if (read(P->asfd, asrs, sizeof (asrset_t)) != sizeof (asrset_t)) { + dprintf("Pgrab_core: failed to read NT_ASRS\n"); + free(asrs); + return (-1); + } + + lwp->lwp_asrs = asrs; + return (0); +} +#endif /* __sparcv9 */ +#endif /* __sparc */ + +/*ARGSUSED*/ +static int +note_notsup(struct ps_prochandle *P, size_t nbytes) +{ + dprintf("skipping unsupported note type\n"); + return (0); +} + +/* + * Populate a table of function pointers indexed by Note type with our + * functions to process each type of core file note: + */ +static int (*nhdlrs[])(struct ps_prochandle *, size_t) = { + note_notsup, /* 0 unassigned */ + note_notsup, /* 1 NT_PRSTATUS (old) */ + note_notsup, /* 2 NT_PRFPREG (old) */ + note_notsup, /* 3 NT_PRPSINFO (old) */ +#ifdef __sparc + note_xreg, /* 4 NT_PRXREG */ +#else + note_notsup, /* 4 NT_PRXREG */ +#endif + note_platform, /* 5 NT_PLATFORM */ + note_auxv, /* 6 NT_AUXV */ +#ifdef __sparc + note_gwindows, /* 7 NT_GWINDOWS */ +#ifdef __sparcv9 + note_asrs, /* 8 NT_ASRS */ +#else + note_notsup, /* 8 NT_ASRS */ +#endif +#else + note_notsup, /* 7 NT_GWINDOWS */ + note_notsup, /* 8 NT_ASRS */ +#endif +#if defined(__i386) || defined(__amd64) + note_ldt, /* 9 NT_LDT */ +#else + note_notsup, /* 9 NT_LDT */ +#endif + note_pstatus, /* 10 NT_PSTATUS */ + note_notsup, /* 11 unassigned */ + note_notsup, /* 12 unassigned */ + note_psinfo, /* 13 NT_PSINFO */ + note_cred, /* 14 NT_PRCRED */ + note_utsname, /* 15 NT_UTSNAME */ + note_lwpstatus, /* 16 NT_LWPSTATUS */ + note_lwpsinfo, /* 17 NT_LWPSINFO */ + note_priv, /* 18 NT_PRPRIV */ + note_priv_info, /* 19 NT_PRPRIVINFO */ + note_content, /* 20 NT_CONTENT */ + note_zonename, /* 21 NT_ZONENAME */ +}; + +/* + * Add information on the address space mapping described by the given + * PT_LOAD program header. We fill in more information on the mapping later. + */ +static int +core_add_mapping(struct ps_prochandle *P, GElf_Phdr *php) +{ + int err = 0; + prmap_t pmap; + + dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n", + (u_longlong_t)php->p_vaddr, (u_longlong_t)php->p_filesz, + (u_longlong_t)php->p_memsz, (u_longlong_t)php->p_offset); + + pmap.pr_vaddr = (uintptr_t)php->p_vaddr; + pmap.pr_size = php->p_memsz; + + /* + * If Pgcore() or elfcore() fail to write a mapping, they will set + * PF_SUNW_FAILURE in the Phdr and try to stash away the errno for us. + */ + if (php->p_flags & PF_SUNW_FAILURE) { + (void) pread64(P->asfd, &err, + sizeof (err), (off64_t)php->p_offset); + + Perror_printf(P, "core file data for mapping at %p not saved: " + "%s\n", (void *)(uintptr_t)php->p_vaddr, strerror(err)); + dprintf("core file data for mapping at %p not saved: %s\n", + (void *)(uintptr_t)php->p_vaddr, strerror(err)); + + } else if (php->p_filesz != 0 && php->p_offset >= P->core->core_size) { + Perror_printf(P, "core file may be corrupt -- data for mapping " + "at %p is missing\n", (void *)(uintptr_t)php->p_vaddr); + dprintf("core file may be corrupt -- data for mapping " + "at %p is missing\n", (void *)(uintptr_t)php->p_vaddr); + } + + /* + * The mapping name and offset will hopefully be filled in + * by the librtld_db agent. Unfortunately, if it isn't a + * shared library mapping, this information is gone forever. + */ + pmap.pr_mapname[0] = '\0'; + pmap.pr_offset = 0; + + pmap.pr_mflags = 0; + if (php->p_flags & PF_R) + pmap.pr_mflags |= MA_READ; + if (php->p_flags & PF_W) + pmap.pr_mflags |= MA_WRITE; + if (php->p_flags & PF_X) + pmap.pr_mflags |= MA_EXEC; + + if (php->p_filesz == 0) + pmap.pr_mflags |= MA_RESERVED1; + + /* + * At the time of adding this mapping, we just zero the pagesize. + * Once we've processed more of the core file, we'll have the + * pagesize from the auxv's AT_PAGESZ element and we can fill this in. + */ + pmap.pr_pagesize = 0; + + /* + * Unfortunately whether or not the mapping was a System V + * shared memory segment is lost. We use -1 to mark it as not shm. + */ + pmap.pr_shmid = -1; + + return (Padd_mapping(P, php->p_offset, NULL, &pmap)); +} + +/* + * Given a virtual address, name the mapping at that address using the + * specified name, and return the map_info_t pointer. + */ +static map_info_t * +core_name_mapping(struct ps_prochandle *P, uintptr_t addr, const char *name) +{ + map_info_t *mp = Paddr2mptr(P, addr); + + if (mp != NULL) { + (void) strncpy(mp->map_pmap.pr_mapname, name, PRMAPSZ); + mp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; + } + + return (mp); +} + +/* + * libproc uses libelf for all of its symbol table manipulation. This function + * takes a symbol table and string table from a core file and places them + * in a memory backed elf file. + */ +static void +fake_up_symtab(struct ps_prochandle *P, GElf_Ehdr *ehdr, + GElf_Shdr *symtab, GElf_Shdr *strtab) +{ + size_t size; + off64_t off, base; + map_info_t *mp; + file_info_t *fp; + Elf_Scn *scn; + Elf_Data *data; + + if (symtab->sh_addr == 0 || + (mp = Paddr2mptr(P, symtab->sh_addr)) == NULL || + (fp = mp->map_file) == NULL || + fp->file_symtab.sym_data != NULL) + return; + + if (P->status.pr_dmodel == PR_MODEL_ILP32) { + struct { + Elf32_Ehdr ehdr; + Elf32_Shdr shdr[3]; + char data[1]; + } *b; + + base = sizeof (b->ehdr) + sizeof (b->shdr); + size = base + symtab->sh_size + strtab->sh_size; + + if ((b = calloc(1, size)) == NULL) + return; + + (void) memcpy(&b->ehdr, ehdr, offsetof(GElf_Ehdr, e_entry)); + b->ehdr.e_ehsize = sizeof (b->ehdr); + b->ehdr.e_shoff = sizeof (b->ehdr); + b->ehdr.e_shentsize = sizeof (b->shdr[0]); + b->ehdr.e_shnum = 3; + off = 0; + + b->shdr[1].sh_size = symtab->sh_size; + b->shdr[1].sh_type = SHT_SYMTAB; + b->shdr[1].sh_offset = off + base; + b->shdr[1].sh_entsize = sizeof (Elf32_Sym); + b->shdr[1].sh_link = 2; + b->shdr[1].sh_info = symtab->sh_info; + b->shdr[1].sh_addralign = symtab->sh_addralign; + + if (pread64(P->asfd, &b->data[off], b->shdr[1].sh_size, + symtab->sh_offset) != b->shdr[1].sh_size) { + free(b); + return; + } + + off += b->shdr[1].sh_size; + + b->shdr[2].sh_flags = SHF_STRINGS; + b->shdr[2].sh_size = strtab->sh_size; + b->shdr[2].sh_type = SHT_STRTAB; + b->shdr[2].sh_offset = off + base; + b->shdr[2].sh_info = strtab->sh_info; + b->shdr[2].sh_addralign = 1; + + if (pread64(P->asfd, &b->data[off], b->shdr[2].sh_size, + strtab->sh_offset) != b->shdr[2].sh_size) { + free(b); + return; + } + + off += b->shdr[2].sh_size; + + fp->file_symtab.sym_elf = elf_memory((char *)b, size); + if (fp->file_symtab.sym_elf == NULL) { + free(b); + return; + } + + fp->file_symtab.sym_elfmem = b; +#ifdef _LP64 + } else { + struct { + Elf64_Ehdr ehdr; + Elf64_Shdr shdr[3]; + char data[1]; + } *b; + + base = sizeof (b->ehdr) + sizeof (b->shdr); + size = base + symtab->sh_size + strtab->sh_size; + + if ((b = calloc(1, size)) == NULL) + return; + + (void) memcpy(&b->ehdr, ehdr, offsetof(GElf_Ehdr, e_entry)); + b->ehdr.e_ehsize = sizeof (b->ehdr); + b->ehdr.e_shoff = sizeof (b->ehdr); + b->ehdr.e_shentsize = sizeof (b->shdr[0]); + b->ehdr.e_shnum = 3; + off = 0; + + b->shdr[1].sh_size = symtab->sh_size; + b->shdr[1].sh_type = SHT_SYMTAB; + b->shdr[1].sh_offset = off + base; + b->shdr[1].sh_entsize = sizeof (Elf64_Sym); + b->shdr[1].sh_link = 2; + b->shdr[1].sh_info = symtab->sh_info; + b->shdr[1].sh_addralign = symtab->sh_addralign; + + if (pread64(P->asfd, &b->data[off], b->shdr[1].sh_size, + symtab->sh_offset) != b->shdr[1].sh_size) { + free(b); + return; + } + + off += b->shdr[1].sh_size; + + b->shdr[2].sh_flags = SHF_STRINGS; + b->shdr[2].sh_size = strtab->sh_size; + b->shdr[2].sh_type = SHT_STRTAB; + b->shdr[2].sh_offset = off + base; + b->shdr[2].sh_info = strtab->sh_info; + b->shdr[2].sh_addralign = 1; + + if (pread64(P->asfd, &b->data[off], b->shdr[2].sh_size, + strtab->sh_offset) != b->shdr[2].sh_size) { + free(b); + return; + } + + off += b->shdr[2].sh_size; + + fp->file_symtab.sym_elf = elf_memory((char *)b, size); + if (fp->file_symtab.sym_elf == NULL) { + free(b); + return; + } + + fp->file_symtab.sym_elfmem = b; +#endif + } + + if ((scn = elf_getscn(fp->file_symtab.sym_elf, 1)) == NULL || + (fp->file_symtab.sym_data = elf_getdata(scn, NULL)) == NULL || + (scn = elf_getscn(fp->file_symtab.sym_elf, 2)) == NULL || + (data = elf_getdata(scn, NULL)) == NULL) + goto err; + + fp->file_symtab.sym_strs = data->d_buf; + fp->file_symtab.sym_strsz = data->d_size; + fp->file_symtab.sym_symn = symtab->sh_size / symtab->sh_entsize; + fp->file_symtab.sym_hdr = *symtab; + fp->file_symtab.sym_strhdr = *strtab; + + optimize_symtab(&fp->file_symtab); + + return; +err: + (void) elf_end(fp->file_symtab.sym_elf); + free(fp->file_symtab.sym_elfmem); + fp->file_symtab.sym_elf = NULL; + fp->file_symtab.sym_elfmem = NULL; +} + +static void +core_ehdr_to_gelf(const Elf32_Ehdr *src, GElf_Ehdr *dst) +{ + (void) memcpy(dst->e_ident, src->e_ident, EI_NIDENT); + dst->e_type = src->e_type; + dst->e_machine = src->e_machine; + dst->e_version = src->e_version; + dst->e_entry = (Elf64_Addr)src->e_entry; + dst->e_phoff = (Elf64_Off)src->e_phoff; + dst->e_shoff = (Elf64_Off)src->e_shoff; + dst->e_flags = src->e_flags; + dst->e_ehsize = src->e_ehsize; + dst->e_phentsize = src->e_phentsize; + dst->e_phnum = src->e_phnum; + dst->e_shentsize = src->e_shentsize; + dst->e_shnum = src->e_shnum; + dst->e_shstrndx = src->e_shstrndx; +} + +static void +core_phdr_to_gelf(const Elf32_Phdr *src, GElf_Phdr *dst) +{ + dst->p_type = src->p_type; + dst->p_flags = src->p_flags; + dst->p_offset = (Elf64_Off)src->p_offset; + dst->p_vaddr = (Elf64_Addr)src->p_vaddr; + dst->p_paddr = (Elf64_Addr)src->p_paddr; + dst->p_filesz = (Elf64_Xword)src->p_filesz; + dst->p_memsz = (Elf64_Xword)src->p_memsz; + dst->p_align = (Elf64_Xword)src->p_align; +} + +static void +core_shdr_to_gelf(const Elf32_Shdr *src, GElf_Shdr *dst) +{ + dst->sh_name = src->sh_name; + dst->sh_type = src->sh_type; + dst->sh_flags = (Elf64_Xword)src->sh_flags; + dst->sh_addr = (Elf64_Addr)src->sh_addr; + dst->sh_offset = (Elf64_Off)src->sh_offset; + dst->sh_size = (Elf64_Xword)src->sh_size; + dst->sh_link = src->sh_link; + dst->sh_info = src->sh_info; + dst->sh_addralign = (Elf64_Xword)src->sh_addralign; + dst->sh_entsize = (Elf64_Xword)src->sh_entsize; +} + +/* + * Perform elf_begin on efp->e_fd and verify the ELF file's type and class. + */ +static int +core_elf_fdopen(elf_file_t *efp, GElf_Half type, int *perr) +{ +#ifdef _BIG_ENDIAN + uchar_t order = ELFDATA2MSB; +#else + uchar_t order = ELFDATA2LSB; +#endif + Elf32_Ehdr e32; + int is_noelf = -1; + int isa_err = 0; + + /* + * Because 32-bit libelf cannot deal with large files, we need to read, + * check, and convert the file header manually in case type == ET_CORE. + */ + if (pread64(efp->e_fd, &e32, sizeof (e32), 0) != sizeof (e32)) { + if (perr != NULL) + *perr = G_FORMAT; + goto err; + } + if ((is_noelf = memcmp(&e32.e_ident[EI_MAG0], ELFMAG, SELFMAG)) != 0 || + e32.e_type != type || (isa_err = (e32.e_ident[EI_DATA] != order)) || + e32.e_version != EV_CURRENT) { + if (perr != NULL) { + if (is_noelf == 0 && isa_err) { + *perr = G_ISAINVAL; + } else { + *perr = G_FORMAT; + } + } + goto err; + } + + /* + * If the file is 64-bit and we are 32-bit, fail with G_LP64. If the + * file is 64-bit and we are 64-bit, re-read the header as a Elf64_Ehdr. + * Otherwise, the file is 32-bit, so convert e32 to a GElf_Ehdr. + */ + if (e32.e_ident[EI_CLASS] == ELFCLASS64) { +#ifdef _LP64 + if (pread64(efp->e_fd, &efp->e_hdr, + sizeof (GElf_Ehdr), 0) != sizeof (GElf_Ehdr)) { + if (perr != NULL) + *perr = G_FORMAT; + goto err; + } +#else /* _LP64 */ + if (perr != NULL) + *perr = G_LP64; + goto err; +#endif /* _LP64 */ + } else + core_ehdr_to_gelf(&e32, &efp->e_hdr); + + /* + * The libelf implementation was never ported to be large-file aware. + * This is typically not a problem for your average executable or + * shared library, but a large 32-bit core file can exceed 2GB in size. + * So if type is ET_CORE, we don't bother doing elf_begin; the code + * in Pfgrab_core() below will do its own i/o and struct conversion. + */ + + if (type == ET_CORE) { + efp->e_elf = NULL; + return (0); + } + + if ((efp->e_elf = elf_begin(efp->e_fd, ELF_C_READ, NULL)) == NULL) { + if (perr != NULL) + *perr = G_ELF; + goto err; + } + + return (0); + +err: + efp->e_elf = NULL; + return (-1); +} + +/* + * Open the specified file and then do a core_elf_fdopen on it. + */ +static int +core_elf_open(elf_file_t *efp, const char *path, GElf_Half type, int *perr) +{ + (void) memset(efp, 0, sizeof (elf_file_t)); + + if ((efp->e_fd = open64(path, O_RDONLY)) >= 0) { + if (core_elf_fdopen(efp, type, perr) == 0) + return (0); + + (void) close(efp->e_fd); + efp->e_fd = -1; + } + + return (-1); +} + +/* + * Close the ELF handle and file descriptor. + */ +static void +core_elf_close(elf_file_t *efp) +{ + if (efp->e_elf != NULL) { + (void) elf_end(efp->e_elf); + efp->e_elf = NULL; + } + + if (efp->e_fd != -1) { + (void) close(efp->e_fd); + efp->e_fd = -1; + } +} + +/* + * Given an ELF file for a statically linked executable, locate the likely + * primary text section and fill in rl_base with its virtual address. + */ +static map_info_t * +core_find_text(struct ps_prochandle *P, Elf *elf, rd_loadobj_t *rlp) +{ + GElf_Ehdr ehdr; + GElf_Phdr phdr; + uint_t i; + + if (gelf_getehdr(elf, &ehdr) != NULL) { + for (i = 0; i < ehdr.e_phnum; i++) { + if (gelf_getphdr(elf, i, &phdr) != NULL && + phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) { + rlp->rl_base = phdr.p_vaddr; + return (Paddr2mptr(P, rlp->rl_base)); + } + } + } + + return (NULL); +} + +/* + * Given an ELF file and the librtld_db structure corresponding to its primary + * text mapping, deduce where its data segment was loaded and fill in + * rl_data_base and prmap_t.pr_offset accordingly. + */ +static map_info_t * +core_find_data(struct ps_prochandle *P, Elf *elf, rd_loadobj_t *rlp) +{ + GElf_Ehdr ehdr; + GElf_Phdr phdr; + + map_info_t *mp; + uint_t i, pagemask; + + rlp->rl_data_base = NULL; + + /* + * Find the first loadable, writeable Phdr and compute rl_data_base + * as the virtual address at which is was loaded. + */ + if (gelf_getehdr(elf, &ehdr) != NULL) { + for (i = 0; i < ehdr.e_phnum; i++) { + if (gelf_getphdr(elf, i, &phdr) != NULL && + phdr.p_type == PT_LOAD && (phdr.p_flags & PF_W)) { + + rlp->rl_data_base = phdr.p_vaddr; + if (ehdr.e_type == ET_DYN) + rlp->rl_data_base += rlp->rl_base; + break; + } + } + } + + /* + * If we didn't find an appropriate phdr or if the address we + * computed has no mapping, return NULL. + */ + if (rlp->rl_data_base == NULL || + (mp = Paddr2mptr(P, rlp->rl_data_base)) == NULL) + return (NULL); + + /* + * It wouldn't be procfs-related code if we didn't make use of + * unclean knowledge of segvn, even in userland ... the prmap_t's + * pr_offset field will be the segvn offset from mmap(2)ing the + * data section, which will be the file offset & PAGEMASK. + */ + pagemask = ~(mp->map_pmap.pr_pagesize - 1); + mp->map_pmap.pr_offset = phdr.p_offset & pagemask; + + return (mp); +} + +/* + * Librtld_db agent callback for iterating over load object mappings. + * For each load object, we allocate a new file_info_t, perform naming, + * and attempt to construct a symbol table for the load object. + */ +static int +core_iter_mapping(const rd_loadobj_t *rlp, struct ps_prochandle *P) +{ + char lname[PATH_MAX]; + file_info_t *fp; + map_info_t *mp; + + if (Pread_string(P, lname, PATH_MAX, (off_t)rlp->rl_nameaddr) <= 0) { + dprintf("failed to read name %p\n", (void *)rlp->rl_nameaddr); + return (1); /* Keep going; forget this if we can't get a name */ + } + + dprintf("rd_loadobj name = \"%s\" rl_base = %p\n", + lname, (void *)rlp->rl_base); + + if ((mp = Paddr2mptr(P, rlp->rl_base)) == NULL) { + dprintf("no mapping for %p\n", (void *)rlp->rl_base); + return (1); /* No mapping; advance to next mapping */ + } + + if ((fp = mp->map_file) == NULL) { + if ((fp = malloc(sizeof (file_info_t))) == NULL) { + P->core->core_errno = errno; + dprintf("failed to malloc mapping data\n"); + return (0); /* Abort */ + } + + (void) memset(fp, 0, sizeof (file_info_t)); + + list_link(fp, &P->file_head); + mp->map_file = fp; + P->num_files++; + + fp->file_ref = 1; + fp->file_fd = -1; + } + + if ((fp->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) { + P->core->core_errno = errno; + dprintf("failed to malloc mapping data\n"); + return (0); /* Abort */ + } + + *fp->file_lo = *rlp; + + if (fp->file_lname == NULL && + strcmp(mp->map_pmap.pr_mapname, "a.out") == 0) { + /* + * Naming dance part 1: if the file_info_t is unnamed and + * it represents the main executable, name it after the + * execname. + */ + fp->file_lname = P->execname ? + strdup(P->execname) : strdup("a.out"); + } + + if (lname[0] != '\0') { + /* + * Naming dance part 2: if we got a name from librtld_db, then + * copy this name to the prmap_t if it is unnamed. If the + * file_info_t is unnamed, name it after the lname. + */ + if (mp->map_pmap.pr_mapname[0] == '\0') { + (void) strncpy(mp->map_pmap.pr_mapname, lname, PRMAPSZ); + mp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; + } + + if (fp->file_lname == NULL) + fp->file_lname = strdup(lname); + + } else if (fp->file_lname == NULL && + mp->map_pmap.pr_mapname[0] != '\0') { + /* + * Naming dance part 3: if the mapping is named and the + * file_info_t is not, name the file after the mapping. + */ + fp->file_lname = strdup(mp->map_pmap.pr_mapname); + } + + if (fp->file_lname != NULL) + fp->file_lbase = basename(fp->file_lname); + + /* + * Associate the file and the mapping, and attempt to build + * a symbol table for this file. + */ + (void) strcpy(fp->file_pname, mp->map_pmap.pr_mapname); + fp->file_map = mp; + + Pbuild_file_symtab(P, fp); + + if (fp->file_elf == NULL) + return (1); /* No symbol table; advance to next mapping */ + + /* + * Locate the start of a data segment associated with this file, + * name it after the file, and establish the mp->map_file link: + */ + if ((mp = core_find_data(P, fp->file_elf, fp->file_lo)) != NULL) { + dprintf("found data for %s at %p (pr_offset 0x%llx)\n", + fp->file_pname, (void *)fp->file_lo->rl_data_base, + mp->map_pmap.pr_offset); + + for (; mp < P->mappings + P->map_count; mp++) { + if (mp->map_pmap.pr_vaddr > fp->file_lo->rl_bend) + break; + if (mp->map_file == NULL) { + mp->map_file = fp; + fp->file_ref++; + } + + if (!(mp->map_pmap.pr_mflags & MA_BREAK)) + (void) strcpy(mp->map_pmap.pr_mapname, + fp->file_pname); + } + } + + return (1); /* Advance to next mapping */ +} + +/* + * Callback function for Pfindexec(). In order to confirm a given pathname, + * we verify that we can open it as an ELF file of type ET_EXEC. + */ +static int +core_exec_open(const char *path, void *efp) +{ + return (core_elf_open(efp, path, ET_EXEC, NULL) == 0); +} + +/* + * Attempt to load any section headers found in the core file. If present, + * this will refer to non-loadable data added to the core file by the kernel + * based on coreadm(1M) settings, including CTF data and the symbol table. + */ +static void +core_load_shdrs(struct ps_prochandle *P, elf_file_t *efp) +{ + GElf_Shdr *shp, *shdrs = NULL; + char *shstrtab = NULL; + ulong_t shstrtabsz; + const char *name; + map_info_t *mp; + + size_t nbytes; + void *buf; + int i; + + if (efp->e_hdr.e_shstrndx >= efp->e_hdr.e_shnum) { + dprintf("corrupt shstrndx (%u) exceeds shnum (%u)\n", + (uint_t)efp->e_hdr.e_shstrndx, (uint_t)efp->e_hdr.e_shnum); + return; + } + + /* + * Read the section header table from the core file and then iterate + * over the section headers, converting each to a GElf_Shdr. + */ + shdrs = malloc(efp->e_hdr.e_shnum * sizeof (GElf_Shdr)); + nbytes = efp->e_hdr.e_shnum * efp->e_hdr.e_shentsize; + buf = malloc(nbytes); + + if (shdrs == NULL || buf == NULL) { + dprintf("failed to malloc %u section headers: %s\n", + (uint_t)efp->e_hdr.e_shnum, strerror(errno)); + free(buf); + goto out; + } + + if (pread64(efp->e_fd, buf, nbytes, efp->e_hdr.e_shoff) != nbytes) { + dprintf("failed to read section headers at off %lld: %s\n", + (longlong_t)efp->e_hdr.e_shoff, strerror(errno)); + free(buf); + goto out; + } + + for (i = 0; i < efp->e_hdr.e_shnum; i++) { + void *p = (uchar_t *)buf + efp->e_hdr.e_shentsize * i; + + if (efp->e_hdr.e_ident[EI_CLASS] == ELFCLASS32) + core_shdr_to_gelf(p, &shdrs[i]); + else + (void) memcpy(&shdrs[i], p, sizeof (GElf_Shdr)); + } + + free(buf); + buf = NULL; + + /* + * Read the .shstrtab section from the core file, terminating it with + * an extra \0 so that a corrupt section will not cause us to die. + */ + shp = &shdrs[efp->e_hdr.e_shstrndx]; + shstrtabsz = shp->sh_size; + + if ((shstrtab = malloc(shstrtabsz + 1)) == NULL) { + dprintf("failed to allocate %lu bytes for shstrtab\n", + (ulong_t)shstrtabsz); + goto out; + } + + if (pread64(efp->e_fd, shstrtab, shstrtabsz, + shp->sh_offset) != shstrtabsz) { + dprintf("failed to read %lu bytes of shstrs at off %lld: %s\n", + shstrtabsz, (longlong_t)shp->sh_offset, strerror(errno)); + goto out; + } + + shstrtab[shstrtabsz] = '\0'; + + /* + * Now iterate over each section in the section header table, locating + * sections of interest and initializing more of the ps_prochandle. + */ + for (i = 0; i < efp->e_hdr.e_shnum; i++) { + shp = &shdrs[i]; + name = shstrtab + shp->sh_name; + + if (shp->sh_name >= shstrtabsz) { + dprintf("skipping section [%d]: corrupt sh_name\n", i); + continue; + } + + if (shp->sh_link >= efp->e_hdr.e_shnum) { + dprintf("skipping section [%d]: corrupt sh_link\n", i); + continue; + } + + dprintf("found section header %s (sh_addr 0x%llx)\n", + name, (u_longlong_t)shp->sh_addr); + + if (strcmp(name, ".SUNW_ctf") == 0) { + if ((mp = Paddr2mptr(P, shp->sh_addr)) == NULL) { + dprintf("no map at addr 0x%llx for %s [%d]\n", + (u_longlong_t)shp->sh_addr, name, i); + continue; + } + + if (mp->map_file == NULL || + mp->map_file->file_ctf_buf != NULL) { + dprintf("no mapping file or duplicate buffer " + "for %s [%d]\n", name, i); + continue; + } + + if ((buf = malloc(shp->sh_size)) == NULL || + pread64(efp->e_fd, buf, shp->sh_size, + shp->sh_offset) != shp->sh_size) { + dprintf("skipping section %s [%d]: %s\n", + name, i, strerror(errno)); + free(buf); + continue; + } + + mp->map_file->file_ctf_size = shp->sh_size; + mp->map_file->file_ctf_buf = buf; + + if (shdrs[shp->sh_link].sh_type == SHT_DYNSYM) + mp->map_file->file_ctf_dyn = 1; + + } else if (strcmp(name, ".symtab") == 0) { + fake_up_symtab(P, &efp->e_hdr, + shp, &shdrs[shp->sh_link]); + } + } +out: + free(shstrtab); + free(shdrs); +} + +/* + * Main engine for core file initialization: given an fd for the core file + * and an optional pathname, construct the ps_prochandle. The aout_path can + * either be a suggested executable pathname, or a suggested directory to + * use as a possible current working directory. + */ +struct ps_prochandle * +Pfgrab_core(int core_fd, const char *aout_path, int *perr) +{ + struct ps_prochandle *P; + map_info_t *stk_mp, *brk_mp; + const char *execname; + char *interp; + int i, notes, pagesize; + uintptr_t addr, base_addr; + struct stat64 stbuf; + void *phbuf, *php; + size_t nbytes; + + elf_file_t aout; + elf_file_t core; + + Elf_Scn *scn, *intp_scn = NULL; + Elf_Data *dp; + + GElf_Phdr phdr, note_phdr; + GElf_Shdr shdr; + GElf_Xword nleft; + + if (elf_version(EV_CURRENT) == EV_NONE) { + dprintf("libproc ELF version is more recent than libelf\n"); + *perr = G_ELF; + return (NULL); + } + + aout.e_elf = NULL; + aout.e_fd = -1; + + core.e_elf = NULL; + core.e_fd = core_fd; + + /* + * Allocate and initialize a ps_prochandle structure for the core. + * There are several key pieces of initialization here: + * + * 1. The PS_DEAD state flag marks this prochandle as a core file. + * PS_DEAD also thus prevents all operations which require state + * to be PS_STOP from operating on this handle. + * + * 2. We keep the core file fd in P->asfd since the core file contains + * the remnants of the process address space. + * + * 3. We set the P->info_valid bit because all information about the + * core is determined by the end of this function; there is no need + * for proc_update_maps() to reload mappings at any later point. + * + * 4. The read/write ops vector uses our core_rw() function defined + * above to handle i/o requests. + */ + if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) { + *perr = G_STRANGE; + return (NULL); + } + + (void) memset(P, 0, sizeof (struct ps_prochandle)); + (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL); + P->state = PS_DEAD; + P->pid = (pid_t)-1; + P->asfd = core.e_fd; + P->ctlfd = -1; + P->statfd = -1; + P->agentctlfd = -1; + P->agentstatfd = -1; + P->info_valid = 1; + P->ops = &P_core_ops; + + Pinitsym(P); + + /* + * Fstat and open the core file and make sure it is a valid ELF core. + */ + if (fstat64(P->asfd, &stbuf) == -1) { + *perr = G_STRANGE; + goto err; + } + + if (core_elf_fdopen(&core, ET_CORE, perr) == -1) + goto err; + + /* + * Allocate and initialize a core_info_t to hang off the ps_prochandle + * structure. We keep all core-specific information in this structure. + */ + if ((P->core = malloc(sizeof (core_info_t))) == NULL) { + *perr = G_STRANGE; + goto err; + } + + list_link(&P->core->core_lwp_head, NULL); + P->core->core_errno = 0; + P->core->core_lwp = NULL; + P->core->core_nlwp = 0; + P->core->core_size = stbuf.st_size; + P->core->core_platform = NULL; + P->core->core_uts = NULL; + P->core->core_cred = NULL; + /* + * In the days before adjustable core file content, this was the + * default core file content. For new core files, this value will + * be overwritten by the NT_CONTENT note section. + */ + P->core->core_content = CC_CONTENT_STACK | CC_CONTENT_HEAP | + CC_CONTENT_DATA | CC_CONTENT_RODATA | CC_CONTENT_ANON | + CC_CONTENT_SHANON; + P->core->core_priv = NULL; + P->core->core_priv_size = 0; + P->core->core_privinfo = NULL; + P->core->core_zonename = NULL; + P->core->core_ppii = NULL; + +#if defined(__i386) || defined(__amd64) + P->core->core_ldt = NULL; + P->core->core_nldt = 0; +#endif + + switch (core.e_hdr.e_ident[EI_CLASS]) { + case ELFCLASS32: + P->core->core_dmodel = PR_MODEL_ILP32; + break; + case ELFCLASS64: + P->core->core_dmodel = PR_MODEL_LP64; + break; + default: + *perr = G_FORMAT; + goto err; + } + + /* + * Because the core file may be a large file, we can't use libelf to + * read the Phdrs. We use e_phnum and e_phentsize to simplify things. + */ + nbytes = core.e_hdr.e_phnum * core.e_hdr.e_phentsize; + + if ((phbuf = malloc(nbytes)) == NULL) { + *perr = G_STRANGE; + goto err; + } + + if (pread64(core_fd, phbuf, nbytes, core.e_hdr.e_phoff) != nbytes) { + *perr = G_STRANGE; + free(phbuf); + goto err; + } + + /* + * Iterate through the program headers in the core file. + * We're interested in two types of Phdrs: PT_NOTE (which + * contains a set of saved /proc structures), and PT_LOAD (which + * represents a memory mapping from the process's address space). + * In the case of PT_NOTE, we're interested in the last PT_NOTE + * in the core file; currently the first PT_NOTE (if present) + * contains /proc structs in the pre-2.6 unstructured /proc format. + */ + for (php = phbuf, notes = 0, i = 0; i < core.e_hdr.e_phnum; i++) { + if (core.e_hdr.e_ident[EI_CLASS] == ELFCLASS64) + (void) memcpy(&phdr, php, sizeof (GElf_Phdr)); + else + core_phdr_to_gelf(php, &phdr); + + switch (phdr.p_type) { + case PT_NOTE: + note_phdr = phdr; + notes++; + break; + + case PT_LOAD: + if (core_add_mapping(P, &phdr) == -1) { + *perr = G_STRANGE; + free(phbuf); + goto err; + } + break; + } + + php = (char *)php + core.e_hdr.e_phentsize; + } + + free(phbuf); + + Psort_mappings(P); + + /* + * If we couldn't find anything of type PT_NOTE, or only one PT_NOTE + * was present, abort. The core file is either corrupt or too old. + */ + if (notes == 0 || notes == 1) { + *perr = G_NOTE; + goto err; + } + + /* + * Advance the seek pointer to the start of the PT_NOTE data + */ + if (lseek64(P->asfd, note_phdr.p_offset, SEEK_SET) == (off64_t)-1) { + dprintf("Pgrab_core: failed to lseek to PT_NOTE data\n"); + *perr = G_STRANGE; + goto err; + } + + /* + * Now process the PT_NOTE structures. Each one is preceded by + * an Elf{32/64}_Nhdr structure describing its type and size. + * + * +--------+ + * | header | + * +--------+ + * | name | + * | ... | + * +--------+ + * | desc | + * | ... | + * +--------+ + */ + for (nleft = note_phdr.p_filesz; nleft > 0; ) { + Elf64_Nhdr nhdr; + off64_t off, namesz; + + /* + * Although <sys/elf.h> defines both Elf32_Nhdr and Elf64_Nhdr + * as different types, they are both of the same content and + * size, so we don't need to worry about 32/64 conversion here. + */ + if (read(P->asfd, &nhdr, sizeof (nhdr)) != sizeof (nhdr)) { + dprintf("Pgrab_core: failed to read ELF note header\n"); + *perr = G_NOTE; + goto err; + } + + /* + * According to the System V ABI, the amount of padding + * following the name field should align the description + * field on a 4 byte boundary for 32-bit binaries or on an 8 + * byte boundary for 64-bit binaries. However, this change + * was not made correctly during the 64-bit port so all + * descriptions can assume only 4-byte alignment. We ignore + * the name field and the padding to 4-byte alignment. + */ + namesz = P2ROUNDUP((off64_t)nhdr.n_namesz, (off64_t)4); + if (lseek64(P->asfd, namesz, SEEK_CUR) == (off64_t)-1) { + dprintf("failed to seek past name and padding\n"); + *perr = G_STRANGE; + goto err; + } + + dprintf("Note hdr n_type=%u n_namesz=%u n_descsz=%u\n", + nhdr.n_type, nhdr.n_namesz, nhdr.n_descsz); + + off = lseek64(P->asfd, (off64_t)0L, SEEK_CUR); + + /* + * Invoke the note handler function from our table + */ + if (nhdr.n_type < sizeof (nhdlrs) / sizeof (nhdlrs[0])) { + if (nhdlrs[nhdr.n_type](P, nhdr.n_descsz) < 0) { + *perr = G_NOTE; + goto err; + } + } else + (void) note_notsup(P, nhdr.n_descsz); + + /* + * Seek past the current note data to the next Elf_Nhdr + */ + if (lseek64(P->asfd, off + nhdr.n_descsz, + SEEK_SET) == (off64_t)-1) { + dprintf("Pgrab_core: failed to seek to next nhdr\n"); + *perr = G_STRANGE; + goto err; + } + + /* + * Subtract the size of the header and its data from what + * we have left to process. + */ + nleft -= sizeof (nhdr) + namesz + nhdr.n_descsz; + } + + if (nleft != 0) { + dprintf("Pgrab_core: note section malformed\n"); + *perr = G_STRANGE; + goto err; + } + + if ((pagesize = Pgetauxval(P, AT_PAGESZ)) == -1) { + pagesize = getpagesize(); + dprintf("AT_PAGESZ missing; defaulting to %d\n", pagesize); + } + + /* + * Locate and label the mappings corresponding to the end of the + * heap (MA_BREAK) and the base of the stack (MA_STACK). + */ + if ((P->status.pr_brkbase != 0 || P->status.pr_brksize != 0) && + (brk_mp = Paddr2mptr(P, P->status.pr_brkbase + + P->status.pr_brksize - 1)) != NULL) + brk_mp->map_pmap.pr_mflags |= MA_BREAK; + else + brk_mp = NULL; + + if ((stk_mp = Paddr2mptr(P, P->status.pr_stkbase)) != NULL) + stk_mp->map_pmap.pr_mflags |= MA_STACK; + + /* + * At this point, we have enough information to look for the + * executable and open it: we have access to the auxv, a psinfo_t, + * and the ability to read from mappings provided by the core file. + */ + (void) Pfindexec(P, aout_path, core_exec_open, &aout); + dprintf("P->execname = \"%s\"\n", P->execname ? P->execname : "NULL"); + execname = P->execname ? P->execname : "a.out"; + + /* + * Iterate through the sections, looking for the .dynamic and .interp + * sections. If we encounter them, remember their section pointers. + */ + for (scn = NULL; (scn = elf_nextscn(aout.e_elf, scn)) != NULL; ) { + char *sname; + + if ((gelf_getshdr(scn, &shdr) == NULL) || + (sname = elf_strptr(aout.e_elf, aout.e_hdr.e_shstrndx, + (size_t)shdr.sh_name)) == NULL) + continue; + + if (strcmp(sname, ".interp") == 0) + intp_scn = scn; + } + + /* + * Get the AT_BASE auxv element. If this is missing (-1), then + * we assume this is a statically-linked executable. + */ + base_addr = Pgetauxval(P, AT_BASE); + + /* + * In order to get librtld_db initialized, we'll need to identify + * and name the mapping corresponding to the run-time linker. The + * AT_BASE auxv element tells us the address where it was mapped, + * and the .interp section of the executable tells us its path. + * If for some reason that doesn't pan out, just use ld.so.1. + */ + if (intp_scn != NULL && (dp = elf_getdata(intp_scn, NULL)) != NULL && + dp->d_size != 0) { + dprintf(".interp = <%s>\n", (char *)dp->d_buf); + interp = dp->d_buf; + + } else if (base_addr != (uintptr_t)-1L) { + if (P->core->core_dmodel == PR_MODEL_LP64) + interp = "/usr/lib/64/ld.so.1"; + else + interp = "/usr/lib/ld.so.1"; + + dprintf(".interp section is missing or could not be read; " + "defaulting to %s\n", interp); + } else + dprintf("detected statically linked executable\n"); + + /* + * If we have an AT_BASE element, name the mapping at that address + * using the interpreter pathname. Name the corresponding data + * mapping after the interpreter as well. + */ + if (base_addr != (uintptr_t)-1L) { + elf_file_t intf; + + P->map_ldso = core_name_mapping(P, base_addr, interp); + + if (core_elf_open(&intf, interp, ET_DYN, NULL) == 0) { + rd_loadobj_t rl; + map_info_t *dmp; + + rl.rl_base = base_addr; + dmp = core_find_data(P, intf.e_elf, &rl); + + if (dmp != NULL) { + dprintf("renamed data at %p to %s\n", + (void *)rl.rl_data_base, interp); + (void) strncpy(dmp->map_pmap.pr_mapname, + interp, PRMAPSZ); + dmp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; + } + } + + core_elf_close(&intf); + } + + /* + * If we have an AT_ENTRY element, name the mapping at that address + * using the special name "a.out" just like /proc does. + */ + if ((addr = Pgetauxval(P, AT_ENTRY)) != (uintptr_t)-1L) + P->map_exec = core_name_mapping(P, addr, "a.out"); + + /* + * If we're a statically linked executable, then just locate the + * executable's text and data and name them after the executable. + */ + if (base_addr == (uintptr_t)-1L) { + map_info_t *tmp, *dmp; + file_info_t *fp; + rd_loadobj_t rl; + + if ((tmp = core_find_text(P, aout.e_elf, &rl)) != NULL && + (dmp = core_find_data(P, aout.e_elf, &rl)) != NULL) { + (void) strncpy(tmp->map_pmap.pr_mapname, + execname, PRMAPSZ); + tmp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; + (void) strncpy(dmp->map_pmap.pr_mapname, + execname, PRMAPSZ); + dmp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; + } + + if ((P->map_exec = tmp) != NULL && + (fp = malloc(sizeof (file_info_t))) != NULL) { + + (void) memset(fp, 0, sizeof (file_info_t)); + + list_link(fp, &P->file_head); + tmp->map_file = fp; + P->num_files++; + + fp->file_ref = 1; + fp->file_fd = -1; + + fp->file_lo = malloc(sizeof (rd_loadobj_t)); + fp->file_lname = strdup(execname); + + if (fp->file_lo) + *fp->file_lo = rl; + if (fp->file_lname) + fp->file_lbase = basename(fp->file_lname); + + (void) strcpy(fp->file_pname, + P->mappings[0].map_pmap.pr_mapname); + fp->file_map = tmp; + + Pbuild_file_symtab(P, fp); + + if (dmp != NULL) { + dmp->map_file = fp; + fp->file_ref++; + } + } + } + + core_elf_close(&aout); + + /* + * We now have enough information to initialize librtld_db. + * After it warms up, we can iterate through the load object chain + * in the core, which will allow us to construct the file info + * we need to provide symbol information for the other shared + * libraries, and also to fill in the missing mapping names. + */ + rd_log(_libproc_debug); + + if ((P->rap = rd_new(P)) != NULL) { + (void) rd_loadobj_iter(P->rap, (rl_iter_f *) + core_iter_mapping, P); + + if (P->core->core_errno != 0) { + errno = P->core->core_errno; + *perr = G_STRANGE; + goto err; + } + } else + dprintf("failed to initialize rtld_db agent\n"); + + /* + * If there are sections, load them and process the data from any + * sections that we can use to annotate the file_info_t's. + */ + core_load_shdrs(P, &core); + + /* + * If we previously located a stack or break mapping, and they are + * still anonymous, we now assume that they were MAP_ANON mappings. + * If brk_mp turns out to now have a name, then the heap is still + * sitting at the end of the executable's data+bss mapping: remove + * the previous MA_BREAK setting to be consistent with /proc. + */ + if (stk_mp != NULL && stk_mp->map_pmap.pr_mapname[0] == '\0') + stk_mp->map_pmap.pr_mflags |= MA_ANON; + if (brk_mp != NULL && brk_mp->map_pmap.pr_mapname[0] == '\0') + brk_mp->map_pmap.pr_mflags |= MA_ANON; + else if (brk_mp != NULL) + brk_mp->map_pmap.pr_mflags &= ~MA_BREAK; + + *perr = 0; + return (P); + +err: + Pfree(P); + core_elf_close(&aout); + return (NULL); +} + +/* + * Grab a core file using a pathname. We just open it and call Pfgrab_core(). + */ +struct ps_prochandle * +Pgrab_core(const char *core, const char *aout, int gflag, int *perr) +{ + int fd, oflag = (gflag & PGRAB_RDONLY) ? O_RDONLY : O_RDWR; + + if ((fd = open64(core, oflag)) >= 0) + return (Pfgrab_core(fd, aout, perr)); + + if (errno != ENOENT) + *perr = G_STRANGE; + else + *perr = G_NOCORE; + + return (NULL); +} diff --git a/usr/src/lib/libproc/common/Pexecname.c b/usr/src/lib/libproc/common/Pexecname.c new file mode 100644 index 0000000000..3c4fee08d1 --- /dev/null +++ b/usr/src/lib/libproc/common/Pexecname.c @@ -0,0 +1,286 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define __EXTENSIONS__ +#include <string.h> +#undef __EXTENSIONS__ + +#include <libgen.h> +#include <limits.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> + +#include "Pcontrol.h" + +/* + * Pexecname.c - Way too much code to attempt to derive the full pathname of + * the executable file from a process handle, be it dead or alive. + */ + +/* + * Once we've computed a cwd and a relative path, we use try_exec() to + * form an absolute path, call resolvepath() on it, and then let the + * caller's function do the final confirmation. + */ +static int +try_exec(const char *cwd, const char *path, char *buf, + int (*isexec)(const char *, void *), void *isdata) +{ + int i; + + if (path[0] != '/') + (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path); + else + (void) strcpy(buf, path); + + dprintf("try_exec \"%s\"\n", buf); + + if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) { + buf[i] = '\0'; + return (isexec(buf, isdata)); + } + + return (0); /* resolvepath failed */ +} + +/* + * The Pfindexec function contains the logic for the executable name dance. + * The caller provides a possible executable name or likely directory (the + * aout parameter), and a function which is responsible for doing any + * final confirmation on the executable pathname once a possible full + * pathname has been chosen. + */ +char * +Pfindexec(struct ps_prochandle *P, const char *aout, + int (*isexec)(const char *, void *), void *isdata) +{ + char cwd[PATH_MAX * 2]; + char path[PATH_MAX]; + char buf[PATH_MAX]; + struct stat st; + uintptr_t addr; + char *p, *q; + + if (P->execname) + return (P->execname); /* Already found */ + + errno = 0; /* Set to zero so we can tell if stat() failed */ + + /* + * First try: use the provided default value, if it is not a directory. + * If the aout parameter turns out to be a directory, this is + * interpreted as the directory to use as an alternate cwd for + * our subsequent attempts to locate the executable. + */ + if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { + if (try_exec(".", aout, buf, isexec, isdata)) + goto found; + else + aout = "."; + + } else if (aout == NULL || errno != 0) + aout = "."; + + /* + * At this point 'aout' is either "." or an alternate cwd. We use + * realpath(3c) to turn this into a full pathname free of ".", "..", + * and symlinks. If this fails for some reason, fall back to "." + */ + if (realpath(aout, cwd) == NULL) + (void) strcpy(cwd, "."); + + /* + * Second try: read the string pointed to by the AT_SUN_EXECNAME + * auxv element, saved when the program was exec'd. If the full + * pathname try_exec() forms fails, try again using just the + * basename appended to our cwd. + */ + if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && + Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { + + if (try_exec(cwd, path, buf, isexec, isdata)) + goto found; + + if (strchr(path, '/') != NULL && basename(path) != NULL && + try_exec(cwd, path, buf, isexec, isdata)) + goto found; + } + + /* + * Third try: try using the first whitespace-separated token + * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). + */ + if (Ppsinfo(P) != NULL) { + (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); + path[PRARGSZ] = '\0'; + + if ((p = strchr(path, ' ')) != NULL) + *p = '\0'; + + if (try_exec(cwd, path, buf, isexec, isdata)) + goto found; + + if (strchr(path, '/') != NULL && basename(path) != NULL && + try_exec(cwd, path, buf, isexec, isdata)) + goto found; + } + + /* + * Fourth try: read the string pointed to by argv[0] out of the + * stack in the process's address space. + */ + if (P->psinfo.pr_argv != NULL && + Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && + Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { + + if (try_exec(cwd, path, buf, isexec, isdata)) + goto found; + + if (strchr(path, '/') != NULL && basename(path) != NULL && + try_exec(cwd, path, buf, isexec, isdata)) + goto found; + } + + /* + * Fifth try: read the process's $PATH environment variable and + * search each directory named there for the name matching pr_fname. + */ + if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { + /* + * If the name from pr_psargs contains pr_fname as its + * leading string, then accept the name from pr_psargs + * because more bytes are saved there. Otherwise use + * pr_fname because this gives us new information. + */ + (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); + path[PRARGSZ] = '\0'; + + if ((p = strchr(path, ' ')) != NULL) + *p = '\0'; + + if (strchr(path, '/') != NULL || strncmp(path, + P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) + (void) strcpy(path, P->psinfo.pr_fname); + + /* + * Now iterate over the $PATH elements, trying to form + * an executable pathname with each one. + */ + for (p = strtok_r(cwd, ":", &q); p != NULL; + p = strtok_r(NULL, ":", &q)) { + + if (*p != '/') + continue; /* Ignore anything relative */ + + if (try_exec(p, path, buf, isexec, isdata)) + goto found; + } + } + + errno = ENOENT; + return (NULL); + +found: + if ((P->execname = strdup(buf)) == NULL) + dprintf("failed to malloc; executable name is \"%s\"", buf); + + return (P->execname); +} + +/* + * Callback function for Pfindexec(). We return a match if we can stat the + * suggested pathname and confirm its device and inode number match our + * previous information about the /proc/<pid>/object/a.out file. + */ +static int +stat_exec(const char *path, struct stat64 *stp) +{ + struct stat64 st; + + return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && + stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); +} + +/* + * Return the full pathname for the executable file. If the process handle is + * a core file, we've already tried our best to get the executable name. + * Otherwise, we make an attempt using Pfindexec(). + */ +char * +Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) +{ + if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) { + char exec_name[PATH_MAX]; + char cwd[PATH_MAX]; + char proc_cwd[64]; + struct stat64 st; + int ret; + + /* + * Try to get the path information first. + */ + (void) snprintf(exec_name, sizeof (exec_name), + "/proc/%d/path/a.out", (int)P->pid); + if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { + buf[ret] = '\0'; + return (buf); + } + + /* + * Stat the executable file so we can compare Pfindexec's + * suggestions to the actual device and inode number. + */ + (void) snprintf(exec_name, sizeof (exec_name), + "/proc/%d/object/a.out", (int)P->pid); + + if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) + return (NULL); + + /* + * Attempt to figure out the current working directory of the + * target process. This only works if the target process has + * not changed its current directory since it was exec'd. + */ + (void) snprintf(proc_cwd, sizeof (proc_cwd), + "/proc/%d/path/cwd", (int)P->pid); + + if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) + cwd[ret] = '\0'; + + (void) Pfindexec(P, ret > 0 ? cwd : NULL, + (int (*)(const char *, void *))stat_exec, &st); + } + + if (P->execname != NULL) { + (void) strncpy(buf, P->execname, buflen); + return (buf); + } + + return (NULL); +} diff --git a/usr/src/lib/libproc/common/Pgcore.c b/usr/src/lib/libproc/common/Pgcore.c new file mode 100644 index 0000000000..9147bcb201 --- /dev/null +++ b/usr/src/lib/libproc/common/Pgcore.c @@ -0,0 +1,1534 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <procfs.h> +#include <priv.h> +#include <sys/elf.h> +#include <sys/machelf.h> +#include <sys/sysmacros.h> +#include <sys/systeminfo.h> +#include <sys/proc.h> +#include <sys/utsname.h> + +#include <sys/old_procfs.h> + +#include "Pcontrol.h" +#include "P32ton.h" + +typedef enum { + STR_CTF, + STR_SYMTAB, + STR_DYNSYM, + STR_STRTAB, + STR_DYNSTR, + STR_SHSTRTAB, + STR_NUM +} shstrtype_t; + +static const char *shstrtab_data[] = { + ".SUNW_ctf", + ".symtab", + ".dynsym", + ".strtab", + ".dynstr", + ".shstrtab" +}; + +typedef struct shstrtab { + int sst_ndx[STR_NUM]; + int sst_cur; +} shstrtab_t; + +typedef struct { + struct ps_prochandle *P; + int pgc_fd; + off64_t *pgc_poff; + off64_t *pgc_soff; + off64_t *pgc_doff; + core_content_t pgc_content; + void *pgc_chunk; + size_t pgc_chunksz; + + shstrtab_t pgc_shstrtab; +} pgcore_t; + +static void +shstrtab_init(shstrtab_t *s) +{ + bzero(&s->sst_ndx, sizeof (s->sst_ndx)); + s->sst_cur = 1; +} + +static int +shstrtab_ndx(shstrtab_t *s, shstrtype_t type) +{ + int ret; + + if ((ret = s->sst_ndx[type]) != 0) + return (ret); + + ret = s->sst_ndx[type] = s->sst_cur; + s->sst_cur += strlen(shstrtab_data[type]) + 1; + + return (ret); +} + +static size_t +shstrtab_size(const shstrtab_t *s) +{ + return (s->sst_cur); +} + +int +Pgcore(struct ps_prochandle *P, const char *fname, core_content_t content) +{ + int fd; + int err; + + if ((fd = creat64(fname, 0666)) < 0) + return (-1); + + if ((err = Pfgcore(P, fd, content)) != 0) { + (void) close(fd); + (void) unlink(fname); + return (err); + } + + return (close(fd)); +} + +/* + * Since we don't want to use the old-school procfs interfaces, we use the + * new-style data structures we already have to construct the old-style + * data structures. We include these data structures in core files for + * backward compatability. + */ + +static void +mkprstatus(struct ps_prochandle *P, const lwpstatus_t *lsp, + const lwpsinfo_t *lip, prstatus_t *psp) +{ + bzero(psp, sizeof (*psp)); + + if (lsp->pr_flags & PR_STOPPED) + psp->pr_flags = 0x0001; + if (lsp->pr_flags & PR_ISTOP) + psp->pr_flags = 0x0002; + if (lsp->pr_flags & PR_DSTOP) + psp->pr_flags = 0x0004; + if (lsp->pr_flags & PR_ASLEEP) + psp->pr_flags = 0x0008; + if (lsp->pr_flags & PR_FORK) + psp->pr_flags = 0x0010; + if (lsp->pr_flags & PR_RLC) + psp->pr_flags = 0x0020; + /* + * Note that PR_PTRACE (0x0040) from <sys/old_procfs.h> is never set; + * PR_PCOMPAT corresponds to PR_PTRACE in the newer <sys/procfs.h>. + */ + if (lsp->pr_flags & PR_PCINVAL) + psp->pr_flags = 0x0080; + if (lsp->pr_flags & PR_ISSYS) + psp->pr_flags = 0x0100; + if (lsp->pr_flags & PR_STEP) + psp->pr_flags = 0x0200; + if (lsp->pr_flags & PR_KLC) + psp->pr_flags = 0x0400; + if (lsp->pr_flags & PR_ASYNC) + psp->pr_flags = 0x0800; + if (lsp->pr_flags & PR_PTRACE) + psp->pr_flags = 0x1000; + if (lsp->pr_flags & PR_MSACCT) + psp->pr_flags = 0x2000; + if (lsp->pr_flags & PR_BPTADJ) + psp->pr_flags = 0x4000; + if (lsp->pr_flags & PR_ASLWP) + psp->pr_flags = 0x8000; + + psp->pr_why = lsp->pr_why; + psp->pr_what = lsp->pr_what; + psp->pr_info = lsp->pr_info; + psp->pr_cursig = lsp->pr_cursig; + psp->pr_nlwp = P->status.pr_nlwp; + psp->pr_sigpend = P->status.pr_sigpend; + psp->pr_sighold = lsp->pr_lwphold; + psp->pr_altstack = lsp->pr_altstack; + psp->pr_action = lsp->pr_action; + psp->pr_pid = P->status.pr_pid; + psp->pr_ppid = P->status.pr_ppid; + psp->pr_pgrp = P->status.pr_pgid; + psp->pr_sid = P->status.pr_sid; + psp->pr_utime = P->status.pr_utime; + psp->pr_stime = P->status.pr_stime; + psp->pr_cutime = P->status.pr_cutime; + psp->pr_cstime = P->status.pr_cstime; + (void) strncpy(psp->pr_clname, lsp->pr_clname, sizeof (psp->pr_clname)); + psp->pr_syscall = lsp->pr_syscall; + psp->pr_nsysarg = lsp->pr_nsysarg; + bcopy(lsp->pr_sysarg, psp->pr_sysarg, sizeof (psp->pr_sysarg)); + psp->pr_who = lsp->pr_lwpid; + psp->pr_lwppend = lsp->pr_lwppend; + psp->pr_oldcontext = (ucontext_t *)lsp->pr_oldcontext; + psp->pr_brkbase = (caddr_t)P->status.pr_brkbase; + psp->pr_brksize = P->status.pr_brksize; + psp->pr_stkbase = (caddr_t)P->status.pr_stkbase; + psp->pr_stksize = P->status.pr_stksize; + psp->pr_processor = (short)lip->pr_onpro; + psp->pr_bind = (short)lip->pr_bindpro; + psp->pr_instr = lsp->pr_instr; + bcopy(lsp->pr_reg, psp->pr_reg, sizeof (psp->pr_sysarg)); +} + +static void +mkprpsinfo(struct ps_prochandle *P, prpsinfo_t *psp) +{ + bzero(psp, sizeof (*psp)); + psp->pr_state = P->psinfo.pr_lwp.pr_state; + psp->pr_sname = P->psinfo.pr_lwp.pr_sname; + psp->pr_zomb = (psp->pr_state == SZOMB); + psp->pr_nice = P->psinfo.pr_lwp.pr_nice; + psp->pr_flag = P->psinfo.pr_lwp.pr_flag; + psp->pr_uid = P->psinfo.pr_uid; + psp->pr_gid = P->psinfo.pr_gid; + psp->pr_pid = P->psinfo.pr_pid; + psp->pr_ppid = P->psinfo.pr_ppid; + psp->pr_pgrp = P->psinfo.pr_pgid; + psp->pr_sid = P->psinfo.pr_sid; + psp->pr_addr = (caddr_t)P->psinfo.pr_addr; + psp->pr_size = P->psinfo.pr_size; + psp->pr_rssize = P->psinfo.pr_rssize; + psp->pr_wchan = (caddr_t)P->psinfo.pr_lwp.pr_wchan; + psp->pr_start = P->psinfo.pr_start; + psp->pr_time = P->psinfo.pr_time; + psp->pr_pri = P->psinfo.pr_lwp.pr_pri; + psp->pr_oldpri = P->psinfo.pr_lwp.pr_oldpri; + psp->pr_cpu = P->psinfo.pr_lwp.pr_cpu; + psp->pr_ottydev = cmpdev(P->psinfo.pr_ttydev); + psp->pr_lttydev = P->psinfo.pr_ttydev; + (void) strncpy(psp->pr_clname, P->psinfo.pr_lwp.pr_clname, + sizeof (psp->pr_clname)); + (void) strncpy(psp->pr_fname, P->psinfo.pr_fname, + sizeof (psp->pr_fname)); + bcopy(&P->psinfo.pr_psargs, &psp->pr_psargs, + sizeof (psp->pr_psargs)); + psp->pr_syscall = P->psinfo.pr_lwp.pr_syscall; + psp->pr_ctime = P->psinfo.pr_ctime; + psp->pr_bysize = psp->pr_size * PAGESIZE; + psp->pr_byrssize = psp->pr_rssize * PAGESIZE; + psp->pr_argc = P->psinfo.pr_argc; + psp->pr_argv = (char **)P->psinfo.pr_argv; + psp->pr_envp = (char **)P->psinfo.pr_envp; + psp->pr_wstat = P->psinfo.pr_wstat; + psp->pr_pctcpu = P->psinfo.pr_pctcpu; + psp->pr_pctmem = P->psinfo.pr_pctmem; + psp->pr_euid = P->psinfo.pr_euid; + psp->pr_egid = P->psinfo.pr_egid; + psp->pr_aslwpid = 0; + psp->pr_dmodel = P->psinfo.pr_dmodel; +} + +#ifdef _LP64 + +static void +mkprstatus32(struct ps_prochandle *P, const lwpstatus_t *lsp, + const lwpsinfo_t *lip, prstatus32_t *psp) +{ + bzero(psp, sizeof (*psp)); + + if (lsp->pr_flags & PR_STOPPED) + psp->pr_flags = 0x0001; + if (lsp->pr_flags & PR_ISTOP) + psp->pr_flags = 0x0002; + if (lsp->pr_flags & PR_DSTOP) + psp->pr_flags = 0x0004; + if (lsp->pr_flags & PR_ASLEEP) + psp->pr_flags = 0x0008; + if (lsp->pr_flags & PR_FORK) + psp->pr_flags = 0x0010; + if (lsp->pr_flags & PR_RLC) + psp->pr_flags = 0x0020; + /* + * Note that PR_PTRACE (0x0040) from <sys/old_procfs.h> is never set; + * PR_PCOMPAT corresponds to PR_PTRACE in the newer <sys/procfs.h>. + */ + if (lsp->pr_flags & PR_PCINVAL) + psp->pr_flags = 0x0080; + if (lsp->pr_flags & PR_ISSYS) + psp->pr_flags = 0x0100; + if (lsp->pr_flags & PR_STEP) + psp->pr_flags = 0x0200; + if (lsp->pr_flags & PR_KLC) + psp->pr_flags = 0x0400; + if (lsp->pr_flags & PR_ASYNC) + psp->pr_flags = 0x0800; + if (lsp->pr_flags & PR_PTRACE) + psp->pr_flags = 0x1000; + if (lsp->pr_flags & PR_MSACCT) + psp->pr_flags = 0x2000; + if (lsp->pr_flags & PR_BPTADJ) + psp->pr_flags = 0x4000; + if (lsp->pr_flags & PR_ASLWP) + psp->pr_flags = 0x8000; + + psp->pr_why = lsp->pr_why; + psp->pr_what = lsp->pr_what; + siginfo_n_to_32(&lsp->pr_info, &psp->pr_info); + psp->pr_cursig = lsp->pr_cursig; + psp->pr_nlwp = P->status.pr_nlwp; + psp->pr_sigpend = P->status.pr_sigpend; + psp->pr_sighold = lsp->pr_lwphold; + stack_n_to_32(&lsp->pr_altstack, &psp->pr_altstack); + sigaction_n_to_32(&lsp->pr_action, &psp->pr_action); + psp->pr_pid = P->status.pr_pid; + psp->pr_ppid = P->status.pr_ppid; + psp->pr_pgrp = P->status.pr_pgid; + psp->pr_sid = P->status.pr_sid; + timestruc_n_to_32(&P->status.pr_utime, &psp->pr_utime); + timestruc_n_to_32(&P->status.pr_stime, &psp->pr_stime); + timestruc_n_to_32(&P->status.pr_cutime, &psp->pr_cutime); + timestruc_n_to_32(&P->status.pr_cstime, &psp->pr_cstime); + (void) strncpy(psp->pr_clname, lsp->pr_clname, sizeof (psp->pr_clname)); + psp->pr_syscall = lsp->pr_syscall; + psp->pr_nsysarg = lsp->pr_nsysarg; + bcopy(lsp->pr_sysarg, psp->pr_sysarg, + sizeof (psp->pr_sysarg)); psp->pr_who = lsp->pr_lwpid; + psp->pr_lwppend = lsp->pr_lwppend; + psp->pr_oldcontext = (caddr32_t)lsp->pr_oldcontext; + psp->pr_brkbase = (caddr32_t)P->status.pr_brkbase; + psp->pr_brksize = P->status.pr_brksize; + psp->pr_stkbase = (caddr32_t)P->status.pr_stkbase; + psp->pr_stksize = P->status.pr_stksize; + psp->pr_processor = (short)lip->pr_onpro; + psp->pr_bind = (short)lip->pr_bindpro; + psp->pr_instr = lsp->pr_instr; + bcopy(lsp->pr_reg, psp->pr_reg, sizeof (psp->pr_sysarg)); +} + +static void +mkprpsinfo32(struct ps_prochandle *P, prpsinfo32_t *psp) +{ + bzero(psp, sizeof (*psp)); + psp->pr_state = P->psinfo.pr_lwp.pr_state; + psp->pr_sname = P->psinfo.pr_lwp.pr_sname; + psp->pr_zomb = (psp->pr_state == SZOMB); + psp->pr_nice = P->psinfo.pr_lwp.pr_nice; + psp->pr_flag = P->psinfo.pr_lwp.pr_flag; + psp->pr_uid = P->psinfo.pr_uid; + psp->pr_gid = P->psinfo.pr_gid; + psp->pr_pid = P->psinfo.pr_pid; + psp->pr_ppid = P->psinfo.pr_ppid; + psp->pr_pgrp = P->psinfo.pr_pgid; + psp->pr_sid = P->psinfo.pr_sid; + psp->pr_addr = (caddr32_t)P->psinfo.pr_addr; + psp->pr_size = P->psinfo.pr_size; + psp->pr_rssize = P->psinfo.pr_rssize; + psp->pr_wchan = (caddr32_t)P->psinfo.pr_lwp.pr_wchan; + timestruc_n_to_32(&P->psinfo.pr_start, &psp->pr_start); + timestruc_n_to_32(&P->psinfo.pr_time, &psp->pr_time); + psp->pr_pri = P->psinfo.pr_lwp.pr_pri; + psp->pr_oldpri = P->psinfo.pr_lwp.pr_oldpri; + psp->pr_cpu = P->psinfo.pr_lwp.pr_cpu; + psp->pr_ottydev = cmpdev(P->psinfo.pr_ttydev); + psp->pr_lttydev = prcmpldev(P->psinfo.pr_ttydev); + (void) strncpy(psp->pr_clname, P->psinfo.pr_lwp.pr_clname, + sizeof (psp->pr_clname)); + (void) strncpy(psp->pr_fname, P->psinfo.pr_fname, + sizeof (psp->pr_fname)); + bcopy(&P->psinfo.pr_psargs, &psp->pr_psargs, + sizeof (psp->pr_psargs)); + psp->pr_syscall = P->psinfo.pr_lwp.pr_syscall; + timestruc_n_to_32(&P->psinfo.pr_ctime, &psp->pr_ctime); + psp->pr_bysize = psp->pr_size * PAGESIZE; + psp->pr_byrssize = psp->pr_rssize * PAGESIZE; + psp->pr_argc = P->psinfo.pr_argc; + psp->pr_argv = (caddr32_t)P->psinfo.pr_argv; + psp->pr_envp = (caddr32_t)P->psinfo.pr_envp; + psp->pr_wstat = P->psinfo.pr_wstat; + psp->pr_pctcpu = P->psinfo.pr_pctcpu; + psp->pr_pctmem = P->psinfo.pr_pctmem; + psp->pr_euid = P->psinfo.pr_euid; + psp->pr_egid = P->psinfo.pr_egid; + psp->pr_aslwpid = 0; + psp->pr_dmodel = P->psinfo.pr_dmodel; +} + +#endif /* _LP64 */ + +static int +write_note(int fd, uint_t type, const void *desc, size_t descsz, off64_t *offp) +{ + /* + * Note headers are the same regardless of the data model of the + * ELF file; we arbitrarily use Elf64_Nhdr here. + */ + struct { + Elf64_Nhdr nhdr; + char name[8]; + } n; + + bzero(&n, sizeof (n)); + bcopy("CORE", n.name, 4); + n.nhdr.n_type = type; + n.nhdr.n_namesz = 5; + n.nhdr.n_descsz = roundup(descsz, 4); + + if (pwrite64(fd, &n, sizeof (n), *offp) != sizeof (n)) + return (-1); + + *offp += sizeof (n); + + if (pwrite64(fd, desc, n.nhdr.n_descsz, *offp) != n.nhdr.n_descsz) + return (-1); + + *offp += n.nhdr.n_descsz; + + return (0); +} + +static int +old_per_lwp(void *data, const lwpstatus_t *lsp, const lwpsinfo_t *lip) +{ + pgcore_t *pgc = data; + struct ps_prochandle *P = pgc->P; + + /* + * Legacy core files don't contain information about zombie LWPs. + * We use Plwp_iter_all() so that we get the lwpsinfo_t structure + * more cheaply. + */ + if (lsp == NULL) + return (0); + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + prstatus_t prstatus; + mkprstatus(P, lsp, lip, &prstatus); + if (write_note(pgc->pgc_fd, NT_PRSTATUS, &prstatus, + sizeof (prstatus_t), pgc->pgc_doff) != 0) + return (0); + if (write_note(pgc->pgc_fd, NT_PRFPREG, &lsp->pr_fpreg, + sizeof (prfpregset_t), pgc->pgc_doff) != 0) + return (1); +#ifdef _LP64 + } else { + prstatus32_t pr32; + prfpregset32_t pf32; + mkprstatus32(P, lsp, lip, &pr32); + if (write_note(pgc->pgc_fd, NT_PRSTATUS, &pr32, + sizeof (prstatus32_t), pgc->pgc_doff) != 0) + return (1); + prfpregset_n_to_32(&lsp->pr_fpreg, &pf32); + if (write_note(pgc->pgc_fd, NT_PRFPREG, &pf32, + sizeof (prfpregset32_t), pgc->pgc_doff) != 0) + return (1); +#endif /* _LP64 */ + } + +#ifdef sparc + { + prxregset_t xregs; + if (Plwp_getxregs(P, lsp->pr_lwpid, &xregs) == 0 && + write_note(pgc->pgc_fd, NT_PRXREG, &xregs, + sizeof (prxregset_t), pgc->pgc_doff) != 0) + return (1); + } +#endif /* sparc */ + + return (0); +} + +static int +new_per_lwp(void *data, const lwpstatus_t *lsp, const lwpsinfo_t *lip) +{ + pgcore_t *pgc = data; + struct ps_prochandle *P = pgc->P; + + /* + * If lsp is NULL this indicates that this is a zombie LWP in + * which case we dump only the lwpsinfo_t structure and none of + * the other ancillary LWP state data. + */ + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (write_note(pgc->pgc_fd, NT_LWPSINFO, lip, + sizeof (lwpsinfo_t), pgc->pgc_doff) != 0) + return (1); + if (lsp == NULL) + return (0); + if (write_note(pgc->pgc_fd, NT_LWPSTATUS, lsp, + sizeof (lwpstatus_t), pgc->pgc_doff) != 0) + return (1); +#ifdef _LP64 + } else { + lwpsinfo32_t li32; + lwpstatus32_t ls32; + lwpsinfo_n_to_32(lip, &li32); + if (write_note(pgc->pgc_fd, NT_LWPSINFO, &li32, + sizeof (lwpsinfo32_t), pgc->pgc_doff) != 0) + return (1); + if (lsp == NULL) + return (0); + lwpstatus_n_to_32(lsp, &ls32); + if (write_note(pgc->pgc_fd, NT_LWPSTATUS, &ls32, + sizeof (lwpstatus32_t), pgc->pgc_doff) != 0) + return (1); +#endif /* _LP64 */ + } + +#ifdef sparc + { + prxregset_t xregs; + gwindows_t gwins; + size_t size; + + if (Plwp_getxregs(P, lsp->pr_lwpid, &xregs) == 0) { + if (write_note(pgc->pgc_fd, NT_PRXREG, &xregs, + sizeof (prxregset_t), pgc->pgc_doff) != 0) + return (1); + } + + if (Plwp_getgwindows(P, lsp->pr_lwpid, &gwins) == 0 && + gwins.wbcnt > 0) { + size = sizeof (gwins) - sizeof (gwins.wbuf) + + gwins.wbcnt * sizeof (gwins.wbuf[0]); + + if (write_note(pgc->pgc_fd, NT_GWINDOWS, &gwins, size, + pgc->pgc_doff) != 0) + return (1); + } + + } +#ifdef __sparcv9 + if (P->status.pr_dmodel == PR_MODEL_LP64) { + asrset_t asrs; + if (Plwp_getasrs(P, lsp->pr_lwpid, asrs) == 0) { + if (write_note(pgc->pgc_fd, NT_ASRS, &asrs, + sizeof (asrset_t), pgc->pgc_doff) != 0) + return (1); + } + } +#endif /* __sparcv9 */ +#endif /* sparc */ + + return (0); +} + +static uint_t +count_sections(pgcore_t *pgc) +{ + struct ps_prochandle *P = pgc->P; + file_info_t *fptr; + uint_t cnt; + uint_t nshdrs = 0; + + if (!(pgc->pgc_content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB))) + return (0); + + fptr = list_next(&P->file_head); + for (cnt = P->num_files; cnt > 0; cnt--, fptr = list_next(fptr)) { + int hit_symtab = 0; + + Pbuild_file_symtab(P, fptr); + + if ((pgc->pgc_content & CC_CONTENT_CTF) && + Pbuild_file_ctf(P, fptr) != NULL) { + sym_tbl_t *sym; + + nshdrs++; + + if (fptr->file_ctf_dyn) { + sym = &fptr->file_dynsym; + } else { + sym = &fptr->file_symtab; + hit_symtab = 1; + } + + if (sym->sym_data != NULL && sym->sym_symn != 0 && + sym->sym_strs != NULL) + nshdrs += 2; + } + + if ((pgc->pgc_content & CC_CONTENT_SYMTAB) && !hit_symtab && + fptr->file_symtab.sym_data != NULL && + fptr->file_symtab.sym_symn != 0 && + fptr->file_symtab.sym_strs != NULL) { + nshdrs += 2; + } + } + + return (nshdrs == 0 ? 0 : nshdrs + 2); +} + +static int +write_shdr(pgcore_t *pgc, shstrtype_t name, uint_t type, ulong_t flags, + uintptr_t addr, ulong_t offset, size_t size, uint_t link, uint_t info, + uintptr_t addralign, uintptr_t entsize) +{ + if (pgc->P->status.pr_dmodel == PR_MODEL_ILP32) { + Elf32_Shdr shdr; + + bzero(&shdr, sizeof (shdr)); + shdr.sh_name = shstrtab_ndx(&pgc->pgc_shstrtab, name); + shdr.sh_type = type; + shdr.sh_flags = flags; + shdr.sh_addr = (Elf32_Addr)addr; + shdr.sh_offset = offset; + shdr.sh_size = size; + shdr.sh_link = link; + shdr.sh_info = info; + shdr.sh_addralign = addralign; + shdr.sh_entsize = entsize; + + if (pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr), + *pgc->pgc_soff) != sizeof (shdr)) + return (-1); + + *pgc->pgc_soff += sizeof (shdr); +#ifdef _LP64 + } else { + Elf64_Shdr shdr; + + bzero(&shdr, sizeof (shdr)); + shdr.sh_name = shstrtab_ndx(&pgc->pgc_shstrtab, name); + shdr.sh_type = type; + shdr.sh_flags = flags; + shdr.sh_addr = addr; + shdr.sh_offset = offset; + shdr.sh_size = size; + shdr.sh_link = link; + shdr.sh_info = info; + shdr.sh_addralign = addralign; + shdr.sh_entsize = entsize; + + if (pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr), + *pgc->pgc_soff) != sizeof (shdr)) + return (-1); + + *pgc->pgc_soff += sizeof (shdr); +#endif /* _LP64 */ + } + + return (0); +} + +static int +dump_symtab(pgcore_t *pgc, file_info_t *fptr, uint_t index, int dynsym) +{ + sym_tbl_t *sym = dynsym ? &fptr->file_dynsym : &fptr->file_symtab; + shstrtype_t symname = dynsym ? STR_DYNSYM : STR_SYMTAB; + shstrtype_t strname = dynsym ? STR_DYNSTR : STR_STRTAB; + uint_t symtype = dynsym ? SHT_DYNSYM : SHT_SYMTAB; + size_t size; + uintptr_t addr = fptr->file_map->map_pmap.pr_vaddr; + + if (sym->sym_data == NULL || sym->sym_symn == 0 || + sym->sym_strs == NULL) + return (0); + + size = sym->sym_hdr.sh_size; + if (pwrite64(pgc->pgc_fd, sym->sym_data->d_buf, size, + *pgc->pgc_doff) != size) + return (-1); + + if (write_shdr(pgc, symname, symtype, 0, addr, *pgc->pgc_doff, size, + index + 1, sym->sym_hdr.sh_info, sym->sym_hdr.sh_addralign, + sym->sym_hdr.sh_entsize) != 0) + return (-1); + + *pgc->pgc_doff += roundup(size, 8); + + size = sym->sym_strhdr.sh_size; + if (pwrite64(pgc->pgc_fd, sym->sym_strs, size, *pgc->pgc_doff) != size) + return (-1); + + if (write_shdr(pgc, strname, SHT_STRTAB, SHF_STRINGS, addr, + *pgc->pgc_doff, size, 0, 0, 1, 0) != 0) + return (-1); + + *pgc->pgc_doff += roundup(size, 8); + + return (0); +} + +static int +dump_sections(pgcore_t *pgc) +{ + struct ps_prochandle *P = pgc->P; + file_info_t *fptr; + uint_t cnt; + uint_t index = 1; + + if (!(pgc->pgc_content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB))) + return (0); + + fptr = list_next(&P->file_head); + for (cnt = P->num_files; cnt > 0; cnt--, fptr = list_next(fptr)) { + int hit_symtab = 0; + + Pbuild_file_symtab(P, fptr); + + if ((pgc->pgc_content & CC_CONTENT_CTF) && + Pbuild_file_ctf(P, fptr) != NULL) { + sym_tbl_t *sym; + uint_t dynsym; + uint_t symindex = 0; + + /* + * Write the symtab out first so we can correctly + * set the sh_link field in the CTF section header. + * symindex will be 0 if there is no corresponding + * symbol table section. + */ + if (fptr->file_ctf_dyn) { + sym = &fptr->file_dynsym; + dynsym = 1; + } else { + sym = &fptr->file_symtab; + dynsym = 0; + hit_symtab = 1; + } + + if (sym->sym_data != NULL && sym->sym_symn != 0 && + sym->sym_strs != NULL) { + symindex = index; + if (dump_symtab(pgc, fptr, index, dynsym) != 0) + return (-1); + index += 2; + } + + /* + * Write the CTF data that we've read out of the + * file itself into the core file. + */ + if (pwrite64(pgc->pgc_fd, fptr->file_ctf_buf, + fptr->file_ctf_size, *pgc->pgc_doff) != + fptr->file_ctf_size) + return (-1); + + if (write_shdr(pgc, STR_CTF, SHT_PROGBITS, 0, + fptr->file_map->map_pmap.pr_vaddr, *pgc->pgc_doff, + fptr->file_ctf_size, symindex, 0, 4, 0) != 0) + return (-1); + + index++; + *pgc->pgc_doff += roundup(fptr->file_ctf_size, 8); + } + + if ((pgc->pgc_content & CC_CONTENT_SYMTAB) && !hit_symtab && + fptr->file_symtab.sym_data != NULL && + fptr->file_symtab.sym_symn != 0 && + fptr->file_symtab.sym_strs != NULL) { + if (dump_symtab(pgc, fptr, index, 0) != 0) + return (-1); + index += 2; + } + } + + return (0); +} + +/*ARGSUSED*/ +static int +dump_map(void *data, const prmap_t *pmp, const char *name) +{ + pgcore_t *pgc = data; + struct ps_prochandle *P = pgc->P; +#ifdef _LP64 + Elf64_Phdr phdr; +#else + Elf32_Phdr phdr; +#endif + size_t n; + + bzero(&phdr, sizeof (phdr)); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = pmp->pr_vaddr; + phdr.p_memsz = pmp->pr_size; + if (pmp->pr_mflags & MA_READ) + phdr.p_flags |= PF_R; + if (pmp->pr_mflags & MA_WRITE) + phdr.p_flags |= PF_W; + if (pmp->pr_mflags & MA_EXEC) + phdr.p_flags |= PF_X; + + if (pmp->pr_vaddr + pmp->pr_size > P->status.pr_stkbase && + pmp->pr_vaddr < P->status.pr_stkbase + P->status.pr_stksize) { + if (!(pgc->pgc_content & CC_CONTENT_STACK)) + goto exclude; + + } else if ((pmp->pr_mflags & MA_ANON) && + pmp->pr_vaddr + pmp->pr_size > P->status.pr_brkbase && + pmp->pr_vaddr < P->status.pr_brkbase + P->status.pr_brksize) { + if (!(pgc->pgc_content & CC_CONTENT_HEAP)) + goto exclude; + + } else if (pmp->pr_mflags & MA_ISM) { + if (pmp->pr_mflags & MA_NORESERVE) { + if (!(pgc->pgc_content & CC_CONTENT_DISM)) + goto exclude; + } else { + if (!(pgc->pgc_content & CC_CONTENT_ISM)) + goto exclude; + } + + } else if (pmp->pr_mflags & MA_SHM) { + if (!(pgc->pgc_content & CC_CONTENT_SHM)) + goto exclude; + + } else if (pmp->pr_mflags & MA_SHARED) { + if (pmp->pr_mflags & MA_ANON) { + if (!(pgc->pgc_content & CC_CONTENT_SHANON)) + goto exclude; + } else { + if (!(pgc->pgc_content & CC_CONTENT_SHFILE)) + goto exclude; + } + + } else if (pmp->pr_mflags & MA_ANON) { + if (!(pgc->pgc_content & CC_CONTENT_ANON)) + goto exclude; + + } else if (phdr.p_flags == (PF_R | PF_X)) { + if (!(pgc->pgc_content & CC_CONTENT_TEXT)) + goto exclude; + + } else if (phdr.p_flags == PF_R) { + if (!(pgc->pgc_content & CC_CONTENT_RODATA)) + goto exclude; + + } else { + if (!(pgc->pgc_content & CC_CONTENT_DATA)) + goto exclude; + } + + n = 0; + while (n < pmp->pr_size) { + size_t csz = MIN(pmp->pr_size - n, pgc->pgc_chunksz); + + /* + * If we can't read out part of the victim's address + * space for some reason ignore that failure and try to + * emit a partial core file without that mapping's data. + * As in the kernel, we mark these failures with the + * PF_SUNW_FAILURE flag and store the errno where the + * mapping would have been. + */ + if (Pread(P, pgc->pgc_chunk, csz, pmp->pr_vaddr + n) != csz || + pwrite64(pgc->pgc_fd, pgc->pgc_chunk, csz, + *pgc->pgc_doff + n) != csz) { + int err = errno; + (void) pwrite64(pgc->pgc_fd, &err, sizeof (err), + *pgc->pgc_doff); + *pgc->pgc_doff += roundup(sizeof (err), 8); + + phdr.p_flags |= PF_SUNW_FAILURE; + (void) ftruncate64(pgc->pgc_fd, *pgc->pgc_doff); + goto exclude; + } + + n += csz; + } + + phdr.p_offset = *pgc->pgc_doff; + phdr.p_filesz = pmp->pr_size; + *pgc->pgc_doff += roundup(phdr.p_filesz, 8); + +exclude: + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (pwrite64(pgc->pgc_fd, &phdr, sizeof (phdr), + *pgc->pgc_poff) != sizeof (phdr)) + return (1); + + *pgc->pgc_poff += sizeof (phdr); +#ifdef _LP64 + } else { + Elf32_Phdr phdr32; + + bzero(&phdr32, sizeof (phdr32)); + phdr32.p_type = phdr.p_type; + phdr32.p_vaddr = (Elf32_Addr)phdr.p_vaddr; + phdr32.p_memsz = (Elf32_Word)phdr.p_memsz; + phdr32.p_flags = phdr.p_flags; + phdr32.p_offset = (Elf32_Off)phdr.p_offset; + phdr32.p_filesz = (Elf32_Word)phdr.p_filesz; + + if (pwrite64(pgc->pgc_fd, &phdr32, sizeof (phdr32), + *pgc->pgc_poff) != sizeof (phdr32)) + return (1); + + *pgc->pgc_poff += sizeof (phdr32); +#endif /* _LP64 */ + } + + return (0); +} + +int +write_shstrtab(struct ps_prochandle *P, pgcore_t *pgc) +{ + off64_t off = *pgc->pgc_doff; + size_t size = 0; + shstrtab_t *s = &pgc->pgc_shstrtab; + int i, ndx; + + if (shstrtab_size(s) == 1) + return (0); + + /* + * Preemptively stick the name of the shstrtab in the string table. + */ + (void) shstrtab_ndx(&pgc->pgc_shstrtab, STR_SHSTRTAB); + size = shstrtab_size(s); + + if (pwrite64(pgc->pgc_fd, "", 1, off) != 1) + return (1); + + /* + * Dump all the strings that we used being sure we include the + * terminating null character. + */ + for (i = 0; i < STR_NUM; i++) { + if ((ndx = s->sst_ndx[i]) != 0) { + const char *str = shstrtab_data[i]; + size_t len = strlen(str) + 1; + if (pwrite64(pgc->pgc_fd, str, len, off + ndx) != len) + return (1); + } + } + + if (P->status.pr_dmodel == PR_MODEL_ILP32) { + Elf32_Shdr shdr; + + bzero(&shdr, sizeof (shdr)); + shdr.sh_name = shstrtab_ndx(&pgc->pgc_shstrtab, STR_SHSTRTAB); + shdr.sh_size = size; + shdr.sh_offset = *pgc->pgc_doff; + shdr.sh_addralign = 1; + shdr.sh_flags = SHF_STRINGS; + shdr.sh_type = SHT_STRTAB; + + if (pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr), + *pgc->pgc_soff) != sizeof (shdr)) + return (1); + + *pgc->pgc_soff += sizeof (shdr); +#ifdef _LP64 + } else { + Elf64_Shdr shdr; + + bzero(&shdr, sizeof (shdr)); + shdr.sh_name = shstrtab_ndx(&pgc->pgc_shstrtab, STR_SHSTRTAB); + shdr.sh_size = size; + shdr.sh_offset = *pgc->pgc_doff; + shdr.sh_addralign = 1; + shdr.sh_flags = SHF_STRINGS; + shdr.sh_type = SHT_STRTAB; + + if (pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr), + *pgc->pgc_soff) != sizeof (shdr)) + return (1); + + *pgc->pgc_soff += sizeof (shdr); +#endif /* _LP64 */ + } + + *pgc->pgc_doff += roundup(size, 8); + + return (0); +} + +/* + * Don't explicity stop the process; that's up to the consumer. + */ +int +Pfgcore(struct ps_prochandle *P, int fd, core_content_t content) +{ + char plat[SYS_NMLN]; + char zonename[ZONENAME_MAX]; + int platlen = -1; + pgcore_t pgc; + off64_t poff, soff, doff, boff; + struct utsname uts; + uint_t nphdrs, nshdrs; + + if (ftruncate64(fd, 0) != 0) + return (-1); + + if (content == CC_CONTENT_INVALID) { + errno = EINVAL; + return (-1); + } + + /* + * Cache the mappings and other useful data. + */ + (void) Prd_agent(P); + (void) Ppsinfo(P); + + pgc.P = P; + pgc.pgc_fd = fd; + pgc.pgc_poff = &poff; + pgc.pgc_soff = &soff; + pgc.pgc_doff = &doff; + pgc.pgc_content = content; + pgc.pgc_chunksz = PAGESIZE; + if ((pgc.pgc_chunk = malloc(pgc.pgc_chunksz)) == NULL) + return (-1); + + shstrtab_init(&pgc.pgc_shstrtab); + + /* + * There are two PT_NOTE program headers for ancillary data, and + * one for each mapping. + */ + nphdrs = 2 + P->map_count; + nshdrs = count_sections(&pgc); + + (void) Pplatform(P, plat, sizeof (plat)); + platlen = strlen(plat) + 1; + Preadauxvec(P); + (void) Puname(P, &uts); + if (Pzonename(P, zonename, sizeof (zonename)) == NULL) + zonename[0] = '\0'; + + /* + * Set up the ELF header. + */ + if (P->status.pr_dmodel == PR_MODEL_ILP32) { + Elf32_Ehdr ehdr; + + bzero(&ehdr, sizeof (ehdr)); + 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_type = ET_CORE; + + ehdr.e_ident[EI_CLASS] = ELFCLASS32; +#if defined(__sparc) + ehdr.e_machine = EM_SPARC; + ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#elif defined(__i386) || defined(__amd64) + ehdr.e_machine = EM_386; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else +#error "unknown machine type" +#endif + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + + ehdr.e_version = EV_CURRENT; + ehdr.e_ehsize = sizeof (ehdr); + ehdr.e_phentsize = sizeof (Elf32_Phdr); + ehdr.e_phnum = (unsigned short)nphdrs; + ehdr.e_phoff = ehdr.e_ehsize; + + if (nshdrs != 0) { + ehdr.e_shentsize = sizeof (Elf32_Shdr); + ehdr.e_shnum = (unsigned short)nshdrs; + ehdr.e_shoff = ehdr.e_phoff + + ehdr.e_phentsize * ehdr.e_phnum; + ehdr.e_shstrndx = nshdrs - 1; + } + + if (pwrite64(fd, &ehdr, sizeof (ehdr), 0) != sizeof (ehdr)) + goto err; + + poff = ehdr.e_phoff; + soff = ehdr.e_shoff + ehdr.e_shentsize; + doff = boff = ehdr.e_ehsize + + ehdr.e_phentsize * ehdr.e_phnum + + ehdr.e_shentsize * ehdr.e_shnum; + +#ifdef _LP64 + } else { + Elf64_Ehdr ehdr; + + bzero(&ehdr, sizeof (ehdr)); + 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_type = ET_CORE; + + ehdr.e_ident[EI_CLASS] = ELFCLASS64; +#if defined(__sparc) + ehdr.e_machine = EM_SPARCV9; + ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#elif defined(__i386) || defined(__amd64) + ehdr.e_machine = EM_AMD64; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else +#error "unknown machine type" +#endif + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + + ehdr.e_version = EV_CURRENT; + ehdr.e_ehsize = sizeof (ehdr); + ehdr.e_phentsize = sizeof (Elf64_Phdr); + ehdr.e_phnum = (unsigned short)nphdrs; + ehdr.e_phoff = ehdr.e_ehsize; + + if (nshdrs != 0) { + ehdr.e_shentsize = sizeof (Elf64_Shdr); + ehdr.e_shnum = (unsigned short)nshdrs; + ehdr.e_shoff = ehdr.e_phoff + + ehdr.e_phentsize * ehdr.e_phnum; + ehdr.e_shstrndx = nshdrs - 1; + } + + if (pwrite64(fd, &ehdr, sizeof (ehdr), 0) != sizeof (ehdr)) + goto err; + + poff = ehdr.e_phoff; + soff = ehdr.e_shoff + ehdr.e_shentsize; + doff = boff = sizeof (ehdr) + + ehdr.e_phentsize * ehdr.e_phnum + + ehdr.e_shentsize * ehdr.e_shnum; + +#endif /* _LP64 */ + } + + /* + * Construct the old-style note header and section. + */ + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + prpsinfo_t prpsinfo; + + mkprpsinfo(P, &prpsinfo); + if (write_note(fd, NT_PRPSINFO, &prpsinfo, sizeof (prpsinfo_t), + &doff) != 0) { + goto err; + } + if (write_note(fd, NT_AUXV, P->auxv, + P->nauxv * sizeof (P->auxv[0]), &doff) != 0) { + goto err; + } +#ifdef _LP64 + } else { + prpsinfo32_t pi32; + auxv32_t *av32; + size_t size = sizeof (auxv32_t) * P->nauxv; + int i; + + mkprpsinfo32(P, &pi32); + if (write_note(fd, NT_PRPSINFO, &pi32, sizeof (prpsinfo32_t), + &doff) != 0) { + goto err; + } + + if ((av32 = malloc(size)) == NULL) + goto err; + + for (i = 0; i < P->nauxv; i++) { + auxv_n_to_32(&P->auxv[i], &av32[i]); + } + + if (write_note(fd, NT_AUXV, av32, size, &doff) != 0) { + free(av32); + goto err; + } + + free(av32); +#endif /* _LP64 */ + } + + if (write_note(fd, NT_PLATFORM, plat, platlen, &doff) != 0) + goto err; + + if (Plwp_iter_all(P, old_per_lwp, &pgc) != 0) + goto err; + + if (P->status.pr_dmodel == PR_MODEL_ILP32) { + Elf32_Phdr phdr; + + bzero(&phdr, sizeof (phdr)); + phdr.p_type = PT_NOTE; + phdr.p_flags = PF_R; + phdr.p_offset = (Elf32_Off)boff; + phdr.p_filesz = doff - boff; + boff = doff; + + if (pwrite64(fd, &phdr, sizeof (phdr), poff) != sizeof (phdr)) + goto err; + poff += sizeof (phdr); +#ifdef _LP64 + } else { + Elf64_Phdr phdr; + + bzero(&phdr, sizeof (phdr)); + phdr.p_type = PT_NOTE; + phdr.p_flags = PF_R; + phdr.p_offset = boff; + phdr.p_filesz = doff - boff; + boff = doff; + + if (pwrite64(fd, &phdr, sizeof (phdr), poff) != sizeof (phdr)) + goto err; + poff += sizeof (phdr); +#endif /* _LP64 */ + } + + /* + * Construct the new-style note header and section. + */ + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (write_note(fd, NT_PSINFO, &P->psinfo, sizeof (psinfo_t), + &doff) != 0) { + goto err; + } + if (write_note(fd, NT_PSTATUS, &P->status, sizeof (pstatus_t), + &doff) != 0) { + goto err; + } + if (write_note(fd, NT_AUXV, P->auxv, + P->nauxv * sizeof (P->auxv[0]), &doff) != 0) { + goto err; + } +#ifdef _LP64 + } else { + psinfo32_t pi32; + pstatus32_t ps32; + auxv32_t *av32; + size_t size = sizeof (auxv32_t) * P->nauxv; + int i; + + psinfo_n_to_32(&P->psinfo, &pi32); + if (write_note(fd, NT_PSINFO, &pi32, sizeof (psinfo32_t), + &doff) != 0) { + goto err; + } + pstatus_n_to_32(&P->status, &ps32); + if (write_note(fd, NT_PSTATUS, &ps32, sizeof (pstatus32_t), + &doff) != 0) { + goto err; + } + if ((av32 = malloc(size)) == NULL) + goto err; + + for (i = 0; i < P->nauxv; i++) { + auxv_n_to_32(&P->auxv[i], &av32[i]); + } + + if (write_note(fd, NT_AUXV, av32, size, &doff) != 0) { + free(av32); + goto err; + } + + free(av32); +#endif /* _LP64 */ + } + + if (write_note(fd, NT_PLATFORM, plat, platlen, &doff) != 0 || + write_note(fd, NT_UTSNAME, &uts, sizeof (uts), &doff) != 0 || + write_note(fd, NT_CONTENT, &content, sizeof (content), &doff) != 0) + goto err; + + { + prcred_t cred, *cp; + size_t size = sizeof (prcred_t); + + if (Pcred(P, &cred, 0) != 0) + goto err; + + if (cred.pr_ngroups > 0) + size += sizeof (gid_t) * (cred.pr_ngroups - 1); + if ((cp = malloc(size)) == NULL) + goto err; + + if (Pcred(P, cp, cred.pr_ngroups) != 0 || + write_note(fd, NT_PRCRED, cp, size, &doff) != 0) { + free(cp); + goto err; + } + + free(cp); + } + + { + prpriv_t *ppriv; + const priv_impl_info_t *pinfo; + size_t pprivsz, pinfosz; + + if ((ppriv = proc_get_priv(P->pid)) == NULL) + goto err; + pprivsz = PRIV_PRPRIV_SIZE(ppriv); + + if (write_note(fd, NT_PRPRIV, ppriv, pprivsz, &doff) != 0) { + free(ppriv); + goto err; + } + free(ppriv); + + if ((pinfo = getprivimplinfo()) == NULL) + goto err; + pinfosz = PRIV_IMPL_INFO_SIZE(pinfo); + + if (write_note(fd, NT_PRPRIVINFO, pinfo, pinfosz, &doff) != 0) + goto err; + } + + if (write_note(fd, NT_ZONENAME, zonename, strlen(zonename) + 1, + &doff) != 0) + goto err; + +#if defined(__i386) || defined(__amd64) + /* CSTYLED */ + { + struct ssd *ldtp; + size_t size; + int nldt; + + nldt = Pldt(P, NULL, 0); + size = sizeof (struct ssd) * nldt; + if ((ldtp = malloc(size)) == NULL) + goto err; + + if (Pldt(P, ldtp, nldt) == -1 || + write_note(fd, NT_LDT, ldtp, size, &doff) != 0) { + free(ldtp); + goto err; + } + + free(ldtp); + } +#endif /* __i386 || __amd64 */ + + if (Plwp_iter_all(P, new_per_lwp, &pgc) != 0) + goto err; + + if (P->status.pr_dmodel == PR_MODEL_ILP32) { + Elf32_Phdr phdr; + + bzero(&phdr, sizeof (phdr)); + phdr.p_type = PT_NOTE; + phdr.p_flags = PF_R; + phdr.p_offset = (Elf32_Off)boff; + phdr.p_filesz = doff - boff; + boff = doff; + + if (pwrite64(fd, &phdr, sizeof (phdr), poff) != sizeof (phdr)) + goto err; + poff += sizeof (phdr); +#ifdef _LP64 + } else { + Elf64_Phdr phdr; + + bzero(&phdr, sizeof (phdr)); + phdr.p_type = PT_NOTE; + phdr.p_flags = PF_R; + phdr.p_offset = boff; + phdr.p_filesz = doff - boff; + boff = doff; + + if (pwrite64(fd, &phdr, sizeof (phdr), poff) != sizeof (phdr)) + goto err; + poff += sizeof (phdr); +#endif /* _LP64 */ + } + + /* + * Construct the headers for each mapping and write out its data + * if the content parameter indicates that it should be present + * in the core file. + */ + if (Pmapping_iter(P, dump_map, &pgc) != 0) + goto err; + + if (dump_sections(&pgc) != 0) + goto err; + + if (write_shstrtab(P, &pgc) != 0) + goto err; + + free(pgc.pgc_chunk); + + return (0); + +err: + /* + * Wipe out anything we may have written if there was an error. + */ + (void) ftruncate64(fd, 0); + free(pgc.pgc_chunk); + return (-1); +} + +static const char *content_str[] = { + "stack", /* CC_CONTENT_STACK */ + "heap", /* CC_CONTENT_HEAP */ + "shfile", /* CC_CONTENT_SHFILE */ + "shanon", /* CC_CONTENT_SHANON */ + "text", /* CC_CONTENT_TEXT */ + "data", /* CC_CONTENT_DATA */ + "rodata", /* CC_CONTENT_RODATA */ + "anon", /* CC_CONTENT_ANON */ + "shm", /* CC_CONTENT_SHM */ + "ism", /* CC_CONTENT_ISM */ + "dism", /* CC_CONTENT_DISM */ + "ctf", /* CC_CONTENT_CTF */ + "symtab", /* CC_CONTENT_SYMTAB */ +}; + +static uint_t ncontent_str = sizeof (content_str) / sizeof (content_str[0]); + +#define STREQ(a, b, n) (strlen(b) == (n) && strncmp(a, b, n) == 0) + +int +proc_str2content(const char *str, core_content_t *cp) +{ + const char *cur = str; + int add = 1; + core_content_t mask, content = 0; + + for (;;) { + for (cur = str; isalpha(*cur); cur++) + continue; + + if (STREQ(str, "default", cur - str)) { + mask = CC_CONTENT_DEFAULT; + } else if (STREQ(str, "all", cur - str)) { + mask = CC_CONTENT_ALL; + } else if (STREQ(str, "none", cur - str)) { + mask = 0; + } else { + int i = 0; + + while (!STREQ(str, content_str[i], cur - str)) { + i++; + + if (i >= ncontent_str) + return (-1); + } + + mask = (core_content_t)1 << i; + } + + if (add) + content |= mask; + else + content &= ~mask; + + switch (*cur) { + case '\0': + *cp = content; + return (0); + case '+': + add = 1; + break; + case '-': + add = 0; + break; + default: + return (-1); + } + + str = cur + 1; + } +} + +static int +popc(core_content_t x) +{ + int i; + + for (i = 0; x != 0; i++) + x &= x - 1; + + return (i); +} + +int +proc_content2str(core_content_t content, char *buf, size_t size) +{ + int nonecnt, defcnt, allcnt; + core_content_t mask, bit; + int first; + uint_t index; + size_t n, tot = 0; + + if (content == 0) + return ((int)strlcpy(buf, "none", size)); + + if (content & ~CC_CONTENT_ALL) + return ((int)strlcpy(buf, "<invalid>", size)); + + nonecnt = popc(content); + defcnt = 1 + popc(content ^ CC_CONTENT_DEFAULT); + allcnt = 1 + popc(content ^ CC_CONTENT_ALL); + + if (defcnt <= nonecnt && defcnt <= allcnt) { + mask = content ^ CC_CONTENT_DEFAULT; + first = 0; + tot += (n = strlcpy(buf, "default", size)); + if (n > size) + n = size; + buf += n; + size -= n; + } else if (allcnt < nonecnt) { + mask = content ^ CC_CONTENT_ALL; + first = 0; + tot += (n = strlcpy(buf, "all", size)); + if (n > size) + n = size; + buf += n; + size -= n; + } else { + mask = content; + first = 1; + } + + while (mask != 0) { + bit = mask ^ (mask & (mask - 1)); + + if (!first) { + if (size > 1) { + *buf = (bit & content) ? '+' : '-'; + buf++; + size--; + } + + tot++; + } + index = popc(bit - 1); + tot += (n = strlcpy(buf, content_str[index], size)); + if (n > size) + n = size; + buf += n; + size -= n; + + mask ^= bit; + first = 0; + } + + return ((int)tot); +} diff --git a/usr/src/lib/libproc/common/Pidle.c b/usr/src/lib/libproc/common/Pidle.c new file mode 100644 index 0000000000..bf05e6a2e9 --- /dev/null +++ b/usr/src/lib/libproc/common/Pidle.c @@ -0,0 +1,259 @@ +/* + * 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 <stdlib.h> +#include <libelf.h> +#include <libgen.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <sys/sysmacros.h> + +#include "Pcontrol.h" + +static ssize_t +Pread_idle(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) +{ + size_t resid = n; + + while (resid > 0) { + map_info_t *mp; + uintptr_t mapoff; + ssize_t len; + off64_t off; + + if ((mp = Paddr2mptr(P, addr)) == NULL) + break; + + mapoff = addr - mp->map_pmap.pr_vaddr; + len = MIN(resid, mp->map_pmap.pr_size - mapoff); + off = mp->map_offset + mapoff; + + if ((len = pread64(P->asfd, buf, len, off)) <= 0) + break; + + resid -= len; + addr += len; + buf = (char *)buf + len; + } + + return (n - resid); +} + +/*ARGSUSED*/ +static ssize_t +Pwrite_idle(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) +{ + errno = EIO; + return (-1); +} + +static const ps_rwops_t P_idle_ops = { + Pread_idle, + Pwrite_idle +}; + +static int +idle_add_mapping(struct ps_prochandle *P, GElf_Phdr *php, file_info_t *fp) +{ + prmap_t pmap; + + dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n", + (u_longlong_t)php->p_vaddr, (u_longlong_t)php->p_filesz, + (u_longlong_t)php->p_memsz, (u_longlong_t)php->p_offset); + + pmap.pr_vaddr = (uintptr_t)php->p_vaddr; + pmap.pr_size = php->p_filesz; + (void) strncpy(pmap.pr_mapname, fp->file_pname, + sizeof (pmap.pr_mapname)); + pmap.pr_offset = php->p_offset; + + pmap.pr_mflags = 0; + if (php->p_flags & PF_R) + pmap.pr_mflags |= MA_READ; + if (php->p_flags & PF_W) + pmap.pr_mflags |= MA_WRITE; + if (php->p_flags & PF_X) + pmap.pr_mflags |= MA_EXEC; + + pmap.pr_pagesize = 0; + pmap.pr_shmid = -1; + + return (Padd_mapping(P, php->p_offset, fp, &pmap)); +} + +struct ps_prochandle * +Pgrab_file(const char *fname, int *perr) +{ + struct ps_prochandle *P = NULL; + GElf_Ehdr ehdr; + Elf *elf = NULL; + file_info_t *fp = NULL; + int fd; + int i; + + if ((fd = open64(fname, O_RDONLY)) < 0) { + dprintf("couldn't open file"); + *perr = (errno == ENOENT) ? G_NOEXEC : G_STRANGE; + return (NULL); + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + dprintf("libproc ELF version is more recent than libelf"); + *perr = G_ELF; + goto err; + } + + if ((P = calloc(1, sizeof (struct ps_prochandle))) == NULL) { + *perr = G_STRANGE; + goto err; + } + + (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL); + P->state = PS_IDLE; + P->pid = (pid_t)-1; + P->asfd = fd; + P->ctlfd = -1; + P->statfd = -1; + P->agentctlfd = -1; + P->agentstatfd = -1; + P->info_valid = -1; + P->ops = &P_idle_ops; + Pinitsym(P); + + if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + *perr = G_ELF; + return (NULL); + } + + /* + * Construct a file_info_t that corresponds to this file. + */ + if ((fp = calloc(1, sizeof (file_info_t))) == NULL) { + *perr = G_STRANGE; + goto err; + } + + if ((fp->file_lo = calloc(1, sizeof (rd_loadobj_t))) == NULL) { + *perr = G_STRANGE; + goto err; + } + + if (*fname == '/') { + (void) strncpy(fp->file_pname, fname, sizeof (fp->file_pname)); + } else { + size_t sz; + + if (getcwd(fp->file_pname, sizeof (fp->file_pname) - 1) == + NULL) { + *perr = G_STRANGE; + goto err; + } + + sz = strlen(fp->file_pname); + (void) snprintf(&fp->file_pname[sz], + sizeof (fp->file_pname) - sz, "/%s", fname); + } + + fp->file_fd = fd; + fp->file_lo->rl_lmident = LM_ID_BASE; + fp->file_lname = strdup(fp->file_pname); + fp->file_lbase = basename(fp->file_lname); + + P->execname = strdup(fp->file_pname); + + P->num_files++; + list_link(fp, &P->file_head); + + if (gelf_getehdr(elf, &ehdr) == NULL) { + *perr = G_STRANGE; + goto err; + } + + dprintf("Pgrab_file: ehdr.e_phnum = %d\n", ehdr.e_phnum); + + /* + * Sift through the program headers making the relevant maps. + */ + for (i = 0; i < ehdr.e_phnum; i++) { + GElf_Phdr phdr, *php; + + if ((php = gelf_getphdr(elf, i, &phdr)) == NULL) { + *perr = G_STRANGE; + goto err; + } + + if (php->p_type != PT_LOAD) + continue; + + if (idle_add_mapping(P, php, fp) != 0) { + *perr = G_STRANGE; + goto err; + } + } + Psort_mappings(P); + + (void) elf_end(elf); + + P->map_exec = fp->file_map; + + P->status.pr_flags = PR_STOPPED; + P->status.pr_nlwp = 0; + P->status.pr_pid = (pid_t)-1; + P->status.pr_ppid = (pid_t)-1; + P->status.pr_pgid = (pid_t)-1; + P->status.pr_sid = (pid_t)-1; + P->status.pr_taskid = (taskid_t)-1; + P->status.pr_projid = (projid_t)-1; + switch (ehdr.e_ident[EI_CLASS]) { + case ELFCLASS32: + P->status.pr_dmodel = PR_MODEL_ILP32; + break; + case ELFCLASS64: + P->status.pr_dmodel = PR_MODEL_LP64; + break; + default: + *perr = G_FORMAT; + goto err; + } + + /* + * The file and map lists are complete, and will never need to be + * adjusted. + */ + P->info_valid = 1; + + return (P); +err: + (void) close(fd); + if (P != NULL) + Pfree(P); + if (elf != NULL) + (void) elf_end(elf); + return (NULL); +} diff --git a/usr/src/lib/libproc/common/Pisadep.h b/usr/src/lib/libproc/common/Pisadep.h new file mode 100644 index 0000000000..1d7e785ae3 --- /dev/null +++ b/usr/src/lib/libproc/common/Pisadep.h @@ -0,0 +1,101 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PISADEP_H +#define _PISADEP_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Internal ISA-dependent functions. + * + * Note that some ISA-dependent functions are exposed to applications, and found + * in libproc.h: + * + * Ppltdest() + * Pissyscall_prev() + * Pstack_iter() + */ + +/* + * ISA dependent function to determine if the instruction at the given address + * is a syscall instruction. On x86, we have multiple system call instructions. + * this function returns 1 if there is a system call at the given address, 2 if + * there is a less preferred system call, and 0 if there is no system call + * there. + */ +extern int Pissyscall(struct ps_prochandle *, uintptr_t); +/* + * Works the same way as Pissyscall(), except operates on an in-memory buffer. + */ +extern int Pissyscall_text(struct ps_prochandle *, const void *buf, + size_t buflen); + +#if defined(__amd64) +/* amd64 stack doubleword aligned, unaligned in 32-bit mode */ +#define PSTACK_ALIGN32(sp) ((sp) & ~(2 * sizeof (int64_t) - 1)) +#define PSTACK_ALIGN64(sp) (sp) +#elif defined(__i386) +/* i386 stack is unaligned */ +#define PSTACK_ALIGN32(sp) (sp) +#define PSTACK_ALIGN64(sp) ALIGN32(sp) +#elif defined(__sparc) +/* sparc stack is doubleword aligned for 64-bit values */ +#define PSTACK_ALIGN32(sp) ((sp) & ~(2 * sizeof (int32_t) - 1)) +#define PSTACK_ALIGN64(sp) ((sp) & ~(2 * sizeof (int64_t) - 1)) +#else +#error Unknown ISA +#endif + +/* + * Given an argument count, stack pointer, and syscall index, sets up the stack + * and appropriate registers. The stack pointer should be the top of the stack + * area, after any space reserved for arguments passed by reference. Returns a + * pointer which is later passed to Psyscall_copyargs(). + */ +extern uintptr_t Psyscall_setup(struct ps_prochandle *, int, int, uintptr_t); + +/* + * Copies all arguments out to the stack once we're stopped before the syscall. + */ +extern int Psyscall_copyinargs(struct ps_prochandle *, int, argdes_t *, + uintptr_t); + +/* + * Copies out arguments to their original values. + */ +extern int Psyscall_copyoutargs(struct ps_prochandle *, int, argdes_t *, + uintptr_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _PISADEP_H */ diff --git a/usr/src/lib/libproc/common/Pisprocdir.c b/usr/src/lib/libproc/common/Pisprocdir.c new file mode 100644 index 0000000000..b617b2edda --- /dev/null +++ b/usr/src/lib/libproc/common/Pisprocdir.c @@ -0,0 +1,62 @@ +/* + * 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 1997-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "Pcontrol.h" + +/* + * Return TRUE iff dir is the /proc directory. + */ +int +Pisprocdir(struct ps_prochandle *Pr, const char *dir) +{ + char path[PATH_MAX]; + struct stat statb; + struct statvfs statvfsb; + + if (*dir == '/') + (void) snprintf(path, sizeof (path), "/proc/%d/root%s", + (int)Pr->pid, dir); + else + (void) snprintf(path, sizeof (path), "/proc/%d/cwd/%s", + (int)Pr->pid, dir); + + /* + * We can't compare the statb.st_fstype string to "proc" because + * a loop-back mount of /proc would show "lofs" instead of "proc". + * Instead we use the statvfs() f_basetype string. + */ + return (stat(path, &statb) == 0 && + statvfs(path, &statvfsb) == 0 && + (statb.st_mode & S_IFMT) == S_IFDIR && + statb.st_ino == 2 && + strcmp(statvfsb.f_basetype, "proc") == 0); +} diff --git a/usr/src/lib/libproc/common/Plwpregs.c b/usr/src/lib/libproc/common/Plwpregs.c new file mode 100644 index 0000000000..bb16b50370 --- /dev/null +++ b/usr/src/lib/libproc/common/Plwpregs.c @@ -0,0 +1,500 @@ +/* + * 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/types.h> +#include <sys/uio.h> +#include <string.h> +#include <errno.h> + +#include "Pcontrol.h" +#include "P32ton.h" + +/* + * This file implements the routines to read and write per-lwp register + * information from either a live process or core file opened with libproc. + * We build up a few common routines for reading and writing register + * information, and then the public functions are all trivial calls to these. + */ + +/* + * Utility function to return a pointer to the structure of cached information + * about an lwp in the core file, given its lwpid. + */ +static lwp_info_t * +getlwpcore(struct ps_prochandle *P, lwpid_t lwpid) +{ + lwp_info_t *lwp = list_next(&P->core->core_lwp_head); + uint_t i; + + for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { + if (lwp->lwp_id == lwpid) + return (lwp); + } + + errno = EINVAL; + return (NULL); +} + +/* + * Utility function to open and read the contents of a per-lwp /proc file. + * This function is used to slurp in lwpstatus, xregs, and asrs. + */ +static int +getlwpfile(struct ps_prochandle *P, lwpid_t lwpid, + const char *fbase, void *rp, size_t n) +{ + char fname[64]; + int fd; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/lwp/%d/%s", + (int)P->status.pr_pid, (int)lwpid, fbase); + + if ((fd = open(fname, O_RDONLY)) >= 0) { + if (read(fd, rp, n) > 0) { + (void) close(fd); + return (0); + } + (void) close(fd); + } + return (-1); +} + +/* + * Get the lwpstatus_t for an lwp from either the live process or our + * cached information from the core file. This is used to get the + * general-purpose registers or floating point registers. + */ +int +getlwpstatus(struct ps_prochandle *P, lwpid_t lwpid, lwpstatus_t *lps) +{ + lwp_info_t *lwp; + + /* + * For both live processes and cores, our job is easy if the lwpid + * matches that of the representative lwp: + */ + if (P->status.pr_lwp.pr_lwpid == lwpid) { + (void) memcpy(lps, &P->status.pr_lwp, sizeof (lwpstatus_t)); + return (0); + } + + /* + * If this is a live process, then just read the information out + * of the per-lwp status file: + */ + if (P->state != PS_DEAD) { + return (getlwpfile(P, lwpid, "lwpstatus", + lps, sizeof (lwpstatus_t))); + } + + /* + * If this is a core file, we need to iterate through our list of + * cached lwp information and then copy out the status. + */ + if (P->core != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) { + (void) memcpy(lps, &lwp->lwp_status, sizeof (lwpstatus_t)); + return (0); + } + + return (-1); +} + +/* + * Utility function to modify lwp registers. This is done using either the + * process control file or per-lwp control file as necessary. + */ +static int +setlwpregs(struct ps_prochandle *P, lwpid_t lwpid, long cmd, + const void *rp, size_t n) +{ + iovec_t iov[2]; + char fname[64]; + int fd; + + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + iov[0].iov_base = (caddr_t)&cmd; + iov[0].iov_len = sizeof (long); + iov[1].iov_base = (caddr_t)rp; + iov[1].iov_len = n; + + /* + * Writing the process control file writes the representative lwp. + * Psync before we write to make sure we are consistent with the + * primary interfaces. Similarly, make sure to update P->status + * afterward if we are modifying one of its register sets. + */ + if (P->status.pr_lwp.pr_lwpid == lwpid) { + Psync(P); + + if (writev(P->ctlfd, iov, 2) == -1) + return (-1); + + if (cmd == PCSREG) + (void) memcpy(P->status.pr_lwp.pr_reg, rp, n); + else if (cmd == PCSFPREG) + (void) memcpy(&P->status.pr_lwp.pr_fpreg, rp, n); + + return (0); + } + + /* + * If the lwp we want is not the representative lwp, we need to + * open the ctl file for that specific lwp. + */ + (void) snprintf(fname, sizeof (fname), "/proc/%d/lwp/%d/lwpctl", + (int)P->status.pr_pid, (int)lwpid); + + if ((fd = open(fname, O_WRONLY)) >= 0) { + if (writev(fd, iov, 2) > 0) { + (void) close(fd); + return (0); + } + (void) close(fd); + } + return (-1); +} + +int +Plwp_getregs(struct ps_prochandle *P, lwpid_t lwpid, prgregset_t gregs) +{ + lwpstatus_t lps; + + if (getlwpstatus(P, lwpid, &lps) == -1) + return (-1); + + (void) memcpy(gregs, lps.pr_reg, sizeof (prgregset_t)); + return (0); +} + +int +Plwp_setregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t gregs) +{ + return (setlwpregs(P, lwpid, PCSREG, gregs, sizeof (prgregset_t))); +} + +int +Plwp_getfpregs(struct ps_prochandle *P, lwpid_t lwpid, prfpregset_t *fpregs) +{ + lwpstatus_t lps; + + if (getlwpstatus(P, lwpid, &lps) == -1) + return (-1); + + (void) memcpy(fpregs, &lps.pr_fpreg, sizeof (prfpregset_t)); + return (0); +} + +int Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid, + const prfpregset_t *fpregs) +{ + return (setlwpregs(P, lwpid, PCSFPREG, fpregs, sizeof (prfpregset_t))); +} + +#if defined(sparc) || defined(__sparc) +int +Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t *xregs) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + return (getlwpfile(P, lwpid, "xregs", + xregs, sizeof (prxregset_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL) { + (void) memcpy(xregs, lwp->lwp_xregs, sizeof (prxregset_t)); + return (0); + } + + if (lwp != NULL) + errno = ENODATA; + return (-1); +} + +int +Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs) +{ + return (setlwpregs(P, lwpid, PCSXREG, xregs, sizeof (prxregset_t))); +} + +int +Plwp_getgwindows(struct ps_prochandle *P, lwpid_t lwpid, gwindows_t *gwins) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + return (getlwpfile(P, lwpid, "gwindows", + gwins, sizeof (gwindows_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_gwins != NULL) { + *gwins = *lwp->lwp_gwins; + return (0); + } + + if (lwp != NULL) + errno = ENODATA; + return (-1); +} + +#if defined(__sparcv9) +int +Plwp_getasrs(struct ps_prochandle *P, lwpid_t lwpid, asrset_t asrs) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (P->state != PS_STOP) { + errno = EBUSY; + return (-1); + } + + return (getlwpfile(P, lwpid, "asrs", asrs, sizeof (asrset_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_asrs != NULL) { + (void) memcpy(asrs, lwp->lwp_asrs, sizeof (asrset_t)); + return (0); + } + + if (lwp != NULL) + errno = ENODATA; + return (-1); + +} + +int +Plwp_setasrs(struct ps_prochandle *P, lwpid_t lwpid, const asrset_t asrs) +{ + return (setlwpregs(P, lwpid, PCSASRS, asrs, sizeof (asrset_t))); +} +#endif /* __sparcv9 */ +#endif /* __sparc */ + +int +Plwp_getpsinfo(struct ps_prochandle *P, lwpid_t lwpid, lwpsinfo_t *lps) +{ + lwp_info_t *lwp; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + return (getlwpfile(P, lwpid, "lwpsinfo", + lps, sizeof (lwpsinfo_t))); + } + + if ((lwp = getlwpcore(P, lwpid)) != NULL) { + (void) memcpy(lps, &lwp->lwp_psinfo, sizeof (lwpsinfo_t)); + return (0); + } + + return (-1); +} + +int +Plwp_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp) +{ + uintptr_t addr; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + lwpstatus_t ls; + if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0) + return (-1); + addr = ls.pr_ustack; + } else { + lwp_info_t *lwp; + if ((lwp = getlwpcore(P, lwpid)) == NULL) + return (-1); + addr = lwp->lwp_status.pr_ustack; + } + + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp)) + return (-1); +#ifdef _LP64 + } else { + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32)) + return (-1); + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); +} + +int +Plwp_main_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp) +{ + uintptr_t addr; + lwpstatus_t ls; + + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0) + return (-1); + } else { + lwp_info_t *lwp; + if ((lwp = getlwpcore(P, lwpid)) == NULL) + return (-1); + ls = lwp->lwp_status; + } + + addr = ls.pr_ustack; + + /* + * Read out the current stack; if the SS_ONSTACK flag is set then + * this LWP is operating on the alternate signal stack. We can + * recover the original stack from pr_oldcontext. + */ + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + if (Pread(P, stkp, sizeof (*stkp), addr) != sizeof (*stkp)) + return (-1); + + if (stkp->ss_flags & SS_ONSTACK) + goto on_altstack; +#ifdef _LP64 + } else { + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), addr) != sizeof (stk32)) + return (-1); + + if (stk32.ss_flags & SS_ONSTACK) + goto on_altstack; + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); + +on_altstack: + + if (P->status.pr_dmodel == PR_MODEL_NATIVE) { + ucontext_t *ctxp = (void *)ls.pr_oldcontext; + + if (Pread(P, stkp, sizeof (*stkp), + (uintptr_t)&ctxp->uc_stack) != sizeof (*stkp)) + return (-1); +#ifdef _LP64 + } else { + ucontext32_t *ctxp = (void *)ls.pr_oldcontext; + stack32_t stk32; + + if (Pread(P, &stk32, sizeof (stk32), + (uintptr_t)&ctxp->uc_stack) != sizeof (stk32)) + return (-1); + + stack_32_to_n(&stk32, stkp); +#endif + } + + return (0); +} + +int +Plwp_alt_stack(struct ps_prochandle *P, lwpid_t lwpid, stack_t *stkp) +{ + if (P->state == PS_IDLE) { + errno = ENODATA; + return (-1); + } + + if (P->state != PS_DEAD) { + lwpstatus_t ls; + + if (getlwpfile(P, lwpid, "lwpstatus", &ls, sizeof (ls)) != 0) + return (-1); + + if (ls.pr_altstack.ss_flags & SS_DISABLE) { + errno = ENODATA; + return (-1); + } + + *stkp = ls.pr_altstack; + } else { + lwp_info_t *lwp; + + if ((lwp = getlwpcore(P, lwpid)) == NULL) + return (-1); + + if (lwp->lwp_status.pr_altstack.ss_flags & SS_DISABLE) { + errno = ENODATA; + return (-1); + } + + *stkp = lwp->lwp_status.pr_altstack; + } + + return (0); +} diff --git a/usr/src/lib/libproc/common/Pscantext.c b/usr/src/lib/libproc/common/Pscantext.c new file mode 100644 index 0000000000..d4cb853254 --- /dev/null +++ b/usr/src/lib/libproc/common/Pscantext.c @@ -0,0 +1,183 @@ +/* + * 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 2004 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 <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "libproc.h" +#include "Pcontrol.h" +#include "Pisadep.h" +#include "Putil.h" + +#define BLKSIZE (8 * 1024) + +/* + * Look for a SYSCALL instruction in the process's address space. + */ +int +Pscantext(struct ps_prochandle *P) +{ + char mapfile[100]; + int mapfd; + off_t offset; /* offset in text section */ + off_t endoff; /* ending offset in text section */ + uintptr_t sysaddr; /* address of SYSCALL instruction */ + int syspri; /* priority of SYSCALL instruction */ + int nbytes; /* number of bytes in buffer */ + int n2bytes; /* number of bytes in second buffer */ + int nmappings; /* current number of mappings */ + prmap_t *pdp; /* pointer to map descriptor */ + prmap_t *prbuf; /* buffer for map descriptors */ + unsigned nmap; /* number of map descriptors */ + uint32_t buf[2 * BLKSIZE / sizeof (uint32_t)]; /* text buffer */ + uchar_t *p; + + /* try the most recently-seen syscall address */ + syspri = 0; + sysaddr = 0; + if (P->sysaddr != 0 && + (syspri = Pissyscall(P, P->sysaddr))) + sysaddr = P->sysaddr; + + /* try the previous instruction */ + if (sysaddr == 0 || syspri != 1) + syspri = Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], + &sysaddr); + + if (sysaddr != 0 && syspri == 1) { + P->sysaddr = sysaddr; + return (0); + } + + /* open the /proc/<pid>/map file */ + (void) sprintf(mapfile, "/proc/%d/map", (int)P->pid); + if ((mapfd = open(mapfile, O_RDONLY)) < 0) { + dprintf("failed to open %s: %s\n", mapfile, strerror(errno)); + return (-1); + } + + /* allocate a plausible initial buffer size */ + nmap = 50; + + /* read all the map structures, allocating more space as needed */ + for (;;) { + prbuf = malloc(nmap * sizeof (prmap_t)); + if (prbuf == NULL) { + dprintf("Pscantext: failed to allocate buffer\n"); + (void) close(mapfd); + return (-1); + } + nmappings = pread(mapfd, prbuf, nmap * sizeof (prmap_t), 0L); + if (nmappings < 0) { + dprintf("Pscantext: failed to read map file: %s\n", + strerror(errno)); + free(prbuf); + (void) close(mapfd); + return (-1); + } + nmappings /= sizeof (prmap_t); + if (nmappings < nmap) /* we read them all */ + break; + /* allocate a bigger buffer */ + free(prbuf); + nmap *= 2; + } + (void) close(mapfd); + + /* + * Scan each executable mapping looking for a syscall instruction. + * In dynamically linked executables, syscall instructions are + * typically only found in shared libraries. Because shared libraries + * are most often mapped at the top of the address space, we minimize + * our expected search time by starting at the last mapping and working + * our way down to the first mapping. + */ + for (pdp = &prbuf[nmappings - 1]; sysaddr == 0 && syspri != 1 && + pdp >= prbuf; pdp--) { + + offset = (off_t)pdp->pr_vaddr; /* beginning of text */ + endoff = offset + pdp->pr_size; + + /* avoid non-EXEC mappings; avoid the stack and heap */ + if ((pdp->pr_mflags&MA_EXEC) == 0 || + (endoff > P->status.pr_stkbase && + offset < P->status.pr_stkbase + P->status.pr_stksize) || + (endoff > P->status.pr_brkbase && + offset < P->status.pr_brkbase + P->status.pr_brksize)) + continue; + + (void) lseek(P->asfd, (off_t)offset, 0); + + if ((nbytes = read(P->asfd, buf, 2*BLKSIZE)) <= 0) + continue; + + if (nbytes < BLKSIZE) + n2bytes = 0; + else { + n2bytes = nbytes - BLKSIZE; + nbytes = BLKSIZE; + } + + p = (uchar_t *)buf; + + /* search text for a SYSCALL instruction */ + while (sysaddr == 0 && syspri != 1 && offset < endoff) { + if (nbytes <= 0) { /* shift buffers */ + if ((nbytes = n2bytes) <= 0) + break; + (void) memcpy(buf, + &buf[BLKSIZE / sizeof (buf[0])], + nbytes); + n2bytes = 0; + p = (uchar_t *)buf; + if (nbytes == BLKSIZE && + offset + BLKSIZE < endoff) + n2bytes = read(P->asfd, + &buf[BLKSIZE / sizeof (buf[0])], + BLKSIZE); + } + + if (syspri = Pissyscall_text(P, p, nbytes)) + sysaddr = offset; + + p += sizeof (instr_t); + offset += sizeof (instr_t); + nbytes -= sizeof (instr_t); + } + } + + if ((P->sysaddr = sysaddr) != 0) + return (0); + else + return (-1); +} diff --git a/usr/src/lib/libproc/common/Pservice.c b/usr/src/lib/libproc/common/Pservice.c new file mode 100644 index 0000000000..6850696af4 --- /dev/null +++ b/usr/src/lib/libproc/common/Pservice.c @@ -0,0 +1,372 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdarg.h> +#include <string.h> +#include "Pcontrol.h" + +/* + * This file implements the process services declared in <proc_service.h>. + * This enables libproc to be used in conjunction with libc_db and + * librtld_db. As most of these facilities are already provided by + * (more elegant) interfaces in <libproc.h>, we can just call those. + * + * NOTE: We explicitly do *not* implement the functions ps_kill() and + * ps_lrolltoaddr() in this library. The very existence of these functions + * causes libc_db to create an "agent thread" in the target process. + * The only way to turn off this behavior is to omit these functions. + */ + +#pragma weak ps_pdread = ps_pread +#pragma weak ps_ptread = ps_pread +#pragma weak ps_pdwrite = ps_pwrite +#pragma weak ps_ptwrite = ps_pwrite + +ps_err_e +ps_pdmodel(struct ps_prochandle *P, int *modelp) +{ + *modelp = P->status.pr_dmodel; + return (PS_OK); +} + +ps_err_e +ps_pread(struct ps_prochandle *P, psaddr_t addr, void *buf, size_t size) +{ + if (P->ops->p_pread(P, buf, size, addr) != size) + return (PS_BADADDR); + return (PS_OK); +} + +ps_err_e +ps_pwrite(struct ps_prochandle *P, psaddr_t addr, const void *buf, size_t size) +{ + if (P->ops->p_pwrite(P, buf, size, addr) != size) + return (PS_BADADDR); + return (PS_OK); +} + +/* + * libc_db calls matched pairs of ps_pstop()/ps_pcontinue() + * in the belief that the client may have left the process + * running while calling in to the libc_db interfaces. + * + * We interpret the meaning of these functions to be an inquiry + * as to whether the process is stopped, not an action to be + * performed to make it stopped. For similar reasons, we also + * return PS_OK for core files in order to allow libc_db to + * operate on these as well. + */ +ps_err_e +ps_pstop(struct ps_prochandle *P) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + return (PS_OK); +} + +ps_err_e +ps_pcontinue(struct ps_prochandle *P) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + return (PS_OK); +} + +/* + * ps_lstop() and ps_lcontinue() are not called by any code in libc_db + * or librtld_db. We make them behave like ps_pstop() and ps_pcontinue(). + */ +/* ARGSUSED1 */ +ps_err_e +ps_lstop(struct ps_prochandle *P, lwpid_t lwpid) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + return (PS_OK); +} + +/* ARGSUSED1 */ +ps_err_e +ps_lcontinue(struct ps_prochandle *P, lwpid_t lwpid) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + return (PS_OK); +} + +ps_err_e +ps_lgetregs(struct ps_prochandle *P, lwpid_t lwpid, prgregset_t regs) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + + if (Plwp_getregs(P, lwpid, regs) == 0) + return (PS_OK); + + return (PS_BADLID); +} + +ps_err_e +ps_lsetregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t regs) +{ + if (P->state != PS_STOP) + return (PS_ERR); + + if (Plwp_setregs(P, lwpid, regs) == 0) + return (PS_OK); + + return (PS_BADLID); +} + +ps_err_e +ps_lgetfpregs(struct ps_prochandle *P, lwpid_t lwpid, prfpregset_t *regs) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + + if (Plwp_getfpregs(P, lwpid, regs) == 0) + return (PS_OK); + + return (PS_BADLID); +} + +ps_err_e +ps_lsetfpregs(struct ps_prochandle *P, lwpid_t lwpid, const prfpregset_t *regs) +{ + if (P->state != PS_STOP) + return (PS_ERR); + + if (Plwp_setfpregs(P, lwpid, regs) == 0) + return (PS_OK); + + return (PS_BADLID); +} + +#if defined(sparc) || defined(__sparc) + +ps_err_e +ps_lgetxregsize(struct ps_prochandle *P, lwpid_t lwpid, int *xrsize) +{ + char fname[64]; + struct stat statb; + + if (P->state == PS_DEAD) { + lwp_info_t *lwp = list_next(&P->core->core_lwp_head); + uint_t i; + + for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { + if (lwp->lwp_id == lwpid) { + if (lwp->lwp_xregs != NULL) + *xrsize = sizeof (prxregset_t); + else + *xrsize = 0; + return (PS_OK); + } + } + + return (PS_BADLID); + } + + (void) snprintf(fname, sizeof (fname), "/proc/%d/lwp/%d/xregs", + (int)P->status.pr_pid, (int)lwpid); + + if (stat(fname, &statb) != 0) + return (PS_BADLID); + + *xrsize = (int)statb.st_size; + return (PS_OK); +} + +ps_err_e +ps_lgetxregs(struct ps_prochandle *P, lwpid_t lwpid, caddr_t xregs) +{ + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + + /* LINTED - alignment */ + if (Plwp_getxregs(P, lwpid, (prxregset_t *)xregs) == 0) + return (PS_OK); + + return (PS_BADLID); +} + +ps_err_e +ps_lsetxregs(struct ps_prochandle *P, lwpid_t lwpid, caddr_t xregs) +{ + if (P->state != PS_STOP) + return (PS_ERR); + + /* LINTED - alignment */ + if (Plwp_setxregs(P, lwpid, (prxregset_t *)xregs) == 0) + return (PS_OK); + + return (PS_BADLID); +} + +#endif /* sparc */ + +#if defined(__i386) || defined(__amd64) + +ps_err_e +ps_lgetLDT(struct ps_prochandle *P, lwpid_t lwpid, struct ssd *ldt) +{ +#if defined(__amd64) && defined(_LP64) + if (P->status.pr_dmodel != PR_MODEL_NATIVE) { +#endif + prgregset_t regs; + struct ssd *ldtarray; + ps_err_e error; + uint_t gs; + int nldt; + int i; + + if (P->state != PS_STOP && P->state != PS_DEAD) + return (PS_ERR); + + /* + * We need to get the ldt entry that matches the + * value in the lwp's GS register. + */ + if ((error = ps_lgetregs(P, lwpid, regs)) != PS_OK) + return (error); + + gs = regs[GS]; + + if ((nldt = Pldt(P, NULL, 0)) <= 0 || + (ldtarray = malloc(nldt * sizeof (struct ssd))) == NULL) + return (PS_ERR); + if ((nldt = Pldt(P, ldtarray, nldt)) <= 0) { + free(ldtarray); + return (PS_ERR); + } + + for (i = 0; i < nldt; i++) { + if (gs == ldtarray[i].sel) { + *ldt = ldtarray[i]; + break; + } + } + free(ldtarray); + + if (i < nldt) + return (PS_OK); +#if defined(__amd64) && defined(_LP64) + } +#endif + + return (PS_ERR); +} + +#endif /* __i386 || __amd64 */ + +/* + * Libthread_db doesn't use this function currently, but librtld_db uses + * it for its debugging output. We turn this on via rd_log if our debugging + * switch is on, and then echo the messages sent to ps_plog to stderr. + */ +void +ps_plog(const char *fmt, ...) +{ + va_list ap; + + if (_libproc_debug && fmt != NULL && *fmt != '\0') { + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + if (fmt[strlen(fmt) - 1] != '\n') + (void) fputc('\n', stderr); + } +} + +/* + * Store a pointer to our internal copy of the aux vector at the address + * specified by the caller. It should not hold on to this data for too long. + */ +ps_err_e +ps_pauxv(struct ps_prochandle *P, const auxv_t **aux) +{ + if (P->auxv == NULL) + Preadauxvec(P); + + if (P->auxv == NULL) + return (PS_ERR); + + *aux = (const auxv_t *)P->auxv; + return (PS_OK); +} + +/* + * Search for a symbol by name and return the corresponding address. + */ +ps_err_e +ps_pglobal_lookup(struct ps_prochandle *P, const char *object_name, + const char *sym_name, psaddr_t *sym_addr) +{ + GElf_Sym sym; + + if (Plookup_by_name(P, object_name, sym_name, &sym) == 0) { + dprintf("pglobal_lookup <%s> -> %p\n", + sym_name, (void *)(uintptr_t)sym.st_value); + *sym_addr = (psaddr_t)sym.st_value; + return (PS_OK); + } + + return (PS_NOSYM); +} + +/* + * Search for a symbol by name and return the corresponding symbol + * information. If we're compiled _LP64, we just call Plookup_by_name + * and return because ps_sym_t is defined to be an Elf64_Sym, which + * is the same as a GElf_Sym. In the _ILP32 case, we have to convert + * Plookup_by_name's result back to a ps_sym_t (which is an Elf32_Sym). + */ +ps_err_e +ps_pglobal_sym(struct ps_prochandle *P, const char *object_name, + const char *sym_name, ps_sym_t *symp) +{ +#if defined(_ILP32) + GElf_Sym sym; + + if (Plookup_by_name(P, object_name, sym_name, &sym) == 0) { + symp->st_name = (Elf32_Word)sym.st_name; + symp->st_value = (Elf32_Addr)sym.st_value; + symp->st_size = (Elf32_Word)sym.st_size; + symp->st_info = ELF32_ST_INFO( + GELF_ST_BIND(sym.st_info), GELF_ST_TYPE(sym.st_info)); + symp->st_other = sym.st_other; + symp->st_shndx = sym.st_shndx; + return (PS_OK); + } + +#elif defined(_LP64) + if (Plookup_by_name(P, object_name, sym_name, symp) == 0) + return (PS_OK); +#endif + return (PS_NOSYM); +} diff --git a/usr/src/lib/libproc/common/Pstack.c b/usr/src/lib/libproc/common/Pstack.c new file mode 100644 index 0000000000..f0f8b5ce8d --- /dev/null +++ b/usr/src/lib/libproc/common/Pstack.c @@ -0,0 +1,288 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Pstack.c + * + * Common helper functions for stack walking. The ISA-specific code is found in + * Pstack_iter() in Pisadep.c. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "libproc.h" +#include "Pcontrol.h" +#include "P32ton.h" +#include "Pstack.h" + +/* + * Utility function to prevent stack loops from running on forever by + * detecting when there is a stack loop (the %fp has been seen before). + */ +int +stack_loop(prgreg_t fp, prgreg_t **prevfpp, int *nfpp, uint_t *pfpsizep) +{ + prgreg_t *prevfp = *prevfpp; + uint_t pfpsize = *pfpsizep; + int nfp = *nfpp; + int i; + + for (i = 0; i < nfp; i++) { + if (fp == prevfp[i]) + return (1); /* stack loop detected */ + } + + if (nfp == pfpsize) { + pfpsize = pfpsize ? pfpsize * 2 : 16; + prevfp = realloc(prevfp, pfpsize * sizeof (prgreg_t)); + /* + * Just assume there is no loop in the face of allocation + * failure; the caller still has the original prevfp pointer. + */ + if (prevfp == NULL) + return (0); + } + + prevfp[nfp++] = fp; + *prevfpp = prevfp; + *pfpsizep = pfpsize; + *nfpp = nfp; + + return (0); +} + +/* + * Signal Frame Detection + * + * In order to facilitate detection and processing of signal handler frames + * during a stack backtrace, we define a set of utility routines to operate on + * a uclist (ucontext address list), and then use these routines in the various + * implementations of Pstack_iter below. Certain source-level debuggers and + * virtual machines that shall remain nameless believe that in order to detect + * signal handler frames, one must hard-code checks for symbol names defined + * in libc and libthread and knowledge of their implementation. We make no + * such assumptions, allowing us to operate on programs that manipulate their + * underlying kernel signal handlers (i.e. use __sigaction) and to not require + * changes in the face of future library modifications. + * + * A signal handler frame is essentially a set of data pushed on to the user + * stack by the kernel prior to returning to the user program in one of the + * pre-defined signal handlers. The signal handler itself receives the signal + * number, an optional pointer to a siginfo_t, and a pointer to the interrupted + * ucontext as arguments. When performing a stack backtrace, we would like to + * detect these frames so that we can correctly return the interrupted program + * counter and frame pointer as a separate frame. When a signal handler frame + * is constructed on the stack by the kernel, the signalled LWP has its + * lwp_oldcontext member (exported through /proc as lwpstatus.pr_oldcontext) + * set to the user address at which the ucontext_t was placed on the LWP's + * stack. The ucontext_t's uc_link member is set to the previous value of + * lwp_oldcontext. Thus when signal handlers are active, pr_oldcontext will + * point to the first element of a linked list of ucontext_t addresses. + * + * The stack layout for a signal handler frame is as follows: + * + * SPARC v7/v9: Intel ia32: + * +--------------+ - high +--------------+ - + * | struct fq | ^ addrs | siginfo_t | optional + * +--------------+ | ^ +--------------+ - + * | gwindows_t | | | ucontext_t | ^ + * +--------------+ optional +--------------+ | + * | siginfo_t | | ucontext_t * | | + * +--------------+ | | +--------------+ + * | xregs data | v v | siginfo_t * | mandatory + * +--------------+ - low +--------------+ + * | ucontext_t | ^ addrs | int (signo) | | + * +--------------+ mandatory +--------------+ | + * | struct frame | v | struct frame | v + * +--------------+ - <- %sp on resume +--------------+ - <- %esp on resume + * + * amd64 (64-bit): + * +--------------+ - + * | siginfo_t | optional + * +--------------+ - + * | ucontext_t | ^ + * +--------------+ | + * | siginfo_t * | + * +--------------+ mandatory + * | int (signo) | + * +--------------+ | + * | struct frame | v + * +--------------+ - <- %rsp on resume + * + * The bottom-most struct frame is actually constructed by the kernel by + * copying the previous stack frame, allowing naive backtrace code to simply + * skip over the interrupted frame. The copied frame is never really used, + * since it is presumed the libc or libthread signal handler wrapper function + * will explicitly setcontext(2) to the interrupted context if the user + * program's handler returns. If we detect a signal handler frame, we simply + * read the interrupted context structure from the stack, use its embedded + * gregs to construct the register set for the interrupted frame, and then + * continue our backtrace. Detecting the frame itself is easy according to + * the diagram ("oldcontext" represents any element in the uc_link chain): + * + * On SPARC v7 or v9: + * %fp + sizeof (struct frame) == oldcontext + * + * On Intel ia32: + * %ebp + sizeof (struct frame) + (3 * regsize) == oldcontext + * + * On amd64: + * %rbp + sizeof (struct frame) + (2 * regsize) == oldcontext + * + * A final complication is that we want libproc to support backtraces from + * arbitrary addresses without the caller passing in an LWP id. To do this, + * we must first determine all the known oldcontexts by iterating over all + * LWPs and following their pr_oldcontext pointers. We optimize our search + * by discarding NULL pointers and pointers whose value is less than that + * of the initial stack pointer (since stacks grow down from high memory), + * and then sort the resulting list by virtual address so we can binary search. + */ + +int +load_uclist(uclist_t *ucl, const lwpstatus_t *psp) +{ + struct ps_prochandle *P = ucl->uc_proc; + uintptr_t addr = psp->pr_oldcontext; + + uintptr_t *new_addrs; + uint_t new_size, i; + ucontext_t uc; + + if (addr == NULL) + return (0); + + for (;;) { + if (ucl->uc_nelems == ucl->uc_size) { + new_size = ucl->uc_size ? ucl->uc_size * 2 : 16; + new_addrs = realloc(ucl->uc_addrs, + new_size * sizeof (uintptr_t)); + + if (new_addrs != NULL) { + ucl->uc_addrs = new_addrs; + ucl->uc_size = new_size; + } else + break; /* abort if allocation failure */ + } +#ifdef _LP64 + if (P->status.pr_dmodel == PR_MODEL_ILP32) { + ucontext32_t u32; + + if (Pread(P, &u32, sizeof (u32), addr) != sizeof (u32)) + break; /* abort if we fail to read ucontext */ + uc.uc_link = (ucontext_t *)(uintptr_t)u32.uc_link; + } else +#endif + if (Pread(P, &uc, sizeof (uc), addr) != sizeof (uc)) + break; /* abort if we fail to read ucontext */ + + dprintf("detected lwp %d signal context at %p\n", + (int)psp->pr_lwpid, (void *)addr); + ucl->uc_addrs[ucl->uc_nelems++] = addr; + + addr = (uintptr_t)uc.uc_link; + + /* + * Abort if we find a NULL uc_link pointer or a duplicate + * entry which could indicate a cycle or a very peculiar + * interference pattern between threads. + */ + if (addr == NULL) + break; + + for (i = 0; i < ucl->uc_nelems - 1; i++) { + if (ucl->uc_addrs[i] == addr) + return (0); + } + } + + return (0); +} + +int +sort_uclist(const void *lhp, const void *rhp) +{ + uintptr_t lhs = *((const uintptr_t *)lhp); + uintptr_t rhs = *((const uintptr_t *)rhp); + + if (lhs < rhs) + return (-1); + if (lhs > rhs) + return (+1); + return (0); +} + +void +init_uclist(uclist_t *ucl, struct ps_prochandle *P) +{ + if ((P->state == PS_STOP || P->state == PS_DEAD) && + P->ucaddrs != NULL) { + ucl->uc_proc = P; + ucl->uc_addrs = P->ucaddrs; + ucl->uc_nelems = P->ucnelems; + ucl->uc_size = P->ucnelems; + ucl->uc_cached = 1; + return; + } + + ucl->uc_proc = P; + ucl->uc_addrs = NULL; + ucl->uc_nelems = 0; + ucl->uc_size = 0; + + (void) Plwp_iter(P, (proc_lwp_f *)load_uclist, ucl); + qsort(ucl->uc_addrs, ucl->uc_nelems, sizeof (uintptr_t), sort_uclist); + + if (P->state == PS_STOP || P->state == PS_DEAD) { + P->ucaddrs = ucl->uc_addrs; + P->ucnelems = ucl->uc_nelems; + ucl->uc_cached = 1; + } else { + ucl->uc_cached = 0; + } +} + +void +free_uclist(uclist_t *ucl) +{ + if (!ucl->uc_cached && ucl->uc_addrs != NULL) + free(ucl->uc_addrs); +} + +int +find_uclink(uclist_t *ucl, uintptr_t addr) +{ + if (ucl->uc_nelems != 0) { + return (bsearch(&addr, ucl->uc_addrs, ucl->uc_nelems, + sizeof (uintptr_t), sort_uclist) != NULL); + } + + return (0); +} diff --git a/usr/src/lib/libproc/common/Pstack.h b/usr/src/lib/libproc/common/Pstack.h new file mode 100644 index 0000000000..31bdac9c62 --- /dev/null +++ b/usr/src/lib/libproc/common/Pstack.h @@ -0,0 +1,61 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PSTACK_H +#define _PSTACK_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Support functions for ISA-dependent Pstack_iter(). + */ +int stack_loop(prgreg_t fp, prgreg_t **prevfpp, int *nfpp, uint_t *pfpsizep); + +typedef struct { + struct ps_prochandle *uc_proc; /* libproc handle */ + uintptr_t *uc_addrs; /* array of stack addresses */ + uint_t uc_nelems; /* number of valid elements */ + uint_t uc_size; /* actual size of array */ + uint_t uc_cached; /* is cached in the ps_prochandle */ +} uclist_t; + +int load_uclist(uclist_t *ucl, const lwpstatus_t *psp); +int sort_uclist(const void *lhp, const void *rhp); +void init_uclist(uclist_t *ucl, struct ps_prochandle *P); +void free_uclist(uclist_t *ucl); +int find_uclink(uclist_t *ucl, uintptr_t addr); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _PSTACK_H */ 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); +} diff --git a/usr/src/lib/libproc/common/Psyscall.c b/usr/src/lib/libproc/common/Psyscall.c new file mode 100644 index 0000000000..c34a17a04c --- /dev/null +++ b/usr/src/lib/libproc/common/Psyscall.c @@ -0,0 +1,555 @@ +/* + * 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 2004 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 <unistd.h> +#include <ctype.h> +#include <fcntl.h> +#include <string.h> +#include <memory.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <sys/param.h> +#include <sys/stack.h> +#include <sys/fault.h> +#include <sys/syscall.h> +#include <sys/sysmacros.h> + +#include "libproc.h" +#include "Pcontrol.h" +#include "Putil.h" +#include "P32ton.h" +#include "Pisadep.h" + +extern sigset_t blockable_sigs; + +static void +Pabort_agent(struct ps_prochandle *P) +{ + int sysnum = P->status.pr_lwp.pr_syscall; + int stop; + + dprintf("agent LWP is asleep in syscall %d\n", sysnum); + (void) Pstop(P, 0); + stop = Psysexit(P, sysnum, TRUE); + + if (Psetrun(P, 0, PRSABORT) == 0) { + while (Pwait(P, 0) == -1 && errno == EINTR) + continue; + (void) Psysexit(P, sysnum, stop); + dprintf("agent LWP system call aborted\n"); + } +} + +/* + * Create the /proc agent LWP for further operations. + */ +int +Pcreate_agent(struct ps_prochandle *P) +{ + int fd; + char pathname[100]; + char *fname; + struct { + long cmd; + prgregset_t regs; + } cmd; + + /* + * If not first reference, we already have the /proc agent LWP active. + */ + if (P->agentcnt > 0) { + P->agentcnt++; + return (0); + } + + /* + * The agent is not available for use as a mortician or as an + * obstetrician. + */ + if (P->state == PS_DEAD || P->state == PS_UNDEAD || + P->state == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + /* + * Create the special /proc agent LWP if it doesn't already exist. + * Give it the registers of the representative LWP. + */ + (void) Pstop(P, 0); + Psync(P); + if (!(P->status.pr_lwp.pr_flags & PR_AGENT)) { + cmd.cmd = PCAGENT; + (void) memcpy(&cmd.regs, &P->status.pr_lwp.pr_reg[0], + sizeof (P->status.pr_lwp.pr_reg)); + if (write(P->ctlfd, &cmd, sizeof (cmd)) != sizeof (cmd)) + goto bad; + } + + /* refresh the process status */ + (void) Pstopstatus(P, PCNULL, 0); + + /* open the agent LWP files */ + (void) sprintf(pathname, "/proc/%d/lwp/agent/", (int)P->pid); + fname = pathname + strlen(pathname); + (void) set_minfd(); + + /* + * It is difficult to know how to recover from the two errors + * that follow. The agent LWP exists and we need to kill it, + * but we can't because we need it active in order to kill it. + * We just hope that these failures never occur. + */ + (void) strcpy(fname, "lwpstatus"); + if ((fd = open(pathname, O_RDONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) + goto bad; + P->agentstatfd = fd; + + (void) strcpy(fname, "lwpctl"); + if ((fd = open(pathname, O_WRONLY)) < 0 || + (fd = dupfd(fd, 0)) < 0) + goto bad; + P->agentctlfd = fd; + + /* + * If the agent is currently asleep in a system call, attempt + * to abort the system call so it's ready to serve. + */ + if (P->status.pr_lwp.pr_flags & PR_ASLEEP) { + dprintf("Pcreate_agent: aborting agent syscall\n"); + Pabort_agent(P); + } + + /* get the agent LWP status */ + P->agentcnt++; + if (Pstopstatus(P, PCNULL, 0) != 0) { + Pdestroy_agent(P); + return (-1); + } + + return (0); + +bad: + if (P->agentstatfd >= 0) + (void) close(P->agentstatfd); + if (P->agentctlfd >= 0) + (void) close(P->agentctlfd); + P->agentstatfd = -1; + P->agentctlfd = -1; + /* refresh the process status */ + (void) Pstopstatus(P, PCNULL, 0); + return (-1); +} + +/* + * Decrement the /proc agent agent reference count. + * On last reference, destroy the agent. + */ +void +Pdestroy_agent(struct ps_prochandle *P) +{ + if (P->agentcnt > 1) + P->agentcnt--; + else { + int flags; + + Psync(P); /* Flush out any pending changes */ + + (void) Pstopstatus(P, PCNULL, 0); + flags = P->status.pr_lwp.pr_flags; + + /* + * If the agent is currently asleep in a system call, attempt + * to abort the system call so we can terminate the agent. + */ + if ((flags & (PR_AGENT|PR_ASLEEP)) == (PR_AGENT|PR_ASLEEP)) { + dprintf("Pdestroy_agent: aborting agent syscall\n"); + Pabort_agent(P); + } + + /* + * The agent itself is destroyed by forcing it to execute + * the _lwp_exit(2) system call. Close our agent descriptors + * regardless of whether this is successful. + */ + (void) pr_lwp_exit(P); + (void) close(P->agentctlfd); + (void) close(P->agentstatfd); + P->agentctlfd = -1; + P->agentstatfd = -1; + P->agentcnt = 0; + + /* + * Now that (hopefully) the agent has exited, refresh the + * status: the representative LWP is no longer the agent. + */ + (void) Pstopstatus(P, PCNULL, 0); + } +} + +/* + * Execute the syscall instruction. + */ +static int +execute(struct ps_prochandle *P, int sysindex) +{ + int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; + int washeld = FALSE; + sigset_t hold; /* mask of held signals */ + int cursig; + struct { + long cmd; + siginfo_t siginfo; + } ctl; + int sentry; /* old value of stop-on-syscall-entry */ + + sentry = Psysentry(P, sysindex, TRUE); /* set stop-on-syscall-entry */ + + /* + * If not already blocked, block all signals now. + */ + if (memcmp(&P->status.pr_lwp.pr_lwphold, &blockable_sigs, + sizeof (sigset_t)) != 0) { + hold = P->status.pr_lwp.pr_lwphold; + P->status.pr_lwp.pr_lwphold = blockable_sigs; + P->flags |= SETHOLD; + washeld = TRUE; + } + + /* + * If there is a current signal, remember it and cancel it. + */ + if ((cursig = P->status.pr_lwp.pr_cursig) != 0) { + ctl.cmd = PCSSIG; + ctl.siginfo = P->status.pr_lwp.pr_info; + } + + if (Psetrun(P, 0, PRCSIG | PRCFAULT) == -1) + goto bad; + + while (P->state == PS_RUN) { + (void) Pwait(P, 0); + } + if (P->state != PS_STOP) + goto bad; + + if (cursig) /* restore cursig */ + (void) write(ctlfd, &ctl, sizeof (ctl)); + if (washeld) { /* restore the signal mask if we set it */ + P->status.pr_lwp.pr_lwphold = hold; + P->flags |= SETHOLD; + } + + (void) Psysentry(P, sysindex, sentry); /* restore sysentry stop */ + + if (P->status.pr_lwp.pr_why == PR_SYSENTRY && + P->status.pr_lwp.pr_what == sysindex) + return (0); +bad: + return (-1); +} + + +/* + * Perform system call in controlled process. + */ +int +Psyscall(struct ps_prochandle *P, + sysret_t *rval, /* syscall return values */ + int sysindex, /* system call index */ + uint_t nargs, /* number of arguments to system call */ + argdes_t *argp) /* argument descriptor array */ +{ + int agent_created = FALSE; + pstatus_t save_pstatus; + argdes_t *adp; /* pointer to argument descriptor */ + int i; /* general index value */ + int model; /* data model */ + int error = 0; /* syscall errno */ + int Perr = 0; /* local error number */ + int sexit; /* old value of stop-on-syscall-exit */ + prgreg_t sp; /* adjusted stack pointer */ + prgreg_t ap; /* adjusted argument pointer */ + sigset_t unblock; + + (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock); + + rval->sys_rval1 = 0; /* initialize return values */ + rval->sys_rval2 = 0; + + if (sysindex <= 0 || sysindex > PRMAXSYS || nargs > MAXARGS) + goto bad1; /* programming error */ + + if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) + goto bad1; /* dead processes can't perform system calls */ + + model = P->status.pr_dmodel; +#ifndef _LP64 + /* We must be a 64-bit process to deal with a 64-bit process */ + if (model == PR_MODEL_LP64) + goto bad9; +#endif + + /* + * Create the /proc agent LWP in the process to do all the work. + * (It may already exist; nested create/destroy is permitted + * by virtue of the reference count.) + */ + if (Pcreate_agent(P) != 0) + goto bad8; + + /* + * Save agent's status to restore on exit. + */ + agent_created = TRUE; + save_pstatus = P->status; + + if (P->state != PS_STOP || /* check state of LWP */ + (P->status.pr_flags & PR_ASLEEP)) + goto bad2; + + if (Pscantext(P)) /* bad text ? */ + goto bad3; + + /* + * Validate arguments and compute the stack frame parameters. + * Begin with the current stack pointer. + */ +#ifdef _LP64 + if (model == PR_MODEL_LP64) { + sp = P->status.pr_lwp.pr_reg[R_SP] + STACK_BIAS; + sp = PSTACK_ALIGN64(sp); + } else { +#endif + sp = (uint32_t)P->status.pr_lwp.pr_reg[R_SP]; + sp = PSTACK_ALIGN32(sp); +#ifdef _LP64 + } +#endif + + /* + * For each AT_BYREF argument, compute the necessary + * stack space and the object's stack address. + */ + for (i = 0, adp = argp; i < nargs; i++, adp++) { + rval->sys_rval1 = i; /* in case of error */ + switch (adp->arg_type) { + default: /* programming error */ + goto bad4; + case AT_BYVAL: /* simple argument */ + break; + case AT_BYREF: /* must allocate space */ + switch (adp->arg_inout) { + case AI_INPUT: + case AI_OUTPUT: + case AI_INOUT: + if (adp->arg_object == NULL) + goto bad5; /* programming error */ + break; + default: /* programming error */ + goto bad6; + } + /* allocate stack space for BYREF argument */ + if (adp->arg_size == 0 || adp->arg_size > MAXARGL) + goto bad7; /* programming error */ +#ifdef _LP64 + if (model == PR_MODEL_LP64) + sp = PSTACK_ALIGN64(sp - adp->arg_size); + else +#endif + sp = PSTACK_ALIGN32(sp - adp->arg_size); + adp->arg_value = sp; /* stack address for object */ + break; + } + } + rval->sys_rval1 = 0; /* in case of error */ + /* + * Point of no return. + * Perform the system call entry, adjusting %sp. + * This moves the LWP to the stopped-on-syscall-entry state + * just before the arguments to the system call are fetched. + */ + ap = Psyscall_setup(P, nargs, sysindex, sp); + P->flags |= SETREGS; /* set registers before continuing */ + dprintf("Psyscall(): execute(sysindex = %d)\n", sysindex); + + /* + * Execute the syscall instruction and stop on syscall entry. + */ + if (execute(P, sysindex) != 0 || + (!Pissyscall(P, P->status.pr_lwp.pr_reg[R_PC]) && + !Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], NULL))) + goto bad10; + + dprintf("Psyscall(): copying arguments\n"); + + /* + * The LWP is stopped at syscall entry. + * Copy objects to stack frame for each argument. + */ + for (i = 0, adp = argp; i < nargs; i++, adp++) { + rval->sys_rval1 = i; /* in case of error */ + if (adp->arg_type != AT_BYVAL && + adp->arg_inout != AI_OUTPUT) { + /* copy input byref parameter to process */ + if (Pwrite(P, adp->arg_object, adp->arg_size, + (uintptr_t)adp->arg_value) != adp->arg_size) + goto bad17; + } + } + rval->sys_rval1 = 0; /* in case of error */ + if (Psyscall_copyinargs(P, nargs, argp, ap) != 0) + goto bad18; + + /* + * Complete the system call. + * This moves the LWP to the stopped-on-syscall-exit state. + */ + dprintf("Psyscall(): set running at sysentry\n"); + + sexit = Psysexit(P, sysindex, TRUE); /* catch this syscall exit */ + do { + if (Psetrun(P, 0, 0) == -1) + goto bad21; + while (P->state == PS_RUN) + (void) Pwait(P, 0); + } while (P->state == PS_STOP && P->status.pr_lwp.pr_why != PR_SYSEXIT); + (void) Psysexit(P, sysindex, sexit); /* restore original setting */ + + /* + * If the system call was _lwp_exit(), we expect that our last call + * to Pwait() will yield ENOENT because the LWP no longer exists. + */ + if (sysindex == SYS_lwp_exit && errno == ENOENT) { + dprintf("Psyscall(): _lwp_exit successful\n"); + rval->sys_rval1 = rval->sys_rval2 = 0; + goto out; + } + + if (P->state != PS_STOP || P->status.pr_lwp.pr_why != PR_SYSEXIT) + goto bad22; + + if (P->status.pr_lwp.pr_what != sysindex) + goto bad23; + + if (!Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], NULL)) { + dprintf("Pissyscall_prev() failed\n"); + goto bad24; + } + + dprintf("Psyscall(): caught at sysexit\n"); + + /* + * For each argument. + */ + for (i = 0, adp = argp; i < nargs; i++, adp++) { + rval->sys_rval1 = i; /* in case of error */ + if (adp->arg_type != AT_BYVAL && + adp->arg_inout != AI_INPUT) { + /* copy output byref parameter from process */ + if (Pread(P, adp->arg_object, adp->arg_size, + (uintptr_t)adp->arg_value) != adp->arg_size) + goto bad25; + } + } + + if (Psyscall_copyoutargs(P, nargs, argp, ap) != 0) + goto bad26; + + /* + * Get the return values from the syscall. + */ + if (P->status.pr_lwp.pr_errno) { /* error return */ + error = P->status.pr_lwp.pr_errno; + rval->sys_rval1 = -1L; + rval->sys_rval2 = -1L; + dprintf("Psyscall(%d) fails with errno %d\n", + sysindex, error); + } else { /* normal return */ + rval->sys_rval1 = P->status.pr_lwp.pr_rval1; + rval->sys_rval2 = P->status.pr_lwp.pr_rval2; + dprintf("Psyscall(%d) returns 0x%lx 0x%lx\n", sysindex, + P->status.pr_lwp.pr_rval1, P->status.pr_lwp.pr_rval2); + } + + goto out; + +bad26: Perr++; +bad25: Perr++; +bad24: Perr++; +bad23: Perr++; +bad22: Perr++; +bad21: Perr++; + Perr++; + Perr++; +bad18: Perr++; +bad17: Perr++; + Perr++; + Perr++; + Perr++; + Perr++; + Perr++; + Perr++; +bad10: Perr++; +bad9: Perr++; +bad8: Perr++; +bad7: Perr++; +bad6: Perr++; +bad5: Perr++; +bad4: Perr++; +bad3: Perr++; +bad2: Perr++; +bad1: Perr++; + error = -1; + dprintf("Psyscall(%d) fails with local error %d\n", sysindex, Perr); + +out: + /* + * Destroy the /proc agent LWP now (or just bump down the ref count). + */ + if (agent_created) { + if (P->state != PS_UNDEAD) { + P->status = save_pstatus; + P->flags |= SETREGS; + Psync(P); + } + Pdestroy_agent(P); + } + + (void) sigprocmask(SIG_SETMASK, &unblock, NULL); + return (error); +} diff --git a/usr/src/lib/libproc/common/Putil.c b/usr/src/lib/libproc/common/Putil.c new file mode 100644 index 0000000000..7e06c14f67 --- /dev/null +++ b/usr/src/lib/libproc/common/Putil.c @@ -0,0 +1,152 @@ +/* + * 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 (c) 1998-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <limits.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> + +#include "Pcontrol.h" +#include "Putil.h" + +/* + * Place the new element on the list prior to the existing element. + */ +void +list_link(void *new, void *existing) +{ + list_t *p = new; + list_t *q = existing; + + if (q) { + p->list_forw = q; + p->list_back = q->list_back; + q->list_back->list_forw = p; + q->list_back = p; + } else { + p->list_forw = p->list_back = p; + } +} + +/* + * Unchain the specified element from a list. + */ +void +list_unlink(void *old) +{ + list_t *p = old; + + if (p->list_forw != p) { + p->list_back->list_forw = p->list_forw; + p->list_forw->list_back = p->list_back; + } + p->list_forw = p->list_back = p; +} + +/* + * Routines to manipulate sigset_t, fltset_t, or sysset_t. These routines + * are provided as equivalents for the <sys/procfs.h> macros prfillset, + * premptyset, praddset, and prdelset. These functions are preferable + * because they are not macros which rely on using sizeof (*sp), and thus + * can be used to create common code to manipulate event sets. The set + * size must be passed explicitly, e.g. : prset_fill(&set, sizeof (set)); + */ +void +prset_fill(void *sp, size_t size) +{ + size_t i = size / sizeof (uint32_t); + + while (i != 0) + ((uint32_t *)sp)[--i] = (uint32_t)0xFFFFFFFF; +} + +void +prset_empty(void *sp, size_t size) +{ + size_t i = size / sizeof (uint32_t); + + while (i != 0) + ((uint32_t *)sp)[--i] = (uint32_t)0; +} + +void +prset_add(void *sp, size_t size, uint_t flag) +{ + if (flag - 1 < 32 * size / sizeof (uint32_t)) + ((uint32_t *)sp)[(flag - 1) / 32] |= 1U << ((flag - 1) % 32); +} + +void +prset_del(void *sp, size_t size, uint_t flag) +{ + if (flag - 1 < 32 * size / sizeof (uint32_t)) + ((uint32_t *)sp)[(flag - 1) / 32] &= ~(1U << ((flag - 1) % 32)); +} + +int +prset_ismember(void *sp, size_t size, uint_t flag) +{ + return ((flag - 1 < 32 * size / sizeof (uint32_t)) && + (((uint32_t *)sp)[(flag - 1) / 32] & (1U << ((flag - 1) % 32)))); +} + +/* + * If _libproc_debug is set, printf the debug message to stderr + * with an appropriate prefix. + */ +/*PRINTFLIKE1*/ +void +dprintf(const char *format, ...) +{ + if (_libproc_debug) { + va_list alist; + + va_start(alist, format); + (void) fputs("libproc DEBUG: ", stderr); + (void) vfprintf(stderr, format, alist); + va_end(alist); + } +} + +/* + * Printf-style error reporting function. This is used to supplement the error + * return codes from various libproc functions with additional text. Since we + * are a library, and should not be spewing messages to stderr, we provide a + * default version of this function that does nothing, but by calling this + * function we allow the client program to define its own version of the + * function that will interpose on our empty default. This may be useful for + * clients that wish to display such messages to the user. + */ +/*ARGSUSED*/ +/*PRINTFLIKE2*/ +void +Perror_printf(struct ps_prochandle *P, const char *format, ...) +{ + /* nothing to do here */ +} diff --git a/usr/src/lib/libproc/common/Putil.h b/usr/src/lib/libproc/common/Putil.h new file mode 100644 index 0000000000..328440fc81 --- /dev/null +++ b/usr/src/lib/libproc/common/Putil.h @@ -0,0 +1,71 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PUTIL_H +#define _PUTIL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Circular doubly-linked list: + */ +typedef struct P_list { + struct P_list *list_forw; + struct P_list *list_back; +} list_t; + +/* + * Routines to manipulate linked lists: + */ +extern void list_link(void *, void *); +extern void list_unlink(void *); + +#define list_next(elem) (void *)(((list_t *)(elem))->list_forw) +#define list_prev(elem) (void *)(((list_t *)(elem))->list_back) + +/* + * Routines to manipulate sigset_t, fltset_t, or sysset_t. + */ +extern void prset_fill(void *, size_t); +extern void prset_empty(void *, size_t); +extern void prset_add(void *, size_t, uint_t); +extern void prset_del(void *, size_t, uint_t); +extern int prset_ismember(void *, size_t, uint_t); + +/* + * Routine to print debug messages: + */ +extern void dprintf(const char *, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _PUTIL_H */ diff --git a/usr/src/lib/libproc/common/libproc.h b/usr/src/lib/libproc/common/libproc.h new file mode 100644 index 0000000000..b2f37f45d7 --- /dev/null +++ b/usr/src/lib/libproc/common/libproc.h @@ -0,0 +1,704 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Interfaces available from the process control library, libproc. + * + * libproc provides process control functions for the /proc tools + * (commands in /usr/proc/bin), /usr/bin/truss, and /usr/bin/gcore. + * libproc is a private support library for these commands only. + * It is _not_ a public interface, although it might become one + * in the fullness of time, when the interfaces settle down. + * + * In the meantime, be aware that any program linked with libproc in this + * release of Solaris is almost guaranteed to break in the next release. + * + * In short, do not use this header file or libproc for any purpose. + */ + +#ifndef _LIBPROC_H +#define _LIBPROC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <nlist.h> +#include <door.h> +#include <gelf.h> +#include <proc_service.h> +#include <rtld_db.h> +#include <procfs.h> +#include <rctl.h> +#include <libctf.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <sys/auxv.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/utsname.h> +#include <sys/corectl.h> +#if defined(__i386) || defined(__amd64) +#include <sys/sysi86.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Opaque structure tag reference to a process control structure. + * Clients of libproc cannot look inside the process control structure. + * The implementation of struct ps_prochandle can change w/o affecting clients. + */ +struct ps_prochandle; + +/* + * Opaque structure tag reference to an lwp control structure. + */ +struct ps_lwphandle; + +extern int _libproc_debug; /* set non-zero to enable debugging fprintfs */ + +#if defined(__sparc) +#define R_RVAL1 R_O0 /* register holding a function return value */ +#define R_RVAL2 R_O1 /* 32 more bits for a 64-bit return value */ +#endif /* __sparc */ + +#if defined(__amd64) +#define R_PC REG_RIP +#define R_SP REG_RSP +#define R_RVAL1 REG_RAX /* register holding a function return value */ +#define R_RVAL2 REG_RDX /* 32 more bits for a 64-bit return value */ +#elif defined(__i386) +#define R_PC EIP +#define R_SP UESP +#define R_RVAL1 EAX /* register holding a function return value */ +#define R_RVAL2 EDX /* 32 more bits for a 64-bit return value */ +#endif /* __amd64 || __i386 */ + +#define R_RVAL R_RVAL1 /* simple function return value register */ + +/* maximum sizes of things */ +#define PRMAXSIG (32 * sizeof (sigset_t) / sizeof (uint32_t)) +#define PRMAXFAULT (32 * sizeof (fltset_t) / sizeof (uint32_t)) +#define PRMAXSYS (32 * sizeof (sysset_t) / sizeof (uint32_t)) + +/* State values returned by Pstate() */ +#define PS_RUN 1 /* process is running */ +#define PS_STOP 2 /* process is stopped */ +#define PS_LOST 3 /* process is lost to control (EAGAIN) */ +#define PS_UNDEAD 4 /* process is terminated (zombie) */ +#define PS_DEAD 5 /* process is terminated (core file) */ +#define PS_IDLE 6 /* process has not been run */ + +/* Flags accepted by Pgrab() */ +#define PGRAB_RETAIN 0x01 /* Retain tracing flags, else clear flags */ +#define PGRAB_FORCE 0x02 /* Open the process w/o O_EXCL */ +#define PGRAB_RDONLY 0x04 /* Open the process or core w/ O_RDONLY */ +#define PGRAB_NOSTOP 0x08 /* Open the process but do not stop it */ + +/* Error codes from Pcreate() */ +#define C_STRANGE -1 /* Unanticipated error, errno is meaningful */ +#define C_FORK 1 /* Unable to fork */ +#define C_PERM 2 /* No permission (file set-id or unreadable) */ +#define C_NOEXEC 3 /* Cannot execute file */ +#define C_INTR 4 /* Interrupt received while creating */ +#define C_LP64 5 /* Program is _LP64, self is _ILP32 */ +#define C_NOENT 6 /* Cannot find executable file */ + +/* Error codes from Pgrab(), Pfgrab_core(), and Pgrab_core() */ +#define G_STRANGE -1 /* Unanticipated error, errno is meaningful */ +#define G_NOPROC 1 /* No such process */ +#define G_NOCORE 2 /* No such core file */ +#define G_NOPROCORCORE 3 /* No such proc or core (for proc_arg_grab) */ +#define G_NOEXEC 4 /* Cannot locate executable file */ +#define G_ZOMB 5 /* Zombie process */ +#define G_PERM 6 /* No permission */ +#define G_BUSY 7 /* Another process has control */ +#define G_SYS 8 /* System process */ +#define G_SELF 9 /* Process is self */ +#define G_INTR 10 /* Interrupt received while grabbing */ +#define G_LP64 11 /* Process is _LP64, self is ILP32 */ +#define G_FORMAT 12 /* File is not an ELF format core file */ +#define G_ELF 13 /* Libelf error, elf_errno() is meaningful */ +#define G_NOTE 14 /* Required PT_NOTE Phdr not present in core */ +#define G_ISAINVAL 15 /* Wrong ELF machine type */ +#define G_BADLWPS 16 /* Bad '/lwps' specification */ + + +/* Flags accepted by Prelease */ +#define PRELEASE_CLEAR 0x10 /* Clear all tracing flags */ +#define PRELEASE_RETAIN 0x20 /* Retain final tracing flags */ +#define PRELEASE_HANG 0x40 /* Leave the process stopped */ +#define PRELEASE_KILL 0x80 /* Terminate the process */ + +typedef struct { /* argument descriptor for system call (Psyscall) */ + long arg_value; /* value of argument given to system call */ + void *arg_object; /* pointer to object in controlling process */ + char arg_type; /* AT_BYVAL, AT_BYREF */ + char arg_inout; /* AI_INPUT, AI_OUTPUT, AI_INOUT */ + ushort_t arg_size; /* if AT_BYREF, size of object in bytes */ +} argdes_t; + +/* values for type */ +#define AT_BYVAL 1 +#define AT_BYREF 2 + +/* values for inout */ +#define AI_INPUT 1 +#define AI_OUTPUT 2 +#define AI_INOUT 3 + +/* maximum number of syscall arguments */ +#define MAXARGS 8 + +/* maximum size in bytes of a BYREF argument */ +#define MAXARGL (4*1024) + +/* + * Function prototypes for routines in the process control package. + */ +extern struct ps_prochandle *Pcreate(const char *, char *const *, + int *, char *, size_t); +extern struct ps_prochandle *Pxcreate(const char *, char *const *, + char *const *, int *, char *, size_t); + +extern const char *Pcreate_error(int); + +extern struct ps_prochandle *Pgrab(pid_t, int, int *); +extern struct ps_prochandle *Pgrab_core(const char *, const char *, int, int *); +extern struct ps_prochandle *Pfgrab_core(int, const char *, int *); +extern struct ps_prochandle *Pgrab_file(const char *, int *); +extern const char *Pgrab_error(int); + +extern int Preopen(struct ps_prochandle *); +extern void Prelease(struct ps_prochandle *, int); +extern void Pfree(struct ps_prochandle *); + +extern int Pasfd(struct ps_prochandle *); +extern int Pctlfd(struct ps_prochandle *); +extern int Pcreate_agent(struct ps_prochandle *); +extern void Pdestroy_agent(struct ps_prochandle *); +extern int Pstopstatus(struct ps_prochandle *, long, uint_t); +extern int Pwait(struct ps_prochandle *, uint_t); +extern int Pstop(struct ps_prochandle *, uint_t); +extern int Pdstop(struct ps_prochandle *); +extern int Pstate(struct ps_prochandle *); +extern const psinfo_t *Ppsinfo(struct ps_prochandle *); +extern const pstatus_t *Pstatus(struct ps_prochandle *); +extern int Pcred(struct ps_prochandle *, prcred_t *, int); +extern int Psetcred(struct ps_prochandle *, const prcred_t *); +extern ssize_t Ppriv(struct ps_prochandle *, prpriv_t *, size_t); +extern int Psetpriv(struct ps_prochandle *, prpriv_t *); +extern void *Pprivinfo(struct ps_prochandle *); +extern int Psetzoneid(struct ps_prochandle *, zoneid_t); +extern int Pgetareg(struct ps_prochandle *, int, prgreg_t *); +extern int Pputareg(struct ps_prochandle *, int, prgreg_t); +extern int Psetrun(struct ps_prochandle *, int, int); +extern ssize_t Pread(struct ps_prochandle *, void *, size_t, uintptr_t); +extern ssize_t Pread_string(struct ps_prochandle *, char *, size_t, uintptr_t); +extern ssize_t Pwrite(struct ps_prochandle *, const void *, size_t, uintptr_t); +extern int Pclearsig(struct ps_prochandle *); +extern int Pclearfault(struct ps_prochandle *); +extern int Psetbkpt(struct ps_prochandle *, uintptr_t, ulong_t *); +extern int Pdelbkpt(struct ps_prochandle *, uintptr_t, ulong_t); +extern int Pxecbkpt(struct ps_prochandle *, ulong_t); +extern int Psetwapt(struct ps_prochandle *, const prwatch_t *); +extern int Pdelwapt(struct ps_prochandle *, const prwatch_t *); +extern int Pxecwapt(struct ps_prochandle *, const prwatch_t *); +extern int Psetflags(struct ps_prochandle *, long); +extern int Punsetflags(struct ps_prochandle *, long); +extern int Psignal(struct ps_prochandle *, int, int); +extern int Pfault(struct ps_prochandle *, int, int); +extern int Psysentry(struct ps_prochandle *, int, int); +extern int Psysexit(struct ps_prochandle *, int, int); +extern void Psetsignal(struct ps_prochandle *, const sigset_t *); +extern void Psetfault(struct ps_prochandle *, const fltset_t *); +extern void Psetsysentry(struct ps_prochandle *, const sysset_t *); +extern void Psetsysexit(struct ps_prochandle *, const sysset_t *); + +extern void Psync(struct ps_prochandle *); +extern int Psyscall(struct ps_prochandle *, sysret_t *, + int, uint_t, argdes_t *); +extern int Pisprocdir(struct ps_prochandle *, const char *); + +/* + * Function prototypes for lwp-specific operations. + */ +extern struct ps_lwphandle *Lgrab(struct ps_prochandle *, lwpid_t, int *); +extern const char *Lgrab_error(int); + +extern struct ps_prochandle *Lprochandle(struct ps_lwphandle *); +extern void Lfree(struct ps_lwphandle *); + +extern int Lctlfd(struct ps_lwphandle *); +extern int Lwait(struct ps_lwphandle *, uint_t); +extern int Lstop(struct ps_lwphandle *, uint_t); +extern int Ldstop(struct ps_lwphandle *); +extern int Lstate(struct ps_lwphandle *); +extern const lwpsinfo_t *Lpsinfo(struct ps_lwphandle *); +extern const lwpstatus_t *Lstatus(struct ps_lwphandle *); +extern int Lgetareg(struct ps_lwphandle *, int, prgreg_t *); +extern int Lputareg(struct ps_lwphandle *, int, prgreg_t); +extern int Lsetrun(struct ps_lwphandle *, int, int); +extern int Lclearsig(struct ps_lwphandle *); +extern int Lclearfault(struct ps_lwphandle *); +extern int Lxecbkpt(struct ps_lwphandle *, ulong_t); +extern int Lxecwapt(struct ps_lwphandle *, const prwatch_t *); +extern void Lsync(struct ps_lwphandle *); + +extern int Lstack(struct ps_lwphandle *, stack_t *); +extern int Lmain_stack(struct ps_lwphandle *, stack_t *); +extern int Lalt_stack(struct ps_lwphandle *, stack_t *); + +/* + * Function prototypes for system calls forced on the victim process. + */ +extern int pr_open(struct ps_prochandle *, const char *, int, mode_t); +extern int pr_creat(struct ps_prochandle *, const char *, mode_t); +extern int pr_close(struct ps_prochandle *, int); +extern int pr_access(struct ps_prochandle *, const char *, int); +extern int pr_door_info(struct ps_prochandle *, int, struct door_info *); +extern void *pr_mmap(struct ps_prochandle *, + void *, size_t, int, int, int, off_t); +extern void *pr_zmap(struct ps_prochandle *, + void *, size_t, int, int); +extern int pr_munmap(struct ps_prochandle *, void *, size_t); +extern int pr_memcntl(struct ps_prochandle *, + caddr_t, size_t, int, caddr_t, int, int); +extern int pr_meminfo(struct ps_prochandle *, const uint64_t *addrs, + int addr_count, const uint_t *info, int info_count, + uint64_t *outdata, uint_t *validity); +extern int pr_sigaction(struct ps_prochandle *, + int, const struct sigaction *, struct sigaction *); +extern int pr_getitimer(struct ps_prochandle *, + int, struct itimerval *); +extern int pr_setitimer(struct ps_prochandle *, + int, const struct itimerval *, struct itimerval *); +extern int pr_ioctl(struct ps_prochandle *, int, int, void *, size_t); +extern int pr_fcntl(struct ps_prochandle *, int, int, void *); +extern int pr_stat(struct ps_prochandle *, const char *, struct stat *); +extern int pr_lstat(struct ps_prochandle *, const char *, struct stat *); +extern int pr_fstat(struct ps_prochandle *, int, struct stat *); +extern int pr_stat64(struct ps_prochandle *, const char *, + struct stat64 *); +extern int pr_lstat64(struct ps_prochandle *, const char *, + struct stat64 *); +extern int pr_fstat64(struct ps_prochandle *, int, struct stat64 *); +extern int pr_statvfs(struct ps_prochandle *, const char *, statvfs_t *); +extern int pr_fstatvfs(struct ps_prochandle *, int, statvfs_t *); +extern projid_t pr_getprojid(struct ps_prochandle *Pr); +extern taskid_t pr_gettaskid(struct ps_prochandle *Pr); +extern taskid_t pr_settaskid(struct ps_prochandle *Pr, projid_t project, + int flags); +extern zoneid_t pr_getzoneid(struct ps_prochandle *Pr); +extern int pr_getrctl(struct ps_prochandle *, + const char *, rctlblk_t *, rctlblk_t *, int); +extern int pr_setrctl(struct ps_prochandle *, + const char *, rctlblk_t *, rctlblk_t *, int); +extern int pr_getrlimit(struct ps_prochandle *, + int, struct rlimit *); +extern int pr_setrlimit(struct ps_prochandle *, + int, const struct rlimit *); +#if defined(_LARGEFILE64_SOURCE) +extern int pr_getrlimit64(struct ps_prochandle *, + int, struct rlimit64 *); +extern int pr_setrlimit64(struct ps_prochandle *, + int, const struct rlimit64 *); +#endif /* _LARGEFILE64_SOURCE */ +extern int pr_lwp_exit(struct ps_prochandle *); +extern int pr_exit(struct ps_prochandle *, int); +extern int pr_waitid(struct ps_prochandle *, + idtype_t, id_t, siginfo_t *, int); +extern off_t pr_lseek(struct ps_prochandle *, int, off_t, int); +extern offset_t pr_llseek(struct ps_prochandle *, int, offset_t, int); +extern int pr_rename(struct ps_prochandle *, const char *, const char *); +extern int pr_link(struct ps_prochandle *, const char *, const char *); +extern int pr_unlink(struct ps_prochandle *, const char *); +extern int pr_getpeername(struct ps_prochandle *, + int, struct sockaddr *, socklen_t *); +extern int pr_getsockname(struct ps_prochandle *, + int, struct sockaddr *, socklen_t *); +extern int pr_getsockopt(struct ps_prochandle *, + int, int, int, void *, int *); +extern int pr_processor_bind(struct ps_prochandle *, + idtype_t, id_t, int, int *); +extern int pr_pset_bind(struct ps_prochandle *, + int, idtype_t, id_t, int *); + +/* + * Function prototypes for accessing per-LWP register information. + */ +extern int Plwp_getregs(struct ps_prochandle *, lwpid_t, prgregset_t); +extern int Plwp_setregs(struct ps_prochandle *, lwpid_t, const prgregset_t); + +extern int Plwp_getfpregs(struct ps_prochandle *, lwpid_t, prfpregset_t *); +extern int Plwp_setfpregs(struct ps_prochandle *, lwpid_t, + const prfpregset_t *); + +#if defined(__sparc) + +extern int Plwp_getxregs(struct ps_prochandle *, lwpid_t, prxregset_t *); +extern int Plwp_setxregs(struct ps_prochandle *, lwpid_t, const prxregset_t *); + +extern int Plwp_getgwindows(struct ps_prochandle *, lwpid_t, gwindows_t *); + +#if defined(__sparcv9) +extern int Plwp_getasrs(struct ps_prochandle *, lwpid_t, asrset_t); +extern int Plwp_setasrs(struct ps_prochandle *, lwpid_t, const asrset_t); +#endif /* __sparcv9 */ + +#endif /* __sparc */ + +#if defined(__i386) || defined(__amd64) +extern int Pldt(struct ps_prochandle *, struct ssd *, int); +extern int proc_get_ldt(pid_t, struct ssd *, int); +#endif /* __i386 || __amd64 */ + +extern int Plwp_getpsinfo(struct ps_prochandle *, lwpid_t, lwpsinfo_t *); + +extern int Plwp_stack(struct ps_prochandle *, lwpid_t, stack_t *); +extern int Plwp_main_stack(struct ps_prochandle *, lwpid_t, stack_t *); +extern int Plwp_alt_stack(struct ps_prochandle *, lwpid_t, stack_t *); + +/* + * LWP iteration interface; iterate over all active LWPs. + */ +typedef int proc_lwp_f(void *, const lwpstatus_t *); +extern int Plwp_iter(struct ps_prochandle *, proc_lwp_f *, void *); + +/* + * LWP iteration interface; iterate over all LWPs, active and zombie. + */ +typedef int proc_lwp_all_f(void *, const lwpstatus_t *, const lwpsinfo_t *); +extern int Plwp_iter_all(struct ps_prochandle *, proc_lwp_all_f *, void *); + +/* + * Process iteration interface; iterate over all active processes. + */ +typedef int proc_walk_f(psinfo_t *, lwpsinfo_t *, void *); +extern int proc_walk(proc_walk_f *, void *, int); + +#define PR_WALK_PROC 0 /* walk processes only */ +#define PR_WALK_LWP 1 /* walk all lwps */ + +/* + * Determine if an lwp is in a set as returned from proc_arg_xgrab(). + */ +extern int proc_lwp_in_set(const char *, lwpid_t); +extern int proc_lwp_range_valid(const char *); + +/* + * Symbol table interfaces. + */ + +/* + * Pseudo-names passed to Plookup_by_name() for well-known load objects. + * NOTE: It is required that PR_OBJ_EXEC and PR_OBJ_LDSO exactly match + * the definitions of PS_OBJ_EXEC and PS_OBJ_LDSO from <proc_service.h>. + */ +#define PR_OBJ_EXEC ((const char *)0) /* search the executable file */ +#define PR_OBJ_LDSO ((const char *)1) /* search ld.so.1 */ +#define PR_OBJ_EVERY ((const char *)-1) /* search every load object */ + +/* + * Special Lmid_t passed to Plookup_by_lmid() to search all link maps. The + * special values LM_ID_BASE and LM_ID_LDSO from <link.h> may also be used. + * If PR_OBJ_EXEC is used as the object name, the lmid must be PR_LMID_EVERY + * or LM_ID_BASE in order to return a match. If PR_OBJ_LDSO is used as the + * object name, the lmid must be PR_LMID_EVERY or LM_ID_LDSO to return a match. + */ +#define PR_LMID_EVERY ((Lmid_t)-1UL) /* search every link map */ + +/* + * 'object_name' is the name of a load object obtained from an + * iteration over the process's address space mappings (Pmapping_iter), + * or an iteration over the process's mapped objects (Pobject_iter), + * or else it is one of the special PR_OBJ_* values above. + */ +extern int Plookup_by_name(struct ps_prochandle *, + const char *, const char *, GElf_Sym *); + +extern int Plookup_by_addr(struct ps_prochandle *, + uintptr_t, char *, size_t, GElf_Sym *); + +typedef struct prsyminfo { + const char *prs_object; /* object name */ + const char *prs_name; /* symbol name */ + Lmid_t prs_lmid; /* link map id */ + uint_t prs_id; /* symbol id */ + uint_t prs_table; /* symbol table id */ +} prsyminfo_t; + +extern int Pxlookup_by_name(struct ps_prochandle *, + Lmid_t, const char *, const char *, GElf_Sym *, prsyminfo_t *); + +extern int Pxlookup_by_addr(struct ps_prochandle *, + uintptr_t, char *, size_t, GElf_Sym *, prsyminfo_t *); + +typedef int proc_map_f(void *, const prmap_t *, const char *); + +extern int Pmapping_iter(struct ps_prochandle *, proc_map_f *, void *); +extern int Pobject_iter(struct ps_prochandle *, proc_map_f *, void *); + +extern const prmap_t *Paddr_to_map(struct ps_prochandle *, uintptr_t); +extern const prmap_t *Paddr_to_text_map(struct ps_prochandle *, uintptr_t); +extern const prmap_t *Pname_to_map(struct ps_prochandle *, const char *); +extern const prmap_t *Plmid_to_map(struct ps_prochandle *, + Lmid_t, const char *); + +extern const rd_loadobj_t *Paddr_to_loadobj(struct ps_prochandle *, uintptr_t); +extern const rd_loadobj_t *Pname_to_loadobj(struct ps_prochandle *, + const char *); +extern const rd_loadobj_t *Plmid_to_loadobj(struct ps_prochandle *, + Lmid_t, const char *); + +extern ctf_file_t *Paddr_to_ctf(struct ps_prochandle *, uintptr_t); +extern ctf_file_t *Pname_to_ctf(struct ps_prochandle *, const char *); + +extern char *Pplatform(struct ps_prochandle *, char *, size_t); +extern int Puname(struct ps_prochandle *, struct utsname *); +extern char *Pzonename(struct ps_prochandle *, char *, size_t); + +extern char *Pexecname(struct ps_prochandle *, char *, size_t); +extern char *Pobjname(struct ps_prochandle *, uintptr_t, char *, size_t); +extern int Plmid(struct ps_prochandle *, uintptr_t, Lmid_t *); + +typedef int proc_env_f(void *, struct ps_prochandle *, uintptr_t, const char *); +extern int Penv_iter(struct ps_prochandle *, proc_env_f *, void *); +extern char *Pgetenv(struct ps_prochandle *, const char *, char *, size_t); +extern long Pgetauxval(struct ps_prochandle *, int); +extern const auxv_t *Pgetauxvec(struct ps_prochandle *); + +/* + * Symbol table iteration interface. The special lmid constants LM_ID_BASE, + * LM_ID_LDSO, and PR_LMID_EVERY may be used with Psymbol_iter_by_lmid. + */ +typedef int proc_sym_f(void *, const GElf_Sym *, const char *); +typedef int proc_xsym_f(void *, const GElf_Sym *, const char *, + const prsyminfo_t *); + +extern int Psymbol_iter(struct ps_prochandle *, + const char *, int, int, proc_sym_f *, void *); +extern int Psymbol_iter_by_addr(struct ps_prochandle *, + const char *, int, int, proc_sym_f *, void *); +extern int Psymbol_iter_by_name(struct ps_prochandle *, + const char *, int, int, proc_sym_f *, void *); + +extern int Psymbol_iter_by_lmid(struct ps_prochandle *, + Lmid_t, const char *, int, int, proc_sym_f *, void *); + +extern int Pxsymbol_iter(struct ps_prochandle *, + Lmid_t, const char *, int, int, proc_xsym_f *, void *); + +/* + * 'which' selects which symbol table and can be one of the following. + */ +#define PR_SYMTAB 1 +#define PR_DYNSYM 2 +/* + * 'type' selects the symbols of interest by binding and type. It is a bit- + * mask of one or more of the following flags, whose order MUST match the + * order of STB and STT constants in <sys/elf.h>. + */ +#define BIND_LOCAL 0x0001 +#define BIND_GLOBAL 0x0002 +#define BIND_WEAK 0x0004 +#define BIND_ANY (BIND_LOCAL|BIND_GLOBAL|BIND_WEAK) +#define TYPE_NOTYPE 0x0100 +#define TYPE_OBJECT 0x0200 +#define TYPE_FUNC 0x0400 +#define TYPE_SECTION 0x0800 +#define TYPE_FILE 0x1000 +#define TYPE_ANY (TYPE_NOTYPE|TYPE_OBJECT|TYPE_FUNC|TYPE_SECTION|TYPE_FILE) + +/* + * This returns the rtld_db agent handle for the process. + * The handle will become invalid at the next successful exec() and + * must not be used beyond that point (see Preset_maps(), below). + */ +extern rd_agent_t *Prd_agent(struct ps_prochandle *); + +/* + * This should be called when an RD_DLACTIVITY event with the + * RD_CONSISTENT state occurs via librtld_db's event mechanism. + * This makes libproc's address space mappings and symbol tables current. + * The variant Pupdate_syms() can be used to preload all symbol tables as well. + */ +extern void Pupdate_maps(struct ps_prochandle *); +extern void Pupdate_syms(struct ps_prochandle *); + +/* + * This must be called after the victim process performs a successful + * exec() if any of the symbol table interface functions have been called + * prior to that point. This is essential because an exec() invalidates + * all previous symbol table and address space mapping information. + * It is always safe to call, but if it is called other than after an + * exec() by the victim process it just causes unnecessary overhead. + * + * The rtld_db agent handle obtained from a previous call to Prd_agent() is + * made invalid by Preset_maps() and Prd_agent() must be called again to get + * the new handle. + */ +extern void Preset_maps(struct ps_prochandle *); + +/* + * Given an address, Ppltdest() determines if this is part of a PLT, and if + * so returns a pointer to the symbol name that will be used for resolution. + * If the specified address is not part of a PLT, the function returns NULL. + */ +extern const char *Ppltdest(struct ps_prochandle *, uintptr_t); + +/* + * See comments for Pissyscall(), in Pisadep.h + */ +extern int Pissyscall_prev(struct ps_prochandle *, uintptr_t, uintptr_t *); + +/* + * Stack frame iteration interface. + */ +typedef int proc_stack_f(void *, prgregset_t, uint_t, const long *); + +extern int Pstack_iter(struct ps_prochandle *, + const prgregset_t, proc_stack_f *, void *); + +/* + * The following functions define a set of passive interfaces: libproc provides + * default, empty definitions that are called internally. If a client wishes + * to override these definitions, it can simply provide its own version with + * the same signature that interposes on the libproc definition. + * + * If the client program wishes to report additional error information, it + * can provide its own version of Perror_printf. + * + * If the client program wishes to receive a callback after Pcreate forks + * but before it execs, it can provide its own version of Pcreate_callback. + */ +extern void Perror_printf(struct ps_prochandle *P, const char *format, ...); +extern void Pcreate_callback(struct ps_prochandle *); + +/* + * Remove unprintable characters from psinfo.pr_psargs and replace with + * whitespace characters so it is safe for printing. + */ +extern void proc_unctrl_psinfo(psinfo_t *); + +/* + * Utility functions for processing arguments which should be /proc files, + * pids, and/or core files. The returned error code can be passed to + * Pgrab_error() in order to convert it to an error string. + */ +#define PR_ARG_PIDS 0x1 /* Allow pid and /proc file arguments */ +#define PR_ARG_CORES 0x2 /* Allow core file arguments */ + +#define PR_ARG_ANY (PR_ARG_PIDS | PR_ARG_CORES) + +extern struct ps_prochandle *proc_arg_grab(const char *, int, int, int *); +extern struct ps_prochandle *proc_arg_xgrab(const char *, const char *, int, + int, int *, const char **); +extern pid_t proc_arg_psinfo(const char *, int, psinfo_t *, int *); +extern pid_t proc_arg_xpsinfo(const char *, int, psinfo_t *, int *, + const char **); + +/* + * Utility functions for obtaining information via /proc without actually + * performing a Pcreate() or Pgrab(): + */ +extern int proc_get_auxv(pid_t, auxv_t *, int); +extern int proc_get_cred(pid_t, prcred_t *, int); +extern prpriv_t *proc_get_priv(pid_t); +extern int proc_get_psinfo(pid_t, psinfo_t *); +extern int proc_get_status(pid_t, pstatus_t *); + +/* + * Utility functions for debugging tools to convert numeric fault, + * signal, and system call numbers to symbolic names: + */ +#define FLT2STR_MAX 32 /* max. string length of faults (like SIG2STR_MAX) */ +#define SYS2STR_MAX 32 /* max. string length of syscalls (like SIG2STR_MAX) */ + +extern char *proc_fltname(int, char *, size_t); +extern char *proc_signame(int, char *, size_t); +extern char *proc_sysname(int, char *, size_t); + +/* + * Utility functions for debugging tools to convert fault, signal, and system + * call names back to the numeric constants: + */ +extern int proc_str2flt(const char *, int *); +extern int proc_str2sig(const char *, int *); +extern int proc_str2sys(const char *, int *); + +/* + * Utility functions for debugging tools to convert a fault, signal or system + * call set to a string representation (e.g. "BUS,SEGV" or "open,close,read"). + */ +#define PRSIGBUFSZ 1024 /* buffer size for proc_sigset2str() */ + +extern char *proc_fltset2str(const fltset_t *, const char *, int, + char *, size_t); +extern char *proc_sigset2str(const sigset_t *, const char *, int, + char *, size_t); +extern char *proc_sysset2str(const sysset_t *, const char *, int, + char *, size_t); + +extern int Pgcore(struct ps_prochandle *, const char *, core_content_t); +extern int Pfgcore(struct ps_prochandle *, int, core_content_t); +extern core_content_t Pcontent(struct ps_prochandle *); + +/* + * Utility functions for debugging tools to convert a string representation of + * a fault, signal or system call set back to the numeric value of the + * corresponding set type. + */ +extern char *proc_str2fltset(const char *, const char *, int, fltset_t *); +extern char *proc_str2sigset(const char *, const char *, int, sigset_t *); +extern char *proc_str2sysset(const char *, const char *, int, sysset_t *); + +/* + * Utility functions for converting between strings and core_content_t. + */ +#define PRCONTENTBUFSZ 80 /* buffer size for proc_content2str() */ + +extern int proc_str2content(const char *, core_content_t *); +extern int proc_content2str(core_content_t, char *, size_t); + +/* + * Utility functions for buffering output to stdout, stderr while + * process is grabbed. Prevents deadlocks due to pfiles `pgrep xterm` + * and other varients. + */ +extern int proc_initstdio(void); +extern int proc_flushstdio(void); +extern int proc_finistdio(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBPROC_H */ diff --git a/usr/src/lib/libproc/common/llib-lproc b/usr/src/lib/libproc/common/llib-lproc new file mode 100644 index 0000000000..015de9ca0e --- /dev/null +++ b/usr/src/lib/libproc/common/llib-lproc @@ -0,0 +1,381 @@ +/* + * 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 + */ +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "libproc.h" + +/* + * usr/src/lib/libproc + */ + +/* Pcontrol.c */ +int _libproc_debug; +struct ps_prochandle *Pcreate(const char *file, char *const *argv, + int *perr, char *path, size_t len); +const char *Pcreate_error(int error); +void Pcreate_callback(struct ps_prochandle *Pr); +struct ps_prochandle *Pgrab(pid_t pid, int gflag, int *perr); +const char *Pgrab_error(int error); +void Pfree(struct ps_prochandle *Pr); +int Pstate(struct ps_prochandle *Pr); +int Pasfd(struct ps_prochandle *Pr); +int Pctlfd(struct ps_prochandle *Pr); +const psinfo_t *Ppsinfo(struct ps_prochandle *Pr); +const pstatus_t *Pstatus(struct ps_prochandle *Pr); +int Pcred(struct ps_prochandle *Pr, prcred_t *pcrp, int ngroups); +ssize_t Ppriv(struct ps_prochandle *Pr, prpriv_t *pprivp, size_t); +void Psync(struct ps_prochandle *Pr); +int Pcreate_agent(struct ps_prochandle *Pr); +void Pdestroy_agent(struct ps_prochandle *Pr); +int Preopen(struct ps_prochandle *Pr); +void Prelease(struct ps_prochandle *Pr, int flags); +int Pstopstatus(struct ps_prochandle *Pr, long cmd, uint_t msec); +int Pwait(struct ps_prochandle *Pr, uint_t msec); +int Pstop(struct ps_prochandle *Pr, uint_t msec); +int Pdstop(struct ps_prochandle *Pr); +int Pgetareg(struct ps_prochandle *Pr, int regno, prgreg_t *preg); +int Pputareg(struct ps_prochandle *Pr, int regno, prgreg_t reg); +int Psetrun(struct ps_prochandle *Pr, int sig, int flags); +ssize_t Pread(struct ps_prochandle *Pr, + void *buf, size_t nbyte, uintptr_t address); +ssize_t Pread_string(struct ps_prochandle *Pr, + char *buf, size_t nbyte, uintptr_t address); +ssize_t Pwrite(struct ps_prochandle *Pr, + const void *buf, size_t nbyte, uintptr_t address); +int Pclearsig(struct ps_prochandle *Pr); +int Pclearfault(struct ps_prochandle *Pr); +int Psetbkpt(struct ps_prochandle *Pr, uintptr_t address, ulong_t *saved); +int Pdelbkpt(struct ps_prochandle *Pr, uintptr_t address, ulong_t saved); +int Pxecbkpt(struct ps_prochandle *Pr, ulong_t saved); +int Psetwapt(struct ps_prochandle *Pr, const prwatch_t *wp); +int Pdelwapt(struct ps_prochandle *Pr, const prwatch_t *wp); +int Pxecwapt(struct ps_prochandle *Pr, const prwatch_t *wp); +int Psetflags(struct ps_prochandle *Pr, long flags); +int Punsetflags(struct ps_prochandle *Pr, long flags); +int Psignal(struct ps_prochandle *Pr, int which, int stop); +void Psetsignal(struct ps_prochandle *Pr, const sigset_t *set); +int Pfault(struct ps_prochandle *Pr, int which, int stop); +void Psetfault(struct ps_prochandle *Pr, const fltset_t *set); +int Psysentry(struct ps_prochandle *Pr, int which, int stop); +void Psetsysentry(struct ps_prochandle *Pr, const sysset_t *set); +int Psysexit(struct ps_prochandle *Pr, int which, int stop); +void Psetsysexit(struct ps_prochandle *Pr, const sysset_t *set); +int Plwp_iter(struct ps_prochandle *Pr, proc_lwp_f *func, void *cd); +int Psyscall(struct ps_prochandle *Pr, sysret_t *, + int sysindex, uint_t nargs, argdes_t *argp); + +struct ps_lwphandle *Lgrab(struct ps_prochandle *P, lwpid_t lwpid, int *perr); +const char *Lgrab_error(int error); +struct ps_prochandle *Lprochandle(struct ps_lwphandle *Lwp); +void Lfree(struct ps_lwphandle *Lwp); +int Lctlfd(struct ps_lwphandle *Lwp); +int Lwait(struct ps_lwphandle *Lwp, uint_t msec); +int Lstop(struct ps_lwphandle *Lwp, uint_t msec); +int Ldstop(struct ps_lwphandle *Lwp); +int Lstate(struct ps_lwphandle *Lwp); +const lwpsinfo_t *Lpsinfo(struct ps_lwphandle *Lwp); +const lwpstatus_t *Lstatus(struct ps_lwphandle *Lwp); +int Lgetareg(struct ps_lwphandle *Lwp, int regno, prgreg_t *preg); +int Lputareg(struct ps_lwphandle *Lwp, int regno, prgreg_t reg); +int Lsetrun(struct ps_lwphandle *Lwp, int sig, int flags); +int Lclearsig(struct ps_lwphandle *Lwp); +int Lclearfault(struct ps_lwphandle *Lwp); +int Lxecbkpt(struct ps_lwphandle *Lwp, ulong_t saved); +int Lxecwapt(struct ps_lwphandle *Lwp, const prwatch_t *wp); +void Lsync(struct ps_lwphandle *Lwp); + +/* Plwpregs.c */ +int Plwp_getregs(struct ps_prochandle *Pr, lwpid_t i, prgregset_t gr); +int Plwp_setregs(struct ps_prochandle *Pr, lwpid_t i, const prgregset_t gr); +int Plwp_getfpregs(struct ps_prochandle *Pr, lwpid_t i, prfpregset_t *fp); +int Plwp_setfpregs(struct ps_prochandle *Pr, lwpid_t i, const prfpregset_t *fp); +#if defined(sparc) || defined(__sparc) +int Plwp_getxregs(struct ps_prochandle *Pr, lwpid_t i, prxregset_t *xr); +int Plwp_setxregs(struct ps_prochandle *Pr, lwpid_t i, const prxregset_t *xr); +#if defined(__sparcv9) +int Plwp_getasrs(struct ps_prochandle *Pr, lwpid_t i, asrset_t asrs); +int Plwp_setasrs(struct ps_prochandle *Pr, lwpid_t i, const asrset_t asrs); +#endif /* __sparcv9 */ +#endif /* __sparc */ +int Plwp_getpsinfo(struct ps_prochandle *Pr, lwpid_t i, lwpsinfo_t *lps); + +/* Pcore.c */ +struct ps_prochandle *Pfgrab_core(int fd, const char *aout, int *perr); +struct ps_prochandle *Pgrab_core(const char *core, const char *aout, + int gflag, int *perr); + +/* Pisprocdir.c */ +int Pisprocdir(struct ps_prochandle *Pr, const char *dir); + +/* Pservice.c */ +ps_err_e ps_pdmodel(struct ps_prochandle *Pr, int *modelp); +ps_err_e ps_pread(struct ps_prochandle *Pr, + psaddr_t addr, void *buf, size_t size); +ps_err_e ps_pwrite(struct ps_prochandle *Pr, + psaddr_t addr, const void *buf, size_t size); +ps_err_e ps_pdread(struct ps_prochandle *Pr, + psaddr_t addr, void *buf, size_t size); +ps_err_e ps_pdwrite(struct ps_prochandle *Pr, + psaddr_t addr, const void *buf, size_t size); +ps_err_e ps_ptread(struct ps_prochandle *Pr, + psaddr_t addr, void *buf, size_t size); +ps_err_e ps_ptwrite(struct ps_prochandle *Pr, + psaddr_t addr, const void *buf, size_t size); +ps_err_e ps_pstop(struct ps_prochandle *Pr); +ps_err_e ps_pcontinue(struct ps_prochandle *Pr); +ps_err_e ps_lstop(struct ps_prochandle *Pr, lwpid_t lwpid); +ps_err_e ps_lcontinue(struct ps_prochandle *Pr, lwpid_t lwpid); +ps_err_e ps_lgetregs(struct ps_prochandle *Pr, + lwpid_t lwpid, prgregset_t regs); +ps_err_e ps_lsetregs(struct ps_prochandle *Pr, + lwpid_t lwpid, const prgregset_t regs); +ps_err_e ps_lgetfpregs(struct ps_prochandle *Pr, + lwpid_t lwpid, prfpregset_t *regs); +ps_err_e ps_lsetfpregs(struct ps_prochandle *Pr, + lwpid_t lwpid, const prfpregset_t *regs); +#if defined(sparc) || defined(__sparc) +ps_err_e ps_lgetxregsize(struct ps_prochandle *Pr, + lwpid_t lwpid, int *xrsize); +ps_err_e ps_lgetxregs(struct ps_prochandle *Pr, + lwpid_t lwpid, caddr_t xregs); +ps_err_e ps_lsetxregs(struct ps_prochandle *Pr, + lwpid_t lwpid, caddr_t xregs); +#endif /* sparc */ +#if defined(__i386) || defined(__amd64) +ps_err_e ps_lgetLDT(struct ps_prochandle *Pr, + lwpid_t lwpid, struct ssd *ldt); +#endif /* __i386 || __amd6464 */ +void ps_plog(const char *fmt, ...); + +/* Psymtab.c */ +void Pupdate_maps(struct ps_prochandle *Pr); +void Pupdate_syms(struct ps_prochandle *Pr); +rd_agent_t *Prd_agent(struct ps_prochandle *Pr); +const prmap_t *Paddr_to_map(struct ps_prochandle *Pr, uintptr_t addr); +const prmap_t *Paddr_to_text_map(struct ps_prochandle *Pr, uintptr_t addr); +const prmap_t *Pname_to_map(struct ps_prochandle *Pr, const char *name); +const prmap_t *Plmid_to_map(struct ps_prochandle *Pr, Lmid_t lmid, + const char *name); +int Plookup_by_addr(struct ps_prochandle *Pr, uintptr_t addr, + char *sym_name_buffer, size_t bufsize, GElf_Sym *symbolp); +int Plookup_by_name(struct ps_prochandle *Pr, + const char *object_name, const char *symbol_name, + GElf_Sym *sym); +int Plookup_by_lmid(struct ps_prochandle *Pr, + Lmid_t lmid, const char *object_name, const char *symbol_name, + GElf_Sym *sym); +const rd_loadobj_t *Paddr_to_loadobj(struct ps_prochandle *, uintptr_t); +const rd_loadobj_t *Pname_to_loadobj(struct ps_prochandle *, const char *); +const rd_loadobj_t *Plmid_to_loadobj(struct ps_prochandle *, Lmid_t, + const char *); +int Pmapping_iter(struct ps_prochandle *Pr, proc_map_f *func, void *cd); +int Pobject_iter(struct ps_prochandle *Pr, proc_map_f *func, void *cd); +char *Pobjname(struct ps_prochandle *Pr, uintptr_t addr, + char *buffer, size_t bufsize); +int Plmid(struct ps_prochandle *Pr, uintptr_t addr, Lmid_t *lmidp); +int Psymbol_iter(struct ps_prochandle *Pr, const char *object_name, + int which, int type, proc_sym_f *func, void *cd); +int Psymbol_iter_by_lmid(struct ps_prochandle *Pr, Lmid_t lmid, + const char *object_name, int which, int type, + proc_sym_f *func, void *cd); +char *Pgetenv(struct ps_prochandle *Pr, const char *name, + char *buffer, size_t bufsize); +char *Pplatform(struct ps_prochandle *Pr, char *s, size_t n); +int Puname(struct ps_prochandle *Pr, struct utsname *u); +char *Pzonename(struct ps_prochandle *Pr, char *s, size_t n); +char *Pexecname(struct ps_prochandle *Pr, char *buffer, size_t bufsize); +void Preset_maps(struct ps_prochandle *Pr); + +ps_err_e ps_pglobal_lookup(struct ps_prochandle *Pr, + const char *object_name, const char *sym_name, + psaddr_t *sym_addr); + +ps_err_e ps_pglobal_sym(struct ps_prochandle *Pr, + const char *object_name, const char *sym_name, + ps_sym_t *symp); + +long Pgetauxval(struct ps_prochandle *Pr, int type); +const auxv_t *Pgetauxvec(struct ps_prochandle *Pr); +ps_err_e ps_pauxv(struct ps_prochandle *Pr, const auxv_t **aux); + +/* Putil.c */ +void Perror_printf(struct ps_prochandle *Pr, const char *format, ...); + +/* pr_door.c */ +int pr_door_info(struct ps_prochandle *Pr, int did, door_info_t *di); + +/* pr_exit.c */ +int pr_exit(struct ps_prochandle *Pr, int status); +int pr_lwp_exit(struct ps_prochandle *Pr); + +/* pr_fcntl.c */ +int pr_fcntl(struct ps_prochandle *Pr, int fd, int cmd, void *argp); + +/* pr_getitimer.c */ +int pr_getitimer(struct ps_prochandle *Pr, + int which, struct itimerval *itv); +int pr_setitimer(struct ps_prochandle *Pr, + int which, const struct itimerval *itv, struct itimerval *oitv); + +/* pr_getrctl.c */ +int pr_getrctl(struct ps_prochandle *Pr, const char *rname, + rctlblk_t *old_blk, rctlblk_t *new_blk, int rflag); +int pr_setrctl(struct ps_prochandle *Pr, const char *rname, + rctlblk_t *old_blk, rctlblk_t *new_blk, int rflag); + +/* pr_getrlimit.c */ +int pr_getrlimit(struct ps_prochandle *Pr, + int resource, struct rlimit *rlp); +int pr_setrlimit(struct ps_prochandle *Pr, + int resource, const struct rlimit *rlp); +int pr_getrlimit64(struct ps_prochandle *Pr, + int resource, struct rlimit64 *rlp); +int pr_setrlimit64(struct ps_prochandle *Pr, + int resource, const struct rlimit64 *rlp); + +/* pr_getsockname.c */ +int pr_getsockname(struct ps_prochandle *Pr, + int sock, struct sockaddr *name, socklen_t *namelen); +int pr_getpeername(struct ps_prochandle *Pr, + int sock, struct sockaddr *name, socklen_t *namelen); + +/* pr_ioctl.c */ +int pr_ioctl(struct ps_prochandle *Pr, + int fd, int code, void *buf, size_t size); + +/* pr_lseek.c */ +off_t pr_lseek(struct ps_prochandle *Pr, + int filedes, off_t offset, int whence); +offset_t pr_llseek(struct ps_prochandle *Pr, + int filedes, offset_t offset, int whence); + +/* pr_memcntl.c */ +int pr_memcntl(struct ps_prochandle *Pr, + caddr_t addr, size_t len, int cmd, caddr_t arg, int attr, int mask); + +/* pr_mmap.c */ +void *pr_mmap(struct ps_prochandle *Pr, + void *addr, size_t len, int prot, int flags, int fd, off_t off); +int pr_munmap(struct ps_prochandle *Pr, + void *addr, size_t len); +void *pr_zmap(struct ps_prochandle *Pr, + void *addr, size_t len, int prot, int flags); + +/* pr_open.c */ +int pr_open(struct ps_prochandle *Pr, + const char *filename, int flags, mode_t mode); +int pr_creat(struct ps_prochandle *Pr, + const char *filename, mode_t mode); +int pr_close(struct ps_prochandle *Pr, int fd); +int pr_access(struct ps_prochandle *Pr, const char *path, int amode); + +/* pr_pbind.c */ +int pr_processor_bind(struct ps_prochandle *Pr, idtype_t, id_t, int, int *); +int pr_pset_bind(struct ps_prochandle *Pr, int, idtype_t, id_t, int *); + +/* pr_rename.c */ +int pr_rename(struct ps_prochandle *Pr, const char *old, const char *new); +int pr_link(struct ps_prochandle *Pr, const char *exist, const char *new); +int pr_unlink(struct ps_prochandle *Pr, const char *); + +/* pr_sigaction.c */ +int pr_sigaction(struct ps_prochandle *Pr, + int sig, const struct sigaction *act, struct sigaction *oact); + +/* pr_stat.c */ +int pr_stat(struct ps_prochandle *Pr, const char *path, struct stat *buf); +int pr_lstat(struct ps_prochandle *Pr, const char *path, struct stat *buf); +int pr_fstat(struct ps_prochandle *Pr, int fd, struct stat *buf); +int pr_stat64(struct ps_prochandle *Pr, const char *path, + struct stat64 *buf); +int pr_lstat64(struct ps_prochandle *Pr, const char *path, + struct stat64 *buf); +int pr_fstat64(struct ps_prochandle *Pr, int fd, struct stat64 *buf); + +/* pr_statvfs.c */ +int pr_statvfs(struct ps_prochandle *Pr, const char *path, statvfs_t *buf); +int pr_fstatvfs(struct ps_prochandle *Pr, int fd, statvfs_t *buf); + +/* pr_tasksys.c */ +projid_t pr_getprojid(struct ps_prochandle *Pr); +taskid_t pr_gettaskid(struct ps_prochandle *Pr); +taskid_t pr_settaskid(struct ps_prochandle *Pr, projid_t project, int flags); + +/* pr_waitid.c */ +int pr_waitid(struct ps_prochandle *Pr, + idtype_t idtype, id_t id, siginfo_t *infop, int options); + +/* proc_get_info.c */ +int proc_get_cred(pid_t pid, prcred_t *credp, int ngroups); +prpriv_t *proc_get_priv(pid_t pid); +int proc_get_psinfo(pid_t pid, psinfo_t *psp); +int proc_get_status(pid_t pid, pstatus_t *psp); +int proc_get_auxv(pid_t pid, auxv_t *pauxv, int naux); + +/* proc_names.c */ +char *proc_fltname(int flt, char *buf, size_t bufsz); +char *proc_signame(int sig, char *buf, size_t bufsz); +char *proc_sysname(int sys, char *buf, size_t bufsz); + +int proc_str2flt(const char *str, int *fltnum); +int proc_str2sig(const char *str, int *signum); +int proc_str2sys(const char *str, int *sysnum); + +char *proc_fltset2str(const fltset_t *set, const char *delim, int members, + char *buf, size_t nbytes); +char *proc_sigset2str(const sigset_t *set, const char *delim, int members, + char *buf, size_t nbytes); +char *proc_sysset2str(const sysset_t *set, const char *delim, int members, + char *buf, size_t nbytes); + +char *proc_str2fltset(const char *str, const char *delim, int members, + fltset_t *set); +char *proc_str2sigset(const char *str, const char *delim, int members, + sigset_t *set); +char *proc_str2sysset(const char *str, const char *delim, int members, + sysset_t *set); + +int proc_walk(proc_walk_f *func, void *arg, int flags); + +/* proc_arg.c */ +struct ps_prochandle *proc_arg_grab(const char *arg, + int oflag, int gflag, int *perr); + +pid_t proc_arg_psinfo(const char *arg, int oflag, psinfo_t *psp, int *perr); +void proc_unctrl_psinfo(psinfo_t *psp); + +/* proc_set.c */ +int Psetcred(struct ps_prochandle *Pr, const prcred_t *pcred); + +/* Pstack.c */ +int Pstack_iter(struct ps_prochandle *Pr, + const prgregset_t regs, proc_stack_f *func, void *arg); + +/* Pisadep.c */ +const char *Ppltdest(struct ps_prochandle *Pr, uintptr_t addr); diff --git a/usr/src/lib/libproc/common/pr_door.c b/usr/src/lib/libproc/common/pr_door.c new file mode 100644 index 0000000000..9a294d40f7 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_door.c @@ -0,0 +1,98 @@ +/* + * 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 <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <door.h> +#include "libproc.h" + +/* + * door() system calls -- executed by subject process + */ + +int +pr_door_info(struct ps_prochandle *Pr, int did, door_info_t *di) +{ + sysret_t rval; /* return value from _door_info() */ + argdes_t argd[6]; /* arg descriptors for _door_info() */ + argdes_t *adp = &argd[0]; /* first argument */ + int error; + + if (Pr == NULL) /* no subject process */ + return (door_info(did, di)); + + adp->arg_value = did; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to door_info_t argument */ + + adp->arg_value = 0; + adp->arg_object = di; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; + adp->arg_size = sizeof (door_info_t); + adp++; /* move to unused argument */ + + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to unused argument */ + + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to unused argument */ + + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to subcode argument */ + + adp->arg_value = DOOR_INFO; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_door, 6, &argd[0]); + + if (error) { + errno = (error < 0)? ENOSYS : error; + return (-1); + } + return (0); +} diff --git a/usr/src/lib/libproc/common/pr_exit.c b/usr/src/lib/libproc/common/pr_exit.c new file mode 100644 index 0000000000..d9fb874cc2 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_exit.c @@ -0,0 +1,110 @@ +/* + * 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 1997-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/lwp.h> +#include "libproc.h" + +/* + * exit() system call -- executed by subject process. + */ +int +pr_exit(struct ps_prochandle *Pr, int status) +{ + sysret_t rval; /* return value from exit() */ + argdes_t argd[1]; /* arg descriptors for exit() */ + argdes_t *adp; + int error; + + if (Pr == NULL) { /* no subject process */ + exit(status); + return (0); /* not reached */ + } + + adp = &argd[0]; /* status argument */ + adp->arg_value = status; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_exit, 1, &argd[0]); + /* actually -- never returns. Expect ENOENT */ + + if (error < 0) { + if (errno == ENOENT) /* expected case */ + error = ENOENT; + else + error = ENOSYS; + } + + if (error == 0) /* can't happen? */ + return (rval.sys_rval1); + + if (error == ENOENT) /* expected case */ + return (0); + + errno = error; + return (-1); +} + +/* + * lwp_exit() system call -- executed by subject lwp. + */ +int +pr_lwp_exit(struct ps_prochandle *Pr) +{ + sysret_t rval; /* return value from lwp_exit() */ + int error; + + if (Pr == NULL) { /* no subject process */ + (void) syscall(SYS_lwp_exit); + return (0); /* not reached */ + } + + error = Psyscall(Pr, &rval, SYS_lwp_exit, 0, NULL); + /* actually -- never returns. Expect ENOENT */ + + if (error < 0) { + if (errno == ENOENT) /* expected case */ + error = ENOENT; + else + error = ENOSYS; + } + + if (error == 0) /* can't happen? */ + return (rval.sys_rval1); + + if (error == ENOENT) /* expected case */ + return (0); + + errno = error; + return (-1); +} diff --git a/usr/src/lib/libproc/common/pr_fcntl.c b/usr/src/lib/libproc/common/pr_fcntl.c new file mode 100644 index 0000000000..c2c34e1d41 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_fcntl.c @@ -0,0 +1,140 @@ +/* + * 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 (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/isa_defs.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include "libproc.h" + +/* + * fcntl() system call -- executed by subject process. + */ +int +pr_fcntl(struct ps_prochandle *Pr, int fd, int cmd, void *argp) +{ + sysret_t rval; /* return value from fcntl() */ + argdes_t argd[3]; /* arg descriptors for fcntl() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (fcntl(fd, cmd, argp)); + + adp = &argd[0]; /* file descriptor argument */ + adp->arg_value = fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* cmd argument */ +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + /* + * Guilty knowledge of the large file compilation environment + */ + switch (cmd) { + case F_GETLK: + cmd = 33; + break; + case F_SETLK: + cmd = 34; + break; + case F_SETLKW: + cmd = 35; + break; + case F_FREESP: + cmd = 27; + break; + } + } +#endif /* _LP64 */ + adp->arg_value = cmd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* argp argument */ + if (argp == NULL) { + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = argp; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + case F_ALLOCSP: + case F_FREESP: + adp->arg_size = sizeof (struct flock); + break; +#ifdef _LP64 + case 33: + case 34: + case 35: + case 27: + adp->arg_size = sizeof (struct flock64_32); +#else /* _LP64 */ + case F_GETLK64: + case F_SETLK64: + case F_SETLKW64: + case F_FREESP64: + adp->arg_size = sizeof (struct flock64); +#endif /* _LP64 */ + break; + case F_SHARE: + case F_UNSHARE: + adp->arg_size = sizeof (struct fshare); + break; + default: + adp->arg_value = (long)argp; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + break; + } + } + + error = Psyscall(Pr, &rval, SYS_fcntl, 3, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_getitimer.c b/usr/src/lib/libproc/common/pr_getitimer.c new file mode 100644 index 0000000000..80e1fafdd3 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_getitimer.c @@ -0,0 +1,172 @@ +/* + * 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 (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/isa_defs.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include "libproc.h" + +/* + * getitimer() system call -- executed by victim process. + */ +int +pr_getitimer(struct ps_prochandle *Pr, int which, struct itimerval *itv) +{ + sysret_t rval; /* return value from getitimer() */ + argdes_t argd[2]; /* arg descriptors for getitimer() */ + argdes_t *adp; + int error; +#ifdef _LP64 + int victim32 = (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32); + struct itimerval32 itimerval32; +#endif + + if (Pr == NULL) /* no victim process */ + return (getitimer(which, itv)); + + adp = &argd[0]; /* which argument */ + adp->arg_value = which; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_object = NULL; + adp->arg_size = 0; + + adp++; /* itv argument */ + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (victim32) { + adp->arg_object = &itimerval32; + adp->arg_size = sizeof (itimerval32); + } else { + adp->arg_object = itv; + adp->arg_size = sizeof (*itv); + } +#else /* _LP64 */ + adp->arg_object = itv; + adp->arg_size = sizeof (*itv); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, SYS_getitimer, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (victim32) { + ITIMERVAL32_TO_ITIMERVAL(itv, &itimerval32); + } +#endif /* _LP64 */ + return (rval.sys_rval1); +} + +/* + * setitimer() system call -- executed by victim process. + */ +int +pr_setitimer(struct ps_prochandle *Pr, + int which, const struct itimerval *itv, struct itimerval *oitv) +{ + sysret_t rval; /* return value from setitimer() */ + argdes_t argd[3]; /* arg descriptors for setitimer() */ + argdes_t *adp; + int error; +#ifdef _LP64 + int victim32 = (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32); + struct itimerval32 itimerval32; + struct itimerval32 oitimerval32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no victim process */ + return (setitimer(which, (struct itimerval *)itv, oitv)); + + adp = &argd[0]; /* which argument */ + adp->arg_value = which; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_object = NULL; + adp->arg_size = 0; + + adp++; /* itv argument */ + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; +#ifdef _LP64 + if (victim32) { + ITIMERVAL_TO_ITIMERVAL32(&itimerval32, itv); + adp->arg_object = (void *)&itimerval32; + adp->arg_size = sizeof (itimerval32); + } else { + adp->arg_object = (void *)itv; + adp->arg_size = sizeof (*itv); + } +#else /* _LP64 */ + adp->arg_object = (void *)itv; + adp->arg_size = sizeof (*itv); +#endif /* _LP64 */ + + adp++; /* oitv argument */ + adp->arg_value = 0; + if (oitv == NULL) { + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_object = NULL; + adp->arg_size = 0; + } else { + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (victim32) { + adp->arg_object = (void *)&oitimerval32; + adp->arg_size = sizeof (oitimerval32); + } else { + adp->arg_object = oitv; + adp->arg_size = sizeof (*oitv); + } +#else /* _LP64 */ + adp->arg_object = oitv; + adp->arg_size = sizeof (*oitv); +#endif /* _LP64 */ + } + + error = Psyscall(Pr, &rval, SYS_setitimer, 3, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (victim32 && oitv != NULL) { + ITIMERVAL32_TO_ITIMERVAL(oitv, &oitimerval32); + } +#endif /* _LP64 */ + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_getrctl.c b/usr/src/lib/libproc/common/pr_getrctl.c new file mode 100644 index 0000000000..d54fc228a6 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_getrctl.c @@ -0,0 +1,199 @@ +/* + * 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 (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define _LARGEFILE64_SOURCE + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <strings.h> +#include "libproc.h" + +/* + * getrctl() system call -- executed by subject process + */ +int +pr_getrctl(struct ps_prochandle *Pr, const char *rname, + rctlblk_t *old_blk, rctlblk_t *new_blk, int rflag) +{ + sysret_t rval; + argdes_t argd[6]; + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (getrctl(rname, old_blk, new_blk, rflag)); + + adp = &argd[0]; + adp->arg_value = 0; /* switch for getrctl in rctlsys */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; + adp->arg_value = 0; + adp->arg_object = (void *)rname; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(rname) + 1; + + adp++; + if (old_blk == NULL) { + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = old_blk; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = rctlblk_size(); + } + + adp++; + if (new_blk == NULL) { + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_OUTPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = new_blk; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + adp->arg_size = rctlblk_size(); + } + + adp++; + adp->arg_value = 0; /* obufsz isn't used by getrctl() */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; + adp->arg_value = rflag; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_rctlsys, 6, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * setrctl() system call -- executed by subject process + */ +int +pr_setrctl(struct ps_prochandle *Pr, const char *rname, + rctlblk_t *old_blk, rctlblk_t *new_blk, int rflag) +{ + sysret_t rval; + argdes_t argd[6]; + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (setrctl(rname, old_blk, new_blk, rflag)); + + adp = &argd[0]; + adp->arg_value = 1; /* switch for setrctl in rctlsys */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; + adp->arg_value = 0; + adp->arg_object = (void *)rname; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(rname) + 1; + + adp++; + if (old_blk == NULL) { + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = old_blk; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = rctlblk_size(); + } + + adp++; + if (new_blk == NULL) { + adp->arg_value = 0; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = new_blk; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = rctlblk_size(); + } + + adp++; + adp->arg_value = 0; /* obufsz isn't used by setrctl() */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; + adp->arg_value = rflag; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_rctlsys, 6, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_getrlimit.c b/usr/src/lib/libproc/common/pr_getrlimit.c new file mode 100644 index 0000000000..d5ea456cef --- /dev/null +++ b/usr/src/lib/libproc/common/pr_getrlimit.c @@ -0,0 +1,227 @@ +/* + * 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 (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define _LARGEFILE64_SOURCE + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/resource.h> +#include "libproc.h" + +/* + * getrlimit() system call -- executed by subject process. + */ +int +pr_getrlimit(struct ps_prochandle *Pr, + int resource, struct rlimit *rlp) +{ + sysret_t rval; /* return value from getrlimit() */ + argdes_t argd[2]; /* arg descriptors for getrlimit() */ + argdes_t *adp; + int sysnum; + int error; + + if (Pr == NULL) /* no subject process */ + return (getrlimit(resource, rlp)); + + adp = &argd[0]; /* resource argument */ + adp->arg_value = resource; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* rlp argument */ + adp->arg_value = 0; + adp->arg_object = rlp; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; + adp->arg_size = sizeof (*rlp); + +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + sysnum = SYS_getrlimit64; + else + sysnum = SYS_getrlimit; +#else /* _LP64 */ + sysnum = SYS_getrlimit; +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, sysnum, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * setrlimit() system call -- executed by subject process. + */ +int +pr_setrlimit(struct ps_prochandle *Pr, + int resource, const struct rlimit *rlp) +{ + sysret_t rval; /* return value from setrlimit() */ + argdes_t argd[2]; /* arg descriptors for setrlimit() */ + argdes_t *adp; + int sysnum; + int error; + + if (Pr == NULL) /* no subject process */ + return (setrlimit(resource, rlp)); + + adp = &argd[0]; /* resource argument */ + adp->arg_value = resource; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* rlp argument */ + adp->arg_value = 0; + adp->arg_object = (void *)rlp; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = sizeof (*rlp); + +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + sysnum = SYS_setrlimit64; + else + sysnum = SYS_setrlimit; +#else /* _LP64 */ + sysnum = SYS_setrlimit; +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, sysnum, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * getrlimit64() system call -- executed by subject process. + */ +int +pr_getrlimit64(struct ps_prochandle *Pr, + int resource, struct rlimit64 *rlp) +{ + sysret_t rval; /* return value from getrlimit() */ + argdes_t argd[2]; /* arg descriptors for getrlimit() */ + argdes_t *adp; + int sysnum; + int error; + + if (Pr == NULL) /* no subject process */ + return (getrlimit64(resource, rlp)); + + adp = &argd[0]; /* resource argument */ + adp->arg_value = resource; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* rlp argument */ + adp->arg_value = 0; + adp->arg_object = rlp; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; + adp->arg_size = sizeof (*rlp); + +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + sysnum = SYS_getrlimit64; + else + sysnum = SYS_getrlimit; +#else /* _LP64 */ + sysnum = SYS_getrlimit64; +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, sysnum, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * setrlimit64() system call -- executed by subject process. + */ +int +pr_setrlimit64(struct ps_prochandle *Pr, + int resource, const struct rlimit64 *rlp) +{ + sysret_t rval; /* return value from setrlimit() */ + argdes_t argd[2]; /* arg descriptors for setrlimit() */ + argdes_t *adp; + int sysnum; + int error; + + if (Pr == NULL) /* no subject process */ + return (setrlimit64(resource, rlp)); + + adp = &argd[0]; /* resource argument */ + adp->arg_value = resource; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* rlp argument */ + adp->arg_value = 0; + adp->arg_object = (void *)rlp; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = sizeof (*rlp); + +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + sysnum = SYS_setrlimit64; + else + sysnum = SYS_setrlimit; +#else /* _LP64 */ + sysnum = SYS_setrlimit64; +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, sysnum, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_getsockname.c b/usr/src/lib/libproc/common/pr_getsockname.c new file mode 100644 index 0000000000..ce29c9bdca --- /dev/null +++ b/usr/src/lib/libproc/common/pr_getsockname.c @@ -0,0 +1,169 @@ +/* + * 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 1998-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stream.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include "libproc.h" + +static int +get_sock_peer_name(struct ps_prochandle *Pr, + int syscall, int sock, struct sockaddr *name, socklen_t *namelen) +{ + sysret_t rval; /* return value from get{sock|peer}name() */ + argdes_t argd[4]; /* arg descriptors for get{sock|peer}name() */ + argdes_t *adp; + int error; + + adp = &argd[0]; /* sock argument */ + adp->arg_value = sock; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* name argument */ + adp->arg_value = 0; + adp->arg_object = name; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; + adp->arg_size = *namelen; + + adp++; /* namelen argument */ + adp->arg_value = 0; + adp->arg_object = namelen; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + adp->arg_size = sizeof (*namelen); + + adp++; /* version argument */ + adp->arg_value = SOV_DEFAULT; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, syscall, 4, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (0); +} + +/* libc system call interface */ +extern int _so_getsockname(int, struct sockaddr *, socklen_t *, int); +extern int _so_getpeername(int, struct sockaddr *, socklen_t *, int); +extern int _so_getsockopt(int, int, int, void *, int *); + +/* + * getsockname() system call -- executed by subject process + */ +int +pr_getsockname(struct ps_prochandle *Pr, + int sock, struct sockaddr *name, socklen_t *namelen) +{ + if (Pr == NULL) /* no subject process */ + return (_so_getsockname(sock, name, namelen, SOV_DEFAULT)); + + return (get_sock_peer_name(Pr, SYS_getsockname, sock, name, namelen)); +} + +/* + * getpeername() system call -- executed by subject process + */ +int +pr_getpeername(struct ps_prochandle *Pr, + int sock, struct sockaddr *name, socklen_t *namelen) +{ + if (Pr == NULL) /* no subject process */ + return (_so_getpeername(sock, name, namelen, SOV_DEFAULT)); + + return (get_sock_peer_name(Pr, SYS_getpeername, sock, name, namelen)); +} + +int +pr_getsockopt(struct ps_prochandle *Pr, + int sock, int level, int optname, void *optval, int *optlen) +{ + sysret_t rval; /* return value from getsockopt() */ + argdes_t argd[5]; /* arg descriptors for getsockopt() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (_so_getsockopt(sock, level, optname, optval, optlen)); + + adp = &argd[0]; /* sock argument */ + adp->arg_value = sock; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* level argument */ + adp->arg_value = level; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* optname argument */ + adp->arg_value = optname; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* optval argument */ + adp->arg_value = 0; + adp->arg_object = optval; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; + adp->arg_size = optlen == NULL ? 0 : *optlen; + + adp++; /* optlen argument */ + adp->arg_value = 0; + adp->arg_object = optlen; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + adp->arg_size = sizeof (*optlen); + + error = Psyscall(Pr, &rval, SYS_getsockopt, 5, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (0); +} diff --git a/usr/src/lib/libproc/common/pr_ioctl.c b/usr/src/lib/libproc/common/pr_ioctl.c new file mode 100644 index 0000000000..c33f778031 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_ioctl.c @@ -0,0 +1,84 @@ +/* + * 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 (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include "libproc.h" + +/* + * ioctl() system call -- executed by subject process. + */ +int +pr_ioctl(struct ps_prochandle *Pr, int fd, int code, void *buf, size_t size) +{ + sysret_t rval; /* return value from ioctl() */ + argdes_t argd[3]; /* arg descriptors for ioctl() */ + argdes_t *adp = &argd[0]; /* first argument */ + int error; + + if (Pr == NULL) /* no subject process */ + return (ioctl(fd, code, buf)); + + adp->arg_value = fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to code argument */ + + adp->arg_value = code; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to buffer argument */ + + if (size == 0) { + adp->arg_value = (long)buf; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = buf; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + adp->arg_size = size; + } + + error = Psyscall(Pr, &rval, SYS_ioctl, 3, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_lseek.c b/usr/src/lib/libproc/common/pr_lseek.c new file mode 100644 index 0000000000..1d46f2db01 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_lseek.c @@ -0,0 +1,187 @@ +/* + * 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 (c) 1998-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include "libproc.h" + +typedef union { + offset_t full; /* full 64 bit offset value */ + uint32_t half[2]; /* two 32-bit halves */ +} offsets_t; + +/* + * lseek() system call -- executed by subject process. + */ +off_t +pr_lseek(struct ps_prochandle *Pr, int filedes, off_t offset, int whence) +{ + int syscall; /* SYS_lseek or SYS_llseek */ + int nargs; /* 3 or 4, depending on syscall */ + offsets_t off; + sysret_t rval; /* return value from lseek() */ + argdes_t argd[4]; /* arg descriptors for lseek() */ + argdes_t *adp; + int error; + + if (Pr == NULL) + return (lseek(filedes, offset, whence)); + + adp = &argd[0]; /* filedes argument */ + adp->arg_value = filedes; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* offset argument */ + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_NATIVE) { + syscall = SYS_lseek; + nargs = 3; + adp->arg_value = offset; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + syscall = SYS_llseek; + nargs = 4; + off.full = offset; + adp->arg_value = off.half[0]; /* first 32 bits */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + adp->arg_value = off.half[1]; /* second 32 bits */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } + + adp++; /* whence argument */ + adp->arg_value = whence; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return ((off_t)(-1)); + } + + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_NATIVE) + offset = rval.sys_rval1; + else { + off.half[0] = (uint32_t)rval.sys_rval1; + off.half[1] = (uint32_t)rval.sys_rval2; + offset = (off_t)off.full; + } + + return (offset); +} + +/* + * llseek() system call -- executed by subject process. + */ +offset_t +pr_llseek(struct ps_prochandle *Pr, int filedes, offset_t offset, int whence) +{ + int syscall; /* SYS_lseek or SYS_llseek */ + int nargs; /* 3 or 4, depending on syscall */ + offsets_t off; + sysret_t rval; /* return value from llseek() */ + argdes_t argd[4]; /* arg descriptors for llseek() */ + argdes_t *adp; + int error; + + if (Pr == NULL) + return (llseek(filedes, offset, whence)); + + adp = &argd[0]; /* filedes argument */ + adp->arg_value = filedes; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* offset argument */ + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) { + syscall = SYS_lseek; + nargs = 3; + adp->arg_value = offset; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + syscall = SYS_llseek; + nargs = 4; + off.full = offset; + adp->arg_value = off.half[0]; /* first 32 bits */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + adp->arg_value = off.half[1]; /* second 32 bits */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } + + adp++; /* whence argument */ + adp->arg_value = whence; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return ((offset_t)(-1)); + } + + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) + offset = rval.sys_rval1; + else { + off.half[0] = (uint32_t)rval.sys_rval1; + off.half[1] = (uint32_t)rval.sys_rval2; + offset = off.full; + } + + return (offset); +} diff --git a/usr/src/lib/libproc/common/pr_memcntl.c b/usr/src/lib/libproc/common/pr_memcntl.c new file mode 100644 index 0000000000..0b2a306e75 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_memcntl.c @@ -0,0 +1,116 @@ +/* + * 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 (c) 1998-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/mman.h> +#include "libproc.h" + +/* + * memcntl() system call -- executed by subject process + */ + +int +pr_memcntl(struct ps_prochandle *Pr, + caddr_t addr, size_t len, int cmd, caddr_t arg, int attr, int mask) +{ + sysret_t rval; /* return value from memcntl() */ + argdes_t argd[6]; /* arg descriptors for memcntl() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (memcntl(addr, len, cmd, arg, attr, mask)); + + adp = &argd[0]; /* addr argument */ + adp->arg_value = (uintptr_t)addr; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* len argument */ + adp->arg_value = len; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* cmd argument */ + adp->arg_value = cmd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* arg argument */ + if (cmd == MC_HAT_ADVISE) { + adp->arg_value = 0; + adp->arg_object = arg; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + adp->arg_size = sizeof (struct memcntl_mha32); + else + adp->arg_size = sizeof (struct memcntl_mha); +#else + adp->arg_size = sizeof (struct memcntl_mha); +#endif + } else { + adp->arg_value = (uintptr_t)arg; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } + + adp++; /* attr argument */ + adp->arg_value = attr; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* mask argument */ + adp->arg_value = mask; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_memcntl, 6, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (0); +} diff --git a/usr/src/lib/libproc/common/pr_meminfo.c b/usr/src/lib/libproc/common/pr_meminfo.c new file mode 100644 index 0000000000..475c728267 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_meminfo.c @@ -0,0 +1,188 @@ +/* + * 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 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/mman.h> +#include "libproc.h" +#include "Pcontrol.h" +#include "Putil.h" + + +int +pr_meminfo(struct ps_prochandle *Pr, const uint64_t *addrs, + int addr_count, const uint_t *info, int info_count, + uint64_t *outdata, uint_t *validity) +{ + + + int error; + sysret_t rval; + argdes_t argd[7]; + argdes_t *adp = &argd[0]; + struct meminfo m; +#ifdef _LP64 + struct meminfo32 m32; + int model; +#endif + int retval = -1; + uintptr_t inaddr, infoaddr, outaddr, validityaddr; + size_t outarraysize, infoarraysize; + size_t inarraysize, validityarraysize; + size_t totalsize; + char *totalmap = MAP_FAILED; + + inarraysize = addr_count * sizeof (uint64_t); + outarraysize = sizeof (uint64_t) * addr_count * info_count; + infoarraysize = info_count * sizeof (uint_t); + validityarraysize = sizeof (uint_t) * addr_count; + + + totalsize = inarraysize + outarraysize + infoarraysize + + validityarraysize; + + if ((totalmap = pr_zmap(Pr, 0, totalsize, PROT_READ | PROT_WRITE, + MAP_PRIVATE)) == MAP_FAILED) { + dprintf("pr_meminfo: mmap failed\n"); + goto out; + } + + inaddr = (uintptr_t)totalmap; + + outaddr = inaddr + inarraysize; + + infoaddr = outaddr + outarraysize; + + validityaddr = infoaddr + infoarraysize; + + if (Pwrite(Pr, addrs, inarraysize, inaddr) != inarraysize) { + dprintf("pr_meminfo: Pwrite inaddr failed \n"); + goto out; + } + + if (Pwrite(Pr, info, infoarraysize, infoaddr) != + infoarraysize) { + dprintf("pr_meminfo: Pwrite info failed \n"); + goto out; + } + +#ifdef _LP64 + model = Pr->status.pr_dmodel; + if (model == PR_MODEL_ILP32) { + m32.mi_info_count = info_count; + m32.mi_inaddr = (caddr32_t)inaddr; + m32.mi_outdata = (caddr32_t)outaddr; + m32.mi_info_req = (caddr32_t)infoaddr; + m32.mi_validity = (caddr32_t)validityaddr; + } else +#endif + { + m.mi_info_count = info_count; + m.mi_inaddr = (uint64_t *)inaddr; + m.mi_outdata = (uint64_t *)outaddr; + m.mi_info_req = (uint_t *)infoaddr; + m.mi_validity = (uint_t *)validityaddr; + } + + + /* + * initial command + */ + + adp->arg_value = MISYS_MEMINFO; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + /* + * length of input address vector + */ + + adp->arg_value = addr_count; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + /* + * information wanted vector + */ + + adp->arg_value = 0; +#ifdef _LP64 + if (model == PR_MODEL_ILP32) { + adp->arg_object = &m32; + adp->arg_size = sizeof (struct meminfo32); + } else +#endif + { + adp->arg_object = &m; + adp->arg_size = sizeof (struct meminfo); + } + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + + + error = Psyscall(Pr, &rval, SYS_meminfosys, 3, &argd[0]); + + if (error) { + errno = (error > 0) ? error: ENOSYS; + goto out; + } + + /* syscall was successful, copy out the data */ + + if ((Pread(Pr, outdata, outarraysize, outaddr)) != outarraysize) { + dprintf("pr_meminfo: Pread of outarray failed\n"); + goto out; + } + + if (Pread(Pr, validity, validityarraysize, validityaddr) + != validityarraysize) { + dprintf("pr_meminfo: Pread of validity array failed\n"); + goto out; + } + + retval = rval.sys_rval1; + +out: + + if (totalmap != MAP_FAILED && + pr_munmap(Pr, totalmap, totalsize) == -1) { + dprintf("pr_meminfo: munmap failed\n"); + retval = -1; + } + + return (retval); + +} diff --git a/usr/src/lib/libproc/common/pr_mmap.c b/usr/src/lib/libproc/common/pr_mmap.c new file mode 100644 index 0000000000..2c30aaf224 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_mmap.c @@ -0,0 +1,145 @@ +/* + * 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 (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/mman.h> +#include "libproc.h" + +/* + * mmap() system call -- executed by subject process + */ +void * +pr_mmap(struct ps_prochandle *Pr, + void *addr, size_t len, int prot, int flags, int fd, off_t off) +{ + sysret_t rval; /* return value from mmap() */ + argdes_t argd[6]; /* arg descriptors for mmap() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (mmap(addr, len, prot, flags, fd, off)); + + adp = &argd[0]; /* addr argument */ + adp->arg_value = (long)addr; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* len argument */ + adp->arg_value = (long)len; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* prot argument */ + adp->arg_value = (long)prot; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* flags argument */ + adp->arg_value = (long)(_MAP_NEW|flags); + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* fd argument */ + adp->arg_value = (long)fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* off argument */ + adp->arg_value = (long)off; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_mmap, 6, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return ((void *)(-1)); + } + return ((void *)rval.sys_rval1); +} + +/* + * munmap() system call -- executed by subject process + */ +int +pr_munmap(struct ps_prochandle *Pr, void *addr, size_t len) +{ + sysret_t rval; /* return value from munmap() */ + argdes_t argd[2]; /* arg descriptors for munmap() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (munmap(addr, len)); + + adp = &argd[0]; /* addr argument */ + adp->arg_value = (long)addr; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* len argument */ + adp->arg_value = (long)len; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_munmap, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * zmap() -- convenience function; mmap(MAP_ANON) executed by subject process. + */ +void * +pr_zmap(struct ps_prochandle *Pr, void *addr, size_t len, int prot, int flags) +{ + return (pr_mmap(Pr, addr, len, prot, flags | MAP_ANON, -1, (off_t)0)); +} diff --git a/usr/src/lib/libproc/common/pr_open.c b/usr/src/lib/libproc/common/pr_open.c new file mode 100644 index 0000000000..5ea4ea1393 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_open.c @@ -0,0 +1,183 @@ +/* + * 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 (c) 1997-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include "libproc.h" + +/* + * open() system call -- executed by subject process. + */ +int +pr_open(struct ps_prochandle *Pr, const char *filename, int flags, mode_t mode) +{ + sysret_t rval; /* return value from open() */ + argdes_t argd[3]; /* arg descriptors for open() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (open(filename, flags, mode)); + + adp = &argd[0]; /* filename argument */ + adp->arg_value = 0; + adp->arg_object = (void *)filename; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(filename)+1; + + adp++; /* flags argument */ + adp->arg_value = (long)flags; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* mode argument */ + adp->arg_value = (long)mode; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_open, 3, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * creat() system call -- executed by subject process. + */ +int +pr_creat(struct ps_prochandle *Pr, const char *filename, mode_t mode) +{ + sysret_t rval; /* return value from creat() */ + argdes_t argd[2]; /* arg descriptors for creat() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (creat(filename, mode)); + + adp = &argd[0]; /* filename argument */ + adp->arg_value = 0; + adp->arg_object = (void *)filename; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(filename)+1; + + adp++; /* mode argument */ + adp->arg_value = (long)mode; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_creat, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * close() system call -- executed by subject process. + */ +int +pr_close(struct ps_prochandle *Pr, int fd) +{ + sysret_t rval; /* return value from close() */ + argdes_t argd[1]; /* arg descriptors for close() */ + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (close(fd)); + + adp = &argd[0]; /* fd argument */ + adp->arg_value = (int)fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_close, 1, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * access() system call -- executed by subject process. + */ +int +pr_access(struct ps_prochandle *Pr, const char *path, int amode) +{ + sysret_t rval; /* return from access() */ + argdes_t argd[2]; /* arg descriptors for access() */ + argdes_t *adp; + int err; + + if (Pr == NULL) /* no subject process */ + return (access(path, amode)); + + adp = &argd[0]; /* path argument */ + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path) + 1; + + adp++; /* amode argument */ + adp->arg_value = (long)amode; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + err = Psyscall(Pr, &rval, SYS_access, 2, &argd[0]); + + if (err) { + errno = (err > 0) ? err : ENOSYS; + return (-1); + } + + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_pbind.c b/usr/src/lib/libproc/common/pr_pbind.c new file mode 100644 index 0000000000..1a9f6de689 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_pbind.c @@ -0,0 +1,155 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/procset.h> +#include <sys/processor.h> +#include <sys/pset.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include "libproc.h" + +int +pr_processor_bind(struct ps_prochandle *Pr, idtype_t idtype, id_t id, + int processorid, int *obind) +{ + sysret_t rval; /* return value */ + argdes_t argd[4]; /* arg descriptors */ + argdes_t *adp = &argd[0]; /* first argument */ + int error; + + if (Pr == NULL) /* no subject process */ + return (processor_bind(idtype, id, processorid, obind)); + + adp->arg_value = idtype; /* idtype */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + adp->arg_value = id; /* id */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + adp->arg_value = processorid; /* processorid */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + if (obind == NULL) { + adp->arg_value = 0; /* obind */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = obind; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + adp->arg_size = sizeof (int); + } + + error = Psyscall(Pr, &rval, SYS_processor_bind, 4, &argd[0]); + + if (error) { + errno = (error < 0)? ENOSYS : error; + return (-1); + } + return (rval.sys_rval1); +} + +int +pr_pset_bind(struct ps_prochandle *Pr, int pset, idtype_t idtype, id_t id, + int *opset) +{ + sysret_t rval; /* return value */ + argdes_t argd[5]; /* arg descriptors */ + argdes_t *adp = &argd[0]; /* first argument */ + int error; + + if (Pr == NULL) /* no subject process */ + return (pset_bind(pset, idtype, id, opset)); + + adp->arg_value = PSET_BIND; /* PSET_BIND */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + adp->arg_value = pset; /* pset */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + adp->arg_value = idtype; /* idtype */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + adp->arg_value = id; /* id */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; + + if (opset == NULL) { + adp->arg_value = 0; /* opset */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + } else { + adp->arg_value = 0; + adp->arg_object = opset; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INOUT; + adp->arg_size = sizeof (int); + } + + error = Psyscall(Pr, &rval, SYS_pset, 5, &argd[0]); + + if (error) { + errno = (error < 0)? ENOSYS : error; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_rename.c b/usr/src/lib/libproc/common/pr_rename.c new file mode 100644 index 0000000000..e6c5074eea --- /dev/null +++ b/usr/src/lib/libproc/common/pr_rename.c @@ -0,0 +1,137 @@ +/* + * 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 (c) 1998-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include "libproc.h" + +/* + * rename() system call -- executed by subject process. + */ +int +pr_rename(struct ps_prochandle *Pr, const char *old, const char *new) +{ + sysret_t rval; + argdes_t argd[2]; + argdes_t *adp; + int error; + + if (Pr == NULL) + return (rename(old, new)); + + adp = &argd[0]; /* old argument */ + adp->arg_value = 0; + adp->arg_object = (void *)old; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(old) + 1; + + adp++; /* new argument */ + adp->arg_value = 0; + adp->arg_object = (void *)new; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(new) + 1; + + error = Psyscall(Pr, &rval, SYS_rename, 2, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * link() system call -- executed by subject process. + */ +int +pr_link(struct ps_prochandle *Pr, const char *existing, const char *new) +{ + sysret_t rval; + argdes_t argd[2]; + argdes_t *adp; + int error; + + if (Pr == NULL) + return (link(existing, new)); + + adp = &argd[0]; /* existing argument */ + adp->arg_value = 0; + adp->arg_object = (void *)existing; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(existing) + 1; + + adp++; /* new argument */ + adp->arg_value = 0; + adp->arg_object = (void *)new; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(new) + 1; + + error = Psyscall(Pr, &rval, SYS_link, 2, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +/* + * unlink() system call -- executed by subject process. + */ +int +pr_unlink(struct ps_prochandle *Pr, const char *path) +{ + sysret_t rval; + argdes_t argd[1]; + argdes_t *adp; + int error; + + if (Pr == NULL) + return (unlink(path)); + + adp = &argd[0]; /* path argument */ + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path) + 1; + + error = Psyscall(Pr, &rval, SYS_unlink, 1, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_sigaction.c b/usr/src/lib/libproc/common/pr_sigaction.c new file mode 100644 index 0000000000..2ac068ecbb --- /dev/null +++ b/usr/src/lib/libproc/common/pr_sigaction.c @@ -0,0 +1,126 @@ +/* + * 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 1997-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/isa_defs.h> + +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <memory.h> +#include <errno.h> + +#include "P32ton.h" +#include "libproc.h" + +/* + * sigaction() system call -- executed by subject process. + */ +int +pr_sigaction(struct ps_prochandle *Pr, + int sig, const struct sigaction *act, struct sigaction *oact) +{ + sysret_t rval; /* return value from sigaction() */ + argdes_t argd[3]; /* arg descriptors for sigaction() */ + argdes_t *adp; + int error; +#ifdef _LP64 + struct sigaction32 act32; + struct sigaction32 oact32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (sigaction(sig, act, oact)); + + adp = &argd[0]; /* sig argument */ + adp->arg_value = sig; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* act argument */ + adp->arg_value = 0; + if (act == NULL) { + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_object = NULL; + adp->arg_size = 0; + } else { + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + sigaction_n_to_32(act, &act32); + adp->arg_object = &act32; + adp->arg_size = sizeof (act32); + } else { + adp->arg_object = (void *)act; + adp->arg_size = sizeof (*act); + } +#else /* _LP64 */ + adp->arg_object = (void *)act; + adp->arg_size = sizeof (*act); +#endif /* _LP64 */ + } + + adp++; /* oact argument */ + adp->arg_value = 0; + if (oact == NULL) { + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_object = NULL; + adp->arg_size = 0; + } else { + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &oact32; + adp->arg_size = sizeof (oact32); + } else { + adp->arg_object = oact; + adp->arg_size = sizeof (*oact); + } +#else /* _LP64 */ + adp->arg_object = oact; + adp->arg_size = sizeof (*oact); +#endif /* _LP64 */ + } + + error = Psyscall(Pr, &rval, SYS_sigaction, 3, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (oact != NULL && Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + sigaction_32_to_n(&oact32, oact); +#endif /* _LP64 */ + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_stat.c b/usr/src/lib/libproc/common/pr_stat.c new file mode 100644 index 0000000000..cac5bf9c88 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_stat.c @@ -0,0 +1,500 @@ +/* + * 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 (c) 1997-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/isa_defs.h> + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include "libproc.h" + +#ifdef _LP64 +/* + * in case of 64-bit *stat() and *stat64 library call and 32-bit subject + * process convert 64-bit struct stat/stat64 into 32-bit struct stat64 + */ +static void +stat64_32_to_n(struct stat64_32 *src, struct stat *dest) +{ + (void) memset(dest, 0, sizeof (*dest)); + dest->st_dev = DEVEXPL(src->st_dev); + dest->st_ino = (ino_t)src->st_ino; + dest->st_mode = (mode_t)src->st_mode; + dest->st_nlink = (nlink_t)src->st_nlink; + dest->st_uid = (uid_t)src->st_uid; + dest->st_gid = (gid_t)src->st_gid; + dest->st_rdev = DEVEXPL(src->st_rdev); + dest->st_size = (off_t)src->st_size; + TIMESPEC32_TO_TIMESPEC(&dest->st_atim, &src->st_atim); + TIMESPEC32_TO_TIMESPEC(&dest->st_mtim, &src->st_mtim); + TIMESPEC32_TO_TIMESPEC(&dest->st_ctim, &src->st_ctim); + dest->st_blksize = (blksize_t)src->st_blksize; + dest->st_blocks = (blkcnt_t)src->st_blocks; + (void) memcpy(dest->st_fstype, src->st_fstype, + sizeof (dest->st_fstype)); +} +#endif /* _LP64 */ + +/* + * stat() system call -- executed by subject process + */ +int +pr_stat(struct ps_prochandle *Pr, const char *path, struct stat *buf) +{ + sysret_t rval; /* return value from stat() */ + argdes_t argd[3]; /* arg descriptors for stat() */ + argdes_t *adp = &argd[0]; /* first argument */ + int syscall; /* stat, xstat or stat64 */ + int nargs = 2; /* number of actual arguments */ + int error; +#ifdef _LP64 + struct stat64_32 statb64_32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (stat(path, buf)); + + /* + * This is filthy, but /proc reveals everything about the + * system call interfaces, despite what the architects of the + * header files may desire. We have to know here whether we + * are calling stat() or xstat() in the subject. + */ +#if defined(_STAT_VER) + syscall = SYS_xstat; + nargs = 3; + adp->arg_value = _STAT_VER; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to pathname argument */ +#else + if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) { + /* 64-bit process controls 32-bit subject process */ + syscall = SYS_stat64; + } else { + syscall = SYS_stat; + } +#endif + + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path) + 1; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statb64_32; + adp->arg_size = sizeof (statb64_32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + stat64_32_to_n(&statb64_32, buf); +#endif /* _LP64 */ + return (0); +} + +/* + * lstat() system call -- executed by subject process + */ +int +pr_lstat(struct ps_prochandle *Pr, const char *path, struct stat *buf) +{ + sysret_t rval; /* return value from lstat() */ + argdes_t argd[3]; /* arg descriptors for lstat() */ + argdes_t *adp = &argd[0]; /* first argument */ + int syscall; /* lstat, lxstat or lstat64 */ + int nargs = 2; /* number of actual arguments */ + int error; +#ifdef _LP64 + struct stat64_32 statb64_32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (lstat(path, buf)); + + /* + * This is filthy, but /proc reveals everything about the + * system call interfaces, despite what the architects of the + * header files may desire. We have to know here whether we + * are calling lstat() or lxstat() in the subject. + */ +#if defined(_STAT_VER) + syscall = SYS_lxstat; + nargs = 3; + adp->arg_value = _STAT_VER; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to pathname argument */ +#else + if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) { + /* 64-bit process controls 32-bit subject process */ + syscall = SYS_lstat64; + } else { + syscall = SYS_lstat; + } +#endif + + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path) + 1; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statb64_32; + adp->arg_size = sizeof (statb64_32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + stat64_32_to_n(&statb64_32, buf); +#endif /* _LP64 */ + return (0); +} + +/* + * fstat() system call -- executed by subject process + */ +int +pr_fstat(struct ps_prochandle *Pr, int fd, struct stat *buf) +{ + sysret_t rval; /* return value from fstat() */ + argdes_t argd[3]; /* arg descriptors for fstat() */ + argdes_t *adp = &argd[0]; /* first argument */ + int syscall; /* fstat, fxstat or fstat64 */ + int nargs = 2; /* number of actual arguments */ + int error; +#ifdef _LP64 + struct stat64_32 statb64_32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (fstat(fd, buf)); + + /* + * This is filthy, but /proc reveals everything about the + * system call interfaces, despite what the architects of the + * header files may desire. We have to know here whether we + * are calling fstat() or fxstat() in the subject. + */ +#if defined(_STAT_VER) + syscall = SYS_fxstat; + nargs = 3; + adp->arg_value = _STAT_VER; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to fd argument */ +#else + if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) { + /* 64-bit process controls 32-bit subject process */ + syscall = SYS_fstat64; + } else { + syscall = SYS_fstat; + } +#endif + + adp->arg_value = fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statb64_32; + adp->arg_size = sizeof (statb64_32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + stat64_32_to_n(&statb64_32, buf); +#endif /* _LP64 */ + return (0); +} + +/* + * stat64() system call -- executed by subject process + */ +int +pr_stat64(struct ps_prochandle *Pr, const char *path, struct stat64 *buf) +{ + sysret_t rval; /* return value from stat64() */ + argdes_t argd[2]; /* arg descriptors for stat64() */ + argdes_t *adp = &argd[0]; /* first argument */ + int syscall; /* stat or stat64 */ + int nargs = 2; /* number of actual arguments */ + int error; +#ifdef _LP64 + struct stat64_32 statb64_32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (stat64(path, buf)); + + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + /* + * 32-bit native and + * 64-bit process controls 32-bit subject process + */ + syscall = SYS_stat64; + } else { + /* 64-bit native */ + syscall = SYS_stat; + } + + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path) + 1; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statb64_32; + adp->arg_size = sizeof (statb64_32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + stat64_32_to_n(&statb64_32, (struct stat *)buf); +#endif /* _LP64 */ + return (0); +} + +/* + * lstat64() system call -- executed by subject process + */ +int +pr_lstat64(struct ps_prochandle *Pr, const char *path, struct stat64 *buf) +{ + sysret_t rval; /* return value from lstat64() */ + argdes_t argd[2]; /* arg descriptors for lstat64() */ + argdes_t *adp = &argd[0]; /* first argument */ + int syscall; /* lstat or lstat64 */ + int nargs = 2; /* number of actual arguments */ + int error; +#ifdef _LP64 + struct stat64_32 statb64_32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (lstat64(path, buf)); + + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + /* + * 32-bit native and + * 64-bit process controls 32-bit subject process + */ + syscall = SYS_lstat64; + } else { + /* 64-bit native */ + syscall = SYS_lstat; + } + + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path) + 1; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statb64_32; + adp->arg_size = sizeof (statb64_32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + stat64_32_to_n(&statb64_32, (struct stat *)buf); +#endif /* _LP64 */ + return (0); +} + +/* + * fstat64() system call -- executed by subject process + */ +int +pr_fstat64(struct ps_prochandle *Pr, int fd, struct stat64 *buf) +{ + sysret_t rval; /* return value from fstat64() */ + argdes_t argd[2]; /* arg descriptors for fstat64() */ + argdes_t *adp = &argd[0]; /* first argument */ + int syscall; /* fstat or fstat64 */ + int nargs = 2; /* number of actual arguments */ + int error; +#ifdef _LP64 + struct stat64_32 statb64_32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (fstat64(fd, buf)); + + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + /* + * 32-bit native and + * 64-bit process controls 32-bit subject process + */ + syscall = SYS_fstat64; + } else { + /* 64-bit native */ + syscall = SYS_fstat; + } + + adp->arg_value = fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statb64_32; + adp->arg_size = sizeof (statb64_32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, syscall, nargs, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + stat64_32_to_n(&statb64_32, (struct stat *)buf); +#endif /* _LP64 */ + return (0); +} diff --git a/usr/src/lib/libproc/common/pr_statvfs.c b/usr/src/lib/libproc/common/pr_statvfs.c new file mode 100644 index 0000000000..01d8d3ce67 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_statvfs.c @@ -0,0 +1,165 @@ +/* + * 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 (c) 1998-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/isa_defs.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/statvfs.h> +#include <sys/sysmacros.h> +#include "libproc.h" + +#ifdef _LP64 +static void +statvfs_32_to_n(statvfs32_t *src, statvfs_t *dest) +{ + dest->f_bsize = src->f_bsize; + dest->f_frsize = src->f_frsize; + dest->f_blocks = src->f_blocks; + dest->f_bfree = src->f_bfree; + dest->f_bavail = src->f_bavail; + dest->f_files = src->f_files; + dest->f_ffree = src->f_ffree; + dest->f_favail = src->f_favail; + dest->f_fsid = src->f_fsid; + (void) memcpy(dest->f_basetype, src->f_basetype, + sizeof (dest->f_basetype)); + dest->f_flag = src->f_flag; + dest->f_namemax = src->f_namemax; + (void) memcpy(dest->f_fstr, src->f_fstr, + sizeof (dest->f_fstr)); +} +#endif /* _LP64 */ + +/* + * statvfs() system call -- executed by subject process + */ +int +pr_statvfs(struct ps_prochandle *Pr, const char *path, statvfs_t *buf) +{ + sysret_t rval; /* return value from statvfs() */ + argdes_t argd[2]; /* arg descriptors for statvfs() */ + argdes_t *adp = &argd[0]; /* first argument */ + int error; +#ifdef _LP64 + statvfs32_t statvfs32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (statvfs(path, buf)); + + adp->arg_value = 0; + adp->arg_object = (void *)path; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_INPUT; + adp->arg_size = strlen(path)+1; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statvfs32; + adp->arg_size = sizeof (statvfs32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, SYS_statvfs, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + statvfs_32_to_n(&statvfs32, buf); +#endif /* _LP64 */ + return (0); +} + +/* + * fstatvfs() system call -- executed by subject process + */ +int +pr_fstatvfs(struct ps_prochandle *Pr, int fd, statvfs_t *buf) +{ + sysret_t rval; /* return value from fstatvfs() */ + argdes_t argd[2]; /* arg descriptors for fstatvfs() */ + argdes_t *adp = &argd[0]; /* first argument */ + int error; +#ifdef _LP64 + statvfs32_t statvfs32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (fstatvfs(fd, buf)); + + adp->arg_value = fd; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + adp++; /* move to buffer argument */ + + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &statvfs32; + adp->arg_size = sizeof (statvfs32); + } else { + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); + } +#else /* _LP64 */ + adp->arg_object = buf; + adp->arg_size = sizeof (*buf); +#endif /* _LP64 */ + + error = Psyscall(Pr, &rval, SYS_fstatvfs, 2, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + statvfs_32_to_n(&statvfs32, buf); +#endif /* _LP64 */ + return (0); +} diff --git a/usr/src/lib/libproc/common/pr_tasksys.c b/usr/src/lib/libproc/common/pr_tasksys.c new file mode 100644 index 0000000000..7236e75dff --- /dev/null +++ b/usr/src/lib/libproc/common/pr_tasksys.c @@ -0,0 +1,170 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define _LARGEFILE64_SOURCE + +#include <sys/task.h> +#include <sys/types.h> + +#include <zone.h> +#include <errno.h> +#include <project.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> + +#include "libproc.h" + +zoneid_t +pr_getzoneid(struct ps_prochandle *Pr) +{ + sysret_t rval; + argdes_t argd[2]; + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (getzoneid()); + + adp = &argd[0]; + adp->arg_value = 6; /* switch for zone_lookup in zone */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp = &argd[1]; + adp->arg_value = 0; /* arguement for zone_lookup in zone */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_zone, 2, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +projid_t +pr_getprojid(struct ps_prochandle *Pr) +{ + sysret_t rval; + argdes_t argd[1]; + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (getprojid()); + + adp = &argd[0]; + adp->arg_value = 2; /* switch for getprojid in tasksys */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_tasksys, 1, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +taskid_t +pr_gettaskid(struct ps_prochandle *Pr) +{ + sysret_t rval; + argdes_t argd[1]; + argdes_t *adp; + int error; + + if (Pr == NULL) /* no subject process */ + return (gettaskid()); + + adp = &argd[0]; + adp->arg_value = 1; /* switch for gettaskid in tasksys */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_tasksys, 1, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} + +taskid_t +pr_settaskid(struct ps_prochandle *Pr, projid_t project, int flags) +{ + sysret_t rval; + argdes_t argd[3]; + argdes_t *adp; + int error; + + if (Pr == NULL) /* No subject process */ + return (settaskid(project, flags)); + + adp = &argd[0]; + adp->arg_value = 0; /* switch for settaskid in tasksys */ + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; + adp->arg_value = project; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = sizeof (project); + + adp++; + adp->arg_value = flags; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_tasksys, 3, &argd[0]); + + if (error) { + errno = (error > 0) ? error : ENOSYS; + return (-1); + } + return (rval.sys_rval1); +} diff --git a/usr/src/lib/libproc/common/pr_waitid.c b/usr/src/lib/libproc/common/pr_waitid.c new file mode 100644 index 0000000000..fdf57c29b2 --- /dev/null +++ b/usr/src/lib/libproc/common/pr_waitid.c @@ -0,0 +1,108 @@ +/* + * 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 (c) 1998-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/isa_defs.h> + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "P32ton.h" +#include "libproc.h" + +/* + * waitid() system call -- executed by subject process + */ +int +pr_waitid(struct ps_prochandle *Pr, + idtype_t idtype, id_t id, siginfo_t *infop, int options) +{ + sysret_t rval; /* return value from waitid() */ + argdes_t argd[4]; /* arg descriptors for waitid() */ + argdes_t *adp; + int error; +#ifdef _LP64 + siginfo32_t siginfo32; +#endif /* _LP64 */ + + if (Pr == NULL) /* no subject process */ + return (waitid(idtype, id, infop, options)); + + adp = &argd[0]; /* idtype argument */ + adp->arg_value = idtype; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* id argument */ + adp->arg_value = id; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + adp++; /* infop argument */ + adp->arg_value = 0; + adp->arg_type = AT_BYREF; + adp->arg_inout = AI_OUTPUT; +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) { + adp->arg_object = &siginfo32; + adp->arg_size = sizeof (siginfo32); + } else { + adp->arg_object = infop; + adp->arg_size = sizeof (*infop); + } +#else /* _LP64 */ + adp->arg_object = infop; + adp->arg_size = sizeof (*infop); +#endif /* _LP64 */ + + adp++; /* options argument */ + adp->arg_value = options; + adp->arg_object = NULL; + adp->arg_type = AT_BYVAL; + adp->arg_inout = AI_INPUT; + adp->arg_size = 0; + + error = Psyscall(Pr, &rval, SYS_waitsys, 4, &argd[0]); + + if (error) { + errno = (error > 0)? error : ENOSYS; + return (-1); + } +#ifdef _LP64 + if (Pstatus(Pr)->pr_dmodel == PR_MODEL_ILP32) + siginfo_32_to_n(&siginfo32, infop); +#endif /* _LP64 */ + return (0); +} diff --git a/usr/src/lib/libproc/common/proc_arg.c b/usr/src/lib/libproc/common/proc_arg.c new file mode 100644 index 0000000000..2a79b016c2 --- /dev/null +++ b/usr/src/lib/libproc/common/proc_arg.c @@ -0,0 +1,509 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <libgen.h> +#include <limits.h> +#include <alloca.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <dirent.h> + +#include "libproc.h" + +static int +open_psinfo(const char *arg, int *perr) +{ + /* + * Allocate enough space for "/proc/" + arg + "/psinfo" + */ + char *path = alloca(strlen(arg) + 14); + + struct stat64 st; + int fd; + + if (strchr(arg, '/') == NULL) { + (void) strcpy(path, "/proc/"); + (void) strcat(path, arg); + } else + (void) strcpy(path, arg); + + (void) strcat(path, "/psinfo"); + + /* + * Attempt to open the psinfo file, and return the fd if we can + * confirm this is a regular file provided by /proc. + */ + if ((fd = open64(path, O_RDONLY)) >= 0) { + if (fstat64(fd, &st) != 0 || !S_ISREG(st.st_mode) || + strcmp(st.st_fstype, "proc") != 0) { + (void) close(fd); + fd = -1; + } + } else if (errno == EACCES || errno == EPERM) + *perr = G_PERM; + + return (fd); +} + +static int +open_core(const char *arg, int *perr) +{ +#ifdef _BIG_ENDIAN + uchar_t order = ELFDATA2MSB; +#else + uchar_t order = ELFDATA2LSB; +#endif + GElf_Ehdr ehdr; + int fd; + int is_noelf = -1; + + /* + * Attempt to open the core file, and return the fd if we can confirm + * this is an ELF file of type ET_CORE. + */ + if ((fd = open64(arg, O_RDONLY)) >= 0) { + if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) { + (void) close(fd); + fd = -1; + } else if ((is_noelf = memcmp(&ehdr.e_ident[EI_MAG0], ELFMAG, + SELFMAG)) != 0 || ehdr.e_type != ET_CORE) { + (void) close(fd); + fd = -1; + if (is_noelf == 0 && + ehdr.e_ident[EI_DATA] != order) + *perr = G_ISAINVAL; + } + } else if (errno == EACCES || errno == EPERM) + *perr = G_PERM; + + return (fd); +} + +/* + * Make the error message precisely match the type of arguments the caller + * wanted to process. This ensures that a tool which only accepts pids does + * not produce an error message saying "no such process or core file 'foo'". + */ +static int +open_error(int oflag) +{ + if ((oflag & PR_ARG_ANY) == PR_ARG_PIDS) + return (G_NOPROC); + + if ((oflag & PR_ARG_ANY) == PR_ARG_CORES) + return (G_NOCORE); + + return (G_NOPROCORCORE); +} + +static void * +proc_grab_common(const char *arg, const char *path, int oflag, int gflag, + int *perr, const char **lwps, psinfo_t *psp) +{ + psinfo_t psinfo; + char *core; + int fd; + char *slash; + struct ps_prochandle *Pr; + + *perr = 0; + if (lwps) + *lwps = NULL; + + if (lwps != NULL && (slash = strrchr(arg, '/')) != NULL) { + /* + * Check to see if the user has supplied an lwp range. First, + * try to grab it as a pid/lwp combo. + */ + *slash = '\0'; + if ((oflag & PR_ARG_PIDS) && + (fd = open_psinfo(arg, perr)) != -1) { + if (read(fd, &psinfo, + sizeof (psinfo_t)) == sizeof (psinfo_t)) { + (void) close(fd); + *lwps = slash + 1; + *slash = '/'; + if (proc_lwp_range_valid(*lwps) != 0) { + *perr = G_BADLWPS; + return (NULL); + } + if (psp) { + *psp = psinfo; + return (psp); + } else { + return (Pgrab(psinfo.pr_pid, gflag, + perr)); + } + } + (void) close(fd); + } + + /* + * Next, try grabbing it as a corefile. + */ + if ((oflag & PR_ARG_CORES) && + (fd = open_core(arg, perr)) != -1) { + *lwps = slash + 1; + *slash = '/'; + if (proc_lwp_range_valid(*lwps) != 0) { + *perr = G_BADLWPS; + return (NULL); + } + core = alloca(strlen(arg) + 1); + (void) strcpy(core, arg); + if ((Pr = Pfgrab_core(fd, path == NULL ? + dirname(core) : path, perr)) != NULL) { + if (psp) { + (void) memcpy(psp, Ppsinfo(Pr), + sizeof (psinfo_t)); + Prelease(Pr, 0); + return (psp); + } else { + return (Pr); + } + } + } + + *slash = '/'; + } + + if ((oflag & PR_ARG_PIDS) && (fd = open_psinfo(arg, perr)) != -1) { + if (read(fd, &psinfo, sizeof (psinfo_t)) == sizeof (psinfo_t)) { + (void) close(fd); + if (psp) { + *psp = psinfo; + return (psp); + } else { + return (Pgrab(psinfo.pr_pid, gflag, perr)); + } + } + /* + * If the read failed, the process may have gone away; + * we continue checking for core files or fail with G_NOPROC + */ + (void) close(fd); + } + + if ((oflag & PR_ARG_CORES) && (fd = open_core(arg, perr)) != -1) { + core = alloca(strlen(arg) + 1); + (void) strcpy(core, arg); + if ((Pr = Pfgrab_core(fd, path == NULL ? dirname(core) : path, + perr)) != NULL) { + if (psp) { + (void) memcpy(psp, Ppsinfo(Pr), + sizeof (psinfo_t)); + Prelease(Pr, 0); + return (psp); + } else { + return (Pr); + } + } + } + + /* + * We were unable to open the corefile. If we have no meaningful + * information, report the (ambiguous) error from open_error(). + */ + + if (*perr == 0) + *perr = open_error(oflag); + + return (NULL); +} + +struct ps_prochandle * +proc_arg_xgrab(const char *arg, const char *path, int oflag, int gflag, + int *perr, const char **lwps) +{ + return (proc_grab_common(arg, path, oflag, gflag, perr, lwps, NULL)); +} + +struct ps_prochandle * +proc_arg_grab(const char *arg, int oflag, int gflag, int *perr) +{ + return (proc_grab_common(arg, NULL, oflag, gflag, perr, NULL, NULL)); +} + +pid_t +proc_arg_psinfo(const char *arg, int oflag, psinfo_t *psp, int *perr) +{ + psinfo_t psinfo; + + if (psp == NULL) + psp = &psinfo; + + if (proc_grab_common(arg, NULL, oflag, 0, perr, NULL, psp) == NULL) + return (-1); + else + return (psp->pr_pid); +} + +pid_t +proc_arg_xpsinfo(const char *arg, int oflag, psinfo_t *psp, int *perr, + const char **lwps) +{ + psinfo_t psinfo; + + if (psp == NULL) + psp = &psinfo; + + if (proc_grab_common(arg, NULL, oflag, 0, perr, lwps, psp) == NULL) + return (-1); + else + return (psp->pr_pid); +} + +/* + * Convert psinfo_t.pr_psargs string into itself, replacing unprintable + * characters with space along the way. Stop on a null character. + */ +void +proc_unctrl_psinfo(psinfo_t *psp) +{ + char *s = &psp->pr_psargs[0]; + size_t n = PRARGSZ; + int c; + + while (n-- != 0 && (c = (*s & UCHAR_MAX)) != '\0') { + if (!isprint(c)) + c = ' '; + *s++ = (char)c; + } + + *s = '\0'; +} + +static int +proc_lwp_get_range(char *range, id_t *low, id_t *high) +{ + if (*range == '-') + *low = 0; + else + *low = (id_t)strtol(range, &range, 10); + + if (*range == '\0' || *range == ',') { + *high = *low; + return (0); + } + if (*range != '-') { + return (-1); + } + range++; + + if (*range == '\0') + *high = INT_MAX; + else + *high = (id_t)strtol(range, &range, 10); + + if (*range != '\0' && *range != ',') { + return (-1); + } + + if (*high < *low) { + id_t tmp = *high; + *high = *low; + *low = tmp; + } + + return (0); +} + +/* + * Determine if the specified lwpid is in the given set of lwpids. + * The set can include multiple lwpid ranges separated by commas + * and has the following syntax: + * + * lwp_range[,lwp_range]* + * + * where lwp_range is specifed as: + * + * -n lwpid <= n + * n-m n <= lwpid <= m + * n- lwpid >= n + * n lwpid == n + */ +int +proc_lwp_in_set(const char *set, lwpid_t lwpid) +{ + id_t low, high; + id_t id = (id_t)lwpid; + char *comma; + char *range = (char *)set; + + /* + * A NULL set indicates that all LWPs are valid. + */ + if (set == NULL) + return (1); + + while (range != NULL) { + comma = strchr(range, ','); + if (comma != NULL) + *comma = '\0'; + if (proc_lwp_get_range(range, &low, &high) != 0) { + if (comma != NULL) + *comma = ','; + return (0); + } + if (comma != NULL) { + *comma = ','; + range = comma + 1; + } else { + range = NULL; + } + if (id >= low && id <= high) + return (1); + } + + return (0); +} + +int +proc_lwp_range_valid(const char *set) +{ + char *comma; + char *range = (char *)set; + id_t low, high; + int ret; + + if (range == NULL || *range == '\0' || *range == ',') + return (-1); + + while (range != NULL) { + comma = strchr(range, ','); + if (comma != NULL) + *comma = '\0'; + if ((ret = proc_lwp_get_range(range, &low, &high)) != 0) { + if (comma != NULL) + *comma = ','; + return (ret); + } + if (comma != NULL) { + *comma = ','; + range = comma + 1; + } else { + range = NULL; + } + } + + return (0); +} + +/* + * Walk all processes or LWPs in /proc and call func() for each. + * Stop calling func() if it returns non 0 value and return it. + */ +int +proc_walk(proc_walk_f *func, void *arg, int flag) +{ + DIR *procdir; + struct dirent *dirent; + char *errptr; + char pidstr[80]; + psinfo_t psinfo; + lwpsinfo_t *lwpsinfo; + prheader_t prheader; + void *buf; + char *ptr; + int bufsz; + id_t pid; + int fd, i; + int ret = 0; + + if (flag != PR_WALK_PROC && flag != PR_WALK_LWP) { + errno = EINVAL; + return (-1); + } + if ((procdir = opendir("/proc")) == NULL) + return (-1); + while (dirent = readdir(procdir)) { + if (dirent->d_name[0] == '.') /* skip . and .. */ + continue; + pid = (id_t)strtol(dirent->d_name, &errptr, 10); + if (errptr != NULL && *errptr != '\0') + continue; + /* PR_WALK_PROC case */ + (void) snprintf(pidstr, sizeof (pidstr), + "/proc/%ld/psinfo", pid); + fd = open(pidstr, O_RDONLY); + if (fd < 0) + continue; + if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) { + (void) close(fd); + continue; + } + (void) close(fd); + if (flag == PR_WALK_PROC) { + if ((ret = func(&psinfo, &psinfo.pr_lwp, arg)) != 0) + break; + continue; + } + /* PR_WALK_LWP case */ + (void) snprintf(pidstr, sizeof (pidstr), + "/proc/%ld/lpsinfo", pid); + fd = open(pidstr, O_RDONLY); + if (fd < 0) + continue; + if (read(fd, &prheader, sizeof (prheader)) != + sizeof (prheader)) { + (void) close(fd); + continue; + } + bufsz = prheader.pr_nent * prheader.pr_entsize; + if ((buf = malloc(bufsz)) == NULL) { + (void) close(fd); + ret = -1; + break; + } + ptr = buf; + if (pread(fd, buf, bufsz, sizeof (prheader)) != bufsz) { + free(buf); + (void) close(fd); + continue; + } + (void) close(fd); + for (i = 0; i < prheader.pr_nent; + i++, ptr += prheader.pr_entsize) { + /*LINTED ALIGNMENT*/ + lwpsinfo = (lwpsinfo_t *)ptr; + if ((ret = func(&psinfo, lwpsinfo, arg)) != 0) { + free(buf); + break; + } + } + free(buf); + } + (void) closedir(procdir); + return (ret); +} diff --git a/usr/src/lib/libproc/common/proc_get_info.c b/usr/src/lib/libproc/common/proc_get_info.c new file mode 100644 index 0000000000..d4426f7cc0 --- /dev/null +++ b/usr/src/lib/libproc/common/proc_get_info.c @@ -0,0 +1,183 @@ +/* + * 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 2004 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 <unistd.h> +#include <fcntl.h> +#include <string.h> +#include "libproc.h" + +/* + * These several routines simply get the indicated /proc structures + * for a process identified by process ID. They are convenience + * functions for one-time operations. They do the mechanics of + * open() / read() / close() of the necessary /proc files so the + * caller's code can look relatively less cluttered. + */ + +/* + * 'ngroups' is the number of supplementary group entries allocated in + * the caller's cred structure. It should equal zero or one unless extra + * space has been allocated for the group list by the caller, like this: + * credp = malloc(sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t)); + */ +int +proc_get_cred(pid_t pid, prcred_t *credp, int ngroups) +{ + char fname[64]; + int fd; + int rv = -1; + ssize_t minsize = sizeof (*credp) - sizeof (gid_t); + size_t size = minsize + ngroups * sizeof (gid_t); + + (void) snprintf(fname, sizeof (fname), "/proc/%d/cred", (int)pid); + if ((fd = open(fname, O_RDONLY)) >= 0) { + if (read(fd, credp, size) >= minsize) + rv = 0; + (void) close(fd); + } + return (rv); +} + +/* + * Malloc and return a properly sized structure. + */ +prpriv_t * +proc_get_priv(pid_t pid) +{ + char fname[64]; + int fd; + struct stat statb; + prpriv_t *rv = NULL; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/priv", (int)pid); + if ((fd = open(fname, O_RDONLY)) >= 0) { + if (fstat(fd, &statb) != 0 || + (rv = malloc(statb.st_size)) == NULL || + read(fd, rv, statb.st_size) != statb.st_size) { + free(rv); + rv = NULL; + } + (void) close(fd); + } + return (rv); +} + +#if defined(__i386) || defined(__amd64) +/* + * Fill in a pointer to a process LDT structure. + * The caller provides a buffer of size 'nldt * sizeof (struct ssd)'; + * If pldt == NULL or nldt == 0, we return the number of existing LDT entries. + * Otherwise we return the actual number of LDT entries fetched (<= nldt). + */ +int +proc_get_ldt(pid_t pid, struct ssd *pldt, int nldt) +{ + char fname[64]; + int fd; + struct stat statb; + size_t size; + ssize_t ssize; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/ldt", (int)pid); + if ((fd = open(fname, O_RDONLY)) < 0) + return (-1); + + if (pldt == NULL || nldt == 0) { + nldt = 0; + if (fstat(fd, &statb) == 0) + nldt = statb.st_size / sizeof (struct ssd); + (void) close(fd); + return (nldt); + } + + size = nldt * sizeof (struct ssd); + if ((ssize = read(fd, pldt, size)) < 0) + nldt = -1; + else + nldt = ssize / sizeof (struct ssd); + + (void) close(fd); + return (nldt); +} +#endif /* __i386 || __amd64 */ + +int +proc_get_psinfo(pid_t pid, psinfo_t *psp) +{ + char fname[64]; + int fd; + int rv = -1; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/psinfo", (int)pid); + if ((fd = open(fname, O_RDONLY)) >= 0) { + if (read(fd, psp, sizeof (*psp)) == sizeof (*psp)) + rv = 0; + (void) close(fd); + } + return (rv); +} + +int +proc_get_status(pid_t pid, pstatus_t *psp) +{ + char fname[64]; + int fd; + int rv = -1; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/status", (int)pid); + if ((fd = open(fname, O_RDONLY)) >= 0) { + if (read(fd, psp, sizeof (*psp)) == sizeof (*psp)) + rv = 0; + (void) close(fd); + } + return (rv); +} + +/* + * Get the process's aux vector. + * 'naux' is the number of aux entries in the caller's buffer. + * We return the number of aux entries actually fetched from + * the process (less than or equal to 'naux') or -1 on failure. + */ +int +proc_get_auxv(pid_t pid, auxv_t *pauxv, int naux) +{ + char fname[64]; + int fd; + int rv = -1; + + (void) snprintf(fname, sizeof (fname), "/proc/%d/auxv", (int)pid); + if ((fd = open(fname, O_RDONLY)) >= 0) { + if ((rv = read(fd, pauxv, naux * sizeof (auxv_t))) >= 0) + rv /= sizeof (auxv_t); + (void) close(fd); + } + return (rv); +} diff --git a/usr/src/lib/libproc/common/proc_names.c b/usr/src/lib/libproc/common/proc_names.c new file mode 100644 index 0000000000..249b9745a3 --- /dev/null +++ b/usr/src/lib/libproc/common/proc_names.c @@ -0,0 +1,711 @@ +/* + * 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> +#define __EXTENSIONS__ +#include <string.h> +#undef __EXTENSIONS__ +#include <signal.h> +#include <alloca.h> +#include <errno.h> +#include "libproc.h" + +static const char * +rawfltname(int flt) +{ + const char *name; + + switch (flt) { + case FLTILL: name = "FLTILL"; break; + case FLTPRIV: name = "FLTPRIV"; break; + case FLTBPT: name = "FLTBPT"; break; + case FLTTRACE: name = "FLTTRACE"; break; + case FLTACCESS: name = "FLTACCESS"; break; + case FLTBOUNDS: name = "FLTBOUNDS"; break; + case FLTIOVF: name = "FLTIOVF"; break; + case FLTIZDIV: name = "FLTIZDIV"; break; + case FLTFPE: name = "FLTFPE"; break; + case FLTSTACK: name = "FLTSTACK"; break; + case FLTPAGE: name = "FLTPAGE"; break; + case FLTWATCH: name = "FLTWATCH"; break; + case FLTCPCOVF: name = "FLTCPCOVF"; break; + default: name = NULL; break; + } + + return (name); +} + +/* + * Return the name of a fault. + * Manufacture a name for unknown fault. + */ +char * +proc_fltname(int flt, char *buf, size_t bufsz) +{ + const char *name = rawfltname(flt); + size_t len; + + if (bufsz == 0) /* force a program failure */ + return (NULL); + + if (name != NULL) { + len = strlen(name); + (void) strncpy(buf, name, bufsz); + } else { + len = snprintf(buf, bufsz, "FLT#%d", flt); + } + + if (len >= bufsz) /* ensure null-termination */ + buf[bufsz-1] = '\0'; + + return (buf); +} + +/* + * Return the name of a signal. + * Manufacture a name for unknown signal. + */ +char * +proc_signame(int sig, char *buf, size_t bufsz) +{ + char name[SIG2STR_MAX+4]; + size_t len; + + if (bufsz == 0) /* force a program failure */ + return (NULL); + + /* sig2str() omits the leading "SIG" */ + (void) strcpy(name, "SIG"); + + if (sig2str(sig, name+3) == 0) { + len = strlen(name); + (void) strncpy(buf, name, bufsz); + } else { + len = snprintf(buf, bufsz, "SIG#%d", sig); + } + + if (len >= bufsz) /* ensure null-termination */ + buf[bufsz-1] = '\0'; + + return (buf); +} + +static const char *const systable[] = { + NULL, /* 0 */ + "_exit", /* 1 */ + "forkall", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "wait", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "exec", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "chown", /* 16 */ + "brk", /* 17 */ + "stat", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "fstat", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "stty", /* 31 */ + "gtty", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "statfs", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "fstatfs", /* 38 */ + "pgrpsys", /* 39 */ + NULL, /* 40 was xenix */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "profil", /* 44 */ + "plock", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "signal", /* 48 */ + "msgsys", /* 49 */ + "sysi86", /* 50 */ + "acct", /* 51 */ + "shmsys", /* 52 */ + "semsys", /* 53 */ + "ioctl", /* 54 */ + "uadmin", /* 55 */ + NULL, /* 56 */ + "utssys", /* 57 */ + "fdsync", /* 58 */ + "execve", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "fcntl", /* 62 */ + "ulimit", /* 63 */ + + /* The following 6 entries were reserved for the UNIX PC */ + NULL, /* 64 */ + NULL, /* 65 */ + NULL, /* 66 */ + NULL, /* 67 */ + NULL, /* 68 */ + NULL, /* 69 */ + + "tasksys", /* 70 */ + "acctctl", /* 71 */ + "exacctsys", /* 72 */ + "getpagesizes", /* 73 */ + "rctlsys", /* 74 */ + "issetugid", /* 75 */ + "fsat", /* 76 */ + "lwp_park", /* 77 */ + "sendfilev", /* 78 */ + "rmdir", /* 79 */ + "mkdir", /* 80 */ + "getdents", /* 81 */ + "privsys", /* 82 */ + "ucredsys", /* 83 */ + "sysfs", /* 84 */ + "getmsg", /* 85 */ + "putmsg", /* 86 */ + "poll", /* 87 */ + "lstat", /* 88 */ + "symlink", /* 89 */ + "readlink", /* 90 */ + "setgroups", /* 91 */ + "getgroups", /* 92 */ + "fchmod", /* 93 */ + "fchown", /* 94 */ + "sigprocmask", /* 95 */ + "sigsuspend", /* 96 */ + "sigaltstack", /* 97 */ + "sigaction", /* 98 */ + "sigpending", /* 99 */ + "context", /* 100 */ + "evsys", /* 101 */ + "evtrapret", /* 102 */ + "statvfs", /* 103 */ + "fstatvfs", /* 104 */ + "getloadavg", /* 105 */ + "nfssys", /* 106 */ + "waitid", /* 107 */ + "sigsendsys", /* 108 */ + "hrtsys", /* 109 */ + "acancel", /* 110 */ + "async", /* 111 */ + "priocntlsys", /* 112 */ + "pathconf", /* 113 */ + "mincore", /* 114 */ + "mmap", /* 115 */ + "mprotect", /* 116 */ + "munmap", /* 117 */ + "fpathconf", /* 118 */ + "vfork", /* 119 */ + "fchdir", /* 120 */ + "readv", /* 121 */ + "writev", /* 122 */ + "xstat", /* 123 */ + "lxstat", /* 124 */ + "fxstat", /* 125 */ + "xmknod", /* 126 */ + "NULL", /* 127 */ + "setrlimit", /* 128 */ + "getrlimit", /* 129 */ + "lchown", /* 130 */ + "memcntl", /* 131 */ + "getpmsg", /* 132 */ + "putpmsg", /* 133 */ + "rename", /* 134 */ + "uname", /* 135 */ + "setegid", /* 136 */ + "sysconfig", /* 137 */ + "adjtime", /* 138 */ + "systeminfo", /* 139 */ + NULL, /* 140 */ + "seteuid", /* 141 */ + NULL, /* 142 */ + "fork1", /* 143 */ + "sigtimedwait", /* 144 */ + "lwp_info", /* 145 */ + "yield", /* 146 */ + "lwp_sema_wait", /* 147 */ + "lwp_sema_post", /* 148 */ + "lwp_sema_trywait", /* 149 */ + "lwp_detatch", /* 150 */ + "corectl", /* 151 */ + "modctl", /* 152 */ + "fchroot", /* 153 */ + "utimes", /* 154 */ + "vhangup", /* 155 */ + "gettimeofday", /* 156 */ + "getitimer", /* 157 */ + "setitimer", /* 158 */ + "lwp_create", /* 159 */ + "lwp_exit", /* 160 */ + "lwp_suspend", /* 161 */ + "lwp_continue", /* 162 */ + "lwp_kill", /* 163 */ + "lwp_self", /* 164 */ + "lwp_sigmask", /* 165 */ + "lwp_private", /* 166 */ + "lwp_wait", /* 167 */ + "lwp_mutex_unlock", /* 168 */ + "lwp_mutex_lock", /* 169 */ + "lwp_cond_wait", /* 170 */ + "lwp_cond_signal", /* 171 */ + "lwp_cond_broadcast", /* 172 */ + "pread", /* 173 */ + "pwrite", /* 174 */ + "llseek", /* 175 */ + "inst_sync", /* 176 */ + NULL, /* 177 */ + "kaio", /* 178 */ + "cpc", /* 179 */ + "lgrpsys", /* 180 */ + "rusagesys", /* 181 */ + "portfs", /* 182 */ + "pollsys", /* 183 */ + NULL, /* 184 */ + "acl", /* 185 */ + "auditsys", /* 186 */ + "processor_bind", /* 187 */ + "processor_info", /* 188 */ + "p_online", /* 189 */ + "sigqueue", /* 190 */ + "clock_gettime", /* 191 */ + "clock_settime", /* 192 */ + "clock_getres", /* 193 */ + "timer_create", /* 194 */ + "timer_delete", /* 195 */ + "timer_settime", /* 196 */ + "timer_gettime", /* 197 */ + "timer_getoverrun", /* 198 */ + "nanosleep", /* 199 */ + "facl", /* 200 */ + "door", /* 201 */ + "setreuid", /* 202 */ + "setregid", /* 203 */ + "install_utrap", /* 204 */ + "signotify", /* 205 */ + "schedctl", /* 206 */ + "pset", /* 207 */ + "sparc_utrap_install", /* 208 */ + "resolvepath", /* 209 */ + "lwp_mutex_timedlock", /* 210 */ + "lwp_sema_timedwait", /* 211 */ + "lwp_rwlock_sys", /* 212 */ + "getdents64", /* 213 */ + "mmap64", /* 214 */ + "stat64", /* 215 */ + "lstat64", /* 216 */ + "fstat64", /* 217 */ + "statvfs64", /* 218 */ + "fstatvfs64", /* 219 */ + "setrlimit64", /* 220 */ + "getrlimit64", /* 221 */ + "pread64", /* 222 */ + "pwrite64", /* 223 */ + "creat64", /* 224 */ + "open64", /* 225 */ + "rpcmod", /* 226 */ + "zone", /* 227 */ + "autofssys", /* 228 */ + "getcwd", /* 229 */ + "so_socket", /* 230 */ + "so_socketpair", /* 231 */ + "bind", /* 232 */ + "listen", /* 233 */ + "accept", /* 234 */ + "connect", /* 235 */ + "shutdown", /* 236 */ + "recv", /* 237 */ + "recvfrom", /* 238 */ + "recvmsg", /* 239 */ + "send", /* 240 */ + "sendmsg", /* 241 */ + "sendto", /* 242 */ + "getpeername", /* 243 */ + "getsockname", /* 244 */ + "getsockopt", /* 245 */ + "setsockopt", /* 246 */ + "sockconfig", /* 247 */ + "ntp_gettime", /* 248 */ + "ntp_adjtime", /* 249 */ + "lwp_mutex_unlock", /* 250 */ + "lwp_mutex_trylock", /* 251 */ + "lwp_mutex_init", /* 252 */ + "cladm", /* 253 */ + NULL, /* 254 */ + "umount2" /* 255 */ +}; + +/* SYSEND == max syscall number + 1 */ +#define SYSEND (sizeof (systable) / sizeof (systable[0])) + +/* + * Return the name of a system call. + * Manufacture a name for unknown system call. + */ +char * +proc_sysname(int sys, char *buf, size_t bufsz) +{ + const char *name; + size_t len; + + if (bufsz == 0) /* force a program failure */ + return (NULL); + + if (sys >= 0 && sys < SYSEND) + name = systable[sys]; + else + name = NULL; + + if (name != NULL) { + len = strlen(name); + (void) strncpy(buf, name, bufsz); + } else { + len = snprintf(buf, bufsz, "SYS#%d", sys); + } + + if (len >= bufsz) /* ensure null-termination */ + buf[bufsz-1] = '\0'; + + return (buf); +} + +/* + * Convert a string representation of a fault to the corresponding number. + */ +int +proc_str2flt(const char *str, int *fltnum) +{ + char *next; + int i; + + i = strtol(str, &next, 0); + if (i > 0 && i <= PRMAXFAULT && *next == '\0') { + *fltnum = i; + return (0); + } + + for (i = 1; i <= PRMAXFAULT; i++) { + const char *s = rawfltname(i); + + if (s && (strcasecmp(s, str) == 0 || + strcasecmp(s + 3, str) == 0)) { + *fltnum = i; + return (0); + } + } + + return (-1); +} + +/* + * Convert a string representation of a signal to the signal number. This + * functionality is already available in libc, but the interface doesn't + * optionally accept a "SIG" prefix. We strip that first, and then call libc. + */ +int +proc_str2sig(const char *str, int *signum) +{ + if (strncasecmp(str, "SIG", 3) == 0) + str += 3; /* skip prefix */ + + return (str2sig(str, signum)); +} + +/* + * Convert a string representation of a system call to the corresponding number. + * We do this by performing a simple linear search of the table above. + */ +int +proc_str2sys(const char *str, int *sysnum) +{ + char *next; + int i; + + i = strtol(str, &next, 0); + if (i > 0 && i <= PRMAXSYS && *next == '\0') { + *sysnum = i; + return (0); + } + + for (i = 1; i < SYSEND; i++) { + if (systable[i] != NULL && strcmp(systable[i], str) == 0) { + *sysnum = i; + return (0); + } + } + + return (-1); +} + +/* + * Convert a fltset_t to a string representation consisting of canonical + * machine fault names separated by the given delimeter string. If + * m is non-zero (TRUE), set members are printed. If m is zero (FALSE), set + * non-members are printed. If the specified buf is too small to hold the + * complete formatted set, NULL is returned; otherwise buf is returned. + */ +char * +proc_fltset2str(const fltset_t *set, const char *delim, int m, + char *buf, size_t len) +{ + char name[FLT2STR_MAX], *p = buf; + size_t n; + int i; + + if (buf == NULL || len < 1) { + errno = EINVAL; + return (NULL); + } + + buf[0] = '\0'; /* Set first byte to \0 */ + + for (i = 1; i <= PRMAXFAULT; i++) { + if ((prismember(set, i) != 0) ^ (m == 0)) { + (void) proc_fltname(i, name, sizeof (name)); + + if (buf[0] != '\0') + n = snprintf(p, len, "%s%s", delim, name); + else + n = snprintf(p, len, "%s", name); + + if (n != strlen(p)) { + errno = ENAMETOOLONG; /* Output was truncated */ + return (NULL); + } + len -= n; + p += n; + } + } + return (buf); +} + +/* + * Convert a sigset_t to a string representation consisting of canonical signal + * names (without the SIG prefix). Parameters and return values analogous to + * proc_fltset2str(). + */ +char * +proc_sigset2str(const sigset_t *set, const char *delim, int m, + char *buf, size_t len) +{ + char name[SIG2STR_MAX], *p = buf; + size_t n; + int i; + + if (buf == NULL || len < 1) { + errno = EINVAL; + return (NULL); + } + + m = (m != 0); /* Make sure m is 0 or 1 */ + buf[0] = '\0'; /* Set first byte to \0 */ + + /* + * Unlike proc_fltset2str() and proc_sysset2str(), we don't loop + * until i <= NSIG here, because sigismember() rejects i == NSIG. + */ + for (i = 1; i < NSIG; i++) { + if (sigismember(set, i) == m) { + (void) sig2str(i, name); + + if (buf[0] != '\0') + n = snprintf(p, len, "%s%s", delim, name); + else + n = snprintf(p, len, "%s", name); + + if (n != strlen(p)) { + errno = ENAMETOOLONG; /* Output was truncated */ + return (NULL); + } + + len -= n; + p += n; + } + } + + return (buf); +} + +/* + * Convert a sysset_t to a string representation consisting of canonical system + * call names. Parameters and return values analogous to proc_fltset2str(). + */ +char * +proc_sysset2str(const sysset_t *set, const char *delim, int m, + char *buf, size_t len) +{ + char name[SYS2STR_MAX], *p = buf; + size_t n; + int i; + + if (buf == NULL || len < 1) { + errno = EINVAL; + return (NULL); + } + + buf[0] = '\0'; /* Set first byte to \0 */ + + for (i = 1; i <= PRMAXSYS; i++) { + if ((prismember(set, i) != 0) ^ (m == 0)) { + (void) proc_sysname(i, name, sizeof (name)); + + if (buf[0] != '\0') + n = snprintf(p, len, "%s%s", delim, name); + else + n = snprintf(p, len, "%s", name); + + if (n != strlen(p)) { + errno = ENAMETOOLONG; /* Output was truncated */ + return (NULL); + } + len -= n; + p += n; + } + } + return (buf); +} + +/* + * Convert a string representation of a fault set (names separated by + * one or more of the given delimeters) to a fltset_t. + * If m is non-zero (TRUE), members of the string representation are set. + * If m is zero (FALSE), non-members of the string representation are set. + * This function returns NULL for success. Otherwise it returns a pointer + * to the token of the string that couldn't be identified as a string + * representation of a fault. + */ +char * +proc_str2fltset(const char *s, const char *delim, int m, fltset_t *set) +{ + char *p, *q, *t = alloca(strlen(s) + 1); + int flt; + + if (m) { + premptyset(set); + } else { + prfillset(set); + } + + (void) strcpy(t, s); + + for (p = strtok_r(t, delim, &q); p != NULL; + p = strtok_r(NULL, delim, &q)) { + if (proc_str2flt(p, &flt) == -1) { + errno = EINVAL; + return ((char *)s + (p - t)); + } + if (m) + praddset(set, flt); + else + prdelset(set, flt); + } + return (NULL); +} + +/* + * Convert a string representation of a signal set (names with or without the + * SIG prefix separated by one or more of the given delimeters) to a sigset_t. + * Parameters and return values analogous to proc_str2fltset(). + */ +char * +proc_str2sigset(const char *s, const char *delim, int m, sigset_t *set) +{ + char *p, *q, *t = alloca(strlen(s) + 1); + int sig; + + if (m) { + premptyset(set); + } else { + prfillset(set); + } + + (void) strcpy(t, s); + + for (p = strtok_r(t, delim, &q); p != NULL; + p = strtok_r(NULL, delim, &q)) { + if (proc_str2sig(p, &sig) == -1) { + errno = EINVAL; + return ((char *)s + (p - t)); + } + if (m) + praddset(set, sig); + else + prdelset(set, sig); + } + return (NULL); +} + +/* + * Convert a string representation of a system call set (names separated by + * one or more of the given delimeters) to a sysset_t. Parameters and return + * values analogous to proc_str2fltset(). + */ +char * +proc_str2sysset(const char *s, const char *delim, int m, sysset_t *set) +{ + char *p, *q, *t = alloca(strlen(s) + 1); + int sys; + + if (m) { + premptyset(set); + } else { + prfillset(set); + } + + (void) strcpy(t, s); + + for (p = strtok_r(t, delim, &q); p != NULL; + p = strtok_r(NULL, delim, &q)) { + if (proc_str2sys(p, &sys) == -1) { + errno = EINVAL; + return ((char *)s + (p - t)); + } + if (m) + praddset(set, sys); + else + prdelset(set, sys); + } + return (NULL); +} diff --git a/usr/src/lib/libproc/common/proc_set.c b/usr/src/lib/libproc/common/proc_set.c new file mode 100644 index 0000000000..bdd7ccb77d --- /dev/null +++ b/usr/src/lib/libproc/common/proc_set.c @@ -0,0 +1,86 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "libproc.h" +#include <alloca.h> +#include <string.h> + +/* + * Convenience wrapper to set the cred attributes of a victim process + * to a set of new values. Caller must supply a prochandle and a + * fully populated prcred structure. + */ +int +Psetcred(struct ps_prochandle *Pr, const prcred_t *credp) +{ + int ngrp; + int ctlsize; + struct { + long cmd; + prcred_t cred; + } *ctlp; + + if (Pr == NULL || credp == NULL) + return (-1); + + ngrp = credp->pr_ngroups; + ctlsize = sizeof (prcred_t) + (ngrp - 1) * sizeof (gid_t); + ctlp = alloca(ctlsize + sizeof (long)); + + ctlp->cmd = PCSCREDX; + (void) memcpy(&ctlp->cred, credp, ctlsize); + + if (write(Pctlfd(Pr), ctlp, sizeof (long) + ctlsize) < 0) + return (-1); + + return (0); +} + +/* + * Convenience wrapper to set the zoneid attribute of a victim process to a new + * value (only to and from GLOBAL_ZONEID makes sense). Caller must supply a + * prochandle and a valid zoneid. + */ +int +Psetzoneid(struct ps_prochandle *Pr, zoneid_t zoneid) +{ + struct { + long cmd; + long zoneid; + } ctl; + + if (Pr == NULL) + return (-1); + + ctl.zoneid = zoneid; + ctl.cmd = PCSZONE; + + if (write(Pctlfd(Pr), &ctl, sizeof (ctl)) < 0) + return (-1); + return (0); +} diff --git a/usr/src/lib/libproc/common/proc_stdio.c b/usr/src/lib/libproc/common/proc_stdio.c new file mode 100644 index 0000000000..b8e74bfe5a --- /dev/null +++ b/usr/src/lib/libproc/common/proc_stdio.c @@ -0,0 +1,171 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Utility functions for buffering output to stdout, stderr while + * process is grabbed. Prevents infamous deadlocks due to pfiles `pgrep xterm` + * and other varients. + */ + +#include "libproc.h" +#include <stdio.h> + +static int cached_stdout_fd = -1; +static int cached_stderr_fd = -1; +static int initialized = 0; + +static char stdout_name[] = "/tmp/.stdoutXXXXXX"; +static char stderr_name[] = "/tmp/.stderrXXXXXX"; + +int +proc_initstdio(void) +{ + int fd; + + (void) fflush(stdout); + (void) fflush(stderr); + + if ((cached_stdout_fd = dup(1)) < 0) { + return (-1); + } + + if ((cached_stderr_fd = dup(2)) < 0) { + (void) close(cached_stdout_fd); + return (-1); + } + + if ((fd = mkstemp(stdout_name)) < 0) { + (void) close(cached_stdout_fd); + (void) close(cached_stderr_fd); + return (-1); + } + + (void) unlink(stdout_name); + + if (dup2(fd, 1) < 0) { + (void) close(fd); + (void) close(cached_stdout_fd); + (void) close(cached_stderr_fd); + return (-1); + } + + (void) close(fd); + + + if ((fd = mkstemp(stderr_name)) < 0) { + (void) dup2(cached_stdout_fd, 1); + (void) close(cached_stdout_fd); + (void) close(cached_stderr_fd); + return (-1); + } + + (void) unlink(stderr_name); + + if (dup2(fd, 2) < 0) { + (void) close(fd); + (void) dup2(cached_stdout_fd, 1); + (void) close(cached_stdout_fd); + (void) dup2(cached_stderr_fd, 2); + (void) close(cached_stderr_fd); + (void) close(fd); + return (-1); + } + + (void) close(fd); + + initialized = 1; + + return (0); +} + +static int +copy_fd(int out, FILE *in, size_t len) +{ + char buffer[8192]; + int rlen, alen; + int errors = 0; + + rewind(in); + while (len > 0 && !errors) { + rlen = (len > sizeof (buffer)) ? sizeof (buffer) : len; + alen = read(fileno(in), buffer, rlen); + if (alen == rlen) { + if (write(out, buffer, alen) < alen) + errors++; + else + len -= alen; + } + else + errors++; + } + rewind(in); + return (errors); +} + +int +proc_flushstdio(void) +{ + size_t len; + int errors = 0; + + /* + * flush any pending IO + */ + + if (!initialized) + return (-1); + + (void) fflush(stdout); + (void) fflush(stderr); + + if ((len = ftell(stdout)) > 0) + errors += copy_fd(cached_stdout_fd, stdout, len); + + + if ((len = ftell(stderr)) > 0) + errors += copy_fd(cached_stderr_fd, stderr, len); + + return (errors?-1:0); +} + +int +proc_finistdio(void) +{ + if (!initialized) + return (-1); + + if (proc_flushstdio() != 0) + return (-1); + + (void) dup2(cached_stdout_fd, 1); + (void) close(cached_stdout_fd); + (void) dup2(cached_stderr_fd, 2); + (void) close(cached_stderr_fd); + + return (0); +} |