Fix zero_ext documentation
[deliverable/binutils-gdb.git] / gdb / amd64-linux-nat.c
index c3b5765b842b0125ede3ab5c7da998e13dc501dc..391a646992b55967b7203399099da7010ea3d5ca 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-2016 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 "linux-nat.h"
 #include "amd64-tdep.h"
 #include "amd64-linux-tdep.h"
 #include "i386-linux-tdep.h"
-#include "amd64-nat.h"
+#include "x86-xstate.h"
 
-/* Mapping between the general-purpose registers in GNU/Linux x86-64
-   `struct user' format and GDB's register cache layout.  */
-
-static int amd64_linux_gregset64_reg_offset[] =
-{
-  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 */
-};
-\f
+#include "x86-linux-nat.h"
+#include "nat/linux-ptrace.h"
+#include "nat/amd64-linux-siginfo.h"
 
 /* Mapping between the general-purpose registers in GNU/Linux x86-64
    `struct user' format and GDB's register cache layout for GNU/Linux
@@ -94,14 +62,13 @@ 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)  */
+  ORIG_RAX * 8                   /* "orig_eax"  */
 };
-
-/* Which ptrace request retrieves which registers?
-   These apply to the corresponding SET requests as well.  */
-
-#define GETFPREGS_SUPPLIES(regno) \
-  (FP0_REGNUM <= (regno) && (regno) <= MXCSR_REGNUM)
 \f
 
 /* Transfering the general-purpose registers between GDB, inferiors
@@ -111,9 +78,9 @@ static int amd64_linux_gregset32_reg_offset[] =
    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
@@ -121,52 +88,21 @@ 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_collect_native_gregset (regcache, gregsetp, regnum);
 }
 
-/* Fetch all general-purpose registers from process/thread TID and
-   store their values in GDB's register cache.  */
-
-static void
-fetch_regs (int tid)
-{
-  elf_gregset_t regs;
-
-  if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
-    perror_with_name ("Couldn't get registers");
-
-  supply_gregset (&regs);
-}
-
-/* Store all valid general-purpose registers in GDB's register cache
-   into the process/thread specified by TID.  */
-
-static void
-store_regs (int tid, int regnum)
-{
-  elf_gregset_t regs;
-
-  if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
-    perror_with_name ("Couldn't get registers");
-
-  fill_gregset (&regs, regnum);
-
-  if (ptrace (PTRACE_SETREGS, tid, 0, (long) &regs) < 0)
-    perror_with_name ("Couldn't write registers");
-}
-\f
-
 /* Transfering floating-point registers between GDB, inferiors and cores.  */
 
 /* Fill GDB's register cache with the floating-point and SSE register
    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
@@ -174,40 +110,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_fill_fxsave ((char *) fpregsetp, regnum);
-}
-
-/* Fetch all floating-point registers from process/thread TID and store
-   thier values in GDB's register cache.  */
-
-static void
-fetch_fpregs (int tid)
-{
-  elf_fpregset_t fpregs;
-
-  if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-    perror_with_name ("Couldn't get floating point status");
-
-  supply_fpregset (&fpregs);
-}
-
-/* Store all valid floating-point registers in GDB's register cache
-   into the process/thread specified by TID.  */
-
-static void
-store_fpregs (int tid, int regnum)
-{
-  elf_fpregset_t fpregs;
-
-  if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-    perror_with_name ("Couldn't get floating point status");
-
-  fill_fpregset (&fpregs, regnum);
-
-  if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
-    perror_with_name ("Couldn't write floating point status");
+  amd64_collect_fxsave (regcache, regnum, fpregsetp);
 }
 \f
 
@@ -217,136 +123,121 @@ store_fpregs (int tid, int regnum)
    this for all registers (including the floating point and SSE
    registers).  */
 
-void
-fetch_inferior_registers (int regnum)
+static void
+amd64_linux_fetch_inferior_registers (struct target_ops *ops,
+                                     struct regcache *regcache, int regnum)
 {
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   int tid;
 
   /* GNU/Linux LWP ID's are process ID's.  */
-  tid = TIDGET (inferior_ptid);
+  tid = ptid_get_lwp (inferior_ptid);
   if (tid == 0)
-    tid = PIDGET (inferior_ptid); /* Not a threaded program.  */
+    tid = ptid_get_pid (inferior_ptid); /* Not a threaded program.  */
 
-  if (regnum == -1 || amd64_native_gregset_supplies_p (regnum))
+  if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
-      fetch_regs (tid);
+      elf_gregset_t regs;
+
+      if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
+       perror_with_name (_("Couldn't get registers"));
+
+      amd64_supply_native_gregset (regcache, &regs, -1);
       if (regnum != -1)
        return;
     }
 
-  if (regnum == -1 || GETFPREGS_SUPPLIES (regnum))
+  if (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
-      fetch_fpregs (tid);
-      return;
-    }
+      elf_fpregset_t fpregs;
+
+      if (have_ptrace_getregset == TRIBOOL_TRUE)
+       {
+         char xstateregs[X86_XSTATE_MAX_SIZE];
+         struct iovec iov;
+
+         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"));
 
-  internal_error (__FILE__, __LINE__,
-                 "Got request for bad register number %d.", regnum);
+         amd64_supply_fxsave (regcache, -1, &fpregs);
+       }
+    }
 }
 
 /* Store register REGNUM back into the child process.  If REGNUM is
    -1, do this for all registers (including the floating-point and SSE
    registers).  */
 
-void
-store_inferior_registers (int regnum)
+static void
+amd64_linux_store_inferior_registers (struct target_ops *ops,
+                                     struct regcache *regcache, int regnum)
 {
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   int tid;
 
   /* GNU/Linux LWP ID's are process ID's.  */
-  tid = TIDGET (inferior_ptid);
+  tid = ptid_get_lwp (inferior_ptid);
   if (tid == 0)
-    tid = PIDGET (inferior_ptid); /* Not a threaded program.  */
+    tid = ptid_get_pid (inferior_ptid); /* Not a threaded program.  */
 
-  if (regnum == -1 || amd64_native_gregset_supplies_p (regnum))
+  if (regnum == -1 || amd64_native_gregset_supplies_p (gdbarch, regnum))
     {
-      store_regs (tid, regnum);
-      if (regnum != -1)
-       return;
-    }
+      elf_gregset_t regs;
 
-  if (regnum == -1 || GETFPREGS_SUPPLIES (regnum))
-    {
-      store_fpregs (tid, regnum);
-      return;
-    }
-
-  internal_error (__FILE__, __LINE__,
-                 "Got request to store bad register number %d.", regnum);
-}
-\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
+      if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
+       perror_with_name (_("Couldn't get registers"));
 
-  return value;
-}
+      amd64_collect_native_gregset (regcache, &regs, regnum);
 
-static void
-amd64_linux_dr_set (int regnum, unsigned long value)
-{
-  int tid;
+      if (ptrace (PTRACE_SETREGS, tid, 0, (long) &regs) < 0)
+       perror_with_name (_("Couldn't write registers"));
 
-  /* 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);
+      if (regnum != -1)
+       return;
+    }
 
-  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 (regnum == -1 || !amd64_native_gregset_supplies_p (gdbarch, regnum))
+    {
+      elf_fpregset_t fpregs;
 
-void
-amd64_linux_dr_set_control (unsigned long control)
-{
-  amd64_linux_dr_set (DR_CONTROL, control);
-}
+      if (have_ptrace_getregset == TRIBOOL_TRUE)
+       {
+         char xstateregs[X86_XSTATE_MAX_SIZE];
+         struct iovec iov;
 
-void
-amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
-{
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+         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_linux_dr_set (DR_FIRSTADDR + regnum, addr);
-}
+         amd64_collect_xsave (regcache, regnum, xstateregs, 0);
 
-void
-amd64_linux_dr_reset_addr (int regnum)
-{
-  gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
+         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"));
 
-  amd64_linux_dr_set (DR_FIRSTADDR + regnum, 0L);
-}
+         amd64_collect_fxsave (regcache, regnum, &fpregs);
 
-unsigned long
-amd64_linux_dr_get_status (void)
-{
-  return amd64_linux_dr_get (DR_STATUS);
+         if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
+           perror_with_name (_("Couldn't write floating point status"));
+       }
+    }
 }
 \f
 
@@ -357,35 +248,23 @@ ps_err_e
 ps_get_thread_area (const struct ps_prochandle *ph,
                     lwpid_t lwpid, int idx, void **base)
 {
-  switch (gdbarch_ptr_bit (current_gdbarch) == 32)
+  if (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 32)
+    {
+      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
     {
-    case bfd_mach_i386_i386:
-    case bfd_mach_i386_i386_intel_syntax:
-      {
-       /* 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;
-      }
-
-    case bfd_mach_x86_64:
-    case bfd_mach_x86_64_intel_syntax:
       /* This definition comes from prctl.h, but some kernels may not
          have it.  */
 #ifndef PTRACE_ARCH_PRCTL
@@ -398,33 +277,73 @@ 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;
        default:                   /* Should not happen.  */
          return PS_BADADDR;
        }
-      return PS_ERR;               /* ptrace failed.  */
-      
-    case bfd_mach_i386_i8086:
-      internal_error (__FILE__, __LINE__, "bad i8086 machine");
-    default:
-      internal_error (__FILE__, __LINE__, "bad_switch");
     }
+  return PS_ERR;               /* ptrace failed.  */
 }
 \f
 
-void
-child_post_startup_inferior (ptid_t ptid)
+/* Convert a native/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 NATIVE.  If DIRECTION is 0, copy from NATIVE to
+   INF.  */
+
+static int
+amd64_linux_siginfo_fixup (siginfo_t *native, 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 (native, 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 (native, inf, direction,
+                                              FIXUP_X32);
+  else
+    return 0;
 }
-\f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 void _initialize_amd64_linux_nat (void);
@@ -432,12 +351,26 @@ void _initialize_amd64_linux_nat (void);
 void
 _initialize_amd64_linux_nat (void)
 {
+  struct target_ops *t;
+
   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);
+
+  /* Create a generic x86 GNU/Linux target.  */
+  t = x86_linux_create_target ();
+
+  /* Add our register access methods.  */
+  t->to_fetch_registers = amd64_linux_fetch_inferior_registers;
+  t->to_store_registers = amd64_linux_store_inferior_registers;
+
+  /* Add the target.  */
+  x86_linux_add_target (t);
+
+  /* Add our siginfo layout converter.  */
+  linux_nat_set_siginfo_fixup (t, amd64_linux_siginfo_fixup);
 }
This page took 0.031187 seconds and 4 git commands to generate.