Index: sys/amd64/linux/linux_ptrace.c =================================================================== --- sys/amd64/linux/linux_ptrace.c +++ sys/amd64/linux/linux_ptrace.c @@ -131,6 +131,36 @@ l_ulong ss; }; +struct linux_pt_regset { + l_ulong r15; + l_ulong r14; + l_ulong r13; + l_ulong r12; + l_ulong rbp; + l_ulong rbx; + l_ulong r11; + l_ulong r10; + l_ulong r9; + l_ulong r8; + l_ulong rax; + l_ulong rcx; + l_ulong rdx; + l_ulong rsi; + l_ulong rdi; + l_ulong orig_rax; + l_ulong rip; + l_ulong cs; + l_ulong eflags; + l_ulong rsp; + l_ulong ss; + l_ulong fs_base; + l_ulong gs_base; + l_ulong ds; + l_ulong es; + l_ulong fs; + l_ulong gs; +}; + /* * Translate amd64 ptrace registers between Linux and FreeBSD formats. * The translation is pretty straighforward, for all registers but @@ -164,6 +194,40 @@ } static void +map_regs_to_linux_regset(struct reg *b_reg, unsigned long fs_base, + unsigned long gs_base, struct linux_pt_regset *l_regset) +{ + + l_regset->r15 = b_reg->r_r15; + l_regset->r14 = b_reg->r_r14; + l_regset->r13 = b_reg->r_r13; + l_regset->r12 = b_reg->r_r12; + l_regset->rbp = b_reg->r_rbp; + l_regset->rbx = b_reg->r_rbx; + l_regset->r11 = b_reg->r_r11; + l_regset->r10 = b_reg->r_r10; + l_regset->r9 = b_reg->r_r9; + l_regset->r8 = b_reg->r_r8; + l_regset->rax = b_reg->r_rax; + l_regset->rcx = b_reg->r_rcx; + l_regset->rdx = b_reg->r_rdx; + l_regset->rsi = b_reg->r_rsi; + l_regset->rdi = b_reg->r_rdi; + l_regset->orig_rax = b_reg->r_rax; + l_regset->rip = b_reg->r_rip; + l_regset->cs = b_reg->r_cs; + l_regset->eflags = b_reg->r_rflags; + l_regset->rsp = b_reg->r_rsp; + l_regset->ss = b_reg->r_ss; + l_regset->fs_base = fs_base; + l_regset->gs_base = gs_base; + l_regset->ds = b_reg->r_ds; + l_regset->es = b_reg->r_es; + l_regset->fs = b_reg->r_fs; + l_regset->gs = b_reg->r_gs; +} + +static void map_regs_from_linux(struct reg *b_reg, struct linux_pt_reg *l_reg) { b_reg->r_r15 = l_reg->r15; @@ -306,14 +370,75 @@ } static int +linux_ptrace_getregset_prstatus(struct thread *td, pid_t pid, l_ulong data) +{ + struct ptrace_lwpinfo lwpinfo; + struct reg b_reg; + struct linux_pt_regset l_regset; + struct iovec iov; + struct pcb *pcb; + unsigned long fsbase, gsbase; + size_t len; + int error; + + error = copyin((const void *)data, &iov, sizeof(iov)); + if (error != 0) { + printf("%s: copyin error %d\n", __func__, error); + return (error); + } + + error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); + if (error != 0) + return (error); + + pcb = td->td_pcb; + if (td == curthread) + update_pcb_bases(pcb); + fsbase = pcb->pcb_fsbase; + gsbase = pcb->pcb_gsbase; + + map_regs_to_linux_regset(&b_reg, fsbase, gsbase, &l_regset); + + /* + * The strace(1) utility depends on RAX being set to -ENOSYS + * on syscall entry; otherwise it loops printing those: + * + * [ Process PID=928 runs in 64 bit mode. ] + * [ Process PID=928 runs in x32 mode. ] + */ + error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); + if (error != 0) { + printf("%s: PT_LWPINFO failed with error %d\n", + __func__, error); + return (error); + } + if (lwpinfo.pl_flags & PL_FLAG_SCE) + l_regset.rax = -38; // XXX: Don't hardcode? + + len = MIN(iov.iov_len, sizeof(l_regset)); + error = copyout(&l_regset, (void *)iov.iov_base, len); + if (error != 0) { + printf("%s: copyout error %d\n", __func__, error); + return (error); + } + + iov.iov_len -= len; + error = copyout(&iov, (void *)data, sizeof(iov)); + if (error != 0) { + printf("%s: iov copyout error %d\n", __func__, error); + return (error); + } + + return (error); +} + +static int linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { switch (addr) { case LINUX_NT_PRSTATUS: - printf("%s: NT_PRSTATUS not implemented; returning EINVAL\n", - __func__); - return (EINVAL); + return (linux_ptrace_getregset_prstatus(td, pid, data)); default: printf("%s: PTRACE_GETREGSET request %ld not implemented; " "returning EINVAL\n", __func__, addr);