diff options
author | Patrick Mooney <patrick.f.mooney@gmail.com> | 2015-06-26 21:55:23 +0000 |
---|---|---|
committer | Patrick Mooney <patrick.f.mooney@gmail.com> | 2015-08-31 20:41:36 +0000 |
commit | 9fb41d79bcf917137f62ea68254281fcfe2c9a50 (patch) | |
tree | f898c01fb38c847182b7e05331d49afe7e78eb22 | |
parent | 6fb0bf827f03910897e781c837f13735d760bf48 (diff) | |
download | illumos-joyent-9fb41d79bcf917137f62ea68254281fcfe2c9a50.tar.gz |
OS-4458 lxbrand complete moving ptrace to IKE
OS-4038 lxbrand PTRACE_PEEKUSER debugregs offset incorrect on 64bit
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/amd64/Makefile | 11 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/amd64/offsets.in | 43 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/lx_brand.c | 6 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/ptrace.c | 786 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/i386/Makefile | 11 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/i386/offsets.in | 40 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_brand.c | 3 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_misc.c | 55 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_ptrace.c | 254 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_syscall.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_brand.h | 149 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_misc.h | 10 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_syscalls.h | 1 | ||||
-rw-r--r-- | usr/src/uts/intel/brand/lx/lx_archdep.c | 1445 |
14 files changed, 1184 insertions, 1634 deletions
diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/Makefile b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile index 664cb8de56..a1db90cd38 100644 --- a/usr/src/lib/brand/lx/lx_brand/amd64/Makefile +++ b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile @@ -12,7 +12,7 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# Copyright 2014 Joyent, Inc. All rights reserved. +# Copyright 2015 Joyent, Inc. # # # lib/brand/lx/amd64/Makefile @@ -32,15 +32,6 @@ MSGFILES= $(CSRCS) ASSYMDEP_OBJS = lx_handler.o -$(ASSYMDEP_OBJS:%=pics/%): assym.h - -OFFSETS = ../$(MACH64)/offsets.in - -assym.h: $(OFFSETS) - $(OFFSETS_CREATE) $(CTF_FLAGS) < $(OFFSETS) > $@ - -CLOBBERFILES += assym.h - install: all $(ROOTLIBS64) $(POFILE): $(MSGFILES) diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/offsets.in b/usr/src/lib/brand/lx/lx_brand/amd64/offsets.in deleted file mode 100644 index 207063a886..0000000000 --- a/usr/src/lib/brand/lx/lx_brand/amd64/offsets.in +++ /dev/null @@ -1,43 +0,0 @@ -\ -\ This file and its contents are supplied under the terms of the -\ Common Development and Distribution License ("CDDL"), version 1.0. -\ You may only use this file in accordance with the terms of version -\ 1.0 of the CDDL. -\ -\ A full copy of the text of the CDDL should have accompanied this -\ source. A copy of the CDDL is also available via the Internet at -\ http://www.illumos.org/license/CDDL. -\ -\ Copyright 2014 Joyent, Inc. All rights reserved. -\ - -#include <sys/lx_brand.h> -#include <sys/lx_sigstack.h> - -lx_regs_t SIZEOF_LX_REGS_T - lxr_fs - lxr_rdi - lxr_rsi - lxr_rbp - lxr_rsp - lxr_rbx - lxr_rdx - lxr_rcx - lxr_rax - lxr_r8 - lxr_r9 - lxr_r10 - lxr_r11 - lxr_r12 - lxr_r13 - lxr_r14 - lxr_r15 - lxr_rip - lxr_orig_rax - -lx_sigstack_t SIZEOF_LX_SIGSTACK_T - retaddr - si - uc - fpstate - pad diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c index 63bc766eea..dafbe9c862 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c +++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c @@ -663,8 +663,6 @@ lx_init(int argc, char *argv[], char *envp[]) if (lx_statfs_init() != 0) lx_err_fatal("failed to setup the statfs translator"); - lx_ptrace_init(); - #if defined(_LP64) vdso_hdr = map_vdso(); edp.ed_vdso = (uintptr_t)vdso_hdr; @@ -1031,7 +1029,7 @@ static lx_syscall_handler_t lx_handlers[] = { lx_getrusage, /* 98: getrusage */ NULL, /* 99: sysinfo */ lx_times, /* 100: times */ - lx_ptrace, /* 101: ptrace */ + NULL, /* 101: ptrace */ lx_getuid, /* 102: getuid */ lx_syslog, /* 103: syslog */ lx_getgid, /* 104: getgid */ @@ -1287,7 +1285,7 @@ static lx_syscall_handler_t lx_handlers[] = { lx_setuid16, /* 23: setuid16 */ lx_getuid16, /* 24: getuid16 */ lx_stime, /* 25: stime */ - lx_ptrace, /* 26: ptrace */ + NULL, /* 26: ptrace */ lx_alarm, /* 27: alarm */ NULL, /* 28: fstat */ lx_pause, /* 29: pause */ diff --git a/usr/src/lib/brand/lx/lx_brand/common/ptrace.c b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c index 8680563c26..bb6e52a112 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/ptrace.c +++ b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c @@ -59,682 +59,6 @@ * detail. */ -/* - * This corresponds to the user_i387_struct Linux structure. - */ -typedef struct lx_user_fpregs { - long lxuf_cwd; - long lxuf_swd; - long lxuf_twd; - long lxuf_fip; - long lxuf_fcs; - long lxuf_foo; - long lxuf_fos; - long lxuf_st_space[20]; -} lx_user_fpregs_t; - -/* - * This corresponds to the user_fxsr_struct Linux structure. - */ -typedef struct lx_user_fpxregs { - uint16_t lxux_cwd; - uint16_t lxux_swd; - uint16_t lxux_twd; - uint16_t lxux_fop; - long lxux_fip; - long lxux_fcs; - long lxux_foo; - long lxux_fos; - long lxux_mxcsr; - long lxux_reserved; - long lxux_st_space[32]; - long lxux_xmm_space[32]; - long lxux_padding[56]; -} lx_user_fpxregs_t; - -typedef struct lx_user { - lx_user_regs_t lxu_regs; - int lxu_fpvalid; - lx_user_fpregs_t lxu_i387; - ulong_t lxu_tsize; - ulong_t lxu_dsize; - ulong_t lxu_ssize; - ulong_t lxu_start_code; - ulong_t lxu_start_stack; - long lxu_signal; - int lxu_reserved; - lx_user_regs_t *lxu_ar0; - lx_user_fpregs_t *lxu_fpstate; - ulong_t lxu_magic; - char lxu_comm[32]; - int lxu_debugreg[8]; -} lx_user_t; - -typedef struct ptrace_state_map { - struct ptrace_state_map *psm_next; /* next pointer */ - pid_t psm_pid; /* Solaris pid */ - uintptr_t psm_debugreg[8]; /* debug registers */ -} ptrace_state_map_t; - -static ptrace_state_map_t *ptrace_state_map = NULL; -static mutex_t ptrace_map_mtx = DEFAULTMUTEX; - -extern void *_START_; - -static sigset_t blockable_sigs; - -static long lx_ptrace_kernel(int, pid_t, uintptr_t, uintptr_t); - -void -lx_ptrace_init(void) -{ - (void) sigfillset(&blockable_sigs); - (void) sigdelset(&blockable_sigs, SIGKILL); - (void) sigdelset(&blockable_sigs, SIGSTOP); -} - -/* - * Given a pid, open the named file under /native/proc/<pid>/name using the - * given mode. - */ -static int -open_procfile(pid_t pid, int mode, const char *name) -{ - char path[MAXPATHLEN]; - - (void) snprintf(path, sizeof (path), "/native/proc/%d/%s", pid, name); - - return (open(path, mode)); -} - -/* - * Given a pid and lwpid, open the named file under - * /native/proc/<pid>/<lwpid>/name using the given mode. - */ -static int -open_lwpfile(pid_t pid, lwpid_t lwpid, int mode, const char *name) -{ - char path[MAXPATHLEN]; - - (void) snprintf(path, sizeof (path), "/native/proc/%d/lwp/%d/%s", - pid, lwpid, name); - - return (open(path, mode)); -} - -static int -get_lwpstatus(pid_t pid, lwpid_t lwpid, lwpstatus_t *lsp) -{ - int fd; - - if ((fd = open_lwpfile(pid, lwpid, O_RDONLY, "lwpstatus")) < 0) - return (-ESRCH); - - if (read(fd, lsp, sizeof (lwpstatus_t)) != sizeof (lwpstatus_t)) { - (void) close(fd); - return (-EIO); - } - - (void) close(fd); - - return (0); -} - -static int -getfpregs(pid_t pid, lwpid_t lwpid, lx_user_fpregs_t *rp) -{ - lwpstatus_t status; - struct _fpstate *fp; -#if defined(_ILP32) - char *data; - int i; -#endif - int ret; - - if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) - return (ret); - - fp = (struct _fpstate *)&status.pr_fpreg.fp_reg_set.fpchip_state; - -#if defined(_ILP32) - rp->lxuf_cwd = fp->cw; - rp->lxuf_swd = fp->sw; - rp->lxuf_twd = fp->tag; - rp->lxuf_fip = fp->ipoff; - rp->lxuf_fcs = fp->cssel; - rp->lxuf_foo = fp->dataoff; - rp->lxuf_fos = fp->datasel; - - /* - * The Linux structure uses 10 bytes per floating-point register. - */ - data = (char *)&rp->lxuf_st_space[0]; - for (i = 0; i < 8; i++) { - bcopy(&fp->_st[i], data, 10); - data += 10; - } -#endif - - return (0); -} - -static int -setfpregs(pid_t pid, lwpid_t lwpid, const lx_user_fpregs_t *rp) -{ - lwpstatus_t status; - struct { - long cmd; - prfpregset_t regs; - } ctl; -#if defined(_ILP32) - struct _fpstate *fp = (struct _fpstate *)&ctl.regs; - char *data; - int i; -#endif - int ret, fd; - - if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) - return (ret); - - bcopy(&status.pr_fpreg, &ctl.regs, sizeof (ctl.regs)); - -#if defined(_ILP32) - fp->cw = rp->lxuf_cwd; - fp->sw = rp->lxuf_swd; - fp->tag = rp->lxuf_twd; - fp->ipoff = rp->lxuf_fip; - fp->cssel = rp->lxuf_fcs; - fp->dataoff = rp->lxuf_foo; - fp->datasel = rp->lxuf_fos; - - /* - * The Linux structure uses 10 bytes per floating-point register. - */ - data = (char *)&rp->lxuf_st_space[0]; - for (i = 0; i < 8; i++) { - bcopy(data, &fp->_st[i], 10); - data += 10; - } -#endif - - if ((fd = open_lwpfile(pid, lwpid, O_WRONLY, "lwpctl")) < 0) - return (-ESRCH); - - ctl.cmd = PCSFPREG; - if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { - (void) close(fd); - return (-EIO); - } - - (void) close(fd); - - return (0); -} - - -static int -getfpxregs(pid_t pid, lwpid_t lwpid, lx_user_fpxregs_t *rp) -{ -#if defined(_ILP32) - lwpstatus_t status; - struct _fpstate *fp; - int ret, i; - - if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) - return (ret); - - fp = (struct _fpstate *)&status.pr_fpreg.fp_reg_set.fpchip_state; - - rp->lxux_cwd = (uint16_t)fp->cw; - rp->lxux_swd = (uint16_t)fp->sw; - rp->lxux_twd = (uint16_t)fp->tag; - rp->lxux_fop = (uint16_t)(fp->cssel >> 16); - rp->lxux_fip = fp->ipoff; - rp->lxux_fcs = (uint16_t)fp->cssel; - rp->lxux_foo = fp->dataoff; - rp->lxux_fos = fp->datasel; - rp->lxux_mxcsr = status.pr_fpreg.fp_reg_set.fpchip_state.mxcsr; - - bcopy(fp->xmm, rp->lxux_xmm_space, sizeof (rp->lxux_xmm_space)); - bzero(rp->lxux_st_space, sizeof (rp->lxux_st_space)); - for (i = 0; i < 8; i++) { - bcopy(&fp->_st[i], &rp->lxux_st_space[i * 4], - sizeof (fp->_st[i])); - } -#endif - - return (0); -} - -static int -setfpxregs(pid_t pid, lwpid_t lwpid, const lx_user_fpxregs_t *rp) -{ -#if defined(_ILP32) - lwpstatus_t status; - struct { - long cmd; - prfpregset_t regs; - } ctl; - struct _fpstate *fp = (struct _fpstate *)&ctl.regs; - int ret, i, fd; - - if ((ret = get_lwpstatus(pid, lwpid, &status)) != 0) - return (ret); - - bcopy(&status.pr_fpreg, &ctl.regs, sizeof (ctl.regs)); - - fp->cw = rp->lxux_cwd; - fp->sw = rp->lxux_swd; - fp->tag = rp->lxux_twd; - fp->ipoff = rp->lxux_fip; - fp->cssel = rp->lxux_fcs | (rp->lxux_fop << 16); - fp->dataoff = rp->lxux_foo; - fp->datasel = rp->lxux_fos; - - bcopy(rp->lxux_xmm_space, fp->xmm, sizeof (rp->lxux_xmm_space)); - for (i = 0; i < 8; i++) { - bcopy(&rp->lxux_st_space[i * 4], &fp->_st[i], - sizeof (fp->_st[i])); - } - - if ((fd = open_lwpfile(pid, lwpid, O_WRONLY, "lwpctl")) < 0) - return (-ESRCH); - - ctl.cmd = PCSFPREG; - if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { - (void) close(fd); - return (-EIO); - } - - (void) close(fd); -#endif - - return (0); -} - -/* - * Solaris does not allow a process to manipulate its own or some - * other process's debug registers. Linux ptrace(2) allows this - * and gdb manipulates them for its watchpoint implementation. - * - * We keep a pseudo set of debug registers for each traced process - * and map their contents into the appropriate PCWATCH /proc - * operations when they are activated by gdb. - * - * To understand how the debug registers work on x86 machines, - * see section 13.1 of the AMD x86-64 Architecture Programmer's - * Manual, Volume 2, System Programming. - */ -static uintptr_t * -debug_registers(pid_t pid) -{ - ptrace_state_map_t *p; - - (void) mutex_lock(&ptrace_map_mtx); - for (p = ptrace_state_map; p != NULL; p = p->psm_next) { - if (p->psm_pid == pid) - break; - } - if (p == NULL && (p = malloc(sizeof (*p))) != NULL) { - bzero(p, sizeof (*p)); - p->psm_pid = pid; - p->psm_next = ptrace_state_map; - p->psm_debugreg[6] = 0xffff0ff0; /* read as ones */ - ptrace_state_map = p; - } - (void) mutex_unlock(&ptrace_map_mtx); - return (p != NULL? p->psm_debugreg : NULL); -} - -static int -setup_watchpoints(pid_t pid, uintptr_t *debugreg) -{ - int dr7 = debugreg[7]; - int lrw; - int fd; - size_t size = NULL; - prwatch_t prwatch[4]; - int nwatch; - int i; - int wflags = NULL; - int error; - struct { - long req; - prwatch_t prwatch; - } ctl; - - /* find all watched areas */ - if ((fd = open_procfile(pid, O_RDONLY, "watch")) < 0) - return (-ESRCH); - nwatch = read(fd, prwatch, sizeof (prwatch)) / sizeof (prwatch_t); - (void) close(fd); - if ((fd = open_procfile(pid, O_WRONLY, "ctl")) < 0) - return (-ESRCH); - /* clear all watched areas */ - for (i = 0; i < nwatch; i++) { - ctl.req = PCWATCH; - ctl.prwatch = prwatch[i]; - ctl.prwatch.pr_wflags = 0; - if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { - error = -errno; - (void) close(fd); - return (error); - } - } - /* establish all new watched areas */ - for (i = 0; i < 4; i++) { - if ((dr7 & (1 << (2 * i))) == 0) /* enabled? */ - continue; - lrw = (dr7 >> (16 + (4 * i))) & 0xf; - switch (lrw >> 2) { /* length */ - case 0: size = 1; break; - case 1: size = 2; break; - case 2: size = 8; break; - case 3: size = 4; break; - } - switch (lrw & 0x3) { /* mode */ - case 0: wflags = WA_EXEC; break; - case 1: wflags = WA_WRITE; break; - case 2: continue; - case 3: wflags = WA_READ | WA_WRITE; break; - } - ctl.req = PCWATCH; - ctl.prwatch.pr_vaddr = debugreg[i]; - ctl.prwatch.pr_size = size; - ctl.prwatch.pr_wflags = wflags | WA_TRAPAFTER; - if (write(fd, &ctl, sizeof (ctl)) != sizeof (ctl)) { - error = -errno; - (void) close(fd); - return (error); - } - } - (void) close(fd); - return (0); -} - -/* - * Returns B_TRUE if the target LWP, identified by its Linux pid, is traced by - * this LWP and is waiting in "ptrace-stop". Returns B_FALSE otherwise. - */ -static boolean_t -is_ptrace_stopped(pid_t lxpid) -{ - ulong_t dummy; - - /* - * We attempt a PTRACE_GETEVENTMSG request to determine if the tracee - * is stopped appropriately. As we are not in the kernel, this is not - * an atomic check; the process is not guaranteed to remain stopped - * once we have dropped the locks protecting that state and left the - * kernel. - */ - if (lx_ptrace_kernel(LX_PTRACE_GETEVENTMSG, lxpid, NULL, - (uintptr_t)&dummy) == 0) { - return (B_TRUE); - } - - /* - * This call should only fail with ESRCH, which tells us that the - * a tracee with that pid was not found in the stopped condition. - */ - assert(errno == ESRCH); - - return (B_FALSE); -} - -/* - * Read a word of data from the given address. Because this is a process-wide - * action, we don't need the lwpid. - */ -static long -ptrace_peek(pid_t pid, uintptr_t addr, long *ret) -{ - int fd; - long data; - - if ((fd = open_procfile(pid, O_RDONLY, "as")) < 0) - return (-ESRCH); - - if (pread(fd, &data, sizeof (data), addr) != sizeof (data)) { - (void) close(fd); - return (-EIO); - } - - (void) close(fd); - - if (uucopy(&data, ret, sizeof (data)) != 0) - return (-errno); - - return (0); -} - -#define LX_USER_BOUND(m) \ -(offsetof(lx_user_t, m) + sizeof (((lx_user_t *)NULL)->m)) - -static int -ptrace_peek_user(pid_t lxpid, pid_t pid, lwpid_t lwpid, uintptr_t off, int *ret) -{ - int err; - long data; - uintptr_t *debugreg; - int dreg; - - /* - * The offset specified by the user is an offset into the Linux - * user structure (seriously). Rather than constructing a full - * user structure, we figure out which part of the user structure - * the offset is in, and fill in just that component. - */ - if (off < LX_USER_BOUND(lxu_regs)) { - lx_user_regs_t regs; - - if ((err = lx_ptrace_kernel(LX_PTRACE_GETREGS, lxpid, NULL, - (uintptr_t)®s)) != 0) { - return (err); - } - - data = *(long *)((uintptr_t)®s + off - - offsetof(lx_user_t, lxu_regs)); - - } else if (off < LX_USER_BOUND(lxu_fpvalid)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_i387)) { - lx_user_fpregs_t regs; - - if ((err = getfpregs(pid, lwpid, ®s)) != 0) - return (err); - - data = *(long *)((uintptr_t)®s + off - - offsetof(lx_user_t, lxu_i387)); - - } else if (off < LX_USER_BOUND(lxu_tsize)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_dsize)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_ssize)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_start_code)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_start_stack)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_signal)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_reserved)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_ar0)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_fpstate)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_magic)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_comm)) { - lx_err("offset = %lu\n", off); - assert(0); - } else if (off < LX_USER_BOUND(lxu_debugreg)) { - dreg = (off - offsetof(lx_user_t, lxu_debugreg)) / sizeof (int); - if (dreg == 4) /* aliased */ - dreg = 6; - else if (dreg == 5) /* aliased */ - dreg = 7; - if ((debugreg = debug_registers(pid)) != NULL) - data = debugreg[dreg]; - else - data = 0; - } else { - lx_unsupported("unsupported ptrace peek user offset: 0x%x\n", - off); - assert(0); - return (-ENOTSUP); - } - - if (uucopy(&data, ret, sizeof (data)) != 0) - return (-errno); - - return (0); -} - -/* - * Write a word of data to the given address. Because this is a process-wide - * action, we don't need the lwpid. Returns EINVAL if the address is not - * word-aligned. - */ -static int -ptrace_poke(pid_t pid, uintptr_t addr, int data) -{ - int fd; - - if (addr & 0x3) - return (-EINVAL); - - if ((fd = open_procfile(pid, O_WRONLY, "as")) < 0) - return (-ESRCH); - - if (pwrite(fd, &data, sizeof (data), addr) != sizeof (data)) { - (void) close(fd); - return (-EIO); - } - - (void) close(fd); - return (0); -} - -static int -ptrace_poke_user(pid_t lxpid, pid_t pid, lwpid_t lwpid, - uintptr_t off, long data) -{ - lx_user_regs_t regs; - int err = 0; - uintptr_t *debugreg; - int dreg; - - if (off & 0x3) - return (-EINVAL); - - if (off < offsetof(lx_user_t, lxu_regs) + sizeof (lx_user_regs_t)) { - if ((err = lx_ptrace_kernel(LX_PTRACE_GETREGS, lxpid, NULL, - (uintptr_t)®s)) != 0) { - return (err); - } - - *(long *)((uintptr_t)®s + off - - offsetof(lx_user_t, lxu_regs)) = data; - - return (lx_ptrace_kernel(LX_PTRACE_SETREGS, lxpid, NULL, - (uintptr_t)®s)); - } - - if (off >= offsetof(lx_user_t, lxu_debugreg) && - off < offsetof(lx_user_t, lxu_debugreg) + 8 * sizeof (int)) { - dreg = (off - offsetof(lx_user_t, lxu_debugreg)) / sizeof (int); - if (dreg == 4) /* aliased */ - dreg = 6; - else if (dreg == 5) /* aliased */ - dreg = 7; - if ((debugreg = debug_registers(pid)) != NULL) { - debugreg[dreg] = data; - if (dreg == 7) - err = setup_watchpoints(pid, debugreg); - } - return (err); - } - - lx_unsupported("unsupported ptrace poke user offset: 0x%x\n", off); - assert(0); - return (-ENOTSUP); -} - -static int -ptrace_kill(pid_t pid) -{ - int ret; - - ret = kill(pid, SIGKILL); - - return (ret == 0 ? ret : -errno); -} - -static int -ptrace_getfpregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) -{ - lx_user_fpregs_t regs; - int ret; - - if ((ret = getfpregs(pid, lwpid, ®s)) != 0) - return (ret); - - if (uucopy(®s, (void *)addr, sizeof (regs)) != 0) - return (-errno); - - return (0); -} - -static int -ptrace_setfpregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) -{ - lx_user_fpregs_t regs; - - if (uucopy((void *)addr, ®s, sizeof (regs)) != 0) - return (-errno); - - return (setfpregs(pid, lwpid, ®s)); -} - -static int -ptrace_getfpxregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) -{ - lx_user_fpxregs_t regs; - int ret; - - if ((ret = getfpxregs(pid, lwpid, ®s)) != 0) - return (ret); - - if (uucopy(®s, (void *)addr, sizeof (regs)) != 0) - return (-errno); - - return (0); -} - -static int -ptrace_setfpxregs(pid_t pid, lwpid_t lwpid, uintptr_t addr) -{ - lx_user_fpxregs_t regs; - - if (uucopy((void *)addr, ®s, sizeof (regs)) != 0) - return (-errno); - - return (setfpxregs(pid, lwpid, ®s)); -} void lx_ptrace_stop_if_option(int option, boolean_t child, ulong_t msg, @@ -779,113 +103,3 @@ lx_ptrace_clone_begin(int option, boolean_t inherit_flag) strerror(errno)); } } - -static long -lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) -{ - int ret; - - /* - * Call into the in-kernel ptrace(2) emulation code. - */ - lx_debug("revectoring to B_PTRACE_KERNEL(%d, %d, %p, %p)", ptrace_op, - lxpid, addr, data); - ret = syscall(SYS_brand, B_PTRACE_KERNEL, ptrace_op, lxpid, addr, - data); - if (ret == 0) { - lx_debug("\t= %d", ret); - } else { - lx_debug("\t= %d (%s)", ret, strerror(errno)); - } - - return (ret == 0 ? ret : -errno); -} - -long -lx_ptrace(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) -{ - int ptrace_op = (int)p1; - pid_t pid, lxpid = (pid_t)p2; - lwpid_t lwpid; - - /* - * Some PTRACE_* requests are emulated entirely in the kernel. - */ - switch (ptrace_op) { - /* - * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of - * one LWP by another. The target LWP must not be traced already. - * Both `data' and `addr' are ignored in both cases. - */ - case LX_PTRACE_TRACEME: - return (lx_ptrace_kernel(ptrace_op, 0, 0, 0)); - - case LX_PTRACE_ATTACH: - return (lx_ptrace_kernel(ptrace_op, lxpid, 0, 0)); - - /* - * PTRACE_DETACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP and PTRACE_CONT - * are all restarting actions. They are only allowed when attached - * to the target LWP and when that target LWP is in a "ptrace-stop" - * condition. - */ - case LX_PTRACE_DETACH: - case LX_PTRACE_SYSCALL: - case LX_PTRACE_CONT: - case LX_PTRACE_SINGLESTEP: - /* - * These actions also require the LWP to be traced and stopped, but do - * not restart the target LWP. - */ - case LX_PTRACE_SETOPTIONS: - case LX_PTRACE_GETEVENTMSG: - case LX_PTRACE_GETSIGINFO: - case LX_PTRACE_GETREGS: - case LX_PTRACE_SETREGS: - return (lx_ptrace_kernel(ptrace_op, lxpid, p3, p4)); - } - - /* - * The rest of the emulated PTRACE_* actions are emulated in userland. - * They require the target LWP to be traced and in currently - * "ptrace-stop", but do not subsequently restart the target LWP. - */ - if (lx_lpid_to_spair(lxpid, &pid, &lwpid) < 0 || - !is_ptrace_stopped(lxpid)) { - return (-ESRCH); - } - - switch (ptrace_op) { - case LX_PTRACE_PEEKTEXT: - case LX_PTRACE_PEEKDATA: - return (ptrace_peek(pid, p3, (long *)p4)); - - case LX_PTRACE_PEEKUSER: - return (ptrace_peek_user(lxpid, pid, lwpid, p3, (int *)p4)); - - case LX_PTRACE_POKETEXT: - case LX_PTRACE_POKEDATA: - return (ptrace_poke(pid, p3, (int)p4)); - - case LX_PTRACE_POKEUSER: - return (ptrace_poke_user(lxpid, pid, lwpid, p3, (long)p4)); - - case LX_PTRACE_KILL: - return (ptrace_kill(pid)); - - case LX_PTRACE_GETFPREGS: - return (ptrace_getfpregs(pid, lwpid, p4)); - - case LX_PTRACE_SETFPREGS: - return (ptrace_setfpregs(pid, lwpid, p4)); - - case LX_PTRACE_GETFPXREGS: - return (ptrace_getfpxregs(pid, lwpid, p4)); - - case LX_PTRACE_SETFPXREGS: - return (ptrace_setfpxregs(pid, lwpid, p4)); - - default: - return (-EINVAL); - } -} diff --git a/usr/src/lib/brand/lx/lx_brand/i386/Makefile b/usr/src/lib/brand/lx/lx_brand/i386/Makefile index e9546dee4a..8723f64292 100644 --- a/usr/src/lib/brand/lx/lx_brand/i386/Makefile +++ b/usr/src/lib/brand/lx/lx_brand/i386/Makefile @@ -21,7 +21,7 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# Copyright 2014 Joyent, Inc. All rights reserved. +# Copyright 2015 Joyent, Inc. # # lib/brand/lx/i386/Makefile @@ -38,15 +38,6 @@ MSGFILES= $(CSRCS) ASSYMDEP_OBJS = lx_handler.o -$(ASSYMDEP_OBJS:%=pics/%): assym.h - -OFFSETS = ../$(MACH)/offsets.in - -assym.h: $(OFFSETS) - $(OFFSETS_CREATE) $(CTF_FLAGS) < $(OFFSETS) > $@ - -CLOBBERFILES += assym.h - install: all $(ROOTLIBS) $(POFILE): $(MSGFILES) diff --git a/usr/src/lib/brand/lx/lx_brand/i386/offsets.in b/usr/src/lib/brand/lx/lx_brand/i386/offsets.in deleted file mode 100644 index ac934ee76c..0000000000 --- a/usr/src/lib/brand/lx/lx_brand/i386/offsets.in +++ /dev/null @@ -1,40 +0,0 @@ -\ -\ Copyright 2006 Sun Microsystems, Inc. All rights reserved. -\ Use is subject to license terms. -\ -\ CDDL HEADER START -\ -\ The contents of this file are subject to the terms of the -\ Common Development and Distribution License (the "License"). -\ You may not use this file except in compliance with the License. -\ -\ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -\ or http://www.opensolaris.org/os/licensing. -\ See the License for the specific language governing permissions -\ and limitations under the License. -\ -\ When distributing Covered Code, include this CDDL HEADER in each -\ file and include the License file at usr/src/OPENSOLARIS.LICENSE. -\ If applicable, add the following below this CDDL HEADER, with the -\ fields enclosed by brackets "[]" replaced with your own identifying -\ information: Portions Copyright [yyyy] [name of copyright owner] -\ -\ CDDL HEADER END -\ - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include <sys/lx_brand.h> - -lx_regs_t SIZEOF_LX_REGS_T - lxr_gs - lxr_edi - lxr_esi - lxr_ebp - lxr_esp - lxr_ebx - lxr_edx - lxr_ecx - lxr_eax - lxr_eip - lxr_orig_eax diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c index 2f3ede8287..05146886c4 100644 --- a/usr/src/uts/common/brand/lx/os/lx_brand.c +++ b/usr/src/uts/common/brand/lx/os/lx_brand.c @@ -1138,9 +1138,6 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2, return (lx_ptrace_set_clone_inherit((int)arg1, arg2 == 0 ? B_FALSE : B_TRUE)); - case B_PTRACE_KERNEL: - return (lx_ptrace_kernel((int)arg1, (pid_t)arg2, arg3, arg4)); - case B_HELPER_WAITID: { idtype_t idtype = (idtype_t)arg1; id_t id = (id_t)arg2; diff --git a/usr/src/uts/common/brand/lx/os/lx_misc.c b/usr/src/uts/common/brand/lx/os/lx_misc.c index 848370bc3c..c81a1f143e 100644 --- a/usr/src/uts/common/brand/lx/os/lx_misc.c +++ b/usr/src/uts/common/brand/lx/os/lx_misc.c @@ -48,6 +48,7 @@ #include <lx_signum.h> #include <lx_syscall.h> #include <sys/proc.h> +#include <sys/procfs.h> #include <net/if.h> #include <sys/sunddi.h> #include <sys/dlpi.h> @@ -1011,3 +1012,57 @@ lx_read_argv_bounds(proc_t *p) pd->l_envs_start = env_start; pd->l_envs_end = env_end; } + +/* Given an LX LWP, determine where user register state is stored. */ +lx_regs_location_t +lx_regs_location(lx_lwp_data_t *lwpd, void **ucp, boolean_t for_write) +{ + switch (lwpd->br_stack_mode) { + case LX_STACK_MODE_BRAND: + /* + * The LWP was stopped with the brand stack and register state + * loaded, e.g. during a syscall emulated within the kernel. + */ + return (LX_REG_LOC_LWP); + + case LX_STACK_MODE_PREINIT: + if (for_write) { + /* setting registers not allowed in this state */ + break; + } + if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED || + lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) { + /* The LWP was stopped by tracing on exec. */ + return (LX_REG_LOC_LWP); + } + break; + + case LX_STACK_MODE_NATIVE: + if (for_write) { + /* setting registers not allowed in this state */ + break; + } + if (lwpd->br_ptrace_whystop == PR_BRAND && + lwpd->br_ptrace_whatstop == LX_PR_EVENT) { + /* Called while ptrace-event-stopped by lx_exec. */ + return (LX_REG_LOC_LWP); + } + break; + default: + break; + } + + if (lwpd->br_ptrace_stopucp != NULL) { + /* + * The LWP was stopped in the usermode emulation library + * but a ucontext_t for the preserved brand stack and + * register state was provided. Return the register state + * from that ucontext_t. + */ + VERIFY(ucp != NULL); + *ucp = (void *)lwpd->br_ptrace_stopucp; + return (LX_REG_LOC_UCP); + } + + return (LX_REG_LOC_UNAVAIL); +} diff --git a/usr/src/uts/common/brand/lx/os/lx_ptrace.c b/usr/src/uts/common/brand/lx/os/lx_ptrace.c index 9a4bc71a0b..6a4bf44aab 100644 --- a/usr/src/uts/common/brand/lx/os/lx_ptrace.c +++ b/usr/src/uts/common/brand/lx/os/lx_ptrace.c @@ -217,6 +217,16 @@ typedef enum lx_ptrace_cont_flags_t { LX_PTC_SINGLESTEP = 0x02 } lx_ptrace_cont_flags_t; + +extern int lx_user_regs_copyin(lx_lwp_data_t *, void *); +extern int lx_user_regs_copyout(lx_lwp_data_t *, void *); +extern int lx_ptrace_peekuser(lx_lwp_data_t *, uintptr_t, void *); +extern int lx_ptrace_pokeuser(lx_lwp_data_t *, uintptr_t, void *); +extern int lx_user_fpregs_copyin(lx_lwp_data_t *, void *); +extern int lx_user_fpregs_copyout(lx_lwp_data_t *, void *); +extern int lx_user_fpxregs_copyin(lx_lwp_data_t *, void *); +extern int lx_user_fpxregs_copyout(lx_lwp_data_t *, void *); + /* * Macros for checking the state of an LWP via "br_ptrace_flags": */ @@ -803,74 +813,6 @@ lx_ptrace_geteventmsg(lx_lwp_data_t *remote, void *umsgp) } static int -lx_ptrace_getregs(lx_lwp_data_t *remote, void *uregsp) -{ - if (remote->br_stack_mode == LX_STACK_MODE_BRAND) { - /* - * The LWP was stopped with the brand stack and register - * state loaded, e.g. during a system call emulated within - * the kernel. Return the LWP register state. - */ - return (lx_regs_to_userregs(remote, uregsp)); - } else if (remote->br_stack_mode == LX_STACK_MODE_PREINIT && - (remote->br_ptrace_whatstop == LX_PR_SIGNALLED || - remote->br_ptrace_whatstop == LX_PR_SYSEXIT)) { - /* - * The LWP was stopped by tracing on exec. - */ - return (lx_regs_to_userregs(remote, uregsp)); - } else if (remote->br_stack_mode == LX_STACK_MODE_NATIVE && - remote->br_ptrace_whystop == PR_BRAND && - remote->br_ptrace_whatstop == LX_PR_EVENT) { - /* - * Called while we're ptrace event stopped by lx_exec. - */ - return (lx_regs_to_userregs(remote, uregsp)); - } else if (remote->br_ptrace_stopucp != NULL) { - /* - * The LWP was stopped in the usermode emulation library - * but a ucontext_t for the preserved brand stack and - * register state was provided. Return the register state - * from that ucontext_t. - */ - return (lx_uc_to_userregs(remote, - (void *)remote->br_ptrace_stopucp, uregsp)); - } else { - /* - * The register state is not currently available. - */ - return (EIO); - } -} - -static int -lx_ptrace_setregs(lx_lwp_data_t *remote, void *uregsp) -{ - if (remote->br_stack_mode == LX_STACK_MODE_BRAND) { - /* - * The LWP was stopped with the brand stack and register - * state loaded, e.g. during a system call emulated within - * the kernel. Write to the LWP register state. - */ - return (lx_userregs_to_regs(remote, uregsp)); - } else if (remote->br_ptrace_stopucp != NULL) { - /* - * The LWP was stopped in the usermode emulation library - * but a ucontext_t for the preserved brand stack and - * register state was provided. Write to the register state - * in that ucontext_t. - */ - return (lx_userregs_to_uc(remote, - (void *)remote->br_ptrace_stopucp, uregsp)); - } else { - /* - * The register state is not currently available. - */ - return (EIO); - } -} - -static int lx_ptrace_getsiginfo(lx_lwp_data_t *remote, void *usiginfo) { klwp_t *lwp = remote->br_lwp; @@ -965,6 +907,7 @@ lx_ptrace_detach(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote, int signo, * Detach the LWP from the accord and set it running. */ VERIFY(!TRACEE_BUSY(remote)); + VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock)); remote->br_ptrace_flags &= ~(LX_PTF_SYSCALL | LX_PTF_INHERIT); VERIFY(list_link_active(&remote->br_ptrace_linkage)); list_remove(&accord->lxpa_tracees, remote); @@ -2285,11 +2228,72 @@ lx_waitid_helper(idtype_t idtype, id_t id, k_siginfo_t *ip, int options, return (0); } -/* - * Some PTRACE_* requests are handled in-kernel by this function. It is called - * through brandsys() via the B_PTRACE_KERNEL subcommand. - */ -int +static int +lx_ptrace_peek(lx_lwp_data_t *lwpd, uintptr_t addr, void *data) +{ + proc_t *p = lwptoproc(lwpd->br_lwp); + long buf; + int error = 0, size = sizeof (buf); + +#if defined(_LP64) + if (get_udatamodel() != DATAMODEL_NATIVE) { + size = sizeof (uint32_t); + } +#endif + if ((addr & (size - 1)) != 0) { + /* unaligned access */ + return (EINVAL); + } + + mutex_exit(&p->p_lock); + error = uread(p, &buf, size, addr); + mutex_enter(&p->p_lock); + + if (error != 0) { + return (EIO); + } + if (copyout(&buf, data, size) != 0) { + return (EFAULT); + } + + return (0); +} + +static int +lx_ptrace_poke(lx_lwp_data_t *lwpd, uintptr_t addr, uintptr_t data) +{ + proc_t *p = lwptoproc(lwpd->br_lwp); + int error = 0, size = sizeof (data); + +#if defined(_LP64) + if (get_udatamodel() != DATAMODEL_NATIVE) { + size = sizeof (uint32_t); + } +#endif + if ((addr & (size - 1)) != 0) { + /* unaligned access */ + return (EINVAL); + } + + mutex_exit(&p->p_lock); + error = uwrite(p, &data, size, addr); + mutex_enter(&p->p_lock); + + if (error != 0) { + return (EIO); + } + return (0); +} + +static int +lx_ptrace_kill(lx_lwp_data_t *lwpd) +{ + sigtoproc(lwptoproc(lwpd->br_lwp), NULL, SIGKILL); + + return (0); +} + +static int lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) { lx_lwp_data_t *local = ttolxlwp(curthread); @@ -2299,12 +2303,10 @@ lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) proc_t *rproc; int error; boolean_t found = B_FALSE; - boolean_t release_hold = B_FALSE; - - _NOTE(ARGUNUSED(addr)); /* - * These actions do not require the target LWP to be traced or stopped. + * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of + * one LWP by another. The target LWP must not be traced already. */ switch (ptrace_op) { case LX_PTRACE_TRACEME: @@ -2365,15 +2367,39 @@ lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) rlwp = remote->br_lwp; rproc = lwptoproc(rlwp); + + if (ptrace_op == LX_PTRACE_DETACH) { + boolean_t release_hold = B_FALSE; + error = lx_ptrace_detach(accord, remote, (int)data, + &release_hold); + /* + * Drop the lock on both the tracee process and the tracee list. + */ + mutex_exit(&rproc->p_lock); + mutex_exit(&accord->lxpa_tracees_lock); + + if (release_hold) { + /* + * Release a hold from the accord. + */ + lx_ptrace_accord_enter(accord); + lx_ptrace_accord_rele(accord); + lx_ptrace_accord_exit(accord); + } + + return (error); + } + + /* + * The tracees lock is not needed for any of the other operations. + * Drop it so further actions can avoid deadlock. + */ + mutex_exit(&accord->lxpa_tracees_lock); + /* * Process the ptrace(2) request: */ switch (ptrace_op) { - case LX_PTRACE_DETACH: - error = lx_ptrace_detach(accord, remote, (int)data, - &release_hold); - break; - case LX_PTRACE_CONT: error = lx_ptrace_cont(remote, LX_PTC_NONE, (int)data); break; @@ -2395,17 +2421,55 @@ lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) break; case LX_PTRACE_GETREGS: - error = lx_ptrace_getregs(remote, (void *)data); + error = lx_user_regs_copyout(remote, (void *)data); break; case LX_PTRACE_SETREGS: - error = lx_ptrace_setregs(remote, (void *)data); + error = lx_user_regs_copyin(remote, (void *)data); break; case LX_PTRACE_GETSIGINFO: error = lx_ptrace_getsiginfo(remote, (void *)data); break; + case LX_PTRACE_PEEKTEXT: + case LX_PTRACE_PEEKDATA: + error = lx_ptrace_peek(remote, addr, (void *)data); + break; + + case LX_PTRACE_POKETEXT: + case LX_PTRACE_POKEDATA: + error = lx_ptrace_poke(remote, addr, data); + break; + + case LX_PTRACE_PEEKUSER: + error = lx_ptrace_peekuser(remote, addr, (void *)data); + break; + + case LX_PTRACE_POKEUSER: + error = lx_ptrace_pokeuser(remote, addr, (void *)data); + break; + + case LX_PTRACE_GETFPREGS: + error = lx_user_fpregs_copyout(remote, (void *)data); + break; + + case LX_PTRACE_SETFPREGS: + error = lx_user_fpregs_copyin(remote, (void *)data); + break; + + case LX_PTRACE_GETFPXREGS: + error = lx_user_fpxregs_copyout(remote, (void *)data); + break; + + case LX_PTRACE_SETFPXREGS: + error = lx_user_fpxregs_copyin(remote, (void *)data); + break; + + case LX_PTRACE_KILL: + error = lx_ptrace_kill(remote); + break; + default: error = EINVAL; } @@ -2414,20 +2478,22 @@ lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) * Drop the lock on both the tracee process and the tracee list. */ mutex_exit(&rproc->p_lock); - mutex_exit(&accord->lxpa_tracees_lock); - - if (release_hold) { - /* - * Release a hold from the accord. - */ - lx_ptrace_accord_enter(accord); - lx_ptrace_accord_rele(accord); - lx_ptrace_accord_exit(accord); - } return (error); } +int +lx_ptrace(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data) +{ + int error; + + error = lx_ptrace_kernel(ptrace_op, lxpid, addr, data); + if (error != 0) { + return (set_errno(error)); + } + return (0); +} + void lx_ptrace_init(void) { diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c index b3ee62ef75..c141a1ea28 100644 --- a/usr/src/uts/common/brand/lx/os/lx_syscall.c +++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c @@ -549,7 +549,7 @@ lx_sysent_t lx_sysent32[] = { {"setuid16", NULL, 0, 1}, /* 23 */ {"getuid16", NULL, 0, 0}, /* 24 */ {"stime", NULL, 0, 1}, /* 25 */ - {"ptrace", NULL, 0, 4}, /* 26 */ + {"ptrace", lx_ptrace, 0, 4}, /* 26 */ {"alarm", NULL, 0, 1}, /* 27 */ {"fstat", NULL, NOSYS_OBSOLETE, 0}, /* 28 */ {"pause", NULL, 0, 0}, /* 29 */ @@ -995,7 +995,7 @@ lx_sysent_t lx_sysent64[] = { {"getrusage", NULL, 0, 2}, /* 98 */ {"sysinfo", lx_sysinfo64, 0, 1}, /* 99 */ {"times", NULL, 0, 1}, /* 100 */ - {"ptrace", NULL, 0, 4}, /* 101 */ + {"ptrace", lx_ptrace, 0, 4}, /* 101 */ {"getuid", NULL, 0, 0}, /* 102 */ {"syslog", NULL, 0, 3}, /* 103 */ {"getgid", NULL, 0, 0}, /* 104 */ diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h index aa682d9538..1b00691ba5 100644 --- a/usr/src/uts/common/brand/lx/sys/lx_brand.h +++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h @@ -82,7 +82,7 @@ extern "C" { #define B_LPID_TO_SPAIR 128 #define B_GET_CURRENT_CONTEXT 129 #define B_EMULATION_DONE 130 -#define B_PTRACE_KERNEL 131 +/* formerly B_PTRACE_KERNEL 131 */ #define B_SET_AFFINITY_MASK 132 #define B_GET_AFFINITY_MASK 133 #define B_PTRACE_CLONE_BEGIN 134 @@ -197,149 +197,6 @@ typedef struct lx_brand_registration32 { uint32_t lxbr_flags; /* LX_PROC_* registration flags */ } lx_brand_registration32_t; -#ifdef __amd64 - -typedef struct lx_regs { - long lxr_fs; - long lxr_rdi; - long lxr_rsi; - long lxr_rbp; - long lxr_rsp; - long lxr_rbx; - long lxr_rdx; - long lxr_rcx; - long lxr_rax; - long lxr_r8; - long lxr_r9; - long lxr_r10; - long lxr_r11; - long lxr_r12; - long lxr_r13; - long lxr_r14; - long lxr_r15; - long lxr_rip; - - long lxr_orig_rax; -} lx_regs_t; - -typedef struct lx_regs32 { - uint32_t lxr_gs; - uint32_t lxr_edi; - uint32_t lxr_esi; - uint32_t lxr_ebp; - uint32_t lxr_esp; - uint32_t lxr_ebx; - uint32_t lxr_edx; - uint32_t lxr_ecx; - uint32_t lxr_eax; - uint32_t lxr_eip; - - uint32_t lxr_orig_eax; -} lx_regs32_t; - -#else /* ! __amd64 */ - -typedef struct lx_regs { - long lxr_gs; - long lxr_edi; - long lxr_esi; - long lxr_ebp; - long lxr_esp; - long lxr_ebx; - long lxr_edx; - long lxr_ecx; - long lxr_eax; - long lxr_eip; - - long lxr_orig_eax; -} lx_regs_t; - -#endif /* __amd64 */ - -#ifdef __amd64 -/* - * The 64-bit native "user_regs_struct" Linux structure. - */ -typedef struct lx_user_regs { - long lxur_r15; - long lxur_r14; - long lxur_r13; - long lxur_r12; - long lxur_rbp; - long lxur_rbx; - long lxur_r11; - long lxur_r10; - long lxur_r9; - long lxur_r8; - long lxur_rax; - long lxur_rcx; - long lxur_rdx; - long lxur_rsi; - long lxur_rdi; - long lxur_orig_rax; - long lxur_rip; - long lxur_xcs; - long lxur_rflags; - long lxur_rsp; - long lxur_xss; - long lxur_xfs_base; - long lxur_xgs_base; - long lxur_xds; - long lxur_xes; - long lxur_xfs; - long lxur_xgs; -} lx_user_regs_t; - -#if defined(_KERNEL) && defined(_SYSCALL32_IMPL) -/* - * 64-bit kernel view of the 32-bit "user_regs_struct" Linux structure. - */ -typedef struct lx_user_regs32 { - int32_t lxur_ebx; - int32_t lxur_ecx; - int32_t lxur_edx; - int32_t lxur_esi; - int32_t lxur_edi; - int32_t lxur_ebp; - int32_t lxur_eax; - int32_t lxur_xds; - int32_t lxur_xes; - int32_t lxur_xfs; - int32_t lxur_xgs; - int32_t lxur_orig_eax; - int32_t lxur_eip; - int32_t lxur_xcs; - int32_t lxur_eflags; - int32_t lxur_esp; - int32_t lxur_xss; -} lx_user_regs32_t; -#endif /* defined(_KERNEL) && defined(_SYSCALL32_IMPL) */ - -#else /* !__amd64 */ -/* - * The 32-bit native "user_regs_struct" Linux structure. - */ -typedef struct lx_user_regs { - long lxur_ebx; - long lxur_ecx; - long lxur_edx; - long lxur_esi; - long lxur_edi; - long lxur_ebp; - long lxur_eax; - long lxur_xds; - long lxur_xes; - long lxur_xfs; - long lxur_xgs; - long lxur_orig_eax; - long lxur_eip; - long lxur_xcs; - long lxur_eflags; - long lxur_esp; - long lxur_xss; -} lx_user_regs_t; -#endif /* __amd64 */ - #endif /* _ASM */ /* @@ -713,10 +570,6 @@ extern void lx_lwp_set_native_stack_current(lx_lwp_data_t *, uintptr_t); extern void lx_divert(klwp_t *, uintptr_t); extern int lx_runexe(klwp_t *, void *); extern void lx_switch_to_native(klwp_t *); -extern int lx_regs_to_userregs(lx_lwp_data_t *, void *); -extern int lx_uc_to_userregs(lx_lwp_data_t *, void *, void *); -extern int lx_userregs_to_regs(lx_lwp_data_t *lwpd, void *); -extern int lx_userregs_to_uc(lx_lwp_data_t *lwpd, void *, void *); extern int lx_syscall_enter(void); extern int lx_syscall_return(klwp_t *, int, long); diff --git a/usr/src/uts/common/brand/lx/sys/lx_misc.h b/usr/src/uts/common/brand/lx/sys/lx_misc.h index 73f7ae1f5c..5e123db2ee 100644 --- a/usr/src/uts/common/brand/lx/sys/lx_misc.h +++ b/usr/src/uts/common/brand/lx/sys/lx_misc.h @@ -50,6 +50,15 @@ extern int stol_ksiginfo32_copyout(k_siginfo_t *, void *); #endif extern void lx_read_argv_bounds(proc_t *p); +typedef enum lx_regs_location { + LX_REG_LOC_UNAVAIL, + LX_REG_LOC_LWP, + LX_REG_LOC_UCP +} lx_regs_location_t; + +extern lx_regs_location_t lx_regs_location(lx_lwp_data_t *, void **, boolean_t); + + typedef enum lx_if_action { LX_IF_FROMNATIVE, LX_IF_TONATIVE @@ -69,7 +78,6 @@ extern boolean_t lx_ptrace_stop(ushort_t); extern void lx_stop_notify(proc_t *, klwp_t *, ushort_t, ushort_t); extern void lx_ptrace_init(void); extern void lx_ptrace_fini(void); -extern int lx_ptrace_kernel(int, pid_t, uintptr_t, uintptr_t); extern int lx_waitid_helper(idtype_t, id_t, k_siginfo_t *, int, boolean_t *, int *); extern void lx_ptrace_exit(proc_t *, klwp_t *); diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h index f0cbcedb8c..6ceb748914 100644 --- a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h +++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h @@ -82,6 +82,7 @@ extern long lx_pipe(); extern long lx_pipe2(); extern long lx_prctl(); extern long lx_prlimit64(); +extern long lx_ptrace(); extern long lx_read(); extern long lx_recv(); extern long lx_recvmsg(); diff --git a/usr/src/uts/intel/brand/lx/lx_archdep.c b/usr/src/uts/intel/brand/lx/lx_archdep.c index 70e54d4fb9..254985f23b 100644 --- a/usr/src/uts/intel/brand/lx/lx_archdep.c +++ b/usr/src/uts/intel/brand/lx/lx_archdep.c @@ -22,6 +22,7 @@ #include <sys/ddi.h> #include <sys/brand.h> #include <sys/lx_brand.h> +#include <sys/lx_misc.h> #include <sys/privregs.h> #include <sys/pcb.h> #include <sys/archsystm.h> @@ -32,12 +33,171 @@ #define LX_REG(ucp, r) ((ucp)->uc_mcontext.gregs[(r)]) +#ifdef __amd64 +/* 64-bit native user_regs_struct */ +typedef struct lx_user_regs64 { + int64_t lxur_r15; + int64_t lxur_r14; + int64_t lxur_r13; + int64_t lxur_r12; + int64_t lxur_rbp; + int64_t lxur_rbx; + int64_t lxur_r11; + int64_t lxur_r10; + int64_t lxur_r9; + int64_t lxur_r8; + int64_t lxur_rax; + int64_t lxur_rcx; + int64_t lxur_rdx; + int64_t lxur_rsi; + int64_t lxur_rdi; + int64_t lxur_orig_rax; + int64_t lxur_rip; + int64_t lxur_xcs; + int64_t lxur_rflags; + int64_t lxur_rsp; + int64_t lxur_xss; + int64_t lxur_xfs_base; + int64_t lxur_xgs_base; + int64_t lxur_xds; + int64_t lxur_xes; + int64_t lxur_xfs; + int64_t lxur_xgs; +} lx_user_regs64_t; + +/* 64-bit native user_fpregs_struct */ +typedef struct lx_user_fpregs64 { + uint16_t lxufp_cwd; + uint16_t lxufp_swd; + uint16_t lxufp_ftw; + uint16_t lxufp_fop; + uint64_t lxufp_rip; + uint64_t lxufp_rdp; + uint32_t lxufp_mxcsr; + uint32_t lxufp_mxcr_mask; + /* 8*16 bytes for each FP-reg = 128 bytes */ + uint32_t lxufp_st_space[32]; + /* 16*16 bytes for each XMM-reg = 256 bytes */ + uint32_t lxufp_xmm_space[64]; + uint32_t lxufp_padding[24]; +} lx_user_fpregs64_t; + +/* 64-bit native user_struct */ +typedef struct lx_user64 { + lx_user_regs64_t lxu_regs; + int32_t lxu_fpvalid; + int32_t lxu_pad0; + lx_user_fpregs64_t lxu_i387; + uint64_t lxu_tsize; + uint64_t lxu_dsize; + uint64_t lxu_ssize; + uint64_t lxu_start_code; + uint64_t lxu_start_stack; + int64_t lxu_signal; + int32_t lxu_reserved; + int32_t lxu_pad1; + /* help gdb to locate user_regs structure */ + caddr_t lxu_ar0; + /* help gdb to locate user_fpregs structure */ + caddr_t lxu_fpstate; + uint64_t lxu_magic; + char lxu_comm[32]; + uint64_t lxu_debugreg[8]; + uint64_t lxu_error_code; + uint64_t lxu_fault_address; +} lx_user64_t; + +#endif /* __amd64 */ + +/* 32-bit native user_regs_struct */ +typedef struct lx_user_regs32 { + int32_t lxur_ebx; + int32_t lxur_ecx; + int32_t lxur_edx; + int32_t lxur_esi; + int32_t lxur_edi; + int32_t lxur_ebp; + int32_t lxur_eax; + int32_t lxur_xds; + int32_t lxur_xes; + int32_t lxur_xfs; + int32_t lxur_xgs; + int32_t lxur_orig_eax; + int32_t lxur_eip; + int32_t lxur_xcs; + int32_t lxur_eflags; + int32_t lxur_esp; + int32_t lxur_xss; +} lx_user_regs32_t; + +/* 32-bit native user_fpregs_struct */ +typedef struct lx_user_fpregs32 { + int32_t lxufp_cwd; + int32_t lxufp_swd; + int32_t lxufp_twd; + int32_t lxufp_fip; + int32_t lxufp_fcs; + int32_t lxufp_foo; + int32_t lxufp_fos; + int32_t lxufp_st_space[20]; +} lx_user_fpregs32_t; + +/* 32-bit native user_fpxregs_struct */ +typedef struct lx_user_fpxregs32 { + uint16_t lxufpx_cwd; + uint16_t lxufpx_swd; + uint16_t lxufpx_twd; + uint16_t lxufpx_fop; + int32_t lxufpx_fip; + int32_t lxufpx_fcs; + int32_t lxufpx_foo; + int32_t lxufpx_fos; + int32_t lxufpx_mxcsr; + int32_t lxufpx_reserved; + /* 8*16 bytes for each FP-reg = 128 bytes */ + int32_t lxufpx_st_space[32]; + /* 8*16 bytes for each XMM-reg = 128 bytes */ + int32_t lxufpx_xmm_space[32]; + int32_t lxufpx_padding[56]; +} lx_user_fpxregs32_t; + +/* 32-bit native user_struct */ +typedef struct lx_user32 { + lx_user_regs32_t lxu_regs; + int32_t lxu_fpvalid; + lx_user_fpregs32_t lxu_i387; + uint32_t lxu_tsize; + uint32_t lxu_dsize; + uint32_t lxu_ssize; + uint32_t lxu_start_code; + uint32_t lxu_start_stack; + int32_t lxu_signal; + int32_t lxu_reserved; + caddr32_t lxu_ar0; + caddr32_t lxu_fpstate; + uint32_t lxu_magic; + char lxu_comm[32]; + int32_t lxu_debugreg[8]; +} lx_user32_t; + +/* + * Certain version of strace (on centos6 for example) use the %cs value to + * determine what kind of process is being traced. Here is a sample comment: + * Check CS register value. On x86-64 linux it is: + * 0x33 for long mode (64 bit and x32)) + * 0x23 for compatibility mode (32 bit) + * %ds = 0x2b for x32 mode (x86-64 in 32 bit) + * We can't change the %cs value in the ucp (see setgregs and _sys_rtt) so we + * emulate the expected value for ptrace use. + */ +#define LX_CS_64BIT 0x33 +#define LX_CS_32BIT 0x23 + extern int getsetcontext(int, void *); #if defined(_SYSCALL32_IMPL) extern int getsetcontext32(int, void *); #endif -#if defined(__amd64) static int lx_rw_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz, boolean_t writing) { @@ -100,616 +260,915 @@ lx_write_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz) { return (lx_rw_uc(p, ucp, kucp, ucsz, B_TRUE)); } + +static void +lx_getfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp) +{ +#ifdef __amd64 + fpregset32_t fp; + getfpregs32(lwpd->br_lwp, &fp); +#else /* __i386 */ + fpregset_t fp; + getfpregs(lwpd->br_lwp, &fp); #endif /* __amd64 */ -/* - * Load register state from a usermode "lx_user_regs_t" in the tracer - * and store it in the tracee ucontext_t. - */ -int -lx_userregs_to_uc(lx_lwp_data_t *lwpd, void *ucp, void *uregsp) + /* + * The fpchip_state.state field should correspond to all 27 fields in + * the 32-bit structure. + */ + bcopy(&fp.fp_reg_set.fpchip_state.state, lfp, sizeof (*lfp)); +} + +static void +lx_setfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp) +{ +#ifdef __amd64 + fpregset32_t fp; +#else /* __i386 */ + fpregset_t fp; +#endif /* __amd64 */ + + /* + * The fpchip_state field should correspond to all 27 fields in the + * native 32-bit structure. + */ + bcopy(lfp, &fp.fp_reg_set.fpchip_state.state, sizeof (*lfp)); + +#ifdef __amd64 + setfpregs32(lwpd->br_lwp, &fp); +#else /* __i386 */ + setfpregs(lwpd->br_lwp, &fp); +#endif /* __amd64 */ +} + +static int +lx_get_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp) { -#if defined(__amd64) - klwp_t *lwp = lwpd->br_lwp; proc_t *p = lwptoproc(lwp); + ucontext32_t uc; - switch (get_udatamodel()) { - case DATAMODEL_LP64: { - lx_user_regs_t lxur; + if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); + } - if (copyin(uregsp, &lxur, sizeof (lxur)) != 0) { - return (EFAULT); - } + lxrp->lxur_ebx = LX_REG(&uc, EBX); + lxrp->lxur_ecx = LX_REG(&uc, ECX); + lxrp->lxur_edx = LX_REG(&uc, EDX); + lxrp->lxur_esi = LX_REG(&uc, ESI); + lxrp->lxur_edi = LX_REG(&uc, EDI); + lxrp->lxur_ebp = LX_REG(&uc, EBP); + lxrp->lxur_eax = LX_REG(&uc, EAX); + lxrp->lxur_orig_eax = 0; + + lxrp->lxur_eip = LX_REG(&uc, EIP); + lxrp->lxur_eflags = LX_REG(&uc, EFL); + lxrp->lxur_esp = LX_REG(&uc, UESP); + lxrp->lxur_xss = LX_REG(&uc, SS); + + /* emulated %cs, see defines */ + lxrp->lxur_xcs = LX_CS_32BIT; + lxrp->lxur_xds = LX_REG(&uc, DS); + lxrp->lxur_xes = LX_REG(&uc, ES); + lxrp->lxur_xfs = LX_REG(&uc, FS); + lxrp->lxur_xgs = LX_REG(&uc, GS); + return (0); +} - switch (lwp_getdatamodel(lwp)) { - case DATAMODEL_LP64: { - ucontext_t uc; +static int +lx_get_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp) +{ + klwp_t *lwp = lwpd->br_lwp; + struct regs *rp = lwptoregs(lwp); + void *ucp; +#ifdef __amd64 + struct pcb *pcb = &lwp->lwp_pcb; +#endif - if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); - } + VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32); - /* - * Note: we currently ignore "lxur_orig_rax" here (as - * this path should not be used for system call stops) - * as well as "lxur_xcs" (lest we get caught up in our - * own lies about %cs from lx_uc_to_userregs()). - */ - LX_REG(&uc, REG_R15) = lxur.lxur_r15; - LX_REG(&uc, REG_R14) = lxur.lxur_r14; - LX_REG(&uc, REG_R13) = lxur.lxur_r13; - LX_REG(&uc, REG_R12) = lxur.lxur_r12; - LX_REG(&uc, REG_RBP) = lxur.lxur_rbp; - LX_REG(&uc, REG_RBX) = lxur.lxur_rbx; - LX_REG(&uc, REG_R11) = lxur.lxur_r11; - LX_REG(&uc, REG_R10) = lxur.lxur_r10; - LX_REG(&uc, REG_R9) = lxur.lxur_r9; - LX_REG(&uc, REG_R8) = lxur.lxur_r8; - LX_REG(&uc, REG_RAX) = lxur.lxur_rax; - LX_REG(&uc, REG_RCX) = lxur.lxur_rcx; - LX_REG(&uc, REG_RDX) = lxur.lxur_rdx; - LX_REG(&uc, REG_RSI) = lxur.lxur_rsi; - LX_REG(&uc, REG_RDI) = lxur.lxur_rdi; - LX_REG(&uc, REG_RIP) = lxur.lxur_rip; - LX_REG(&uc, REG_RFL) = lxur.lxur_rflags; - LX_REG(&uc, REG_RSP) = lxur.lxur_rsp; - LX_REG(&uc, REG_SS) = lxur.lxur_xss; - LX_REG(&uc, REG_FSBASE) = lxur.lxur_xfs_base; - LX_REG(&uc, REG_GSBASE) = lxur.lxur_xgs_base; - - LX_REG(&uc, REG_DS) = lxur.lxur_xds; - LX_REG(&uc, REG_ES) = lxur.lxur_xes; - LX_REG(&uc, REG_FS) = lxur.lxur_xfs; - LX_REG(&uc, REG_GS) = lxur.lxur_xgs; - - if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); - } + switch (lx_regs_location(lwpd, &ucp, B_FALSE)) { + case LX_REG_LOC_UNAVAIL: + return (-1); - return (0); - } + case LX_REG_LOC_UCP: + return (lx_get_user_regs32_uc(lwp, ucp, lxrp)); - case DATAMODEL_ILP32: { - ucontext32_t uc; + case LX_REG_LOC_LWP: + /* transformation below */ + break; - if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); - } + default: + VERIFY(0); + break; + } - /* - * Note: we currently ignore "lxur_orig_eax" here (as - * this path should not be used for system call stops) - * as well as "lxur_xcs" (lest we get caught up in our - * own lies about %cs from lx_uc_to_userregs()). - */ - LX_REG(&uc, EBP) = (int32_t)lxur.lxur_rbp; - LX_REG(&uc, EBX) = (int32_t)lxur.lxur_rbx; - LX_REG(&uc, EAX) = (int32_t)lxur.lxur_rax; - LX_REG(&uc, ECX) = (int32_t)lxur.lxur_rcx; - LX_REG(&uc, EDX) = (int32_t)lxur.lxur_rdx; - LX_REG(&uc, ESI) = (int32_t)lxur.lxur_rsi; - LX_REG(&uc, EDI) = (int32_t)lxur.lxur_rdi; - LX_REG(&uc, EIP) = (int32_t)lxur.lxur_rip; - LX_REG(&uc, EFL) = (int32_t)lxur.lxur_rflags; - LX_REG(&uc, UESP) = (int32_t)lxur.lxur_rsp; - LX_REG(&uc, SS) = (int32_t)lxur.lxur_xss; - - LX_REG(&uc, DS) = (int32_t)lxur.lxur_xds; - LX_REG(&uc, ES) = (int32_t)lxur.lxur_xes; - LX_REG(&uc, FS) = (int32_t)lxur.lxur_xfs; - LX_REG(&uc, GS) = (int32_t)lxur.lxur_xgs; - - if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); - } +#ifdef __amd64 + lxrp->lxur_ebx = (int32_t)rp->r_rbx; + lxrp->lxur_ecx = (int32_t)rp->r_rcx; + lxrp->lxur_edx = (int32_t)rp->r_rdx; + lxrp->lxur_esi = (int32_t)rp->r_rsi; + lxrp->lxur_edi = (int32_t)rp->r_rdi; + lxrp->lxur_ebp = (int32_t)rp->r_rbp; + lxrp->lxur_eax = (int32_t)rp->r_rax; + lxrp->lxur_orig_eax = 0; + lxrp->lxur_eip = (int32_t)rp->r_rip; + lxrp->lxur_eflags = (int32_t)rp->r_rfl; + lxrp->lxur_esp = (int32_t)rp->r_rsp; + lxrp->lxur_xss = (int32_t)rp->r_ss; + + kpreempt_disable(); + if (pcb->pcb_rupdate == 1) { + lxrp->lxur_xds = pcb->pcb_ds; + lxrp->lxur_xes = pcb->pcb_es; + lxrp->lxur_xfs = pcb->pcb_fs; + lxrp->lxur_xgs = pcb->pcb_gs; + } else { + lxrp->lxur_xds = rp->r_ds; + lxrp->lxur_xes = rp->r_es; + lxrp->lxur_xfs = rp->r_fs; + lxrp->lxur_xgs = rp->r_gs; + } + kpreempt_enable(); +#else /* __i386 */ + lxrp->lxur_ebx = rp->r_ebx; + lxrp->lxur_ecx = rp->r_ecx; + lxrp->lxur_edx = rp->r_edx; + lxrp->lxur_esi = rp->r_esi; + lxrp->lxur_edi = rp->r_edi; + lxrp->lxur_ebp = rp->r_ebp; + lxrp->lxur_eax = rp->r_eax; + lxrp->lxur_orig_eax = 0; + lxrp->lxur_eip = rp->r_eip; + lxrp->lxur_eflags = rp->r_efl; + lxrp->lxur_esp = rp->r_esp; + lxrp->lxur_xss = rp->r_ss; + + lxrp->lxur_xds = rp->r_ds; + lxrp->lxur_xes = rp->r_es; + lxrp->lxur_xfs = rp->r_fs; + lxrp->lxur_xgs = rp->r_gs; +#endif /* __amd64 */ - return (0); - } + /* emulated %cs, see defines */ + lxrp->lxur_xcs = LX_CS_32BIT; - default: - return (EIO); - } + if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) { + lxrp->lxur_eax = (int32_t)-lx_errno(ENOTSUP, EINVAL); + lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num; + } else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) { + lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num; + } + + return (0); +} + +static int +lx_set_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp) +{ + proc_t *p = lwptoproc(lwp); + ucontext32_t uc; - return (EIO); + if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); } - case DATAMODEL_ILP32: { - lx_user_regs32_t lxur; - ucontext32_t uc; + /* + * Note: we currently ignore "lxur_orig_rax" here since this + * path should not be used for system call stops. + */ + LX_REG(&uc, EBP) = lxrp->lxur_ebp; + LX_REG(&uc, EBX) = lxrp->lxur_ebx; + LX_REG(&uc, EAX) = lxrp->lxur_eax; + LX_REG(&uc, ECX) = lxrp->lxur_ecx; + LX_REG(&uc, EDX) = lxrp->lxur_edx; + LX_REG(&uc, ESI) = lxrp->lxur_esi; + LX_REG(&uc, EDI) = lxrp->lxur_edi; + LX_REG(&uc, EIP) = lxrp->lxur_eip; + LX_REG(&uc, EFL) = lxrp->lxur_eflags; + LX_REG(&uc, UESP) = lxrp->lxur_esp; + LX_REG(&uc, SS) = lxrp->lxur_xss; + + /* %cs is ignored because of our lies */ + LX_REG(&uc, DS) = lxrp->lxur_xds; + LX_REG(&uc, ES) = lxrp->lxur_xes; + LX_REG(&uc, FS) = lxrp->lxur_xfs; + LX_REG(&uc, GS) = lxrp->lxur_xgs; + + if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); + } + return (0); +} - if (lwp_getdatamodel(lwp) != DATAMODEL_ILP32) { - /* - * The target is not a 32-bit LWP. We refuse to - * present truncated 64-bit registers to a 32-bit - * tracer. - */ - return (EIO); - } +static int +lx_set_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp) +{ + klwp_t *lwp = lwpd->br_lwp; + struct regs *rp = lwptoregs(lwp); + void *ucp; +#ifdef __amd64 + struct pcb *pcb = &lwp->lwp_pcb; +#endif - if (copyin(uregsp, &lxur, sizeof (lxur)) != 0) { - return (EFAULT); - } + VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32); + + switch (lx_regs_location(lwpd, &ucp, B_TRUE)) { + case LX_REG_LOC_UNAVAIL: + return (-1); + + case LX_REG_LOC_UCP: + return (lx_set_user_regs32_uc(lwp, ucp, lxrp)); + + case LX_REG_LOC_LWP: + /* transformation below */ + break; + + default: + VERIFY(0); + break; + } + +#ifdef __amd64 + rp->r_rbx = (int32_t)lxrp->lxur_ebx; + rp->r_rcx = (int32_t)lxrp->lxur_ecx; + rp->r_rdx = (int32_t)lxrp->lxur_edx; + rp->r_rsi = (int32_t)lxrp->lxur_esi; + rp->r_rdi = (int32_t)lxrp->lxur_edi; + rp->r_rbp = (int32_t)lxrp->lxur_ebp; + rp->r_rax = (int32_t)lxrp->lxur_eax; + lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax; + rp->r_rip = (int32_t)lxrp->lxur_eip; + rp->r_rfl = (int32_t)lxrp->lxur_eflags; + rp->r_rsp = (int32_t)lxrp->lxur_esp; + rp->r_ss = (int32_t)lxrp->lxur_xss; + + kpreempt_disable(); + pcb->pcb_rupdate = 1; + pcb->pcb_ds = lxrp->lxur_xds; + pcb->pcb_es = lxrp->lxur_xes; + pcb->pcb_fs = lxrp->lxur_xfs; + pcb->pcb_gs = lxrp->lxur_xgs; + kpreempt_enable(); +#else /* __i386 */ + rp->r_ebx = lxrp->lxur_ebx; + rp->r_ecx = lxrp->lxur_ecx; + rp->r_edx = lxrp->lxur_edx; + rp->r_esi = lxrp->lxur_esi; + rp->r_edi = lxrp->lxur_edi; + rp->r_ebp = lxrp->lxur_ebp; + rp->r_eax = lxrp->lxur_eax; + lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax; + rp->r_eip = lxrp->lxur_eip; + rp->r_efl = lxrp->lxur_eflags; + rp->r_esp = lxrp->lxur_esp; + rp->r_ss = lxrp->lxur_xss; + + rp->r_ds = lxrp->lxur_xds; + rp->r_es = lxrp->lxur_xes; + rp->r_fs = lxrp->lxur_xfs; + rp->r_gs = lxrp->lxur_xgs; +#endif /* __amd64 */ + + return (0); +} + +#ifdef __amd64 + +static void +lx_getfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp) +{ + fpregset_t fp; + + getfpregs(lwpd->br_lwp, &fp); + /* Drop the extra illumos status/xstatus fields when copying state */ + bcopy(&fp.fp_reg_set.fpchip_state, lfp, sizeof (*lfp)); +} + +static void +lx_setfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp) +{ + fpregset_t fp; + + /* + * Since the Linux fpregs structure does not contain the same + * additional status register which illumos contains, we simply + * preserve the existing values when setting fp state. + */ + getfpregs(lwpd->br_lwp, &fp); + + /* Copy the identically formatted state */ + bcopy(lfp, &fp.fp_reg_set.fpchip_state, sizeof (*lfp)); + + setfpregs(lwpd->br_lwp, &fp); +} + +static int +lx_get_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp) +{ + proc_t *p = lwptoproc(lwp); + + switch (lwp_getdatamodel(lwp)) { + case DATAMODEL_LP64: { + ucontext_t uc; if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); + return (-1); } - /* - * Note: we currently ignore "lxur_orig_eax" here, as - * this path should not be used for system call stops. - */ - LX_REG(&uc, EBX) = lxur.lxur_ebx; - LX_REG(&uc, ECX) = lxur.lxur_ecx; - LX_REG(&uc, EDX) = lxur.lxur_edx; - LX_REG(&uc, ESI) = lxur.lxur_esi; - LX_REG(&uc, EDI) = lxur.lxur_edi; - LX_REG(&uc, EBP) = lxur.lxur_ebp; - LX_REG(&uc, EAX) = lxur.lxur_eax; - LX_REG(&uc, EIP) = lxur.lxur_eip; - LX_REG(&uc, EFL) = lxur.lxur_eflags; - LX_REG(&uc, UESP) = lxur.lxur_esp; - LX_REG(&uc, SS) = lxur.lxur_xss; - - LX_REG(&uc, DS) = lxur.lxur_xds; - LX_REG(&uc, ES) = lxur.lxur_xes; - LX_REG(&uc, FS) = lxur.lxur_xfs; - LX_REG(&uc, GS) = lxur.lxur_xgs; + lxrp->lxur_r15 = LX_REG(&uc, REG_R15); + lxrp->lxur_r14 = LX_REG(&uc, REG_R14); + lxrp->lxur_r13 = LX_REG(&uc, REG_R13); + lxrp->lxur_r12 = LX_REG(&uc, REG_R12); + lxrp->lxur_rbp = LX_REG(&uc, REG_RBP); + lxrp->lxur_rbx = LX_REG(&uc, REG_RBX); + lxrp->lxur_r11 = LX_REG(&uc, REG_R11); + lxrp->lxur_r10 = LX_REG(&uc, REG_R10); + lxrp->lxur_r9 = LX_REG(&uc, REG_R9); + lxrp->lxur_r8 = LX_REG(&uc, REG_R8); + lxrp->lxur_rax = LX_REG(&uc, REG_RAX); + lxrp->lxur_rcx = LX_REG(&uc, REG_RCX); + lxrp->lxur_rdx = LX_REG(&uc, REG_RDX); + lxrp->lxur_rsi = LX_REG(&uc, REG_RSI); + lxrp->lxur_rdi = LX_REG(&uc, REG_RDI); + lxrp->lxur_orig_rax = 0; + lxrp->lxur_rip = LX_REG(&uc, REG_RIP); + lxrp->lxur_rflags = LX_REG(&uc, REG_RFL); + lxrp->lxur_rsp = LX_REG(&uc, REG_RSP); + lxrp->lxur_xss = LX_REG(&uc, REG_SS); + lxrp->lxur_xfs_base = LX_REG(&uc, REG_FSBASE); + lxrp->lxur_xgs_base = LX_REG(&uc, REG_GSBASE); + + lxrp->lxur_xds = LX_REG(&uc, REG_DS); + lxrp->lxur_xes = LX_REG(&uc, REG_ES); + lxrp->lxur_xfs = LX_REG(&uc, REG_FS); + lxrp->lxur_xgs = LX_REG(&uc, REG_GS); + + /* emulated %cs, see defines */ + lxrp->lxur_xcs = LX_CS_64BIT; + return (0); + } - if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); + case DATAMODEL_ILP32: { + ucontext32_t uc; + + if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); } - return (EIO); + lxrp->lxur_r15 = 0; + lxrp->lxur_r14 = 0; + lxrp->lxur_r13 = 0; + lxrp->lxur_r12 = 0; + lxrp->lxur_r11 = 0; + lxrp->lxur_r10 = 0; + lxrp->lxur_r9 = 0; + lxrp->lxur_r8 = 0; + lxrp->lxur_rbp = LX_REG(&uc, EBP); + lxrp->lxur_rbx = LX_REG(&uc, EBX); + lxrp->lxur_rax = LX_REG(&uc, EAX); + lxrp->lxur_orig_rax = 0; + lxrp->lxur_rcx = LX_REG(&uc, ECX); + lxrp->lxur_rdx = LX_REG(&uc, EDX); + lxrp->lxur_rsi = LX_REG(&uc, ESI); + lxrp->lxur_rdi = LX_REG(&uc, EDI); + lxrp->lxur_rip = LX_REG(&uc, EIP); + + lxrp->lxur_rflags = LX_REG(&uc, EFL); + lxrp->lxur_rsp = LX_REG(&uc, UESP); + lxrp->lxur_xss = LX_REG(&uc, SS); + lxrp->lxur_xfs_base = 0; + lxrp->lxur_xgs_base = 0; + + lxrp->lxur_xds = LX_REG(&uc, DS); + lxrp->lxur_xes = LX_REG(&uc, ES); + lxrp->lxur_xfs = LX_REG(&uc, FS); + lxrp->lxur_xgs = LX_REG(&uc, GS); + + /* See comment above re: %cs register */ + lxrp->lxur_xcs = LX_CS_32BIT; + return (0); } default: - return (EIO); + break; } -#else - cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__); - exit(CLD_KILLED, SIGSYS); - return (EIO); -#endif /* __amd64 */ + + return (-1); } -/* - * Copy register state from a ucontext_t in the tracee to a usermode - * "lx_user_regs_t" in the tracer. - */ -int -lx_uc_to_userregs(lx_lwp_data_t *lwpd, void *ucp, void *uregsp) +static int +lx_get_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp) { -#if defined(__amd64) klwp_t *lwp = lwpd->br_lwp; - proc_t *p = lwptoproc(lwp); + struct regs *rp = lwptoregs(lwp); + struct pcb *pcb = &lwp->lwp_pcb; + void *ucp; - switch (get_udatamodel()) { - case DATAMODEL_LP64: { - lx_user_regs_t lxur; + switch (lx_regs_location(lwpd, &ucp, B_FALSE)) { + case LX_REG_LOC_UNAVAIL: + return (-1); - switch (lwp_getdatamodel(lwp)) { - case DATAMODEL_LP64: { - ucontext_t uc; + case LX_REG_LOC_UCP: + return (lx_get_user_regs64_uc(lwp, ucp, lxrp)); - if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); - } + case LX_REG_LOC_LWP: + /* transformation below */ + break; - lxur.lxur_r15 = LX_REG(&uc, REG_R15); - lxur.lxur_r14 = LX_REG(&uc, REG_R14); - lxur.lxur_r13 = LX_REG(&uc, REG_R13); - lxur.lxur_r12 = LX_REG(&uc, REG_R12); - lxur.lxur_rbp = LX_REG(&uc, REG_RBP); - lxur.lxur_rbx = LX_REG(&uc, REG_RBX); - lxur.lxur_r11 = LX_REG(&uc, REG_R11); - lxur.lxur_r10 = LX_REG(&uc, REG_R10); - lxur.lxur_r9 = LX_REG(&uc, REG_R9); - lxur.lxur_r8 = LX_REG(&uc, REG_R8); - lxur.lxur_rax = LX_REG(&uc, REG_RAX); - lxur.lxur_rcx = LX_REG(&uc, REG_RCX); - lxur.lxur_rdx = LX_REG(&uc, REG_RDX); - lxur.lxur_rsi = LX_REG(&uc, REG_RSI); - lxur.lxur_rdi = LX_REG(&uc, REG_RDI); - lxur.lxur_orig_rax = 0; - lxur.lxur_rip = LX_REG(&uc, REG_RIP); - /* - * strace on some releases (e.g. centos) uses the %cs - * value to determine what kind of process is being - * traced. Here is a sample comment: - * Check CS register value. On x86-64 linux it is: - * 0x33 for long mode (64 bit and x32)) - * 0x23 for compatibility mode (32 bit) - * %ds = 0x2b for x32 mode (x86-64 in 32 bit) - * We can't change the %cs value in the ucp (see - * setgregs and _sys_rtt) so we emulate the expected - * value for ptrace use. - */ - lxur.lxur_xcs = 0x33; - lxur.lxur_rflags = LX_REG(&uc, REG_RFL); - lxur.lxur_rsp = LX_REG(&uc, REG_RSP); - lxur.lxur_xss = LX_REG(&uc, REG_SS); - lxur.lxur_xfs_base = LX_REG(&uc, REG_FSBASE); - lxur.lxur_xgs_base = LX_REG(&uc, REG_GSBASE); - - lxur.lxur_xds = LX_REG(&uc, REG_DS); - lxur.lxur_xes = LX_REG(&uc, REG_ES); - lxur.lxur_xfs = LX_REG(&uc, REG_FS); - lxur.lxur_xgs = LX_REG(&uc, REG_GS); - - if (copyout(&lxur, uregsp, sizeof (lxur)) != 0) { - return (EFAULT); - } + default: + VERIFY(0); + break; + } - return (0); - } + lxrp->lxur_r15 = rp->r_r15; + lxrp->lxur_r14 = rp->r_r14; + lxrp->lxur_r13 = rp->r_r13; + lxrp->lxur_r12 = rp->r_r12; + lxrp->lxur_rbp = rp->r_rbp; + lxrp->lxur_rbx = rp->r_rbx; + lxrp->lxur_r11 = rp->r_r11; + lxrp->lxur_r10 = rp->r_r10; + lxrp->lxur_r9 = rp->r_r9; + lxrp->lxur_r8 = rp->r_r8; + lxrp->lxur_rax = rp->r_rax; + lxrp->lxur_rcx = rp->r_rcx; + lxrp->lxur_rdx = rp->r_rdx; + lxrp->lxur_rsi = rp->r_rsi; + lxrp->lxur_rdi = rp->r_rdi; + lxrp->lxur_orig_rax = 0; + lxrp->lxur_rip = rp->r_rip; + + lxrp->lxur_rflags = rp->r_rfl; + lxrp->lxur_rsp = rp->r_rsp; + lxrp->lxur_xss = rp->r_ss; + lxrp->lxur_xfs_base = pcb->pcb_fsbase; + lxrp->lxur_xgs_base = pcb->pcb_gsbase; + + /* emulated %cs, see defines */ + switch (lwp_getdatamodel(lwp)) { + case DATAMODEL_LP64: + lxrp->lxur_xcs = LX_CS_64BIT; + break; + case DATAMODEL_ILP32: + lxrp->lxur_xcs = LX_CS_32BIT; + break; + default: + VERIFY(0); + break; + } - case DATAMODEL_ILP32: { - ucontext32_t uc; + kpreempt_disable(); + if (pcb->pcb_rupdate == 1) { + lxrp->lxur_xds = pcb->pcb_ds; + lxrp->lxur_xes = pcb->pcb_es; + lxrp->lxur_xfs = pcb->pcb_fs; + lxrp->lxur_xgs = pcb->pcb_gs; + } else { + lxrp->lxur_xds = rp->r_ds; + lxrp->lxur_xes = rp->r_es; + lxrp->lxur_xfs = rp->r_fs; + lxrp->lxur_xgs = rp->r_gs; + } + kpreempt_enable(); - if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); - } + if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) { + lxrp->lxur_rax = -lx_errno(ENOTSUP, EINVAL); + lxrp->lxur_orig_rax = lwpd->br_syscall_num; + } else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) { + lxrp->lxur_orig_rax = lwpd->br_syscall_num; + } - lxur.lxur_r15 = 0; - lxur.lxur_r14 = 0; - lxur.lxur_r13 = 0; - lxur.lxur_r12 = 0; - lxur.lxur_rbp = LX_REG(&uc, EBP); - lxur.lxur_rbx = LX_REG(&uc, EBX); - lxur.lxur_r11 = 0; - lxur.lxur_r10 = 0; - lxur.lxur_r9 = 0; - lxur.lxur_r8 = 0; - lxur.lxur_rax = LX_REG(&uc, EAX); - lxur.lxur_rcx = LX_REG(&uc, ECX); - lxur.lxur_rdx = LX_REG(&uc, EDX); - lxur.lxur_rsi = LX_REG(&uc, ESI); - lxur.lxur_rdi = LX_REG(&uc, EDI); - lxur.lxur_orig_rax = 0; - lxur.lxur_rip = LX_REG(&uc, EIP); - /* See comment above re: %cs register */ - lxur.lxur_xcs = 0x23; - lxur.lxur_rflags = LX_REG(&uc, EFL); - lxur.lxur_rsp = LX_REG(&uc, UESP); - lxur.lxur_xss = LX_REG(&uc, SS); - lxur.lxur_xfs_base = 0; - lxur.lxur_xgs_base = 0; - - lxur.lxur_xds = LX_REG(&uc, DS); - lxur.lxur_xes = LX_REG(&uc, ES); - lxur.lxur_xfs = LX_REG(&uc, FS); - lxur.lxur_xgs = LX_REG(&uc, GS); - - if (copyout(&lxur, uregsp, sizeof (lxur)) != 0) { - return (EFAULT); - } + return (0); +} - return (0); +static int +lx_set_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp) +{ + proc_t *p = lwptoproc(lwp); + + switch (lwp_getdatamodel(lwp)) { + case DATAMODEL_LP64: { + ucontext_t uc; + + if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); } - default: - return (EIO); + /* + * Note: we currently ignore "lxur_orig_rax" here since this + * path should not be used for system call stops. + */ + LX_REG(&uc, REG_R15) = lxrp->lxur_r15; + LX_REG(&uc, REG_R14) = lxrp->lxur_r14; + LX_REG(&uc, REG_R13) = lxrp->lxur_r13; + LX_REG(&uc, REG_R12) = lxrp->lxur_r12; + LX_REG(&uc, REG_RBP) = lxrp->lxur_rbp; + LX_REG(&uc, REG_RBX) = lxrp->lxur_rbx; + LX_REG(&uc, REG_R11) = lxrp->lxur_r11; + LX_REG(&uc, REG_R10) = lxrp->lxur_r10; + LX_REG(&uc, REG_R9) = lxrp->lxur_r9; + LX_REG(&uc, REG_R8) = lxrp->lxur_r8; + LX_REG(&uc, REG_RAX) = lxrp->lxur_rax; + LX_REG(&uc, REG_RCX) = lxrp->lxur_rcx; + LX_REG(&uc, REG_RDX) = lxrp->lxur_rdx; + LX_REG(&uc, REG_RSI) = lxrp->lxur_rsi; + LX_REG(&uc, REG_RDI) = lxrp->lxur_rdi; + LX_REG(&uc, REG_RIP) = lxrp->lxur_rip; + LX_REG(&uc, REG_RFL) = lxrp->lxur_rflags; + LX_REG(&uc, REG_RSP) = lxrp->lxur_rsp; + LX_REG(&uc, REG_SS) = lxrp->lxur_xss; + LX_REG(&uc, REG_FSBASE) = lxrp->lxur_xfs_base; + LX_REG(&uc, REG_GSBASE) = lxrp->lxur_xgs_base; + + /* %cs is ignored because of our lies */ + LX_REG(&uc, REG_DS) = lxrp->lxur_xds; + LX_REG(&uc, REG_ES) = lxrp->lxur_xes; + LX_REG(&uc, REG_FS) = lxrp->lxur_xfs; + LX_REG(&uc, REG_GS) = lxrp->lxur_xgs; + + if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); } + + return (0); } case DATAMODEL_ILP32: { - lx_user_regs32_t lxur; ucontext32_t uc; - if (lwp_getdatamodel(lwp) != DATAMODEL_ILP32) { - /* - * The target is not a 32-bit LWP. We refuse to - * present truncated 64-bit registers to a 32-bit - * tracer. - */ - return (EIO); - } - if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) { - return (EIO); + return (-1); } - lxur.lxur_ebx = LX_REG(&uc, EBX); - lxur.lxur_ecx = LX_REG(&uc, ECX); - lxur.lxur_edx = LX_REG(&uc, EDX); - lxur.lxur_esi = LX_REG(&uc, ESI); - lxur.lxur_edi = LX_REG(&uc, EDI); - lxur.lxur_ebp = LX_REG(&uc, EBP); - lxur.lxur_eax = LX_REG(&uc, EAX); - lxur.lxur_orig_eax = 0; - lxur.lxur_eip = LX_REG(&uc, EIP); - /* See comment above re: %cs register */ - lxur.lxur_xcs = 0x23; - lxur.lxur_eflags = LX_REG(&uc, EFL); - lxur.lxur_esp = LX_REG(&uc, UESP); - lxur.lxur_xss = LX_REG(&uc, SS); - - lxur.lxur_xds = LX_REG(&uc, DS); - lxur.lxur_xes = LX_REG(&uc, ES); - lxur.lxur_xfs = LX_REG(&uc, FS); - lxur.lxur_xgs = LX_REG(&uc, GS); - - if (copyout(&lxur, uregsp, sizeof (lxur)) != 0) { - return (EFAULT); - } + /* + * Note: we currently ignore "lxur_orig_rax" here since this + * path should not be used for system call stops. + */ + LX_REG(&uc, EBP) = (int32_t)lxrp->lxur_rbp; + LX_REG(&uc, EBX) = (int32_t)lxrp->lxur_rbx; + LX_REG(&uc, EAX) = (int32_t)lxrp->lxur_rax; + LX_REG(&uc, ECX) = (int32_t)lxrp->lxur_rcx; + LX_REG(&uc, EDX) = (int32_t)lxrp->lxur_rdx; + LX_REG(&uc, ESI) = (int32_t)lxrp->lxur_rsi; + LX_REG(&uc, EDI) = (int32_t)lxrp->lxur_rdi; + LX_REG(&uc, EIP) = (int32_t)lxrp->lxur_rip; + LX_REG(&uc, EFL) = (int32_t)lxrp->lxur_rflags; + LX_REG(&uc, UESP) = (int32_t)lxrp->lxur_rsp; + LX_REG(&uc, SS) = (int32_t)lxrp->lxur_xss; + + /* %cs is ignored because of our lies */ + LX_REG(&uc, DS) = (int32_t)lxrp->lxur_xds; + LX_REG(&uc, ES) = (int32_t)lxrp->lxur_xes; + LX_REG(&uc, FS) = (int32_t)lxrp->lxur_xfs; + LX_REG(&uc, GS) = (int32_t)lxrp->lxur_xgs; + if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) { + return (-1); + } return (0); } default: - return (EIO); + break; } -#else - cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__); - exit(CLD_KILLED, SIGSYS); - return (EIO); -#endif + + return (-1); } -/* - * Load a usermode "lx_user_regs_t" into the register state of the target LWP. - */ -int -lx_userregs_to_regs(lx_lwp_data_t *lwpd, void *uregsp) +static int +lx_set_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp) { klwp_t *lwp = lwpd->br_lwp; - proc_t *p = lwptoproc(lwp); - - VERIFY(MUTEX_HELD(&p->p_lock)); - -#if defined(__amd64) struct regs *rp = lwptoregs(lwp); struct pcb *pcb = &lwp->lwp_pcb; + void *ucp; - switch (get_udatamodel()) { - case DATAMODEL_LP64: { - lx_user_regs_t lxur; + switch (lx_regs_location(lwpd, &ucp, B_TRUE)) { + case LX_REG_LOC_UNAVAIL: + return (-1); - if (copyin(uregsp, &lxur, sizeof (lxur)) != 0) { - return (EFAULT); - } + case LX_REG_LOC_UCP: + return (lx_set_user_regs64_uc(lwp, ucp, lxrp)); + + case LX_REG_LOC_LWP: + /* transformation below */ + break; - rp->r_r15 = lxur.lxur_r15; - rp->r_r14 = lxur.lxur_r14; - rp->r_r13 = lxur.lxur_r13; - rp->r_r12 = lxur.lxur_r12; - rp->r_rbp = lxur.lxur_rbp; - rp->r_rbx = lxur.lxur_rbx; - rp->r_r11 = lxur.lxur_r11; - rp->r_r10 = lxur.lxur_r10; - rp->r_r9 = lxur.lxur_r9; - rp->r_r8 = lxur.lxur_r8; - rp->r_rax = lxur.lxur_rax; - rp->r_rcx = lxur.lxur_rcx; - rp->r_rdx = lxur.lxur_rdx; - rp->r_rsi = lxur.lxur_rsi; - rp->r_rdi = lxur.lxur_rdi; - lwpd->br_syscall_num = (int)lxur.lxur_orig_rax; - rp->r_rip = lxur.lxur_rip; - rp->r_rfl = lxur.lxur_rflags; - rp->r_rsp = lxur.lxur_rsp; - rp->r_ss = lxur.lxur_xss; - pcb->pcb_fsbase = lxur.lxur_xfs_base; - pcb->pcb_gsbase = lxur.lxur_xgs_base; + default: + VERIFY(0); + break; + } - kpreempt_disable(); - pcb->pcb_rupdate = 1; - pcb->pcb_ds = lxur.lxur_xds; - pcb->pcb_es = lxur.lxur_xes; - pcb->pcb_fs = lxur.lxur_xfs; - pcb->pcb_gs = lxur.lxur_xgs; - kpreempt_enable(); + rp->r_r15 = lxrp->lxur_r15; + rp->r_r14 = lxrp->lxur_r14; + rp->r_r13 = lxrp->lxur_r13; + rp->r_r12 = lxrp->lxur_r12; + rp->r_rbp = lxrp->lxur_rbp; + rp->r_rbx = lxrp->lxur_rbx; + rp->r_r11 = lxrp->lxur_r11; + rp->r_r10 = lxrp->lxur_r10; + rp->r_r9 = lxrp->lxur_r9; + rp->r_r8 = lxrp->lxur_r8; + rp->r_rax = lxrp->lxur_rax; + rp->r_rcx = lxrp->lxur_rcx; + rp->r_rdx = lxrp->lxur_rdx; + rp->r_rsi = lxrp->lxur_rsi; + rp->r_rdi = lxrp->lxur_rdi; + lwpd->br_syscall_num = (int)lxrp->lxur_orig_rax; + rp->r_rip = lxrp->lxur_rip; + rp->r_rfl = lxrp->lxur_rflags; + rp->r_rsp = lxrp->lxur_rsp; + rp->r_ss = lxrp->lxur_xss; + pcb->pcb_fsbase = lxrp->lxur_xfs_base; + pcb->pcb_gsbase = lxrp->lxur_xgs_base; + + kpreempt_disable(); + pcb->pcb_rupdate = 1; + pcb->pcb_ds = lxrp->lxur_xds; + pcb->pcb_es = lxrp->lxur_xes; + pcb->pcb_fs = lxrp->lxur_xfs; + pcb->pcb_gs = lxrp->lxur_xgs; + kpreempt_enable(); + + return (0); +} + +#endif /* __amd64 */ +static int +lx_peekuser32(lx_lwp_data_t *lwpd, uintptr_t offset, uint32_t *res) +{ + lx_user32_t lxu; + boolean_t valid = B_FALSE; + + bzero(&lxu, sizeof (lxu)); + if (offset < sizeof (lx_user_regs32_t)) { + if (lx_get_user_regs32(lwpd, &lxu.lxu_regs) == 0) { + valid = B_TRUE; + } + } + if (valid) { + uint32_t *data = (uint32_t *)&lxu; + *res = data[offset / sizeof (uint32_t)]; return (0); } + return (-1); +} - case DATAMODEL_ILP32: { - lx_user_regs32_t lxur; - - if (lwp_getdatamodel(lwp) != DATAMODEL_ILP32) { - /* - * The target is not a 32-bit LWP. We refuse to - * present truncated 64-bit registers to a 32-bit - * tracer. - */ - return (EIO); +#ifdef __amd64 +static int +lx_peekuser64(lx_lwp_data_t *lwpd, uintptr_t offset, uintptr_t *res) +{ + lx_user64_t lxu; + boolean_t valid = B_FALSE; + + bzero(&lxu, sizeof (lxu)); + if (offset < sizeof (lx_user_regs64_t)) { + lx_user_regs64_t regs; + if (lx_get_user_regs64(lwpd, ®s) == 0) { + valid = B_TRUE; } + } + if (valid) { + uintptr_t *data = (uintptr_t *)&lxu; + *res = data[offset / sizeof (uintptr_t)]; + return (0); + } + return (-1); +} +#endif /* __amd64 */ + +int +lx_user_regs_copyin(lx_lwp_data_t *lwpd, void *uregsp) +{ + model_t target_model = lwp_getdatamodel(lwpd->br_lwp); + + switch (get_udatamodel()) { + case DATAMODEL_ILP32: + if (target_model == DATAMODEL_ILP32) { + lx_user_regs32_t regs; - if (copyin(uregsp, &lxur, sizeof (lxur)) != 0) { - return (EFAULT); + if (copyin(uregsp, ®s, sizeof (regs)) != 0) { + return (EFAULT); + } + if (lx_set_user_regs32(lwpd, ®s) != 0) { + return (EIO); + } + return (0); } - rp->r_rbx = lxur.lxur_ebx; - rp->r_rcx = lxur.lxur_ecx; - rp->r_rdx = lxur.lxur_edx; - rp->r_rsi = lxur.lxur_esi; - rp->r_rdi = lxur.lxur_edi; - rp->r_rbp = lxur.lxur_ebp; - rp->r_rax = lxur.lxur_eax; - lwpd->br_syscall_num = (int)lxur.lxur_orig_eax; - rp->r_rip = lxur.lxur_eip; - rp->r_rfl = lxur.lxur_eflags; - rp->r_rsp = lxur.lxur_esp; - rp->r_ss = lxur.lxur_xss; +#ifdef __amd64 + case DATAMODEL_LP64: + if (target_model == DATAMODEL_ILP32 || + target_model == DATAMODEL_LP64) { + lx_user_regs64_t regs; - kpreempt_disable(); - pcb->pcb_rupdate = 1; - pcb->pcb_ds = lxur.lxur_xds; - pcb->pcb_es = lxur.lxur_xes; - pcb->pcb_fs = lxur.lxur_xfs; - pcb->pcb_gs = lxur.lxur_xgs; - kpreempt_enable(); + if (copyin(uregsp, ®s, sizeof (regs)) != 0) { + return (EFAULT); + } + if (lx_set_user_regs64(lwpd, ®s) != 0) { + return (EIO); + } + return (0); + } + break; +#endif /* __amd64 */ - return (0); + default: + break; } + return (EIO); +} + +int +lx_user_regs_copyout(lx_lwp_data_t *lwpd, void *uregsp) +{ + model_t target_model = lwp_getdatamodel(lwpd->br_lwp); + + switch (get_udatamodel()) { + case DATAMODEL_ILP32: + if (target_model == DATAMODEL_ILP32) { + lx_user_regs32_t regs; + + if (lx_get_user_regs32(lwpd, ®s) != 0) { + return (EIO); + } + if (copyout(®s, uregsp, sizeof (regs)) != 0) { + return (EFAULT); + } + return (0); + } + +#ifdef __amd64 + case DATAMODEL_LP64: + if (target_model == DATAMODEL_ILP32 || + target_model == DATAMODEL_LP64) { + lx_user_regs64_t regs; + + if (lx_get_user_regs64(lwpd, ®s) != 0) { + return (EIO); + } + if (copyout(®s, uregsp, sizeof (regs)) != 0) { + return (EFAULT); + } + return (0); + } + break; +#endif /* __amd64 */ default: - return (EIO); + break; } -#else - cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__); - exit(CLD_KILLED, SIGSYS); return (EIO); -#endif /* __amd64 */ } -/* - * Copy the current LWP register state of the target LWP to a usermode - * "lx_user_regs_t". - */ int -lx_regs_to_userregs(lx_lwp_data_t *lwpd, void *uregsp) +lx_user_fpregs_copyin(lx_lwp_data_t *lwpd, void *uregsp) { -#if defined(__amd64) - klwp_t *lwp = lwpd->br_lwp; - struct regs *rp = lwptoregs(lwp); - proc_t *p = lwptoproc(lwp); + model_t target_model = lwp_getdatamodel(lwpd->br_lwp); - VERIFY(MUTEX_HELD(&p->p_lock)); + switch (get_udatamodel()) { + case DATAMODEL_ILP32: + if (target_model == DATAMODEL_ILP32) { + lx_user_fpregs32_t regs; - struct pcb *pcb = &lwp->lwp_pcb; - long r0, orig_r0; + if (copyin(uregsp, ®s, sizeof (regs)) != 0) { + return (EFAULT); + } + lx_setfpregs32(lwpd, ®s); + return (0); + } - /* - * We must precisely emulate the "syscall-entry-stop" and - * "syscall-exit-stop" register appearance from the Linux kernel. - */ - switch (lwpd->br_ptrace_whatstop) { - case LX_PR_SYSENTRY: - orig_r0 = lwpd->br_syscall_num; - r0 = -lx_errno(ENOTSUP, EINVAL); - break; - case LX_PR_SYSEXIT: - orig_r0 = lwpd->br_syscall_num; - r0 = rp->r_rax; +#ifdef __amd64 + case DATAMODEL_LP64: + if (target_model == DATAMODEL_ILP32 || + target_model == DATAMODEL_LP64) { + lx_user_fpregs64_t regs; + + if (copyin(uregsp, ®s, sizeof (regs)) != 0) { + return (EFAULT); + } + lx_setfpregs64(lwpd, ®s); + return (0); + } break; +#endif /* __amd64 */ + default: - orig_r0 = 0; - r0 = rp->r_rax; + break; } + return (EIO); +} + +int +lx_user_fpregs_copyout(lx_lwp_data_t *lwpd, void *uregsp) +{ + model_t target_model = lwp_getdatamodel(lwpd->br_lwp); switch (get_udatamodel()) { - case DATAMODEL_LP64: { - lx_user_regs_t lxur; - - lxur.lxur_r15 = rp->r_r15; - lxur.lxur_r14 = rp->r_r14; - lxur.lxur_r13 = rp->r_r13; - lxur.lxur_r12 = rp->r_r12; - lxur.lxur_rbp = rp->r_rbp; - lxur.lxur_rbx = rp->r_rbx; - lxur.lxur_r11 = rp->r_r11; - lxur.lxur_r10 = rp->r_r10; - lxur.lxur_r9 = rp->r_r9; - lxur.lxur_r8 = rp->r_r8; - lxur.lxur_rax = r0; - lxur.lxur_rcx = rp->r_rcx; - lxur.lxur_rdx = rp->r_rdx; - lxur.lxur_rsi = rp->r_rsi; - lxur.lxur_rdi = rp->r_rdi; - lxur.lxur_orig_rax = orig_r0; - lxur.lxur_rip = rp->r_rip; - /* - * strace on some releases (e.g. centos) uses the %cs value to - * determine what kind of process is being traced. Here is a - * sample comment: - * Check CS register value. On x86-64 linux it is: - * 0x33 for long mode (64 bit and x32)) - * 0x23 for compatibility mode (32 bit) - * %ds = 0x2b for x32 mode (x86-64 in 32 bit) - * We can't change the %cs value in the ucp (see setgregs and - * _sys_rtt) so we emulate the expected value for ptrace use. - */ - if (lwp_getdatamodel(lwp) == DATAMODEL_ILP32) { - lxur.lxur_xcs = 0x23; - } else { - lxur.lxur_xcs = 0x33; - } - lxur.lxur_rflags = rp->r_rfl; - lxur.lxur_rsp = rp->r_rsp; - lxur.lxur_xss = rp->r_ss; - lxur.lxur_xfs_base = pcb->pcb_fsbase; - lxur.lxur_xgs_base = pcb->pcb_gsbase; + case DATAMODEL_ILP32: + if (target_model == DATAMODEL_ILP32) { + lx_user_fpregs32_t regs; - kpreempt_disable(); - if (pcb->pcb_rupdate == 1) { - lxur.lxur_xds = pcb->pcb_ds; - lxur.lxur_xes = pcb->pcb_es; - lxur.lxur_xfs = pcb->pcb_fs; - lxur.lxur_xgs = pcb->pcb_gs; - } else { - lxur.lxur_xds = rp->r_ds; - lxur.lxur_xes = rp->r_es; - lxur.lxur_xfs = rp->r_fs; - lxur.lxur_xgs = rp->r_gs; + lx_getfpregs32(lwpd, ®s); + if (copyout(®s, uregsp, sizeof (regs)) != 0) { + return (EFAULT); + } + return (0); } - kpreempt_enable(); - if (copyout(&lxur, uregsp, sizeof (lxur)) != 0) { - return (EFAULT); +#ifdef __amd64 + case DATAMODEL_LP64: + if (target_model == DATAMODEL_ILP32 || + target_model == DATAMODEL_LP64) { + lx_user_fpregs64_t regs; + + lx_getfpregs64(lwpd, ®s); + if (copyout(®s, uregsp, sizeof (regs)) != 0) { + return (EFAULT); + } + return (0); } + break; +#endif /* __amd64 */ - return (0); + default: + break; } + return (EIO); +} - case DATAMODEL_ILP32: { - lx_user_regs32_t lxur; +int +lx_user_fpxregs_copyin(lx_lwp_data_t *lwpd, void *uregsp) +{ + /* Punt on fpxregs for now */ + return (EIO); +} - if (lwp_getdatamodel(lwp) != DATAMODEL_ILP32) { - /* - * The target is not a 32-bit LWP. We refuse to - * present truncated 64-bit registers to a 32-bit - * tracer. - */ - return (EIO); - } +int +lx_user_fpxregs_copyout(lx_lwp_data_t *lwpd, void *uregsp) +{ + /* Punt on fpxregs for now */ + return (EIO); +} - lxur.lxur_ebx = (int32_t)rp->r_rbx; - lxur.lxur_ecx = (int32_t)rp->r_rcx; - lxur.lxur_edx = (int32_t)rp->r_rdx; - lxur.lxur_esi = (int32_t)rp->r_rsi; - lxur.lxur_edi = (int32_t)rp->r_rdi; - lxur.lxur_ebp = (int32_t)rp->r_rbp; - lxur.lxur_eax = (int32_t)r0; - lxur.lxur_orig_eax = (int32_t)orig_r0; - lxur.lxur_eip = (int32_t)rp->r_rip; - /* See comment above for 64-bit datamodel */ - lxur.lxur_xcs = 0x23; - lxur.lxur_eflags = (int32_t)rp->r_rfl; - lxur.lxur_esp = (int32_t)rp->r_rsp; - lxur.lxur_xss = (int32_t)rp->r_ss; +int +lx_ptrace_peekuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr) +{ + model_t target_model = lwp_getdatamodel(lwpd->br_lwp); - kpreempt_disable(); - if (pcb->pcb_rupdate == 1) { - lxur.lxur_xds = pcb->pcb_ds; - lxur.lxur_xes = pcb->pcb_es; - lxur.lxur_xfs = pcb->pcb_fs; - lxur.lxur_xgs = pcb->pcb_gs; - } else { - lxur.lxur_xds = rp->r_ds; - lxur.lxur_xes = rp->r_es; - lxur.lxur_xfs = rp->r_fs; - lxur.lxur_xgs = rp->r_gs; + switch (get_udatamodel()) { + case DATAMODEL_ILP32: + if ((offset & (sizeof (uint32_t) - 1)) != 0) { + /* Must be aligned to 32bit boundary */ + break; } - kpreempt_enable(); + if (target_model == DATAMODEL_ILP32) { + uint32_t res; - if (copyout(&lxur, uregsp, sizeof (lxur)) != 0) { - return (EFAULT); + if (lx_peekuser32(lwpd, offset, &res) != 0) { + return (EIO); + } + if (copyout(&res, uptr, sizeof (res)) != 0) { + return (EFAULT); + } + return (0); } - return (0); - } +#ifdef __amd64 + case DATAMODEL_LP64: + if ((offset & (sizeof (uintptr_t) - 1)) != 0) { + /* Must be aligned to 64bit boundary */ + break; + } + if (target_model == DATAMODEL_ILP32 || + target_model == DATAMODEL_LP64) { + uintptr_t res; + + if (lx_peekuser64(lwpd, offset, &res) != 0) { + return (EIO); + } + if (copyout(&res, uptr, sizeof (res)) != 0) { + return (EFAULT); + } + return (0); + } + break; +#endif /* __amd64 */ default: - return (EIO); + break; } -#else - cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__); - exit(CLD_KILLED, SIGSYS); return (EIO); -#endif /* __amd64 */ } +int +lx_ptrace_pokeuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr) +{ + return (EIO); +} + + /* * Load registers and repoint the stack and program counter. This function is * used by the B_JUMP_TO_LINUX brand system call to revector to a Linux |