[PATCH] Add support for the x86 XSAVE extended state on FreeBSD/x86.

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] Add support for the x86 XSAVE extended state on FreeBSD/x86.

John Baldwin
Recognize NT_X86_XSTATE notes in FreeBSD process cores.  Recent
FreeBSD versions include a note containing the XSAVE state for each
thread in the process when XSAVE is in use.  The note stores a copy of
the current XSAVE mask in a reserved section of the machine-defined
XSAVE state at the same offset as Linux's NT_X86_XSTATE note.

For native processes, use the PT_GETXSTATE_INFO ptrace request to
determine if XSAVE is enabled, and if so the active XSAVE state mask
(that is, the value of %xcr0 for the target process) as well as the
size of XSAVE state area.  Use the PT_GETXSTATE and PT_SETXSTATE requests
to fetch and store the XSAVE state, respectively, in the BSD x86
native targets.

In addition, the FreeBSD amd64 and i386 native targets now include
"read_description" target methods to determine the correct x86 target
description for the current XSAVE mask.  On FreeBSD amd64 this also
properly returns an i386 target description for 32-bit binaries which
allows the 64-bit GDB to run 32-bit binaries.

Note that the ptrace changes are in the BSD native targets, not the
FreeBSD-specific native targets since that is where the other ptrace
register accesses occur.  Of the other BSDs, NetBSD and DragonFly use
XSAVE in the kernel but do not currently export the extended state via
ptrace(2).  OpenBSD does not currently support XSAVE.

bfd/ChangeLog:

        * elf.c (elfcore_grok_note): Recognize NT_X86_XSTATE on
        FreeBSD.
        (elfcore_write_xstatereg): Use correct note name on FreeBSD.

gdb/ChangeLog:

        * amd64-tdep.h: Export tdesc_amd64, tdesc_amd64_avx, tdesc_amd64_mpx,
        and tdesc_amd64_avx512.
        * amd64bsd-nat.c [PT_GETXSTATE_INFO]: New variable x86_xsave_len.
        (amd64bsd_fetch_inferior_registers) [PT_GETXSTATE_INFO]: Handle
        x86 extended save area.
        (amd64bsd_store_inferior_registers) [PT_GETXSTATE_INFO]: Likewise.
        * amd64bsd-nat.h: Export x86_xsave_len.
        * amd64fbsd-nat.c (amd64fbsd_read_description): New function.
        (_initialize_amd64fbsd_nat): Set "to_read_description" to
        "amd64fbsd_read_description".
        * amd64fbsd-tdep.c (amd64fbsd_core_read_description): New function.
        (amd64fbsd_supply_xstateregset): New function.
        (amd64fbsd_collect_xstateregset): New function.
        Add "amd64fbsd_xstateregset".
        (amd64fbsd_iterate_over_regset_sections): New function.
        (amd64fbsd_init_abi): Set "xsave_xcr0_offset" to
        "I386_FBSD_XSAVE_XCR0_OFFSET".
        Add "iterate_over_regset_sections" gdbarch method.
        Add "core_read_description" gdbarch method.
        * i386-tdep.h: Export tdesc_i386, tdesc_i386_avx, tdesc_i386_mpx,
        and tdesc_i386_avx512.
        * i386bsd-nat.c [PT_GETXSTATE_INFO]: New variable x86_xsave_len.
        (i386bsd_fetch_inferior_registers) [PT_GETXSTATE_INFO]: Handle
        x86 extended save area.
        (i386bsd_store_inferior_registers) [PT_GETXSTATE_INFO]: Likewise.
        * i386bsd-nat.h: Export x86_xsave_len.
        * i386fbsd-nat.c (i386fbsd_read_description): New function.
        (_initialize_i386fbsd_nat): Set "to_read_description" to
        "i386fbsd_read_description".
        * i386fbsd-tdep.c (i386fbsd_core_read_xcr0): New function.
        (i386fbsd_core_read_description): New function.
        (i386fbsd_supply_xstateregset): New function.
        (i386fbsd_collect_xstateregset): New function.
        Add "i386fbsd_xstateregset".
        (i386fbsd_iterate_over_regset_sections): New function.
        (i386fbsd4_init_abi): Set "xsave_xcr0_offset" to
        "I386_FBSD_XSAVE_XCR0_OFFSET".
        Add "iterate_over_regset_sections" gdbarch method.
        Add "core_read_description" gdbarch method.
        * i386fbsd-tdep.h: New file.
---
diff --git a/bfd/elf.c b/bfd/elf.c
index 13d4272..0be2564 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -8670,6 +8670,9 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note)
       if (note->namesz == 6
   && strcmp (note->namedata, "LINUX") == 0)
  return elfcore_grok_xstatereg (abfd, note);
+      else if (note->namesz == 8
+  && strcmp (note->namedata, "FreeBSD") == 0)
+ return elfcore_grok_xstatereg (abfd, note);
       else
  return TRUE;
 
@@ -9490,6 +9493,8 @@ elfcore_write_xstatereg (bfd *abfd, char *buf, int *bufsiz,
  const void *xfpregs, int size)
 {
   char *note_name = "LINUX";
+  if (get_elf_backend_data (abfd)->elf_osabi == ELFOSABI_FREEBSD)
+      note_name = "FreeBSD";
   return elfcore_write_note (abfd, buf, bufsiz,
      note_name, NT_X86_XSTATE, xfpregs, size);
 }
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 318fd43..4fbb38e 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -84,6 +84,11 @@ enum amd64_regnum
 
 #define AMD64_NUM_REGS (AMD64_ZMM31H_REGNUM + 1)
 
+extern struct target_desc *tdesc_amd64;
+extern struct target_desc *tdesc_amd64_avx;
+extern struct target_desc *tdesc_amd64_mpx;
+extern struct target_desc *tdesc_amd64_avx512;
+
 extern struct displaced_step_closure *amd64_displaced_step_copy_insn
   (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to,
    struct regcache *regs);
diff --git a/gdb/amd64bsd-nat.c b/gdb/amd64bsd-nat.c
index 31060a123..d489dda 100644
--- a/gdb/amd64bsd-nat.c
+++ b/gdb/amd64bsd-nat.c
@@ -35,6 +35,10 @@
 #include "inf-ptrace.h"
 
 
+#ifdef PT_GETXSTATE_INFO
+size_t x86_xsave_len;
+#endif
+
 /* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
    for all registers (including the floating-point registers).  */
 
@@ -60,6 +64,20 @@ amd64bsd_fetch_inferior_registers (struct target_ops *ops,
   if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       struct fpreg fpregs;
+#ifdef PT_GETXSTATE_INFO
+      char *xstateregs;
+
+      if (x86_xsave_len != 0)
+ {
+  xstateregs = alloca (x86_xsave_len);
+  if (ptrace (PT_GETXSTATE, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+    perror_with_name (_("Couldn't get extended state status"));
+
+  amd64_supply_xsave (regcache, -1, xstateregs);
+  return;
+ }
+#endif
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
   (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
@@ -99,6 +117,24 @@ amd64bsd_store_inferior_registers (struct target_ops *ops,
   if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       struct fpreg fpregs;
+#ifdef PT_GETXSTATE_INFO
+      char *xstateregs;
+
+      if (x86_xsave_len != 0)
+ {
+  xstateregs = alloca (x86_xsave_len);
+  if (ptrace (PT_GETXSTATE, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+    perror_with_name (_("Couldn't get extended state status"));
+
+  amd64_collect_xsave (regcache, regnum, xstateregs, 0);
+
+  if (ptrace (PT_SETXSTATE, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) xstateregs, x86_xsave_len) == -1)
+    perror_with_name (_("Couldn't write extended state status"));
+  return;
+ }
+#endif
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
   (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
diff --git a/gdb/amd64bsd-nat.h b/gdb/amd64bsd-nat.h
index 167eb56..77bd5f3 100644
--- a/gdb/amd64bsd-nat.h
+++ b/gdb/amd64bsd-nat.h
@@ -20,6 +20,9 @@
 #ifndef AMD64BSD_NAT_H
 #define AMD64BSD_NAT_H
 
+/* Low level amd64 XSAVE info. */
+extern size_t x86_xsave_len;
+
 /* Low level amd64 debug register functions.  */
 
 extern void amd64bsd_dr_set_control (unsigned long control);
diff --git a/gdb/amd64fbsd-nat.c b/gdb/amd64fbsd-nat.c
index b1b261c..639191d 100644
--- a/gdb/amd64fbsd-nat.c
+++ b/gdb/amd64fbsd-nat.c
@@ -34,6 +34,7 @@
 #include "amd64-nat.h"
 #include "amd64bsd-nat.h"
 #include "x86-nat.h"
+#include "x86-xstate.h"
 
 
 /* Offset in `struct reg' where MEMBER is stored.  */
@@ -151,6 +152,68 @@ amd64fbsd_mourn_inferior (struct target_ops *ops)
   super_mourn_inferior (ops);
 }
 
+static const struct target_desc *
+amd64fbsd_read_description (struct target_ops *ops)
+{
+#ifdef PT_GETXSTATE_INFO
+  static int xsave_probed;
+  static uint64_t xcr0;
+#endif
+  struct reg regs;
+  int is64;
+
+  if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+    perror_with_name (_("Couldn't get registers"));
+  is64 = (regs.r_cs == GSEL (GUCODE_SEL, SEL_UPL));
+#ifdef PT_GETXSTATE_INFO
+  if (!xsave_probed)
+    {
+      struct ptrace_xstate_info info;
+
+      if (ptrace (PT_GETXSTATE_INFO, ptid_get_pid (inferior_ptid),
+  (PTRACE_TYPE_ARG3) &info, sizeof(info)) == 0)
+ {
+  x86_xsave_len = info.xsave_len;
+  xcr0 = info.xsave_mask;
+ }
+      xsave_probed = 1;
+    }
+
+  if (x86_xsave_len != 0)
+    {
+      switch (xcr0 & X86_XSTATE_ALL_MASK)
+ {
+ case X86_XSTATE_MPX_AVX512_MASK:
+ case X86_XSTATE_AVX512_MASK:
+  if (is64)
+    return tdesc_amd64_avx512;
+  else
+    return tdesc_i386_avx512;
+ case X86_XSTATE_MPX_MASK:
+  if (is64)
+    return tdesc_amd64_mpx;
+  else
+    return tdesc_i386_mpx;
+ case X86_XSTATE_AVX_MASK:
+  if (is64)
+    return tdesc_amd64_avx;
+  else
+    return tdesc_i386_avx;
+ default:
+  if (is64)
+    return tdesc_amd64;
+  else
+    return tdesc_i386;
+ }
+    }
+#endif
+  if (is64)
+    return tdesc_amd64;
+  else
+    return tdesc_i386;
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 void _initialize_amd64fbsd_nat (void);
 
@@ -181,6 +244,7 @@ _initialize_amd64fbsd_nat (void)
 
   super_mourn_inferior = t->to_mourn_inferior;
   t->to_mourn_inferior = amd64fbsd_mourn_inferior;
+  t->to_read_description = amd64fbsd_read_description;
 
   t->to_pid_to_exec_file = fbsd_pid_to_exec_file;
   t->to_find_memory_regions = fbsd_find_memory_regions;
diff --git a/gdb/amd64fbsd-tdep.c b/gdb/amd64fbsd-tdep.c
index 62dcb83..cb097d3 100644
--- a/gdb/amd64fbsd-tdep.c
+++ b/gdb/amd64fbsd-tdep.c
@@ -23,6 +23,9 @@
 #include "gdbcore.h"
 #include "regcache.h"
 #include "osabi.h"
+#include "regset.h"
+#include "i386fbsd-tdep.h"
+#include "x86-xstate.h"
 
 #include "amd64-tdep.h"
 #include "bsd-uthread.h"
@@ -169,6 +172,71 @@ static int amd64fbsd_jmp_buf_reg_offset[] =
   0 * 8 /* %rip */
 };
 
+static const struct target_desc *
+amd64fbsd_core_read_description (struct gdbarch *gdbarch,
+ struct target_ops *target,
+ bfd *abfd)
+{
+  uint64_t xcr0 = i386fbsd_core_read_xcr0 (abfd);
+
+  switch (xcr0 & X86_XSTATE_ALL_MASK)
+    {
+    case X86_XSTATE_MPX_AVX512_MASK:
+    case X86_XSTATE_AVX512_MASK:
+      return tdesc_amd64_avx512;
+    case X86_XSTATE_MPX_MASK:
+      return tdesc_amd64_mpx;
+    case X86_XSTATE_AVX_MASK:
+      return tdesc_amd64_avx;
+    default:
+      return tdesc_amd64;
+    }
+}
+
+/* Similar to amd64_supply_fpregset, but use XSAVE extended state.  */
+
+static void
+amd64fbsd_supply_xstateregset (const struct regset *regset,
+       struct regcache *regcache, int regnum,
+       const void *xstateregs, size_t len)
+{
+  amd64_supply_xsave (regcache, regnum, xstateregs);
+}
+
+/* Similar to amd64_collect_fpregset, but use XSAVE extended state.  */
+
+static void
+amd64fbsd_collect_xstateregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *xstateregs, size_t len)
+{
+  amd64_collect_xsave (regcache, regnum, xstateregs, 1);
+}
+
+static const struct regset amd64fbsd_xstateregset =
+  {
+    NULL,
+    amd64fbsd_supply_xstateregset,
+    amd64fbsd_collect_xstateregset,
+    REGSET_VARIABLE_SIZE,
+  };
+
+/* Iterate over core file register note sections.  */
+
+static void
+amd64fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  cb (".reg", tdep->sizeof_gregset, &i386_gregset, NULL, cb_data);
+  cb (".reg2", tdep->sizeof_fpregset, &amd64_fpregset, NULL, cb_data);
+  cb (".reg-xstate", regcache ? X86_XSTATE_MAX_SIZE : 0,
+      &amd64fbsd_xstateregset, "XSAVE extended state", cb_data);
+}
+
 static void
 amd64fbsd_supply_uthread (struct regcache *regcache,
   int regnum, CORE_ADDR addr)
@@ -233,6 +301,15 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   tdep->sc_reg_offset = amd64fbsd_sc_reg_offset;
   tdep->sc_num_regs = ARRAY_SIZE (amd64fbsd_sc_reg_offset);
 
+  tdep->xsave_xcr0_offset = I386_FBSD_XSAVE_XCR0_OFFSET;
+
+  /* Iterate over core file register note sections.  */
+  set_gdbarch_iterate_over_regset_sections
+    (gdbarch, amd64fbsd_iterate_over_regset_sections);
+
+  set_gdbarch_core_read_description (gdbarch,
+     amd64fbsd_core_read_description);
+
   /* FreeBSD provides a user-level threads implementation.  */
   bsd_uthread_set_supply_uthread (gdbarch, amd64fbsd_supply_uthread);
   bsd_uthread_set_collect_uthread (gdbarch, amd64fbsd_collect_uthread);
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 8bfd412..0682f0e 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -328,6 +328,11 @@ enum record_i386_regnum
 /* Size of the largest register.  */
 #define I386_MAX_REGISTER_SIZE 64
 
+extern struct target_desc *tdesc_i386;
+extern struct target_desc *tdesc_i386_avx;
+extern struct target_desc *tdesc_i386_mpx;
+extern struct target_desc *tdesc_i386_avx512;
+
 /* Types for i386-specific registers.  */
 extern struct type *i387_ext_type (struct gdbarch *gdbarch);
 
diff --git a/gdb/i386bsd-nat.c b/gdb/i386bsd-nat.c
index 16e0707..33a3700 100644
--- a/gdb/i386bsd-nat.c
+++ b/gdb/i386bsd-nat.c
@@ -81,6 +81,10 @@ static int i386bsd_r_reg_offset[] =
    so that we try PT_GETXMMREGS the first time around.  */
 static int have_ptrace_xmmregs = -1;
 #endif
+
+#ifdef PT_GETXSTATE_INFO
+size_t x86_xsave_len;
+#endif
 
 
 /* Supply the general-purpose registers in GREGS, to REGCACHE.  */
@@ -148,7 +152,24 @@ i386bsd_fetch_inferior_registers (struct target_ops *ops,
       struct fpreg fpregs;
 #ifdef HAVE_PT_GETXMMREGS
       char xmmregs[512];
+#endif
+
+#ifdef PT_GETXSTATE_INFO
+      if (x86_xsave_len != 0)
+ {
+  char *xstateregs;
+
+  xstateregs = alloca (x86_xsave_len);
+  if (ptrace (PT_GETXSTATE, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+    perror_with_name (_("Couldn't get extended state status"));
 
+  i387_supply_xsave (regcache, -1, xstateregs);
+  return;
+ }
+#endif
+      
+#ifdef HAVE_PT_GETXMMREGS
       if (have_ptrace_xmmregs != 0
   && ptrace(PT_GETXMMREGS, ptid_get_pid (inferior_ptid),
     (PTRACE_TYPE_ARG3) xmmregs, 0) == 0)
@@ -158,18 +179,15 @@ i386bsd_fetch_inferior_registers (struct target_ops *ops,
  }
       else
  {
+  have_ptrace_xmmregs = 0;
+#endif
           if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
       (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
     perror_with_name (_("Couldn't get floating point status"));
 
   i387_supply_fsave (regcache, -1, &fpregs);
+#ifdef HAVE_PT_GETXMMREGS
  }
-#else
-      if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
-  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
- perror_with_name (_("Couldn't get floating point status"));
-
-      i387_supply_fsave (regcache, -1, &fpregs);
 #endif
     }
 }
@@ -204,7 +222,28 @@ i386bsd_store_inferior_registers (struct target_ops *ops,
       struct fpreg fpregs;
 #ifdef HAVE_PT_GETXMMREGS
       char xmmregs[512];
+#endif
+
+#ifdef PT_GETXSTATE_INFO
+      if (x86_xsave_len != 0)
+ {
+  char *xstateregs;
+
+  xstateregs = alloca (x86_xsave_len);
+  if (ptrace (PT_GETXSTATE, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) xstateregs, 0) == -1)
+    perror_with_name (_("Couldn't get extended state status"));
 
+  i387_collect_xsave (regcache, -1, xstateregs, 0);
+
+  if (ptrace (PT_SETXSTATE, ptid_get_pid (inferior_ptid),
+      (PTRACE_TYPE_ARG3) xstateregs, x86_xsave_len) == -1)
+    perror_with_name (_("Couldn't write extended state status"));
+  return;
+ }
+#endif
+
+#ifdef HAVE_PT_GETXMMREGS
       if (have_ptrace_xmmregs != 0
   && ptrace(PT_GETXMMREGS, ptid_get_pid (inferior_ptid),
     (PTRACE_TYPE_ARG3) xmmregs, 0) == 0)
diff --git a/gdb/i386bsd-nat.h b/gdb/i386bsd-nat.h
index a11f554..f086a6c 100644
--- a/gdb/i386bsd-nat.h
+++ b/gdb/i386bsd-nat.h
@@ -25,6 +25,9 @@
 
 extern struct target_ops *i386bsd_target (void);
 
+/* Low level i386 XSAVE info. */
+extern size_t x86_xsave_len;
+
 /* low level i386 debug register functions used in i386fbsd-nat.c.  */
 
 extern void i386bsd_dr_set_control (unsigned long control);
diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c
index ad439e3..ac8c2e9 100644
--- a/gdb/i386fbsd-nat.c
+++ b/gdb/i386fbsd-nat.c
@@ -31,6 +31,7 @@
 #include "i386-tdep.h"
 #include "x86-nat.h"
 #include "i386bsd-nat.h"
+#include "x86-xstate.h"
 
 /* Resume execution of the inferior process.  If STEP is nonzero,
    single-step it.  If SIGNAL is nonzero, give it that signal.  */
@@ -116,6 +117,46 @@ i386fbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb)
 }
 
 
+#ifdef PT_GETXSTATE_INFO
+static const struct target_desc *
+i386fbsd_read_description (struct target_ops *ops)
+{
+  static int xsave_probed;
+  static uint64_t xcr0;
+
+  if (!xsave_probed)
+    {
+      struct ptrace_xstate_info info;
+
+      if (ptrace (PT_GETXSTATE_INFO, ptid_get_pid (inferior_ptid),
+  (PTRACE_TYPE_ARG3) &info, sizeof(info)) == 0)
+ {
+  x86_xsave_len = info.xsave_len;
+  xcr0 = info.xsave_mask;
+ }
+      xsave_probed = 1;
+    }
+
+  if (x86_xsave_len != 0)
+    {
+      switch (xcr0 & X86_XSTATE_ALL_MASK)
+ {
+ case X86_XSTATE_MPX_AVX512_MASK:
+ case X86_XSTATE_AVX512_MASK:
+  return tdesc_i386_avx512;
+ case X86_XSTATE_MPX_MASK:
+  return tdesc_i386_mpx;
+ case X86_XSTATE_AVX_MASK:
+  return tdesc_i386_avx;
+ default:
+  return tdesc_i386;
+ }
+    }
+  else
+    return tdesc_i386;
+}
+#endif
+
 /* Prevent warning from -Wmissing-prototypes.  */
 void _initialize_i386fbsd_nat (void);
 
@@ -140,6 +181,9 @@ _initialize_i386fbsd_nat (void)
 
 #endif /* HAVE_PT_GETDBREGS */
 
+#ifdef PT_GETXSTATE_INFO
+  t->to_read_description = i386fbsd_read_description;
+#endif
 
   t->to_resume = i386fbsd_resume;
   t->to_pid_to_exec_file = fbsd_pid_to_exec_file;
diff --git a/gdb/i386fbsd-tdep.c b/gdb/i386fbsd-tdep.c
index ed41706..1e7e124 100644
--- a/gdb/i386fbsd-tdep.c
+++ b/gdb/i386fbsd-tdep.c
@@ -22,6 +22,9 @@
 #include "gdbcore.h"
 #include "osabi.h"
 #include "regcache.h"
+#include "regset.h"
+#include "i386fbsd-tdep.h"
+#include "x86-xstate.h"
 
 #include "i386-tdep.h"
 #include "i387-tdep.h"
@@ -235,6 +238,113 @@ static int i386fbsd_jmp_buf_reg_offset[] =
   0 * 4 /* %eip */
 };
 
+/* Get XSAVE extended state xcr0 from core dump.  */
+
+uint64_t
+i386fbsd_core_read_xcr0 (bfd *abfd)
+{
+  asection *xstate = bfd_get_section_by_name (abfd, ".reg-xstate");
+  uint64_t xcr0;
+
+  if (xstate)
+    {
+      size_t size = bfd_section_size (abfd, xstate);
+
+      /* Check extended state size.  */
+      if (size < X86_XSTATE_AVX_SIZE)
+ xcr0 = X86_XSTATE_SSE_MASK;
+      else
+ {
+  char contents[8];
+
+  if (! bfd_get_section_contents (abfd, xstate, contents,
+  I386_FBSD_XSAVE_XCR0_OFFSET,
+  8))
+    {
+      warning (_("Couldn't read `xcr0' bytes from "
+ "`.reg-xstate' section in core file."));
+      return 0;
+    }
+
+  xcr0 = bfd_get_64 (abfd, contents);
+ }
+    }
+  else
+    xcr0 = 0;
+
+  return xcr0;
+}
+
+static const struct target_desc *
+i386fbsd_core_read_description (struct gdbarch *gdbarch,
+ struct target_ops *target,
+ bfd *abfd)
+{
+  uint64_t xcr0 = i386fbsd_core_read_xcr0 (abfd);
+
+  switch (xcr0 & X86_XSTATE_ALL_MASK)
+    {
+    case X86_XSTATE_MPX_AVX512_MASK:
+    case X86_XSTATE_AVX512_MASK:
+      return tdesc_i386_avx512;
+    case X86_XSTATE_MPX_MASK:
+      return tdesc_i386_mpx;
+    case X86_XSTATE_AVX_MASK:
+      return tdesc_i386_avx;
+    default:
+      return tdesc_i386;
+    }
+}
+
+/* Similar to i386_supply_fpregset, but use XSAVE extended state.  */
+
+static void
+i386fbsd_supply_xstateregset (const struct regset *regset,
+      struct regcache *regcache, int regnum,
+      const void *xstateregs, size_t len)
+{
+  i387_supply_xsave (regcache, regnum, xstateregs);
+}
+
+/* Similar to i386_collect_fpregset, but use XSAVE extended state.  */
+
+static void
+i386fbsd_collect_xstateregset (const struct regset *regset,
+       const struct regcache *regcache,
+       int regnum, void *xstateregs, size_t len)
+{
+  i387_collect_xsave (regcache, regnum, xstateregs, 1);
+}
+
+/* Register set definitions.  */
+
+static const struct regset i386fbsd_xstateregset =
+  {
+    NULL,
+    i386fbsd_supply_xstateregset,
+    i386fbsd_collect_xstateregset,
+    REGSET_VARIABLE_SIZE
+  };
+
+/* Iterate over core file register note sections.  */
+
+static void
+i386fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
+       iterate_over_regset_sections_cb *cb,
+       void *cb_data,
+       const struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  cb (".reg", tdep->sizeof_gregset, &i386_gregset, NULL, cb_data);
+  cb (".reg2", tdep->sizeof_fpregset, &i386_fpregset, NULL, cb_data);
+
+  if (tdep->xcr0 & X86_XSTATE_AVX)
+    /* Use max size for writing, accept any size when reading.  */
+    cb (".reg-xstate", regcache ? X86_XSTATE_MAX_SIZE : 0,
+ &i386fbsd_xstateregset, "XSAVE extended state", cb_data);
+}
+
 static void
 i386fbsd_supply_uthread (struct regcache *regcache,
  int regnum, CORE_ADDR addr)
@@ -376,6 +486,15 @@ i386fbsd4_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* FreeBSD 4.0 introduced a new `struct sigcontext'.  */
   tdep->sc_reg_offset = i386fbsd4_sc_reg_offset;
   tdep->sc_num_regs = ARRAY_SIZE (i386fbsd4_sc_reg_offset);
+
+  tdep->xsave_xcr0_offset = I386_FBSD_XSAVE_XCR0_OFFSET;
+
+  /* Iterate over core file register note sections.  */
+  set_gdbarch_iterate_over_regset_sections
+    (gdbarch, i386fbsd_iterate_over_regset_sections);
+
+  set_gdbarch_core_read_description (gdbarch,
+     i386fbsd_core_read_description);
 }
 
 
diff --git a/gdb/i386fbsd-tdep.h b/gdb/i386fbsd-tdep.h
new file mode 100644
index 0000000..9a39ee1
--- /dev/null
+++ b/gdb/i386fbsd-tdep.h
@@ -0,0 +1,33 @@
+/* Target-dependent code for FreeBSD x86.
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef I386FBSD_TDEP_H
+#define I386FBSD_TDEP_H
+
+/* Get XSAVE extended state xcr0 from core dump.  */
+extern uint64_t i386fbsd_core_read_xcr0 (bfd *abfd);
+
+/*
+ * The format of the XSAVE extended area is determined by hardware.
+ * Cores store the XSAVE extended area in a NT_X86_XSTATE note that
+ * matches the layout on Linux.
+ */
+#define I386_FBSD_XSAVE_XCR0_OFFSET 464
+
+#endif /* i386fbsd-tdep.h */
--
2.2.1

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Add support for the x86 XSAVE extended state on FreeBSD/x86.

Pedro Alves-7
Hi John,

The bfd/ changes are owned by binutils@.  The repo is the same,
so you'll end up pushing it all in one commit, but you'll need an
OK from the binutils maintainers for that bit.  Please resend
with binutils@ in TO/CC.

Overall this looks very good to me.  Mark do you have comments?

> @@ -9490,6 +9493,8 @@ elfcore_write_xstatereg (bfd *abfd, char *buf, int *bufsiz,
>   const void *xfpregs, int size)
>  {
>    char *note_name = "LINUX";
> +  if (get_elf_backend_data (abfd)->elf_osabi == ELFOSABI_FREEBSD)
> +      note_name = "FreeBSD";

Alignment here looks odd.

>    return elfcore_write_note (abfd, buf, bufsiz,
>       note_name, NT_X86_XSTATE, xfpregs, size);
>  }

We've been trying to make sure that all functions have
an intro comment.  For hook implementations, the comment
should just point at the hook implemented.  Something like:

/* Implement the to_read_description method.  */

>  
> +static const struct target_desc *
> +amd64fbsd_read_description (struct target_ops *ops)
> +{
> +#ifdef PT_GETXSTATE_INFO
> +  static int xsave_probed;
> +  static uint64_t xcr0;
> +#endif
> +  struct reg regs;
> +  int is64;
> +
> +  if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
> +      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
> +    perror_with_name (_("Couldn't get registers"));
> +  is64 = (regs.r_cs == GSEL (GUCODE_SEL, SEL_UPL));
> +#ifdef PT_GETXSTATE_INFO
> +  if (!xsave_probed)
> +    {
> +      struct ptrace_xstate_info info;
> +
> +      if (ptrace (PT_GETXSTATE_INFO, ptid_get_pid (inferior_ptid),
> +  (PTRACE_TYPE_ARG3) &info, sizeof(info)) == 0)

Space before parens.

> +  if (x86_xsave_len != 0)
> +    {
> +      switch (xcr0 & X86_XSTATE_ALL_MASK)
> + {
> + case X86_XSTATE_MPX_AVX512_MASK:
> + case X86_XSTATE_AVX512_MASK:
> +  if (is64)
> +    return tdesc_amd64_avx512;
> +  else
> +    return tdesc_i386_avx512;
> + case X86_XSTATE_MPX_MASK:
> +  if (is64)
> +    return tdesc_amd64_mpx;
> +  else
> +    return tdesc_i386_mpx;
> + case X86_XSTATE_AVX_MASK:
> +  if (is64)
> +    return tdesc_amd64_avx;
> +  else
> +    return tdesc_i386_avx;
> + default:
> +  if (is64)
> +    return tdesc_amd64;
> +  else
> +    return tdesc_i386;
> + }

These xcr0 -> tdesc mappings need to appear in multiple places.
I wonder whether it'd make sense to put them in a single helper
function (in the fbsd tdep file) that takes "xcr0" and "is64" as
parameters, and returns the corresponding tdesc.


> @@ -169,6 +172,71 @@ static int amd64fbsd_jmp_buf_reg_offset[] =
>    0 * 8 /* %rip */
>  };
>  
> +static const struct target_desc *
> +amd64fbsd_core_read_description (struct gdbarch *gdbarch,
> + struct target_ops *target,
> + bfd *abfd)

Likewise, comment.

> +/* Iterate over core file register note sections.  */
> +
> +static void
> +amd64fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
> + iterate_over_regset_sections_cb *cb,
> + void *cb_data,
> + const struct regcache *regcache)
> +{
> +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> +
> +  cb (".reg", tdep->sizeof_gregset, &i386_gregset, NULL, cb_data);
> +  cb (".reg2", tdep->sizeof_fpregset, &amd64_fpregset, NULL, cb_data);
> +  cb (".reg-xstate", regcache ? X86_XSTATE_MAX_SIZE : 0,
> +      &amd64fbsd_xstateregset, "XSAVE extended state", cb_data);

I think you'll want to update this per:

commit dde9acd693251ccbe28d2d9c6c8b3cdc8ca884ed
Author:     Andreas Arnez <[hidden email]>
AuthorDate: Wed Jan 14 17:53:23 2015 +0000

    x86: Use correct .reg-xstate section size

Thanks,
Pedro Alves

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Add support for the x86 XSAVE extended state on FreeBSD/x86.

John Baldwin
On 3/18/15 3:59 PM, Pedro Alves wrote:
> Hi John,
>
> The bfd/ changes are owned by binutils@.  The repo is the same,
> so you'll end up pushing it all in one commit, but you'll need an
> OK from the binutils maintainers for that bit.  Please resend
> with binutils@ in TO/CC.

Will do for the updated patch.

> Overall this looks very good to me.  Mark do you have comments?
>
>> @@ -9490,6 +9493,8 @@ elfcore_write_xstatereg (bfd *abfd, char *buf, int *bufsiz,
>>   const void *xfpregs, int size)
>>  {
>>    char *note_name = "LINUX";
>> +  if (get_elf_backend_data (abfd)->elf_osabi == ELFOSABI_FREEBSD)
>> +      note_name = "FreeBSD";
>
> Alignment here looks odd.

Yes, will fix.
 
>>    return elfcore_write_note (abfd, buf, bufsiz,
>>       note_name, NT_X86_XSTATE, xfpregs, size);
>>  }
>
> We've been trying to make sure that all functions have
> an intro comment.  For hook implementations, the comment
> should just point at the hook implemented.  Something like:
>
> /* Implement the to_read_description method.  */

Ok.

>> +      if (ptrace (PT_GETXSTATE_INFO, ptid_get_pid (inferior_ptid),
>> +  (PTRACE_TYPE_ARG3) &info, sizeof(info)) == 0)
>
> Space before parens.

Ok.
 

>> +  if (x86_xsave_len != 0)
>> +    {
>> +      switch (xcr0 & X86_XSTATE_ALL_MASK)
>> + {
>> + case X86_XSTATE_MPX_AVX512_MASK:
>> + case X86_XSTATE_AVX512_MASK:
>> +  if (is64)
>> +    return tdesc_amd64_avx512;
>> +  else
>> +    return tdesc_i386_avx512;
>> + case X86_XSTATE_MPX_MASK:
>> +  if (is64)
>> +    return tdesc_amd64_mpx;
>> +  else
>> +    return tdesc_i386_mpx;
>> + case X86_XSTATE_AVX_MASK:
>> +  if (is64)
>> +    return tdesc_amd64_avx;
>> +  else
>> +    return tdesc_i386_avx;
>> + default:
>> +  if (is64)
>> +    return tdesc_amd64;
>> +  else
>> +    return tdesc_i386;
>> + }
>
> These xcr0 -> tdesc mappings need to appear in multiple places.
> I wonder whether it'd make sense to put them in a single helper
> function (in the fbsd tdep file) that takes "xcr0" and "is64" as
> parameters, and returns the corresponding tdesc.

There are a couple of options I've thought about for this.  One
has been to have a shared to_read_description implementation in
an x86fbsd-nat.c (Linux uses a shared one in x86-linux-nat.c).
However, these case statements are also not really FreeBSD (or BSD)
specific.  What if I added functions in amd64-tdep.c and i386-tdep.c
that returned the correct target description for a given xcr0
value?  Something like:

struct target_desc *
i386_target_description(uint64_t xcr0)
{

  /* i386 switch statement here */
}

That could be reused for the core read_description callback as
well as the native ones.  This could also be reused by other
systems that grow XSAVE support in the future.


>> +/* Iterate over core file register note sections.  */
>> +
>> +static void
>> +amd64fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
>> + iterate_over_regset_sections_cb *cb,
>> + void *cb_data,
>> + const struct regcache *regcache)
>> +{
>> +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
>> +
>> +  cb (".reg", tdep->sizeof_gregset, &i386_gregset, NULL, cb_data);
>> +  cb (".reg2", tdep->sizeof_fpregset, &amd64_fpregset, NULL, cb_data);
>> +  cb (".reg-xstate", regcache ? X86_XSTATE_MAX_SIZE : 0,
>> +      &amd64fbsd_xstateregset, "XSAVE extended state", cb_data);
>
> I think you'll want to update this per:
>
> commit dde9acd693251ccbe28d2d9c6c8b3cdc8ca884ed
> Author:     Andreas Arnez <[hidden email]>
> AuthorDate: Wed Jan 14 17:53:23 2015 +0000
>
>     x86: Use correct .reg-xstate section size

Ah, yes, thanks for the pointer.  I wanted to do that anyway to
generate more accurate notes for gcore.

--
John Baldwin
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Add support for the x86 XSAVE extended state on FreeBSD/x86.

Mark Kettenis
> Date: Sat, 21 Mar 2015 15:14:48 -0400
> From: John Baldwin <[hidden email]>
>
> >> +  if (x86_xsave_len != 0)
> >> +    {
> >> +      switch (xcr0 & X86_XSTATE_ALL_MASK)
> >> + {
> >> + case X86_XSTATE_MPX_AVX512_MASK:
> >> + case X86_XSTATE_AVX512_MASK:
> >> +  if (is64)
> >> +    return tdesc_amd64_avx512;
> >> +  else
> >> +    return tdesc_i386_avx512;
> >> + case X86_XSTATE_MPX_MASK:
> >> +  if (is64)
> >> +    return tdesc_amd64_mpx;
> >> +  else
> >> +    return tdesc_i386_mpx;
> >> + case X86_XSTATE_AVX_MASK:
> >> +  if (is64)
> >> +    return tdesc_amd64_avx;
> >> +  else
> >> +    return tdesc_i386_avx;
> >> + default:
> >> +  if (is64)
> >> +    return tdesc_amd64;
> >> +  else
> >> +    return tdesc_i386;
> >> + }
> >
> > These xcr0 -> tdesc mappings need to appear in multiple places.
> > I wonder whether it'd make sense to put them in a single helper
> > function (in the fbsd tdep file) that takes "xcr0" and "is64" as
> > parameters, and returns the corresponding tdesc.
>
> There are a couple of options I've thought about for this.  One
> has been to have a shared to_read_description implementation in
> an x86fbsd-nat.c (Linux uses a shared one in x86-linux-nat.c).
> However, these case statements are also not really FreeBSD (or BSD)
> specific.  What if I added functions in amd64-tdep.c and i386-tdep.c
> that returned the correct target description for a given xcr0
> value?  Something like:
>
> struct target_desc *
> i386_target_description(uint64_t xcr0)
> {
>
>   /* i386 switch statement here */
> }
>
> That could be reused for the core read_description callback as
> well as the native ones.  This could also be reused by other
> systems that grow XSAVE support in the future.

Probably a good idea.  I'm working on XSAVE support in the OpenBSD
kernel, so I'll eventually need this as well.

I have no real objection to adding the ptrace-specific bits to the
generic BSD native code like your diff is doing.  I'll probably try to
use the same interface for my OpenBSD implementation.  I have one
concern about that code though.  The _supply_xsave() and
_collect_xsave() functions don't accept a length, so they can't do any
bounds checking.  Therefore, 'xstat_bv' as returned by the kernel must
be set correctly (i.e. not have bits sets that imply state beyond
x86_save_len is present.  Does the FreeBSD kernel guarantee that?

Oh, and please rename x86_xsave_len into amd64bsd_xsave_len and
i386bsd_xsave_len to keep the "namesapce" clean.

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Add support for the x86 XSAVE extended state on FreeBSD/x86.

John Baldwin
On 3/21/15 4:18 PM, Mark Kettenis wrote:

>> That could be reused for the core read_description callback as
>> well as the native ones.  This could also be reused by other
>> systems that grow XSAVE support in the future.
>
> Probably a good idea.  I'm working on XSAVE support in the OpenBSD
> kernel, so I'll eventually need this as well.
>
> I have no real objection to adding the ptrace-specific bits to the
> generic BSD native code like your diff is doing.  I'll probably try to
> use the same interface for my OpenBSD implementation.  I have one
> concern about that code though.  The _supply_xsave() and
> _collect_xsave() functions don't accept a length, so they can't do any
> bounds checking.  Therefore, 'xstat_bv' as returned by the kernel must
> be set correctly (i.e. not have bits sets that imply state beyond
> x86_save_len is present.  Does the FreeBSD kernel guarantee that?

FreeBSD determines a system-wide xsave mask during boot and then leaves
%xcr0 unchanged.  The x86_xstate_len returned by PT_GETXSTATE_INFO is
also determined at boot time and doesn't change after that.  The kernel
does assume that xsave/xsaveopt do not store any bits in xstate_bv that
aren't set in %xcr0.  Attempts to restore a saved state via PT_SETXSTATE
that set a bit in xstate_bv that isn't set in %xcr0 results in
PT_SETXSTATE failing.  Those should ensure that xstate_bv does not
return an invalid length.

However, we could do additional bounds checking in the routines in
*bsd-nat.c by making them compare X86_XSTATE_SIZE(xstate_bv) against
x86_xstate_len and fail if x86_xstate_len is too small.

> Oh, and please rename x86_xsave_len into amd64bsd_xsave_len and
> i386bsd_xsave_len to keep the "namesapce" clean.

Will do.

--
John Baldwin