Page MenuHomeFreeBSD

D19831.id101539.diff
No OneTemporary

D19831.id101539.diff

Index: lib/libc/sys/ptrace.2
===================================================================
--- lib/libc/sys/ptrace.2
+++ lib/libc/sys/ptrace.2
@@ -2,7 +2,7 @@
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
.\"
.\" This file is in the public domain.
-.Dd May 20, 2021
+.Dd Jan ??, 2022
.Dt PTRACE 2
.Os
.Sh NAME
@@ -462,6 +462,40 @@
.In machine/reg.h )
pointed to by
.Fa addr .
+.It Dv PT_GETREGSET
+This request reads the traced process's machine registers.
+The
+.Fa data
+argument specifies the register set to read, with the
+.Fa addr
+argument pointing at a
+.Vt "struct iovec"
+where the
+.Va iov_base
+field points to a register set specific structure to hold the registers,
+and the
+.Va iov_len
+field holds the length of the structure.
+.It Dv PT_SETREGSET
+This request writes to the traced process's machine registers.
+The
+.Fa data
+argument specifies the register set to write to, with the
+.Fa addr
+argument pointing at a
+.Vt "struct iovec"
+where the
+.Va iov_base
+field points to a register set specific structure to hold the registers,
+and the
+.Va iov_len
+field holds the length of the structure.
+If
+.Va iov_base
+is NULL the kernel will return the expected length of the register set
+specific structure in the
+.Va iov_len
+field and not change the target register set.
.It Dv PT_LWPINFO
This request can be used to obtain information about the kernel thread,
also known as light-weight process, that caused the traced process to stop.
Index: sys/amd64/amd64/elf_machdep.c
===================================================================
--- sys/amd64/amd64/elf_machdep.c
+++ sys/amd64/amd64/elf_machdep.c
@@ -35,6 +35,7 @@
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
+#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
@@ -95,6 +96,8 @@
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval = x86_set_fork_retval,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
struct sysentvec elf64_freebsd_sysvec_la57 = {
@@ -137,6 +140,8 @@
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval= x86_set_fork_retval,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
static void
Index: sys/arm/arm/elf_machdep.c
===================================================================
--- sys/arm/arm/elf_machdep.c
+++ sys/arm/arm/elf_machdep.c
@@ -34,6 +34,7 @@
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/linker.h>
+#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/proc.h>
@@ -102,6 +103,8 @@
.sv_hwcap2 = &elf_hwcap2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
Index: sys/arm64/arm64/elf32_machdep.c
===================================================================
--- sys/arm64/arm64/elf32_machdep.c
+++ sys/arm64/arm64/elf32_machdep.c
@@ -44,6 +44,7 @@
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
+#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
@@ -119,6 +120,8 @@
.sv_hwcap2 = &elf32_hwcap2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
Index: sys/arm64/arm64/elf_machdep.c
===================================================================
--- sys/arm64/arm64/elf_machdep.c
+++ sys/arm64/arm64/elf_machdep.c
@@ -41,6 +41,7 @@
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
+#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/syscall.h>
@@ -98,6 +99,8 @@
.sv_hwcap2 = &elf_hwcap2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec);
Index: sys/compat/freebsd32/freebsd32_misc.c
===================================================================
--- sys/compat/freebsd32/freebsd32_misc.c
+++ sys/compat/freebsd32/freebsd32_misc.c
@@ -964,6 +964,7 @@
struct dbreg32 dbreg;
struct fpreg32 fpreg;
struct reg32 reg;
+ struct iovec vec;
register_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret psr;
int ptevents;
@@ -975,6 +976,7 @@
struct ptrace_coredump32 pc;
uint32_t args[nitems(td->td_sa.args)];
struct ptrace_sc_ret32 psr;
+ struct iovec32 vec;
} r32;
void *addr;
int data, error = 0, i;
@@ -1020,6 +1022,22 @@
case PT_SETDBREGS:
error = copyin(uap->addr, &r.dbreg, sizeof(r.dbreg));
break;
+ case PT_SETREGSET:
+ error = copyin(uap->addr, &r32.vec, sizeof(r32.vec));
+ if (error != 0)
+ break;
+
+ r.vec.iov_len = r32.vec.iov_len;
+ r.vec.iov_base = PTRIN(r32.vec.iov_base);
+ break;
+ case PT_GETREGSET:
+ error = copyin(uap->addr, &r32.vec, sizeof(r32.vec));
+ if (error != 0)
+ break;
+
+ r.vec.iov_len = r32.vec.iov_len;
+ r.vec.iov_base = PTRIN(r32.vec.iov_base);
+ break;
case PT_SET_EVENT_MASK:
if (uap->data != sizeof(r.ptevents))
error = EINVAL;
@@ -1098,6 +1116,10 @@
case PT_GETDBREGS:
error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg));
break;
+ case PT_GETREGSET:
+ r32.vec.iov_len = r.vec.iov_len;
+ error = copyout(&r32.vec, uap->addr, sizeof(r32.vec));
+ break;
case PT_GET_EVENT_MASK:
/* NB: The size in uap->data is validated in kern_ptrace(). */
error = copyout(&r.ptevents, uap->addr, uap->data);
Index: sys/compat/ia32/ia32_sysvec.c
===================================================================
--- sys/compat/ia32/ia32_sysvec.c
+++ sys/compat/ia32/ia32_sysvec.c
@@ -33,6 +33,7 @@
#define __ELF_WORD_SIZE 32
#include <sys/param.h>
+#include <sys/elf.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
#include <sys/imgact.h>
@@ -44,6 +45,7 @@
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/procfs.h>
+#include <sys/reg.h>
#include <sys/resourcevar.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
@@ -141,6 +143,8 @@
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval = x86_set_fork_retval,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec);
Index: sys/i386/i386/elf_machdep.c
===================================================================
--- sys/i386/i386/elf_machdep.c
+++ sys/i386/i386/elf_machdep.c
@@ -36,6 +36,7 @@
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/linker.h>
+#include <sys/reg.h>
#include <sys/proc.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
@@ -89,6 +90,8 @@
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
.sv_set_fork_retval = x86_set_fork_retval,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
Index: sys/kern/imgact_elf.c
===================================================================
--- sys/kern/imgact_elf.c
+++ sys/kern/imgact_elf.c
@@ -2179,16 +2179,16 @@
*sizep = sizeof(*psinfo);
}
-static void
-__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
+static bool
+__elfN(get_prstatus)(struct regset *rs, struct thread *td, void *buf,
+ size_t *sizep)
{
- struct thread *td;
elf_prstatus_t *status;
- td = arg;
- if (sb != NULL) {
- KASSERT(*sizep == sizeof(*status), ("invalid size"));
- status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
+ if (buf != NULL) {
+ KASSERT(*sizep == sizeof(*status), ("%s: invalid size",
+ __func__));
+ status = buf;
status->pr_version = PRSTATUS_VERSION;
status->pr_statussz = sizeof(elf_prstatus_t);
status->pr_gregsetsz = sizeof(elf_gregset_t);
@@ -2201,12 +2201,97 @@
#else
fill_regs(td, &status->pr_reg);
#endif
+ }
+ *sizep = sizeof(*status);
+ return (true);
+}
+
+static bool
+__elfN(set_prstatus)(struct regset *rs, struct thread *td, void *buf,
+ size_t size)
+{
+ elf_prstatus_t *status;
+
+ KASSERT(size == sizeof(*status), ("%s: invalid size", __func__));
+ status = buf;
+#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
+ set_regs32(td, &status->pr_reg);
+#else
+ set_regs(td, &status->pr_reg);
+#endif
+ return (true);
+}
+
+static struct regset __elfN(regset_prstatus) = {
+ .note = NT_PRSTATUS,
+ .size = sizeof(elf_prstatus_t),
+ .get = __elfN(get_prstatus),
+ .set = __elfN(set_prstatus),
+};
+ELF_REGSET(__elfN(regset_prstatus));
+
+static void
+__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep)
+{
+ struct thread *td;
+ elf_prstatus_t *status;
+
+ td = arg;
+ if (sb != NULL) {
+ KASSERT(*sizep == sizeof(*status), ("%s: invalid size",
+ __func__));
+ status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK);
+ __elfN(get_prstatus)(NULL, td, status, sizep);
sbuf_bcat(sb, status, sizeof(*status));
free(status, M_TEMP);
}
*sizep = sizeof(*status);
}
+static bool
+__elfN(get_fpregset)(struct regset *rs, struct thread *td, void *buf,
+ size_t *sizep)
+{
+ elf_prfpregset_t *fpregset;
+
+ if (buf != NULL) {
+ KASSERT(*sizep == sizeof(*fpregset), ("%s: invalid size",
+ __func__));
+ fpregset = buf;
+#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
+ fill_fpregs32(td, fpregset);
+#else
+ fill_fpregs(td, fpregset);
+#endif
+ }
+ *sizep = sizeof(fpregset);
+ return (true);
+}
+
+static bool
+__elfN(set_fpregset)(struct regset *rs, struct thread *td, void *buf,
+ size_t size)
+{
+ elf_prfpregset_t *fpregset;
+
+ fpregset = buf;
+ KASSERT(size == sizeof(*fpregset), ("%s: invalid size", __func__));
+#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
+ set_fpregs32(td, fpregset);
+#else
+ set_fpregs(td, fpregset);
+#endif
+ return (true);
+}
+
+static struct regset __elfN(regset_fpregset) = {
+ .note = NT_FPREGSET,
+ .size = sizeof(elf_prfpregset_t),
+ .get = __elfN(get_fpregset),
+ .set = __elfN(set_fpregset),
+};
+ELF_REGSET(__elfN(regset_fpregset));
+
static void
__elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep)
{
@@ -2217,11 +2302,7 @@
if (sb != NULL) {
KASSERT(*sizep == sizeof(*fpregset), ("invalid size"));
fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK);
-#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32
- fill_fpregs32(td, fpregset);
-#else
- fill_fpregs(td, fpregset);
-#endif
+ __elfN(get_fpregset)(NULL, td, fpregset, sizep);
sbuf_bcat(sb, fpregset, sizeof(*fpregset));
free(fpregset, M_TEMP);
}
Index: sys/kern/init_main.c
===================================================================
--- sys/kern/init_main.c
+++ sys/kern/init_main.c
@@ -437,6 +437,8 @@
.sv_thread_detach = NULL,
.sv_trap = NULL,
.sv_set_fork_retval = null_set_fork_retval,
+ .sv_regset_begin = NULL,
+ .sv_regset_end = NULL,
};
/*
Index: sys/kern/sys_process.c
===================================================================
--- sys/kern/sys_process.c
+++ sys/kern/sys_process.c
@@ -153,6 +153,149 @@
PROC_ACTION(set_fpregs(td, fpregs));
}
+static struct regset *
+proc_find_regset(struct thread *td, int note)
+{
+ struct regset **regsetp, **regset_end, *regset;
+ struct sysentvec *sv;
+
+ sv = td->td_proc->p_sysent;
+ regsetp = sv->sv_regset_begin;
+ if (regsetp == NULL)
+ return (NULL);
+ regset_end = sv->sv_regset_end;
+ MPASS(regset_end != NULL);
+ for (; regsetp < regset_end; regsetp++) {
+ regset = *regsetp;
+ if (regset->note != note)
+ continue;
+
+ return (regset);
+ }
+
+ return (NULL);
+}
+
+static int
+proc_read_regset_alloc_iov(struct thread *td, int note, struct iovec *iov,
+ struct iovec *kiov)
+{
+ struct regset *regset;
+
+ kiov->iov_base = NULL;
+ kiov->iov_len = 0;
+
+ regset = proc_find_regset(td, note);
+ if (regset == NULL)
+ return (EINVAL);
+
+ if (iov->iov_base == NULL)
+ return (0);
+
+ /* The length is wrong, return an error */
+ if (iov->iov_len != regset->size)
+ return (EINVAL);
+
+ if (regset->get == NULL)
+ return (EINVAL);
+
+ kiov->iov_len = regset->size;
+ kiov->iov_base = malloc(regset->size, M_TEMP, M_WAITOK);
+
+ return (0);
+}
+
+static int
+proc_read_regset(struct thread *td, int note, struct iovec *iov)
+{
+ struct regset *regset;
+ size_t size;
+ int error;
+
+ regset = proc_find_regset(td, note);
+ if (regset == NULL)
+ return (EINVAL);
+
+ if (iov->iov_base == NULL) {
+ iov->iov_len = regset->size;
+ if (iov->iov_len == 0)
+ return (EINVAL);
+
+ return (0);
+ }
+
+ /* The length is wrong, return an error */
+ if (iov->iov_len != regset->size)
+ return (EINVAL);
+
+ if (regset->get == NULL)
+ return (EINVAL);
+
+ error = 0;
+ size = regset->size;
+
+ if (!regset->get(regset, td, iov->iov_base, &size)) {
+ error = EINVAL;
+ } else {
+ KASSERT(size == regset->size,
+ ("%s: Getter function changed the size", __func__));
+
+ iov->iov_len = size;
+ }
+
+ return (error);
+}
+
+static int
+proc_write_regset_alloc_iov(struct thread *td, int note, struct iovec *iov,
+ struct iovec *kiov)
+{
+ struct regset *regset;
+ void *buf;
+ int error;
+
+ kiov->iov_base = NULL;
+ kiov->iov_len = 0;
+
+ regset = proc_find_regset(td, note);
+ if (regset == NULL)
+ return (EINVAL);
+
+ /* The length is wrong, return an error */
+ if (iov->iov_len != regset->size)
+ return (EINVAL);
+
+ if (regset->set == NULL)
+ return (EINVAL);
+
+ buf = malloc(regset->size, M_TEMP, M_WAITOK);
+ error = copyin(iov->iov_base, buf, regset->size);
+ if (error != 0) {
+ free(buf, M_TEMP);
+ return (error);
+ }
+
+ kiov->iov_len = regset->size;
+ kiov->iov_base = buf;
+
+ return (0);
+}
+
+static int
+proc_write_regset(struct thread *td, int note, struct iovec *iov)
+{
+ struct regset *regset;
+
+ regset = proc_find_regset(td, note);
+ if (regset == NULL || regset->size != iov->iov_len)
+ return (EINVAL);
+
+ if (!regset->set(regset, td, iov->iov_base, iov->iov_len))
+ return (EINVAL);
+
+ return (0);
+}
+
#ifdef COMPAT_FREEBSD32
/* For 32 bit binaries, we need to expose the 32 bit regs layouts. */
int
@@ -474,6 +617,7 @@
struct dbreg dbreg;
struct fpreg fpreg;
struct reg reg;
+ struct iovec vec;
char args[sizeof(td->td_sa.args)];
struct ptrace_sc_ret psr;
int ptevents;
@@ -500,6 +644,12 @@
case PT_GETDBREGS:
bzero(&r.dbreg, sizeof(r.dbreg));
break;
+ case PT_SETREGSET:
+ error = copyin(uap->addr, &r.vec, sizeof(r.vec));
+ break;
+ case PT_GETREGSET:
+ error = copyin(uap->addr, &r.vec, sizeof(r.vec));
+ break;
case PT_SETREGS:
error = copyin(uap->addr, &r.reg, sizeof(r.reg));
break;
@@ -554,6 +704,9 @@
case PT_GETDBREGS:
error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg));
break;
+ case PT_GETREGSET:
+ error = copyout(&r.vec, uap->addr, sizeof(r.vec));
+ break;
case PT_GET_EVENT_MASK:
/* NB: The size in uap->data is validated in kern_ptrace(). */
error = copyout(&r.ptevents, uap->addr, uap->data);
@@ -864,6 +1017,19 @@
break;
}
+ switch (req) {
+ case PT_GETREGSET:
+ error = proc_read_regset_alloc_iov(td, data, addr, &iov);
+ if (error != 0)
+ goto fail;
+ break;
+ case PT_SETREGSET:
+ error = proc_write_regset_alloc_iov(td, data, addr, &iov);
+ if (error != 0)
+ goto fail;
+ break;
+ }
+
/*
* Keep this process around and request parallel ptrace()
* request to wait until we finish this request.
@@ -1287,6 +1453,19 @@
error = PROC_READ(dbregs, td2, addr);
break;
+ case PT_SETREGSET:
+ CTR2(KTR_PTRACE, "PT_SETREGSET: tid %d (pid %d)", td2->td_tid,
+ p->p_pid);
+ error = proc_write_regset(td2, data, &iov);
+ break;
+
+ case PT_GETREGSET:
+ CTR2(KTR_PTRACE, "PT_GETREGSET: tid %d (pid %d)", td2->td_tid,
+ p->p_pid);
+ error = proc_read_regset(td2, data, &iov);
+ ((struct iovec *)addr)->iov_len = iov.iov_len;
+ break;
+
case PT_LWPINFO:
if (data <= 0 || data > sizeof(*pl)) {
error = EINVAL;
@@ -1456,6 +1635,18 @@
out:
/* Drop our hold on this process now that the request has completed. */
_PRELE(p);
+
+ /* Free the kernel iovec used when writing a regset */
+ switch (req) {
+ case PT_GETREGSET:
+ if (error == 0 && iov.iov_base != NULL)
+ error = copyout(iov.iov_base,
+ ((struct iovec *)addr)->iov_base, iov.iov_len);
+ /* FALLTHROUGH */
+ case PT_SETREGSET:
+ free(iov.iov_base, M_TEMP);
+ break;
+ }
fail:
if (p2_req_set) {
if ((p->p_flag2 & P2_PTRACEREQ) != 0)
Index: sys/powerpc/powerpc/elf32_machdep.c
===================================================================
--- sys/powerpc/powerpc/elf32_machdep.c
+++ sys/powerpc/powerpc/elf32_machdep.c
@@ -136,6 +136,8 @@
.sv_hwcap2 = &cpu_features2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
Index: sys/powerpc/powerpc/elf64_machdep.c
===================================================================
--- sys/powerpc/powerpc/elf64_machdep.c
+++ sys/powerpc/powerpc/elf64_machdep.c
@@ -30,12 +30,14 @@
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/elf.h>
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/namei.h>
#include <sys/fcntl.h>
+#include <sys/reg.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
#include <sys/jail.h>
@@ -98,6 +100,8 @@
.sv_hwcap2 = &cpu_features2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
struct sysentvec elf64_freebsd_sysvec_v2 = {
@@ -139,6 +143,8 @@
.sv_hwcap2 = &cpu_features2,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
static boolean_t ppc64_elfv1_header_match(struct image_params *params,
Index: sys/riscv/riscv/elf_machdep.c
===================================================================
--- sys/riscv/riscv/elf_machdep.c
+++ sys/riscv/riscv/elf_machdep.c
@@ -44,6 +44,7 @@
#include <sys/imgact.h>
#include <sys/linker.h>
#include <sys/proc.h>
+#include <sys/reg.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/imgact_elf.h>
@@ -101,6 +102,8 @@
.sv_machine_arch = riscv_machine_arch,
.sv_onexec_old = exec_onexec_old,
.sv_onexit = exit_onexit,
+ .sv_regset_begin = SET_BEGIN(__elfN(regset)),
+ .sv_regset_end = SET_LIMIT(__elfN(regset)),
};
INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec);
Index: sys/sys/ptrace.h
===================================================================
--- sys/sys/ptrace.h
+++ sys/sys/ptrace.h
@@ -85,6 +85,8 @@
#define PT_VM_TIMESTAMP 40 /* Get VM version (timestamp) */
#define PT_VM_ENTRY 41 /* Get VM map (entry) */
+#define PT_GETREGSET 42 /* Get a target register set */
+#define PT_SETREGSET 43 /* Set a target register set */
#define PT_FIRSTMACH 64 /* for machine-specific requests */
#include <machine/ptrace.h> /* machine-specific requests, if any */
Index: sys/sys/reg.h
===================================================================
--- sys/sys/reg.h
+++ sys/sys/reg.h
@@ -41,6 +41,25 @@
#include <machine/reg.h>
#ifdef _KERNEL
+struct sbuf;
+struct regset;
+
+typedef bool (regset_get)(struct regset *, struct thread *, void *,
+ size_t *);
+typedef bool (regset_set)(struct regset *, struct thread *, void *, size_t);
+
+struct regset {
+ int note;
+ size_t size;
+ regset_get *get;
+ regset_set *set;
+};
+
+#if defined(__ELF_WORD_SIZE)
+SET_DECLARE(__elfN(regset), struct regset);
+#define ELF_REGSET(_regset) DATA_SET(__elfN(regset), _regset)
+#endif
+
int fill_regs(struct thread *, struct reg *);
int set_regs(struct thread *, struct reg *);
int fill_fpregs(struct thread *, struct fpreg *);
Index: sys/sys/sysent.h
===================================================================
--- sys/sys/sysent.h
+++ sys/sys/sysent.h
@@ -160,6 +160,8 @@
struct image_params *imgp);
void (*sv_set_fork_retval)(struct thread *);
/* Only used on x86 */
+ struct regset **sv_regset_begin;
+ struct regset **sv_regset_end;
};
#define SV_ILP32 0x000100 /* 32-bit executable. */
Index: tests/sys/kern/ptrace_test.c
===================================================================
--- tests/sys/kern/ptrace_test.c
+++ tests/sys/kern/ptrace_test.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/cpuset.h>
+#include <sys/elf.h>
#include <sys/event.h>
#include <sys/file.h>
#include <sys/time.h>
@@ -35,6 +36,7 @@
#include <sys/procdesc.h>
#define _WANT_MIPS_REGNUM
#include <sys/ptrace.h>
+#include <sys/procfs.h>
#include <sys/queue.h>
#include <sys/runq.h>
#include <sys/syscall.h>
@@ -3196,6 +3198,64 @@
REQUIRE_EQ(errno, ECHILD);
}
+/*
+ * Verify that PT_GETREGSET returns registers and PT_SETREGSET updates them.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__PT_REGSET);
+ATF_TC_BODY(ptrace__PT_REGSET, tc)
+{
+ struct prstatus prstatus;
+ struct iovec vec;
+ pid_t child, wpid;
+ int status;
+
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ trace_me();
+ exit(1);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ /* Check the size is returned when vec.iov_base is NULL */
+ vec.iov_base = NULL;
+ vec.iov_len = 0;
+ ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
+ -1);
+ ATF_REQUIRE(vec.iov_len == sizeof(prstatus));
+ ATF_REQUIRE(vec.iov_base == NULL);
+
+ /* Read the registers. */
+ memset(&prstatus, 0, sizeof(prstatus));
+ vec.iov_base = &prstatus;
+ ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
+ -1);
+ ATF_REQUIRE(vec.iov_len == sizeof(prstatus));
+ ATF_REQUIRE(vec.iov_base == &prstatus);
+ ATF_REQUIRE(prstatus.pr_statussz == sizeof(prstatus));
+
+ /* Write the registers back. */
+ ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&vec, NT_PRSTATUS) !=
+ -1);
+
+ REQUIRE_EQ(ptrace(PT_CONTINUE, child, (caddr_t)1, 0), 0);
+
+ /* The second wait() should report the exit status. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, child);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 1);
+
+ /* The child should no longer exist. */
+ wpid = waitpid(child, &status, 0);
+ REQUIRE_EQ(wpid, -1);
+ REQUIRE_EQ(errno, ECHILD);
+}
+
static void *
raise_sigstop_thread(void *arg __unused)
{
@@ -4302,6 +4362,7 @@
ATF_TP_ADD_TC(tp, ptrace__killed_with_sigmask);
ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigmask);
ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask);
+ ATF_TP_ADD_TC(tp, ptrace__PT_REGSET);
ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1);
ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2);
ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard);

File Metadata

Mime Type
text/plain
Expires
Fri, Feb 28, 11:11 AM (20 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16884640
Default Alt Text
D19831.id101539.diff (22 KB)

Event Timeline