#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;
Pdprintf("agent LWP is stopped or 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);
Pdprintf("agent LWP system call aborted\n");
}
}
int
Pcreate_agent(struct ps_prochandle *P)
{
int fd;
char pathname[PATH_MAX];
char *fname;
struct {
long cmd;
prgregset_t regs;
} cmd;
if (P->agentcnt > 0) {
P->agentcnt++;
return (0);
}
if (P->state == PS_DEAD || P->state == PS_UNDEAD ||
P->state == PS_IDLE) {
errno = ENOENT;
return (-1);
}
(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;
}
(void) Pstopstatus(P, PCNULL, 0);
(void) snprintf(pathname, sizeof (pathname), "%s/%d/lwp/agent/",
procfs_path, (int)P->pid);
fname = pathname + strlen(pathname);
(void) set_minfd();
(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 ((P->status.pr_lwp.pr_flags & PR_ASLEEP) ||
((P->status.pr_lwp.pr_flags & PR_STOPPED) &&
P->status.pr_lwp.pr_why == PR_SYSENTRY)) {
Pdprintf("Pcreate_agent: aborting agent syscall; lwp is %s\n",
(P->status.pr_lwp.pr_flags & PR_ASLEEP) ?
"asleep" : "stopped");
Pabort_agent(P);
}
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;
(void) Pstopstatus(P, PCNULL, 0);
return (-1);
}
void
Pdestroy_agent(struct ps_prochandle *P)
{
if (P->agentcnt > 1)
P->agentcnt--;
else {
int flags;
Psync(P);
(void) Pstopstatus(P, PCNULL, 0);
flags = P->status.pr_lwp.pr_flags;
if ((flags & (PR_AGENT|PR_ASLEEP)) == (PR_AGENT|PR_ASLEEP)) {
Pdprintf("Pdestroy_agent: aborting agent syscall\n");
Pabort_agent(P);
}
(void) pr_lwp_exit(P);
(void) close(P->agentctlfd);
(void) close(P->agentstatfd);
P->agentctlfd = -1;
P->agentstatfd = -1;
P->agentcnt = 0;
(void) Pstopstatus(P, PCNULL, 0);
}
}
static int
execute(struct ps_prochandle *P, int sysindex)
{
int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd;
int washeld = FALSE;
sigset_t hold;
int cursig;
struct {
long cmd;
siginfo_t siginfo;
} ctl;
int sentry;
sentry = Psysentry(P, sysindex, TRUE);
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 ((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)
(void) write(ctlfd, &ctl, sizeof (ctl));
if (washeld) {
P->status.pr_lwp.pr_lwphold = hold;
P->flags |= SETHOLD;
}
(void) Psysentry(P, sysindex, sentry);
if (P->status.pr_lwp.pr_why == PR_SYSENTRY &&
P->status.pr_lwp.pr_what == sysindex)
return (0);
bad:
return (-1);
}
int
Psyscall(struct ps_prochandle *P,
sysret_t *rval,
int sysindex,
uint_t nargs,
argdes_t *argp)
{
int agent_created = FALSE;
pstatus_t save_pstatus;
argdes_t *adp;
int i;
int model;
int error = 0;
int Perr = 0;
int sexit;
prgreg_t sp;
prgreg_t ap;
sigset_t unblock;
(void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock);
rval->sys_rval1 = 0;
rval->sys_rval2 = 0;
if (sysindex <= 0 || sysindex > PRMAXSYS || nargs > MAXARGS)
goto bad1;
if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE)
goto bad1;
model = P->status.pr_dmodel;
#ifndef _LP64
if (model == PR_MODEL_LP64)
goto bad9;
#endif
if (Pcreate_agent(P) != 0)
goto bad8;
agent_created = TRUE;
save_pstatus = P->status;
if (P->state != PS_STOP ||
(P->status.pr_flags & PR_ASLEEP))
goto bad2;
if (Pscantext(P))
goto bad3;
#ifdef _LP64
if (model == PR_MODEL_LP64) {
sp = P->status.pr_lwp.pr_reg[R_SP] + STACK_BIAS;
#if defined(__amd64)
sp -= STACK_RESERVE64;
#endif
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 (i = 0, adp = argp; i < nargs; i++, adp++) {
rval->sys_rval1 = i;
switch (adp->arg_type) {
default:
goto bad4;
case AT_BYVAL:
break;
case AT_BYREF:
switch (adp->arg_inout) {
case AI_INPUT:
case AI_OUTPUT:
case AI_INOUT:
if (adp->arg_object == NULL)
goto bad5;
break;
default:
goto bad6;
}
if (adp->arg_size == 0 || adp->arg_size > MAXARGL)
goto bad7;
#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;
break;
}
}
rval->sys_rval1 = 0;
ap = Psyscall_setup(P, nargs, sysindex, sp);
P->flags |= SETREGS;
Pdprintf("Psyscall(): execute(sysindex = %d)\n", sysindex);
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;
Pdprintf("Psyscall(): copying arguments\n");
for (i = 0, adp = argp; i < nargs; i++, adp++) {
rval->sys_rval1 = i;
if (adp->arg_type != AT_BYVAL &&
adp->arg_inout != AI_OUTPUT) {
if (Pwrite(P, adp->arg_object, adp->arg_size,
(uintptr_t)adp->arg_value) != adp->arg_size)
goto bad17;
}
}
rval->sys_rval1 = 0;
if (Psyscall_copyinargs(P, nargs, argp, ap) != 0)
goto bad18;
Pdprintf("Psyscall(): set running at sysentry\n");
sexit = Psysexit(P, sysindex, TRUE);
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);
if (sysindex == SYS_lwp_exit && errno == ENOENT) {
Pdprintf("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)) {
Pdprintf("Pissyscall_prev() failed\n");
goto bad24;
}
Pdprintf("Psyscall(): caught at sysexit\n");
for (i = 0, adp = argp; i < nargs; i++, adp++) {
rval->sys_rval1 = i;
if (adp->arg_type != AT_BYVAL &&
adp->arg_inout != AI_INPUT) {
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;
if (P->status.pr_lwp.pr_errno) {
error = P->status.pr_lwp.pr_errno;
rval->sys_rval1 = -1L;
rval->sys_rval2 = -1L;
Pdprintf("Psyscall(%d) fails with errno %d\n",
sysindex, error);
} else {
rval->sys_rval1 = P->status.pr_lwp.pr_rval1;
rval->sys_rval2 = P->status.pr_lwp.pr_rval2;
Pdprintf("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++;
#ifndef _LP64
bad9:
#endif
Perr++;
bad8: Perr++;
bad7: Perr++;
bad6: Perr++;
bad5: Perr++;
bad4: Perr++;
bad3: Perr++;
bad2: Perr++;
bad1: Perr++;
error = -1;
Pdprintf("Psyscall(%d) fails with local error %d\n", sysindex, Perr);
out:
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);
}