]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/amd64-linux-nat.c
gdb, gdbserver, gdbsupport: remove includes of early headers
[thirdparty/binutils-gdb.git] / gdb / amd64-linux-nat.c
index c2f189d8bdb8049fdb20c4c8dabd49353ad19210..aa9295d57230d4f559f6b5c346658b786c0b0688 100644 (file)
@@ -1,6 +1,6 @@
 /* Native-dependent code for GNU/Linux x86-64.
 
-   Copyright (C) 2001-2019 Free Software Foundation, Inc.
+   Copyright (C) 2001-2024 Free Software Foundation, Inc.
    Contributed by Jiri Smid, SuSE Labs.
 
    This file is part of GDB.
@@ -18,7 +18,6 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#include "defs.h"
 #include "inferior.h"
 #include "regcache.h"
 #include "elf/common.h"
@@ -33,7 +32,7 @@
 #include "amd64-tdep.h"
 #include "amd64-linux-tdep.h"
 #include "i386-linux-tdep.h"
-#include "x86-xstate.h"
+#include "gdbsupport/x86-xstate.h"
 
 #include "x86-linux-nat.h"
 #include "nat/linux-ptrace.h"
@@ -92,6 +91,71 @@ static int amd64_linux_gregset32_reg_offset[] =
 /* Transfering the general-purpose registers between GDB, inferiors
    and core files.  */
 
+/* See amd64_collect_native_gregset.  This linux specific version handles
+   issues with negative EAX values not being restored correctly upon syscall
+   return when debugging 32-bit targets.  It has no effect on 64-bit
+   targets.  */
+
+static void
+amd64_linux_collect_native_gregset (const struct regcache *regcache,
+                                   void *gregs, int regnum)
+{
+  amd64_collect_native_gregset (regcache, gregs, regnum);
+
+  struct gdbarch *gdbarch = regcache->arch ();
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    {
+      /* Sign extend EAX value to avoid potential syscall restart
+        problems.  
+
+        On Linux, when a syscall is interrupted by a signal, the
+        (kernel function implementing the) syscall may return
+        -ERESTARTSYS when a signal occurs.  Doing so indicates that
+        the syscall is restartable.  Then, depending on settings
+        associated with the signal handler, and after the signal
+        handler is called, the kernel can then either return -EINTR
+        or it can cause the syscall to be restarted.  We are
+        concerned with the latter case here.
+        
+        On (32-bit) i386, the status (-ERESTARTSYS) is placed in the
+        EAX register.  When debugging a 32-bit process from a 64-bit
+        (amd64) GDB, the debugger fetches 64-bit registers even
+        though the process being debugged is only 32-bit.  The
+        register cache is only 32 bits wide though; GDB discards the
+        high 32 bits when placing 64-bit values in the 32-bit
+        regcache.  Normally, this is not a problem since the 32-bit
+        process should only care about the lower 32-bit portions of
+        these registers.  That said, it can happen that the 64-bit
+        value being restored will be different from the 64-bit value
+        that was originally retrieved from the kernel.  The one place
+        (that we know of) where it does matter is in the kernel's
+        syscall restart code.  The kernel's code for restarting a
+        syscall after a signal expects to see a negative value
+        (specifically -ERESTARTSYS) in the 64-bit RAX register in
+        order to correctly cause a syscall to be restarted.
+        
+        The call to amd64_collect_native_gregset, above, is setting
+        the high 32 bits of RAX (and other registers too) to 0.  For
+        syscall restart, we need to sign extend EAX so that RAX will
+        appear as a negative value when EAX is set to -ERESTARTSYS. 
+        This in turn will cause the signal handling code in the
+        kernel to recognize -ERESTARTSYS which will in turn cause the
+        syscall to be restarted.
+
+        The test case gdb.base/interrupt.exp tests for this problem.
+        Without this sign extension code in place, it'll show
+        a number of failures when testing against unix/-m32.  */
+
+      if (regnum == -1 || regnum == I386_EAX_REGNUM)
+       {
+         void *ptr = ((gdb_byte *) gregs 
+                      + amd64_linux_gregset32_reg_offset[I386_EAX_REGNUM]);
+
+         *(int64_t *) ptr = *(int32_t *) ptr;
+       }
+    }
+}
+
 /* Fill GDB's register cache with the general-purpose register values
    in *GREGSETP.  */
 
@@ -109,7 +173,7 @@ void
 fill_gregset (const struct regcache *regcache,
              elf_gregset_t *gregsetp, int regnum)
 {
-  amd64_collect_native_gregset (regcache, gregsetp, regnum);
+  amd64_linux_collect_native_gregset (regcache, gregsetp, regnum);
 }
 
 /* Transfering floating-point registers between GDB, inferiors and cores.  */
@@ -145,6 +209,7 @@ void
 amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
 {
   struct gdbarch *gdbarch = regcache->arch ();
+  const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
   int tid;
 
   /* GNU/Linux LWP ID's are process ID's.  */
@@ -170,9 +235,15 @@ amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
 
       if (have_ptrace_getregset == TRIBOOL_TRUE)
        {
-         char xstateregs[X86_XSTATE_MAX_SIZE];
+         char xstateregs[tdep->xsave_layout.sizeof_xsave];
          struct iovec iov;
 
+         /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
+            "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
+            Intel Skylake CPUs") that sometimes causes the mxcsr location in
+            xstateregs not to be copied by PTRACE_GETREGSET.  Make sure that
+            the location is at least initialized with a defined value.  */
+         memset (xstateregs, 0, sizeof (xstateregs));
          iov.iov_base = xstateregs;
          iov.iov_len = sizeof (xstateregs);
          if (ptrace (PTRACE_GETREGSET, tid,
@@ -188,30 +259,6 @@ amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
 
          amd64_supply_fxsave (regcache, -1, &fpregs);
        }
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
-      {
-       /* PTRACE_ARCH_PRCTL is obsolete since 2.6.25, where the
-          fs_base and gs_base fields of user_regs_struct can be
-          used directly.  */
-       unsigned long base;
-
-       if (regnum == -1 || regnum == AMD64_FSBASE_REGNUM)
-         {
-           if (ptrace (PTRACE_ARCH_PRCTL, tid, &base, ARCH_GET_FS) < 0)
-             perror_with_name (_("Couldn't get segment register fs_base"));
-
-           regcache->raw_supply (AMD64_FSBASE_REGNUM, &base);
-         }
-
-       if (regnum == -1 || regnum == AMD64_GSBASE_REGNUM)
-         {
-           if (ptrace (PTRACE_ARCH_PRCTL, tid, &base, ARCH_GET_GS) < 0)
-             perror_with_name (_("Couldn't get segment register gs_base"));
-
-           regcache->raw_supply (AMD64_GSBASE_REGNUM, &base);
-         }
-      }
-#endif
     }
 }
 
@@ -223,6 +270,7 @@ void
 amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
 {
   struct gdbarch *gdbarch = regcache->arch ();
+  const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
   int tid;
 
   /* GNU/Linux LWP ID's are process ID's.  */
@@ -237,7 +285,7 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
       if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
        perror_with_name (_("Couldn't get registers"));
 
-      amd64_collect_native_gregset (regcache, &regs, regnum);
+      amd64_linux_collect_native_gregset (regcache, &regs, regnum);
 
       if (ptrace (PTRACE_SETREGS, tid, 0, (long) &regs) < 0)
        perror_with_name (_("Couldn't write registers"));
@@ -252,7 +300,7 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
 
       if (have_ptrace_getregset == TRIBOOL_TRUE)
        {
-         char xstateregs[X86_XSTATE_MAX_SIZE];
+         char xstateregs[tdep->xsave_layout.sizeof_xsave];
          struct iovec iov;
 
          iov.iov_base = xstateregs;
@@ -277,30 +325,6 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
          if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
            perror_with_name (_("Couldn't write floating point status"));
        }
-
-#ifndef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
-      {
-       /* PTRACE_ARCH_PRCTL is obsolete since 2.6.25, where the
-          fs_base and gs_base fields of user_regs_struct can be
-          used directly.  */
-       void *base;
-
-       if (regnum == -1 || regnum == AMD64_FSBASE_REGNUM)
-         {
-           regcache->raw_collect (AMD64_FSBASE_REGNUM, &base);
-
-           if (ptrace (PTRACE_ARCH_PRCTL, tid, base, ARCH_SET_FS) < 0)
-             perror_with_name (_("Couldn't write segment register fs_base"));
-         }
-       if (regnum == -1 || regnum == AMD64_GSBASE_REGNUM)
-         {
-
-           regcache->raw_collect (AMD64_GSBASE_REGNUM, &base);
-           if (ptrace (PTRACE_ARCH_PRCTL, tid, base, ARCH_SET_GS) < 0)
-             perror_with_name (_("Couldn't write segment register gs_base"));
-         }
-      }
-#endif
     }
 }
 \f
@@ -310,9 +334,9 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
 
 ps_err_e
 ps_get_thread_area (struct ps_prochandle *ph,
-                    lwpid_t lwpid, int idx, void **base)
+                   lwpid_t lwpid, int idx, void **base)
 {
-  if (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 32)
+  if (gdbarch_bfd_arch_info (ph->thread->inf->arch ())->bits_per_word == 32)
     {
       unsigned int base_addr;
       ps_err_e result;
@@ -337,11 +361,7 @@ ps_get_thread_area (struct ps_prochandle *ph,
       switch (idx)
        {
        case FS:
-#ifdef HAVE_STRUCT_USER_REGS_STRUCT_FS_BASE
            {
-             /* PTRACE_ARCH_PRCTL is obsolete since 2.6.25, where the
-                fs_base and gs_base fields of user_regs_struct can be
-                used directly.  */
              unsigned long fs;
              errno = 0;
              fs = ptrace (PTRACE_PEEKUSER, lwpid,
@@ -352,12 +372,10 @@ ps_get_thread_area (struct ps_prochandle *ph,
                  return PS_OK;
                }
            }
-#endif
-         if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_FS) == 0)
-           return PS_OK;
+
          break;
+
        case GS:
-#ifdef HAVE_STRUCT_USER_REGS_STRUCT_GS_BASE
            {
              unsigned long gs;
              errno = 0;
@@ -369,10 +387,8 @@ ps_get_thread_area (struct ps_prochandle *ph,
                  return PS_OK;
                }
            }
-#endif
-         if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
-           return PS_OK;
          break;
+
        default:                   /* Should not happen.  */
          return PS_BADADDR;
        }
@@ -407,8 +423,9 @@ amd64_linux_nat_target::low_siginfo_fixup (siginfo_t *ptrace,
     return false;
 }
 
+void _initialize_amd64_linux_nat ();
 void
-_initialize_amd64_linux_nat (void)
+_initialize_amd64_linux_nat ()
 {
   amd64_native_gregset32_reg_offset = amd64_linux_gregset32_reg_offset;
   amd64_native_gregset32_num_regs = I386_LINUX_NUM_REGS;