Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -88,6 +88,11 @@ }; #endif +struct ptrace_regset { + void *prs_addr; + size_t prs_size; +}; + /* * Functions implemented using PROC_ACTION(): * @@ -171,6 +176,71 @@ PROC_ACTION(set_fpregs(td, fpregs)); } +SET_DECLARE(elf_regset, struct regset); +static size_t +proc_regset_maxsize(int note) +{ + struct regset **regsetpp, *regsetp; + + SET_FOREACH(regsetpp, elf_regset) { + regsetp = *regsetpp; + if (regsetp->note != note) + continue; + + return (regsetp->size); + } + + return (0); +} + +static int +proc_read_regset(struct thread *td, int note, struct iovec *vec) +{ + struct regset **regsetpp, *regsetp; + size_t size; + int err; + + SET_FOREACH(regsetpp, elf_regset) { + regsetp = *regsetpp; + if (regsetp->note != note) + continue; + + err = 0; + size = vec->iov_len; + if (!regsetp->get(regsetp, td, vec->iov_base, &size)) + err = EINVAL; + KASSERT(size == vec->iov_len, + ("proc_read_regset: Get function changed the size")); + + return (err); + } + return (EINVAL); +} + +static int +proc_write_regset(struct thread *td, int note, struct iovec *vec) +{ + struct regset **regsetpp, *regsetp; + size_t size; + int err; + + SET_FOREACH(regsetpp, elf_regset) { + regsetp = *regsetpp; + if (regsetp->note != note) + continue; + + err = 0; + size = vec->iov_len; + if (!regsetp->set(regsetp, td, vec->iov_base, &size)) + err = EINVAL; + KASSERT(size == vec->iov_len, + ("proc_write_regset: Set function changed the size")); + + return (err); + } + return (EINVAL); +} + #ifdef COMPAT_FREEBSD32 /* For 32 bit binaries, we need to expose the 32 bit regs layouts. */ int @@ -568,6 +638,7 @@ struct dbreg dbreg; struct fpreg fpreg; struct reg reg; + struct iovec vec; #ifdef COMPAT_FREEBSD32 struct dbreg32 dbreg32; struct fpreg32 fpreg32; @@ -579,6 +650,7 @@ char args[sizeof(td->td_sa.args)]; int ptevents; } r; + struct iovec kern_vec; void *addr; int error = 0; #ifdef COMPAT_FREEBSD32 @@ -591,6 +663,8 @@ AUDIT_ARG_CMD(uap->req); AUDIT_ARG_VALUE(uap->data); addr = &r; + kern_vec.iov_base = NULL; + kern_vec.iov_len = 0; switch (uap->req) { case PT_GET_EVENT_MASK: case PT_LWPINFO: @@ -605,6 +679,56 @@ case PT_GETDBREGS: BZERO(&r.dbreg, sizeof r.dbreg); break; + case PT_SETREGSET: + if (wrap32) { + /* TODO: compat32 support */ + error = EINVAL; + break; + } + + error = copyin(uap->addr, &r.vec, sizeof r.vec); + if (error != 0) + break; + kern_vec.iov_len = proc_regset_maxsize(uap->data); + /* Check the regset is valid and the length is correct */ + if (kern_vec.iov_len == 0 || + kern_vec.iov_len != r.vec.iov_len) { + error = EINVAL; + break; + } + kern_vec.iov_base = malloc(kern_vec.iov_len, M_TEMP, + M_WAITOK); + error = copyin(r.vec.iov_base, kern_vec.iov_base, + kern_vec.iov_len); + if (error != 0) + break; + addr = &kern_vec; + break; + case PT_GETREGSET: + if (wrap32) { + /* TODO: compat32 support */ + error = EINVAL; + break; + } + + error = copyin(uap->addr, &r.vec, sizeof r.vec); + if (error != 0) + break; + kern_vec.iov_len = proc_regset_maxsize(uap->data); + if (kern_vec.iov_len == 0) { + error = EINVAL; + break; + } + /* Just return the size when the base is NULL */ + if (r.vec.iov_base == NULL) { + r.vec.iov_len = kern_vec.iov_len; + error = copyout(&r.vec, uap->addr, sizeof r.vec); + break; + } + kern_vec.iov_base = malloc(kern_vec.iov_len, M_TEMP, + M_WAITOK | M_ZERO); + addr = &kern_vec; + break; case PT_SETREGS: error = COPYIN(uap->addr, &r.reg, sizeof r.reg); break; @@ -631,11 +755,11 @@ break; } if (error) - return (error); + goto out; error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); if (error) - return (error); + goto out; switch (uap->req) { case PT_VM_ENTRY: @@ -653,6 +777,14 @@ case PT_GETDBREGS: error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg); break; + case PT_GETREGSET: + r.vec.iov_len = MIN(r.vec.iov_len, kern_vec.iov_len); + error = copyout(kern_vec.iov_base, r.vec.iov_base, + r.vec.iov_len); + if (error != 0) + break; + 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); @@ -667,6 +799,8 @@ break; } +out: + free(kern_vec.iov_base, M_TEMP); return (error); } #undef COPYIN @@ -1319,6 +1453,18 @@ 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, addr); + 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, addr); + break; + case PT_LWPINFO: if (data <= 0 || #ifdef COMPAT_FREEBSD32 Index: sys/sys/ptrace.h =================================================================== --- sys/sys/ptrace.h +++ sys/sys/ptrace.h @@ -83,6 +83,9 @@ #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-specific requests, if any */ Index: sys/sys/reg.h =================================================================== --- sys/sys/reg.h +++ sys/sys/reg.h @@ -5,6 +5,23 @@ #include #ifdef _KERNEL +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; + size_t align; + regset_get *get; + regset_set *set; +}; + +#define ELF_REGSET(regset) DATA_SET(elf_regset, regset) + int fill_regs(struct thread *, struct reg *); int set_regs(struct thread *, struct reg *); int fill_fpregs(struct thread *, struct fpreg *);