]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/amd64-linux-nat.c
"backtrace full/no-filters/hide" completer
[thirdparty/binutils-gdb.git] / gdb / amd64-linux-nat.c
index 72aa73ccd3df63e18edb134814202c5bafdb5983..8d0e8eb35cd2bd3af599cae48f888044d3393663 100644 (file)
@@ -1,13 +1,13 @@
 /* Native-dependent code for GNU/Linux x86-64.
 
-   Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2001-2019 Free Software Foundation, Inc.
    Contributed by Jiri Smid, SuSE Labs.
 
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
 #include "inferior.h"
-#include "gdbcore.h"
 #include "regcache.h"
-#include "linux-nat.h"
-
-#include "gdb_assert.h"
-#include "gdb_string.h"
-#include <sys/ptrace.h>
-#include <sys/debugreg.h>
-#include <sys/syscall.h>
-#include <sys/procfs.h>
+#include "elf/common.h"
+#include <sys/uio.h>
+#include "nat/gdb_ptrace.h"
 #include <asm/prctl.h>
-/* FIXME ezannoni-2003-07-09: we need <sys/reg.h> to be included after
-   <asm/ptrace.h> because the latter redefines FS and GS for no apparent
-   reason, and those definitions don't match the ones that libpthread_db
-   uses, which come from <sys/reg.h>.  */
-/* ezannoni-2003-07-09: I think this is fixed. The extraneous defs have
-   been removed from ptrace.h in the kernel.  However, better safe than
-   sorry.  */
-#include <asm/ptrace.h>
 #include <sys/reg.h>
-#include "gdb_proc_service.h"
-
-/* Prototypes for supply_gregset etc.  */
 #include "gregset.h"
+#include "gdb_proc_service.h"
 
+#include "amd64-nat.h"
 #include "amd64-tdep.h"
+#include "amd64-linux-tdep.h"
 #include "i386-linux-tdep.h"
-#include "amd64-nat.h"
+#include "common/x86-xstate.h"
 
-/* Mapping between the general-purpose registers in GNU/Linux x86-64
-   `struct user' format and GDB's register cache layout.  */
+#include "x86-linux-nat.h"
+#include "nat/linux-ptrace.h"
+#include "nat/amd64-linux-siginfo.h"
 
-static int amd64_linux_gregset64_reg_offset[] =
+/* This definition comes from prctl.h.  Kernels older than 2.5.64
+   do not have it.  */
+#ifndef PTRACE_ARCH_PRCTL
+#define PTRACE_ARCH_PRCTL      30
+#endif
+
+struct amd64_linux_nat_target final : public x86_linux_nat_target
 {
-  RAX * 8, RBX * 8,            /* %rax, %rbx */
-  RCX * 8, RDX * 8,            /* %rcx, %rdx */
-  RSI * 8, RDI * 8,            /* %rsi, %rdi */
-  RBP * 8, RSP * 8,            /* %rbp, %rsp */
-  R8 * 8, R9 * 8,              /* %r8 ... */
-  R10 * 8, R11 * 8,
-  R12 * 8, R13 * 8,
-  R14 * 8, R15 * 8,            /* ... %r15 */
-  RIP * 8, EFLAGS * 8,         /* %rip, %eflags */
-  CS * 8, SS * 8,              /* %cs, %ss */
-  DS * 8, ES * 8,              /* %ds, %es */
-  FS * 8, GS * 8               /* %fs, %gs */
+  /* Add our register access methods.  */
+  void fetch_registers (struct regcache *, int) override;
+  void store_registers (struct regcache *, int) override;
+
+  bool low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
+    override;
 };
-\f
+
+static amd64_linux_nat_target the_amd64_linux_nat_target;
 
 /* Mapping between the general-purpose registers in GNU/Linux x86-64
    `struct user' format and GDB's register cache layout for GNU/Linux
@@ -93,20 +79,91 @@ static int amd64_linux_gregset32_reg_offset[] =
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1,
-  ORIG_RAX * 8                 /* "orig_eax" */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1,                /* MPX registers BND0 ... BND3.  */
+  -1, -1,                        /* MPX registers BNDCFGU, BNDSTATUS.  */
+  -1, -1, -1, -1, -1, -1, -1, -1, /* k0 ... k7 (AVX512)  */
+  -1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm7 (AVX512)  */
+  -1,                            /* PKEYS register PKRU  */
+  ORIG_RAX * 8                   /* "orig_eax"  */
 };
 \f
 
 /* 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.  */
 
 void
-supply_gregset (elf_gregset_t *gregsetp)
+supply_gregset (struct regcache *regcache, const elf_gregset_t *gregsetp)
 {
-  amd64_supply_native_gregset (current_regcache, gregsetp, -1);
+  amd64_supply_native_gregset (regcache, gregsetp, -1);
 }
 
 /* Fill register REGNUM (if it is a general-purpose register) in
@@ -114,9 +171,10 @@ supply_gregset (elf_gregset_t *gregsetp)
    do this for all registers.  */
 
 void
-fill_gregset (elf_gregset_t *gregsetp, int regnum)
+fill_gregset (const struct regcache *regcache,
+             elf_gregset_t *gregsetp, int regnum)
 {
-  amd64_collect_native_gregset (current_regcache, gregsetp, regnum);
+  amd64_linux_collect_native_gregset (regcache, gregsetp, regnum);
 }
 
 /* Transfering floating-point registers between GDB, inferiors and cores.  */
@@ -125,9 +183,9 @@ fill_gregset (elf_gregset_t *gregsetp, int regnum)
    values in *FPREGSETP.  */
 
 void
-supply_fpregset (elf_fpregset_t *fpregsetp)
+supply_fpregset (struct regcache *regcache, const elf_fpregset_t *fpregsetp)
 {
-  amd64_supply_fxsave (current_regcache, -1, fpregsetp);
+  amd64_supply_fxsave (regcache, -1, fpregsetp);
 }
 
 /* Fill register REGNUM (if it is a floating-point or SSE register) in
@@ -135,9 +193,10 @@ supply_fpregset (elf_fpregset_t *fpregsetp)
    -1, do this for all registers.  */
 
 void
-fill_fpregset (elf_fpregset_t *fpregsetp, int regnum)
+fill_fpregset (const struct regcache *regcache,
+              elf_fpregset_t *fpregsetp, int regnum)
 {
-  amd64_collect_fxsave (current_regcache, regnum, fpregsetp);
+  amd64_collect_fxsave (regcache, regnum, fpregsetp);
 }
 \f
 
@@ -148,35 +207,76 @@ fill_fpregset (elf_fpregset_t *fpregsetp, int regnum)
    registers).  */
 
 void
-fetch_inferior_registers (int regnum)
+amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
 {
+  struct gdbarch *gdbarch = regcache->arch ();
   int tid;
 
   /* GNU/Linux LWP ID's are process ID's.  */
-  tid = TIDGET (inferior_ptid);
+  tid = regcache->ptid ().lwp ();
   if (tid == 0)
-    tid = PIDGET (inferior_ptid); /* Not a threaded program.  */
+    tid = regcache->ptid ().pid (); /* Not a threaded program.  */
 
-  if (regnum == -1 || amd64_native_gregset_supplies_p (regnum))
+  if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       elf_gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
-       perror_with_name ("Couldn't get registers");
+       perror_with_name (_("Couldn't get registers"));
 
-      amd64_supply_native_gregset (current_regcache, &regs, -1);
+      amd64_supply_native_gregset (regcache, &regs, -1);
       if (regnum != -1)
        return;
     }
 
-  if (regnum == -1 || regnum >= AMD64_ST0_REGNUM)
+  if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       elf_fpregset_t fpregs;
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-       perror_with_name ("Couldn't get floating point status");
+      if (have_ptrace_getregset == TRIBOOL_TRUE)
+       {
+         char xstateregs[X86_XSTATE_MAX_SIZE];
+         struct iovec iov;
 
-      amd64_supply_fxsave (current_regcache, -1, &fpregs);
+         iov.iov_base = xstateregs;
+         iov.iov_len = sizeof (xstateregs);
+         if (ptrace (PTRACE_GETREGSET, tid,
+                     (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+           perror_with_name (_("Couldn't get extended state status"));
+
+         amd64_supply_xsave (regcache, -1, xstateregs);
+       }
+      else
+       {
+         if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't get floating point status"));
+
+         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
     }
 }
 
@@ -185,119 +285,88 @@ fetch_inferior_registers (int regnum)
    registers).  */
 
 void
-store_inferior_registers (int regnum)
+amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
 {
+  struct gdbarch *gdbarch = regcache->arch ();
   int tid;
 
   /* GNU/Linux LWP ID's are process ID's.  */
-  tid = TIDGET (inferior_ptid);
+  tid = regcache->ptid ().lwp ();
   if (tid == 0)
-    tid = PIDGET (inferior_ptid); /* Not a threaded program.  */
+    tid = regcache->ptid ().pid (); /* Not a threaded program.  */
 
-  if (regnum == -1 || amd64_native_gregset_supplies_p (regnum))
+  if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       elf_gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
-       perror_with_name ("Couldn't get registers");
+       perror_with_name (_("Couldn't get registers"));
 
-      amd64_collect_native_gregset (current_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");
+       perror_with_name (_("Couldn't write registers"));
 
       if (regnum != -1)
        return;
     }
 
-  if (regnum == -1 || regnum >= AMD64_ST0_REGNUM)
+  if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
       elf_fpregset_t fpregs;
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-       perror_with_name ("Couldn't get floating point status");
-
-      amd64_collect_fxsave (current_regcache, regnum, &fpregs);
-
-      if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
-       perror_with_name ("Couldn't write floating point status");
-
-      return;
-    }
-}
-\f
-
-static unsigned long
-amd64_linux_dr_get (int regnum)
-{
-  int tid;
-  unsigned long value;
-
-  /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
-     multi-threaded processes here.  For now, pretend there is just
-     one thread.  */
-  tid = PIDGET (inferior_ptid);
-
-  /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the
-     ptrace call fails breaks debugging remote targets.  The correct
-     way to fix this is to add the hardware breakpoint and watchpoint
-     stuff to the target vectore.  For now, just return zero if the
-     ptrace call fails.  */
-  errno = 0;
-  value = ptrace (PT_READ_U, tid,
-                 offsetof (struct user, u_debugreg[regnum]), 0);
-  if (errno != 0)
-#if 0
-    perror_with_name ("Couldn't read debug register");
-#else
-    return 0;
-#endif
-
-  return value;
-}
-
-static void
-amd64_linux_dr_set (int regnum, unsigned long value)
-{
-  int tid;
-
-  /* FIXME: kettenis/2001-01-29: It's not clear what we should do with
-     multi-threaded processes here.  For now, pretend there is just
-     one thread.  */
-  tid = PIDGET (inferior_ptid);
-
-  errno = 0;
-  ptrace (PT_WRITE_U, tid, offsetof (struct user, u_debugreg[regnum]), value);
-  if (errno != 0)
-    perror_with_name ("Couldn't write debug register");
-}
+      if (have_ptrace_getregset == TRIBOOL_TRUE)
+       {
+         char xstateregs[X86_XSTATE_MAX_SIZE];
+         struct iovec iov;
 
-void
-amd64_linux_dr_set_control (unsigned long control)
-{
-  amd64_linux_dr_set (DR_CONTROL, control);
-}
+         iov.iov_base = xstateregs;
+         iov.iov_len = sizeof (xstateregs);
+         if (ptrace (PTRACE_GETREGSET, tid,
+                     (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+           perror_with_name (_("Couldn't get extended state status"));
 
-void
-amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
-{
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+         amd64_collect_xsave (regcache, regnum, xstateregs, 0);
 
-  amd64_linux_dr_set (DR_FIRSTADDR + regnum, addr);
-}
+         if (ptrace (PTRACE_SETREGSET, tid,
+                     (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
+           perror_with_name (_("Couldn't write extended state status"));
+       }
+      else
+       {
+         if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't get floating point status"));
 
-void
-amd64_linux_dr_reset_addr (int regnum)
-{
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+         amd64_collect_fxsave (regcache, regnum, &fpregs);
 
-  amd64_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
-}
+         if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't write floating point status"));
+       }
 
-unsigned long
-amd64_linux_dr_get_status (void)
-{
-  return amd64_linux_dr_get (DR_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
 
@@ -305,39 +374,27 @@ amd64_linux_dr_get_status (void)
    a request for a thread's local storage address.  */
 
 ps_err_e
-ps_get_thread_area (const struct ps_prochandle *ph,
+ps_get_thread_area (struct ps_prochandle *ph,
                     lwpid_t lwpid, int idx, void **base)
 {
-  if (gdbarch_ptr_bit (current_gdbarch) == 32)
+  if (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 32)
     {
-      /* The full structure is found in <asm-i386/ldt.h>.  The second
-        integer is the LDT's base_address and that is used to locate
-        the thread's local storage.  See i386-linux-nat.c more
-        info.  */
-      unsigned int desc[4];
-
-      /* This code assumes that "int" is 32 bits and that
-        GET_THREAD_AREA returns no more than 4 int values.  */
-      gdb_assert (sizeof (int) == 4);  
-#ifndef PTRACE_GET_THREAD_AREA
-#define PTRACE_GET_THREAD_AREA 25
-#endif
-      if  (ptrace (PTRACE_GET_THREAD_AREA, 
-                  lwpid, (void *) (long) idx, (unsigned long) &desc) < 0)
-       return PS_ERR;
-      
-      /* Extend the value to 64 bits.  Here it's assumed that a "long"
-        and a "void *" are the same.  */
-      (*base) = (void *) (long) desc[1];
-      return PS_OK;
+      unsigned int base_addr;
+      ps_err_e result;
+
+      result = x86_linux_get_thread_area (lwpid, (void *) (long) idx,
+                                         &base_addr);
+      if (result == PS_OK)
+       {
+         /* Extend the value to 64 bits.  Here it's assumed that
+            a "long" and a "void *" are the same.  */
+         (*base) = (void *) (long) base_addr;
+       }
+      return result;
     }
   else
     {
-      /* This definition comes from prctl.h, but some kernels may not
-         have it.  */
-#ifndef PTRACE_ARCH_PRCTL
-#define PTRACE_ARCH_PRCTL      30
-#endif
+
       /* FIXME: ezannoni-2003-07-09 see comment above about include
         file order.  We could be getting bogus values for these two.  */
       gdb_assert (FS < ELF_NGREG);
@@ -345,10 +402,39 @@ ps_get_thread_area (const 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,
+                          offsetof (struct user_regs_struct, fs_base), 0);
+             if (errno == 0)
+               {
+                 *base = (void *) fs;
+                 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;
+             gs = ptrace (PTRACE_PEEKUSER, lwpid,
+                          offsetof (struct user_regs_struct, gs_base), 0);
+             if (errno == 0)
+               {
+                 *base = (void *) gs;
+                 return PS_OK;
+               }
+           }
+#endif
          if (ptrace (PTRACE_ARCH_PRCTL, lwpid, base, ARCH_GET_GS) == 0)
            return PS_OK;
          break;
@@ -360,26 +446,45 @@ ps_get_thread_area (const struct ps_prochandle *ph,
 }
 \f
 
-void
-child_post_startup_inferior (ptid_t ptid)
+/* Convert a ptrace/host siginfo object, into/from the siginfo in the
+   layout of the inferiors' architecture.  Returns true if any
+   conversion was done; false otherwise.  If DIRECTION is 1, then copy
+   from INF to PTRACE.  If DIRECTION is 0, copy from PTRACE to
+   INF.  */
+
+bool
+amd64_linux_nat_target::low_siginfo_fixup (siginfo_t *ptrace,
+                                          gdb_byte *inf,
+                                          int direction)
 {
-  i386_cleanup_dregs ();
-  linux_child_post_startup_inferior (ptid);
+  struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+
+  /* Is the inferior 32-bit?  If so, then do fixup the siginfo
+     object.  */
+  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+    return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
+                                            FIXUP_32);
+  /* No fixup for native x32 GDB.  */
+  else if (gdbarch_addr_bit (gdbarch) == 32 && sizeof (void *) == 8)
+    return amd64_linux_siginfo_fixup_common (ptrace, inf, direction,
+                                            FIXUP_X32);
+  else
+    return false;
 }
-\f
-
-/* Provide a prototype to silence -Wmissing-prototypes.  */
-void _initialize_amd64_linux_nat (void);
 
 void
 _initialize_amd64_linux_nat (void)
 {
   amd64_native_gregset32_reg_offset = amd64_linux_gregset32_reg_offset;
   amd64_native_gregset32_num_regs = I386_LINUX_NUM_REGS;
-  amd64_native_gregset64_reg_offset = amd64_linux_gregset64_reg_offset;
+  amd64_native_gregset64_reg_offset = amd64_linux_gregset_reg_offset;
+  amd64_native_gregset64_num_regs = AMD64_LINUX_NUM_REGS;
 
   gdb_assert (ARRAY_SIZE (amd64_linux_gregset32_reg_offset)
              == amd64_native_gregset32_num_regs);
-  gdb_assert (ARRAY_SIZE (amd64_linux_gregset64_reg_offset)
-             == amd64_native_gregset64_num_regs);
+
+  linux_target = &the_amd64_linux_nat_target;
+
+  /* Add the target.  */
+  add_inf_child_target (linux_target);
 }