#include <sys/types.h>
#include <sys/uio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include "Pcontrol.h"
#include "P32ton.h"
static lwp_info_t *
getlwpcore(struct ps_prochandle *P, lwpid_t lwpid)
{
core_info_t *core = P->data;
lwp_info_t *lwp;
for (lwp = list_head(&core->core_lwp_head); lwp != NULL;
lwp = list_next(&core->core_lwp_head, lwp)) {
if (lwp->lwp_id == lwpid)
return (lwp);
}
errno = ENOENT;
return (NULL);
}
static int
getlwpfile(struct ps_prochandle *P, lwpid_t lwpid,
const char *fbase, void *rp, size_t n)
{
char fname[PATH_MAX];
int fd;
(void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
procfs_path, (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);
}
int e = errno;
(void) close(fd);
errno = e;
}
return (-1);
}
static int
getlwpfile_alloc(struct ps_prochandle *P, lwpid_t lwpid, const char *fbase,
void **datap, size_t *sizep)
{
char fname[PATH_MAX];
int fd;
(void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/%s",
procfs_path, (int)P->status.pr_pid, (int)lwpid, fbase);
if ((fd = open(fname, O_RDONLY)) >= 0) {
int e;
struct stat st;
if (fstat(fd, &st) == 0) {
prxregset_t *prx;
if (st.st_size == 0) {
errno = ENODATA;
goto clean;
}
prx = malloc(st.st_size);
if (prx == NULL) {
goto clean;
}
if (read(fd, prx, st.st_size) == st.st_size) {
(void) close(fd);
*datap = prx;
*sizep = st.st_size;
return (0);
}
free(prx);
}
clean:
e = errno;
(void) close(fd);
errno = e;
}
return (-1);
}
int
getlwpstatus(struct ps_prochandle *P, lwpid_t lwpid, lwpstatus_t *lps)
{
lwp_info_t *lwp;
if (P->status.pr_lwp.pr_lwpid == lwpid) {
(void) memcpy(lps, &P->status.pr_lwp, sizeof (lwpstatus_t));
return (0);
}
if (P->state != PS_DEAD) {
return (getlwpfile(P, lwpid, "lwpstatus",
lps, sizeof (lwpstatus_t)));
}
if (P->data != NULL && (lwp = getlwpcore(P, lwpid)) != NULL) {
(void) memcpy(lps, &lwp->lwp_status, sizeof (lwpstatus_t));
return (0);
}
return (-1);
}
static void
refresh_status(struct ps_prochandle *P, lwpid_t lwpid, struct ps_lwphandle *L,
long cmd, const void *rp, size_t n)
{
if (P->status.pr_lwp.pr_lwpid == lwpid) {
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);
else if (cmd == PCSXREG)
(void) Pstopstatus(P, PCNULL, 0);
}
if (L != NULL) {
if (cmd == PCSREG)
(void) memcpy(&L->lwp_status.pr_reg, rp, n);
else if (cmd == PCSFPREG)
(void) memcpy(&L->lwp_status.pr_fpreg, rp, n);
else if (cmd == PCSXREG)
(void) Lstopstatus(L, PCNULL, 0);
}
}
static int
setlwpregs_proc(struct ps_prochandle *P, lwpid_t lwpid, long cmd,
const void *rp, size_t n)
{
iovec_t iov[2];
char fname[PATH_MAX];
struct ps_lwphandle *L;
int fd = -1;
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;
if ((L = Lfind(P, lwpid)) != NULL) {
Lsync(L);
fd = L->lwp_ctlfd;
}
if (P->status.pr_lwp.pr_lwpid == lwpid) {
Psync(P);
fd = P->ctlfd;
}
if (fd > -1) {
if (writev(fd, iov, 2) == -1)
return (-1);
refresh_status(P, lwpid, L, cmd, rp, n);
return (0);
}
(void) snprintf(fname, sizeof (fname), "%s/%d/lwp/%d/lwpctl",
procfs_path, (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);
}
int e = errno;
(void) close(fd);
errno = e;
}
return (-1);
}
static int
setlwpregs_lwp(struct ps_lwphandle *L, long cmd, const void *rp, size_t n)
{
iovec_t iov[2];
if (L->lwp_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;
Lsync(L);
if (writev(L->lwp_ctlfd, iov, 2) == -1)
return (-1);
refresh_status(L->lwp_proc, L->lwp_id, L, cmd, rp, n);
return (0);
}
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
Lgetregs(struct ps_lwphandle *L, prgregset_t *gregs)
{
(void) memcpy(gregs, L->lwp_status.pr_reg, sizeof (prgregset_t));
return (0);
}
int
Plwp_setregs(struct ps_prochandle *P, lwpid_t lwpid, const prgregset_t gregs)
{
return (setlwpregs_proc(P, lwpid, PCSREG, gregs, sizeof (prgregset_t)));
}
int
Lsetregs(struct ps_lwphandle *L, const prgregset_t *gregs)
{
return (setlwpregs_lwp(L, 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
Lgetfpregs(struct ps_lwphandle *L, prfpregset_t *fpregs)
{
(void) memcpy(fpregs, &L->lwp_status.pr_fpreg, sizeof (prfpregset_t));
return (0);
}
int
Plwp_setfpregs(struct ps_prochandle *P, lwpid_t lwpid,
const prfpregset_t *fpregs)
{
return (setlwpregs_proc(P, lwpid, PCSFPREG, fpregs,
sizeof (prfpregset_t)));
}
int
Lsetfpregs(struct ps_lwphandle *L, const prfpregset_t *fpregs)
{
return (setlwpregs_lwp(L, PCSFPREG, fpregs, sizeof (prfpregset_t)));
}
void
Plwp_freexregs(struct ps_prochandle *P __unused, prxregset_t *prx,
size_t size __unused)
{
free(prx);
}
int
Plwp_getxregs(struct ps_prochandle *P, lwpid_t lwpid, prxregset_t **xregs,
size_t *sizep)
{
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_alloc(P, lwpid, "xregs",
(void **)xregs, sizep));
}
if ((lwp = getlwpcore(P, lwpid)) != NULL && lwp->lwp_xregs != NULL &&
lwp->lwp_xregsize > 0) {
*xregs = malloc(lwp->lwp_xregsize);
if (*xregs == NULL)
return (-1);
(void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
*sizep = lwp->lwp_xregsize;
return (0);
}
if (lwp != NULL)
errno = ENODATA;
return (-1);
}
int
Lgetxregs(struct ps_lwphandle *L, prxregset_t **xregs, size_t *sizep)
{
lwp_info_t *lwp;
if (L->lwp_state != PS_DEAD) {
if (L->lwp_state != PS_STOP) {
errno = EBUSY;
return (-1);
}
return (getlwpfile_alloc(L->lwp_proc, L->lwp_id, "xregs",
(void **)xregs, sizep));
}
if ((lwp = getlwpcore(L->lwp_proc, L->lwp_id)) != NULL &&
lwp->lwp_xregs != NULL && lwp->lwp_xregsize > 0) {
*xregs = malloc(lwp->lwp_xregsize);
if (*xregs == NULL)
return (-1);
(void) memcpy(*xregs, lwp->lwp_xregs, lwp->lwp_xregsize);
*sizep = lwp->lwp_xregsize;
return (0);
}
if (lwp != NULL)
errno = ENODATA;
return (-1);
}
int
Plwp_setxregs(struct ps_prochandle *P, lwpid_t lwpid, const prxregset_t *xregs,
size_t len)
{
return (setlwpregs_proc(P, lwpid, PCSXREG, xregs, len));
}
int
Lsetxregs(struct ps_lwphandle *L, const prxregset_t *xregs, size_t len)
{
return (setlwpregs_lwp(L, PCSXREG, xregs, len));
}
#if defined(sparc) || defined(__sparc)
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_proc(P, lwpid, PCSASRS, asrs, sizeof (asrset_t)));
}
#endif
#endif
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_getname(struct ps_prochandle *P, lwpid_t lwpid,
char *buf, size_t bufsize)
{
char lwpname[THREAD_NAME_MAX];
char *from = NULL;
lwp_info_t *lwp;
if (P->state == PS_IDLE) {
errno = ENODATA;
return (-1);
}
if (P->state != PS_DEAD) {
if (getlwpfile(P, lwpid, "lwpname",
lwpname, sizeof (lwpname)) != 0)
return (-1);
from = lwpname;
} else {
if ((lwp = getlwpcore(P, lwpid)) == NULL)
return (-1);
from = lwp->lwp_name;
}
if (strlcpy(buf, from, bufsize) >= bufsize) {
errno = ENAMETOOLONG;
return (-1);
}
return (0);
}
int
Plwp_getspymaster(struct ps_prochandle *P, lwpid_t lwpid, psinfo_t *ps)
{
lwpstatus_t lps;
if (P->state == PS_IDLE) {
errno = ENODATA;
return (-1);
}
if (getlwpstatus(P, lwpid, &lps) != 0)
return (-1);
if (!(lps.pr_flags & PR_AGENT)) {
errno = EINVAL;
return (-1);
}
if (P->state != PS_DEAD) {
return (getlwpfile(P, lwpid, "spymaster",
ps, sizeof (psinfo_t)));
}
if (P->spymaster.pr_nlwp != 0) {
(void) memcpy(ps, &P->spymaster, sizeof (psinfo_t));
return (0);
}
errno = ENODATA;
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;
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);
}