diff --git a/contrib/elftoolchain/readelf/readelf.c b/contrib/elftoolchain/readelf/readelf.c --- a/contrib/elftoolchain/readelf/readelf.c +++ b/contrib/elftoolchain/readelf/readelf.c @@ -1195,6 +1195,7 @@ case 17: return "NT_PTLWPINFO"; case 0x100: return "NT_PPC_VMX (ppc Altivec registers)"; case 0x102: return "NT_PPC_VSX (ppc VSX registers)"; + case 0x200: return "NT_X86_SEGBASES (x86 segment base registers)"; case 0x202: return "NT_X86_XSTATE (x86 XSAVE extended state)"; case 0x400: return "NT_ARM_VFP (arm VFP registers)"; case 0x401: return "NT_ARM_TLS (arm TLS register)"; diff --git a/sys/amd64/amd64/ptrace_machdep.c b/sys/amd64/amd64/ptrace_machdep.c --- a/sys/amd64/amd64/ptrace_machdep.c +++ b/sys/amd64/amd64/ptrace_machdep.c @@ -32,11 +32,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -52,6 +54,106 @@ }; #endif +static bool +get_segbases(struct regset *rs, struct thread *td, void *buf, + size_t *sizep) +{ + struct segbasereg *reg; + struct pcb *pcb; + + if (buf != NULL) { + KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + pcb = td->td_pcb; + if (td == curthread) + update_pcb_bases(pcb); + reg->r_fsbase = pcb->pcb_fsbase; + reg->r_gsbase = pcb->pcb_gsbase; + } + *sizep = sizeof(*reg); + return (true); +} + +static bool +set_segbases(struct regset *rs, struct thread *td, void *buf, + size_t size) +{ + struct segbasereg *reg; + struct pcb *pcb; + + KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + pcb = td->td_pcb; + set_pcb_flags(pcb, PCB_FULL_IRET); + pcb->pcb_fsbase = reg->r_fsbase; + td->td_frame->tf_fs = _ufssel; + pcb->pcb_gsbase = reg->r_gsbase; + td->td_frame->tf_gs = _ugssel; + + return (true); +} + +static struct regset regset_segbases = { + .note = NT_X86_SEGBASES, + .size = sizeof(struct segbasereg), + .get = get_segbases, + .set = set_segbases, +}; +ELF_REGSET(regset_segbases); + +#ifdef COMPAT_FREEBSD32 +static bool +get_segbases32(struct regset *rs, struct thread *td, void *buf, + size_t *sizep) +{ + struct segbasereg32 *reg; + struct pcb *pcb; + + if (buf != NULL) { + KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + pcb = td->td_pcb; + if (td == curthread) + update_pcb_bases(pcb); + reg->r_fsbase = (uint32_t)pcb->pcb_fsbase; + reg->r_gsbase = (uint32_t)pcb->pcb_gsbase; + } + *sizep = sizeof(*reg); + return (true); +} + +static bool +set_segbases32(struct regset *rs, struct thread *td, void *buf, + size_t size) +{ + struct segbasereg32 *reg; + struct pcb *pcb; + + KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + pcb = td->td_pcb; + set_pcb_flags(pcb, PCB_FULL_IRET); + pcb->pcb_fsbase = reg->r_fsbase; + td->td_frame->tf_fs = _ufssel; + pcb->pcb_gsbase = reg->r_gsbase; + td->td_frame->tf_gs = _ugssel; + + return (true); +} + +static struct regset regset_segbases32 = { + .note = NT_X86_SEGBASES, + .size = sizeof(struct segbasereg32), + .get = get_segbases32, + .set = set_segbases32, +}; +ELF32_REGSET(regset_segbases32); +#endif + static int cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) { diff --git a/sys/amd64/ia32/ia32_reg.c b/sys/amd64/ia32/ia32_reg.c --- a/sys/amd64/ia32/ia32_reg.c +++ b/sys/amd64/ia32/ia32_reg.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -266,3 +267,52 @@ dr.dr[i] = 0; return (set_dbregs(td, &dr)); } + +static bool +get_i386_segbases(struct regset *rs, struct thread *td, void *buf, + size_t *sizep) +{ + struct segbasereg32 *reg; + struct pcb *pcb; + + if (buf != NULL) { + KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + pcb = td->td_pcb; + if (td == curthread) + update_pcb_bases(pcb); + reg->r_fsbase = pcb->pcb_fsbase; + reg->r_gsbase = pcb->pcb_gsbase; + } + *sizep = sizeof(*reg); + return (true); +} + +static bool +set_i386_segbases(struct regset *rs, struct thread *td, void *buf, + size_t size) +{ + struct segbasereg32 *reg; + struct pcb *pcb; + + KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + pcb = td->td_pcb; + set_pcb_flags(pcb, PCB_FULL_IRET); + pcb->pcb_fsbase = reg->r_fsbase; + td->td_frame->tf_fs = _ufssel; + pcb->pcb_gsbase = reg->r_gsbase; + td->td_frame->tf_gs = _ugssel; + + return (true); +} + +static struct regset regset_i386_segbases = { + .note = NT_X86_SEGBASES, + .size = sizeof(struct segbasereg), + .get = get_i386_segbases, + .set = set_i386_segbases, +}; +ELF32_REGSET(regset_i386_segbases); diff --git a/sys/i386/i386/ptrace_machdep.c b/sys/i386/i386/ptrace_machdep.c --- a/sys/i386/i386/ptrace_machdep.c +++ b/sys/i386/i386/ptrace_machdep.c @@ -34,15 +34,64 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include +static uint32_t +get_segbase(struct segment_descriptor *sdp) +{ + return (sdp->sd_hibase << 24 | sdp->sd_lobase); +} + +static bool +get_segbases(struct regset *rs, struct thread *td, void *buf, + size_t *sizep) +{ + struct segbasereg *reg; + + if (buf != NULL) { + KASSERT(*sizep == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + reg->r_fsbase = get_segbase(&td->td_pcb->pcb_fsd); + reg->r_gsbase = get_segbase(&td->td_pcb->pcb_gsd); + } + *sizep = sizeof(*reg); + return (true); +} + +static bool +set_segbases(struct regset *rs, struct thread *td, void *buf, + size_t size) +{ + struct segbasereg *reg; + + KASSERT(size == sizeof(*reg), ("%s: invalid size", __func__)); + reg = buf; + + fill_based_sd(&td->td_pcb->pcb_fsd, reg->r_fsbase); + td->td_frame->tf_fs = GSEL(GUFS_SEL, SEL_UPL); + fill_based_sd(&td->td_pcb->pcb_gsd, reg->r_gsbase); + td->td_pcb->pcb_gs = GSEL(GUGS_SEL, SEL_UPL); + + return (true); +} + +static struct regset regset_segbases = { + .note = NT_X86_SEGBASES, + .size = sizeof(struct segbasereg), + .get = get_segbases, + .set = set_segbases, +}; +ELF_REGSET(regset_segbases); + static int cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) { @@ -173,7 +222,7 @@ case PT_GETGSBASE: sdp = req == PT_GETFSBASE ? &td->td_pcb->pcb_fsd : &td->td_pcb->pcb_gsd; - r = sdp->sd_hibase << 24 | sdp->sd_lobase; + r = get_segbase(sdp); error = copyout(&r, addr, sizeof(r)); break; diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -823,6 +823,7 @@ #define NT_PTLWPINFO 17 /* Thread ptrace miscellaneous info. */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_X86_SEGBASES 0x200 /* x86 FS/GS base addresses. */ #define NT_X86_XSTATE 0x202 /* x86 XSAVE extended state. */ #define NT_ARM_VFP 0x400 /* ARM VFP registers */ #define NT_ARM_TLS 0x401 /* ARM TLS register */ diff --git a/sys/x86/include/reg.h b/sys/x86/include/reg.h --- a/sys/x86/include/reg.h +++ b/sys/x86/include/reg.h @@ -86,6 +86,7 @@ #define __reg32 reg #define __fpreg32 fpreg #define __dbreg32 dbreg +#define __segbasereg32 segbasereg #else #define __reg32 reg32 #define __reg64 reg @@ -93,6 +94,8 @@ #define __fpreg64 fpreg #define __dbreg32 dbreg32 #define __dbreg64 dbreg +#define __segbasereg32 segbasereg32 +#define __segbasereg64 segbasereg #define __HAVE_REG32 #endif @@ -236,12 +239,27 @@ #define DBREG_DRX(d,x) ((d)->dr[(x)]) /* reference dr0 - dr7 by register number */ +/* + * Register set accessible via NT_X86_SEGBASES. + */ +struct __segbasereg32 { + __uint32_t r_fsbase; + __uint32_t r_gsbase; +}; + +struct __segbasereg64 { + __uint64_t r_fsbase; + __uint64_t r_gsbase; +}; + #undef __reg32 #undef __reg64 #undef __fpreg32 #undef __fpreg64 #undef __dbreg32 #undef __dbreg64 +#undef __segbasereg32 +#undef __segbasereg64 #ifdef _KERNEL /* diff --git a/usr.bin/gcore/elfcore.c b/usr.bin/gcore/elfcore.c --- a/usr.bin/gcore/elfcore.c +++ b/usr.bin/gcore/elfcore.c @@ -379,6 +379,7 @@ elf_putregnote(NT_ARM_VFP, tids[i], sb); #endif #if defined(__i386__) || defined(__amd64__) + elf_putregnote(NT_X86_SEGBASES, tids[i], sb); elf_putnote(NT_X86_XSTATE, elf_note_x86_xstate, tids + i, sb); #endif #if defined(__powerpc__)