Fix all failing FPXX tests for tx39-elf.
[platform/upstream/binutils.git] / gdb / arm-linux-tdep.c
index 9debbeb..e3587f3 100644 (file)
@@ -1,11 +1,12 @@
 /* GNU/Linux on ARM target support.
-   Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+   Copyright (C) 1999-2014 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 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,
@@ -14,9 +15,7 @@
    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 "target.h"
 #include "frame.h"
 #include "regcache.h"
 #include "doublest.h"
+#include "solib-svr4.h"
 #include "osabi.h"
+#include "regset.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "breakpoint.h"
+#include "auxv.h"
+#include "xml-syscall.h"
 
 #include "arm-tdep.h"
-
-/* For shared library handling.  */
-#include "symtab.h"
+#include "arm-linux-tdep.h"
+#include "linux-tdep.h"
+#include "glibc-tdep.h"
+#include "arch-utils.h"
+#include "inferior.h"
+#include "infrun.h"
+#include "gdbthread.h"
 #include "symfile.h"
-#include "objfiles.h"
+
+#include "record-full.h"
+#include "linux-record.h"
+
+#include "cli/cli-utils.h"
+#include "stap-probe.h"
+#include "parser-defs.h"
+#include "user-regs.h"
+#include <ctype.h>
+#include "elf/common.h"
+extern int arm_apcs_32;
 
 /* Under ARM GNU/Linux the traditional way of performing a breakpoint
    is to execute a particular software interrupt, rather than use a
    particular undefined instruction to provoke a trap.  Upon exection
    of the software interrupt the kernel stops the inferior with a
-   SIGTRAP, and wakes the debugger.  Since ARM GNU/Linux is little
-   endian, and doesn't support Thumb at the moment we only override
-   the ARM little-endian breakpoint.  */
-
-static const char arm_linux_arm_le_breakpoint[] = {0x01,0x00,0x9f,0xef};
-
-/* DEPRECATED_CALL_DUMMY_WORDS:
-   This sequence of words is the instructions
-
-   mov  lr, pc
-   mov  pc, r4
-   swi bkpt_swi
-
-   Note this is 12 bytes.  */
-
-LONGEST arm_linux_call_dummy_words[] =
-{
-  0xe1a0e00f, 0xe1a0f004, 0xef9f001
-};
+   SIGTRAP, and wakes the debugger.  */
 
-/* Description of the longjmp buffer.  */
-#define ARM_LINUX_JB_ELEMENT_SIZE      INT_REGISTER_RAW_SIZE
-#define ARM_LINUX_JB_PC                        21
-
-/* Extract from an array REGBUF containing the (raw) register state
-   a function return value of type TYPE, and copy that, in virtual format,
-   into VALBUF.  */
-/* FIXME rearnsha/2002-02-23: This function shouldn't be necessary.
-   The ARM generic one should be able to handle the model used by
-   linux and the low-level formatting of the registers should be
-   hidden behind the regcache abstraction.  */
-static void
-arm_linux_extract_return_value (struct type *type,
-                               char regbuf[REGISTER_BYTES],
-                               char *valbuf)
-{
-  /* ScottB: This needs to be looked at to handle the different
-     floating point emulators on ARM GNU/Linux.  Right now the code
-     assumes that fetch inferior registers does the right thing for
-     GDB.  I suspect this won't handle NWFPE registers correctly, nor
-     will the default ARM version (arm_extract_return_value()).  */
-
-  int regnum = ((TYPE_CODE_FLT == TYPE_CODE (type))
-               ? ARM_F0_REGNUM : ARM_A1_REGNUM);
-  memcpy (valbuf, &regbuf[REGISTER_BYTE (regnum)], TYPE_LENGTH (type));
-}
+static const gdb_byte arm_linux_arm_le_breakpoint[] = { 0x01, 0x00, 0x9f, 0xef };
 
-/* Note: ScottB
-
-   This function does not support passing parameters using the FPA
-   variant of the APCS.  It passes any floating point arguments in the
-   general registers and/or on the stack.
-   
-   FIXME:  This and arm_push_arguments should be merged.  However this 
-          function breaks on a little endian host, big endian target
-          using the COFF file format.  ELF is ok.  
-          
-          ScottB.  */
-          
-/* Addresses for calling Thumb functions have the bit 0 set.
-   Here are some macros to test, set, or clear bit 0 of addresses.  */
-#define IS_THUMB_ADDR(addr)    ((addr) & 1)
-#define MAKE_THUMB_ADDR(addr)  ((addr) | 1)
-#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
-         
-static CORE_ADDR
-arm_linux_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
-                         int struct_return, CORE_ADDR struct_addr)
-{
-  char *fp;
-  int argnum, argreg, nstack_size;
+static const gdb_byte arm_linux_arm_be_breakpoint[] = { 0xef, 0x9f, 0x00, 0x01 };
 
-  /* Walk through the list of args and determine how large a temporary
-     stack is required.  Need to take care here as structs may be
-     passed on the stack, and we have to to push them.  */
-  nstack_size = -4 * DEPRECATED_REGISTER_SIZE; /* Some arguments go into A1-A4.  */
+/* However, the EABI syscall interface (new in Nov. 2005) does not look at
+   the operand of the swi if old-ABI compatibility is disabled.  Therefore,
+   use an undefined instruction instead.  This is supported as of kernel
+   version 2.5.70 (May 2003), so should be a safe assumption for EABI
+   binaries.  */
 
-  if (struct_return)                   /* The struct address goes in A1.  */
-    nstack_size += DEPRECATED_REGISTER_SIZE;
+static const gdb_byte eabi_linux_arm_le_breakpoint[] = { 0xf0, 0x01, 0xf0, 0xe7 };
 
-  /* Walk through the arguments and add their size to nstack_size.  */
-  for (argnum = 0; argnum < nargs; argnum++)
-    {
-      int len;
-      struct type *arg_type;
+static const gdb_byte eabi_linux_arm_be_breakpoint[] = { 0xe7, 0xf0, 0x01, 0xf0 };
 
-      arg_type = check_typedef (VALUE_TYPE (args[argnum]));
-      len = TYPE_LENGTH (arg_type);
-
-      /* ANSI C code passes float arguments as integers, K&R code
-         passes float arguments as doubles.  Correct for this here.  */
-      if (TYPE_CODE_FLT == TYPE_CODE (arg_type) && DEPRECATED_REGISTER_SIZE == len)
-       nstack_size += FP_REGISTER_VIRTUAL_SIZE;
-      else
-       nstack_size += len;
-    }
-
-  /* Allocate room on the stack, and initialize our stack frame
-     pointer.  */
-  fp = NULL;
-  if (nstack_size > 0)
-    {
-      sp -= nstack_size;
-      fp = (char *) sp;
-    }
+/* All the kernels which support Thumb support using a specific undefined
+   instruction for the Thumb breakpoint.  */
 
-  /* Initialize the integer argument register pointer.  */
-  argreg = ARM_A1_REGNUM;
+static const gdb_byte arm_linux_thumb_be_breakpoint[] = {0xde, 0x01};
 
-  /* The struct_return pointer occupies the first parameter passing
-     register.  */
-  if (struct_return)
-    write_register (argreg++, struct_addr);
+static const gdb_byte arm_linux_thumb_le_breakpoint[] = {0x01, 0xde};
 
-  /* Process arguments from left to right.  Store as many as allowed
-     in the parameter passing registers (A1-A4), and save the rest on
-     the temporary stack.  */
-  for (argnum = 0; argnum < nargs; argnum++)
-    {
-      int len;
-      char *val;
-      CORE_ADDR regval;
-      enum type_code typecode;
-      struct type *arg_type, *target_type;
-
-      arg_type = check_typedef (VALUE_TYPE (args[argnum]));
-      target_type = TYPE_TARGET_TYPE (arg_type);
-      len = TYPE_LENGTH (arg_type);
-      typecode = TYPE_CODE (arg_type);
-      val = (char *) VALUE_CONTENTS (args[argnum]);
-
-      /* ANSI C code passes float arguments as integers, K&R code
-         passes float arguments as doubles.  The .stabs record for 
-         for ANSI prototype floating point arguments records the
-         type as FP_INTEGER, while a K&R style (no prototype)
-         .stabs records the type as FP_FLOAT.  In this latter case
-         the compiler converts the float arguments to double before
-         calling the function.  */
-      if (TYPE_CODE_FLT == typecode && DEPRECATED_REGISTER_SIZE == len)
-       {
-         DOUBLEST dblval;
-         dblval = deprecated_extract_floating (val, len);
-         len = TARGET_DOUBLE_BIT / TARGET_CHAR_BIT;
-         val = alloca (len);
-         deprecated_store_floating (val, len, dblval);
-       }
+/* Because the 16-bit Thumb breakpoint is affected by Thumb-2 IT blocks,
+   we must use a length-appropriate breakpoint for 32-bit Thumb
+   instructions.  See also thumb_get_next_pc.  */
 
-      /* If the argument is a pointer to a function, and it is a Thumb
-         function, set the low bit of the pointer.  */
-      if (TYPE_CODE_PTR == typecode
-         && NULL != target_type
-         && TYPE_CODE_FUNC == TYPE_CODE (target_type))
-       {
-         CORE_ADDR regval = extract_address (val, len);
-         if (arm_pc_is_thumb (regval))
-           store_unsigned_integer (val, len, MAKE_THUMB_ADDR (regval));
-       }
+static const gdb_byte arm_linux_thumb2_be_breakpoint[] = { 0xf7, 0xf0, 0xa0, 0x00 };
 
-      /* Copy the argument to general registers or the stack in
-         register-sized pieces.  Large arguments are split between
-         registers and stack.  */
-      while (len > 0)
-       {
-         int partial_len = len < DEPRECATED_REGISTER_SIZE ? len : DEPRECATED_REGISTER_SIZE;
+static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
 
-         if (argreg <= ARM_LAST_ARG_REGNUM)
-           {
-             /* It's an argument being passed in a general register.  */
-             regval = extract_address (val, partial_len);
-             write_register (argreg++, regval);
-           }
-         else
-           {
-             /* Push the arguments onto the stack.  */
-             write_memory ((CORE_ADDR) fp, val, DEPRECATED_REGISTER_SIZE);
-             fp += DEPRECATED_REGISTER_SIZE;
-           }
+/* Description of the longjmp buffer.  The buffer is treated as an array of 
+   elements of size ARM_LINUX_JB_ELEMENT_SIZE.
 
-         len -= partial_len;
-         val += partial_len;
-       }
-    }
+   The location of saved registers in this buffer (in particular the PC
+   to use after longjmp is called) varies depending on the ABI (in 
+   particular the FP model) and also (possibly) the C Library.
 
-  /* Return adjusted stack pointer.  */
-  return sp;
-}
+   For glibc, eglibc, and uclibc the following holds:  If the FP model is 
+   SoftVFP or VFP (which implies EABI) then the PC is at offset 9 in the 
+   buffer.  This is also true for the SoftFPA model.  However, for the FPA 
+   model the PC is at offset 21 in the buffer.  */
+#define ARM_LINUX_JB_ELEMENT_SIZE      INT_REGISTER_SIZE
+#define ARM_LINUX_JB_PC_FPA            21
+#define ARM_LINUX_JB_PC_EABI           9
 
 /*
    Dynamic Linking on ARM GNU/Linux
@@ -235,7 +114,7 @@ arm_linux_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
    GOT = global offset table
 
    As much as possible, ELF dynamic linking defers the resolution of
-   jump/call addresses until the last minute. The technique used is
+   jump/call addresses until the last minute.  The technique used is
    inspired by the i386 ELF design, and is based on the following
    constraints.
 
@@ -277,9 +156,9 @@ arm_linux_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
 
    2) In the PLT:
 
-   The PLT is a synthetic area, created by the linker. It exists in
-   both executables and libraries. It is an array of stubs, one per
-   imported function call. It looks like this:
+   The PLT is a synthetic area, created by the linker.  It exists in
+   both executables and libraries.  It is an array of stubs, one per
+   imported function call.  It looks like this:
 
    PLT[0]:
    str     lr, [sp, #-4]!       @push the return address (lr)
@@ -301,7 +180,7 @@ arm_linux_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
    lr = &GOT[0] + 8
    = &GOT[2]
 
-   NOTE: PLT[0] borrows an offset .word from PLT[1]. This is a little
+   NOTE: PLT[0] borrows an offset .word from PLT[1].  This is a little
    "tight", but allows us to keep all the PLT entries the same size.
 
    PLT[n+1]:
@@ -318,12 +197,12 @@ arm_linux_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
    3) In the GOT:
 
    The GOT contains helper pointers for both code (PLT) fixups and
-   data fixups.  The first 3 entries of the GOT are special. The next
+   data fixups.  The first 3 entries of the GOT are special.  The next
    M entries (where M is the number of entries in the PLT) belong to
-   the PLT fixups. The next D (all remaining) entries belong to
-   various data fixups. The actual size of the GOT is 3 + M + D.
+   the PLT fixups.  The next D (all remaining) entries belong to
+   various data fixups.  The actual size of the GOT is 3 + M + D.
 
-   The GOT is also a synthetic area, created by the linker. It exists
+   The GOT is also a synthetic area, created by the linker.  It exists
    in both executables and libraries.  When the GOT is first
    initialized , all the GOT entries relating to PLT fixups are
    pointing to code back at PLT[0].
@@ -346,202 +225,1416 @@ arm_linux_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
    with.  Before the fixup/resolver code returns, it actually calls
    the requested function and repairs &GOT[n+3].  */
 
-/* Find the minimal symbol named NAME, and return both the minsym
-   struct and its objfile.  This probably ought to be in minsym.c, but
-   everything there is trying to deal with things like C++ and
-   SOFUN_ADDRESS_MAYBE_TURQUOISE, ...  Since this is so simple, it may
-   be considered too special-purpose for general consumption.  */
+/* The constants below were determined by examining the following files
+   in the linux kernel sources:
+
+      arch/arm/kernel/signal.c
+         - see SWI_SYS_SIGRETURN and SWI_SYS_RT_SIGRETURN
+      include/asm-arm/unistd.h
+         - see __NR_sigreturn, __NR_rt_sigreturn, and __NR_SYSCALL_BASE */
+
+#define ARM_LINUX_SIGRETURN_INSTR      0xef900077
+#define ARM_LINUX_RT_SIGRETURN_INSTR   0xef9000ad
+
+/* For ARM EABI, the syscall number is not in the SWI instruction
+   (instead it is loaded into r7).  We recognize the pattern that
+   glibc uses...  alternatively, we could arrange to do this by
+   function name, but they are not always exported.  */
+#define ARM_SET_R7_SIGRETURN           0xe3a07077
+#define ARM_SET_R7_RT_SIGRETURN                0xe3a070ad
+#define ARM_EABI_SYSCALL               0xef000000
+
+/* Equivalent patterns for Thumb2.  */
+#define THUMB2_SET_R7_SIGRETURN1       0xf04f
+#define THUMB2_SET_R7_SIGRETURN2       0x0777
+#define THUMB2_SET_R7_RT_SIGRETURN1    0xf04f
+#define THUMB2_SET_R7_RT_SIGRETURN2    0x07ad
+#define THUMB2_EABI_SYSCALL            0xdf00
+
+/* OABI syscall restart trampoline, used for EABI executables too
+   whenever OABI support has been enabled in the kernel.  */
+#define ARM_OABI_SYSCALL_RESTART_SYSCALL 0xef900000
+#define ARM_LDR_PC_SP_12               0xe49df00c
+#define ARM_LDR_PC_SP_4                        0xe49df004
+
+static void
+arm_linux_sigtramp_cache (struct frame_info *this_frame,
+                         struct trad_frame_cache *this_cache,
+                         CORE_ADDR func, int regs_offset)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  CORE_ADDR base = sp + regs_offset;
+  int i;
+
+  for (i = 0; i < 16; i++)
+    trad_frame_set_reg_addr (this_cache, i, base + i * 4);
+
+  trad_frame_set_reg_addr (this_cache, ARM_PS_REGNUM, base + 16 * 4);
+
+  /* The VFP or iWMMXt registers may be saved on the stack, but there's
+     no reliable way to restore them (yet).  */
+
+  /* Save a frame ID.  */
+  trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+/* There are a couple of different possible stack layouts that
+   we need to support.
+
+   Before version 2.6.18, the kernel used completely independent
+   layouts for non-RT and RT signals.  For non-RT signals the stack
+   began directly with a struct sigcontext.  For RT signals the stack
+   began with two redundant pointers (to the siginfo and ucontext),
+   and then the siginfo and ucontext.
+
+   As of version 2.6.18, the non-RT signal frame layout starts with
+   a ucontext and the RT signal frame starts with a siginfo and then
+   a ucontext.  Also, the ucontext now has a designated save area
+   for coprocessor registers.
+
+   For RT signals, it's easy to tell the difference: we look for
+   pinfo, the pointer to the siginfo.  If it has the expected
+   value, we have an old layout.  If it doesn't, we have the new
+   layout.
+
+   For non-RT signals, it's a bit harder.  We need something in one
+   layout or the other with a recognizable offset and value.  We can't
+   use the return trampoline, because ARM usually uses SA_RESTORER,
+   in which case the stack return trampoline is not filled in.
+   We can't use the saved stack pointer, because sigaltstack might
+   be in use.  So for now we guess the new layout...  */
+
+/* There are three words (trap_no, error_code, oldmask) in
+   struct sigcontext before r0.  */
+#define ARM_SIGCONTEXT_R0 0xc
+
+/* There are five words (uc_flags, uc_link, and three for uc_stack)
+   in the ucontext_t before the sigcontext.  */
+#define ARM_UCONTEXT_SIGCONTEXT 0x14
+
+/* There are three elements in an rt_sigframe before the ucontext:
+   pinfo, puc, and info.  The first two are pointers and the third
+   is a struct siginfo, with size 128 bytes.  We could follow puc
+   to the ucontext, but it's simpler to skip the whole thing.  */
+#define ARM_OLD_RT_SIGFRAME_SIGINFO 0x8
+#define ARM_OLD_RT_SIGFRAME_UCONTEXT 0x88
+
+#define ARM_NEW_RT_SIGFRAME_UCONTEXT 0x80
+
+#define ARM_NEW_SIGFRAME_MAGIC 0x5ac3c35a
+
+static void
+arm_linux_sigreturn_init (const struct tramp_frame *self,
+                         struct frame_info *this_frame,
+                         struct trad_frame_cache *this_cache,
+                         CORE_ADDR func)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  ULONGEST uc_flags = read_memory_unsigned_integer (sp, 4, byte_order);
+
+  if (uc_flags == ARM_NEW_SIGFRAME_MAGIC)
+    arm_linux_sigtramp_cache (this_frame, this_cache, func,
+                             ARM_UCONTEXT_SIGCONTEXT
+                             + ARM_SIGCONTEXT_R0);
+  else
+    arm_linux_sigtramp_cache (this_frame, this_cache, func,
+                             ARM_SIGCONTEXT_R0);
+}
+
+static void
+arm_linux_rt_sigreturn_init (const struct tramp_frame *self,
+                         struct frame_info *this_frame,
+                         struct trad_frame_cache *this_cache,
+                         CORE_ADDR func)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  ULONGEST pinfo = read_memory_unsigned_integer (sp, 4, byte_order);
+
+  if (pinfo == sp + ARM_OLD_RT_SIGFRAME_SIGINFO)
+    arm_linux_sigtramp_cache (this_frame, this_cache, func,
+                             ARM_OLD_RT_SIGFRAME_UCONTEXT
+                             + ARM_UCONTEXT_SIGCONTEXT
+                             + ARM_SIGCONTEXT_R0);
+  else
+    arm_linux_sigtramp_cache (this_frame, this_cache, func,
+                             ARM_NEW_RT_SIGFRAME_UCONTEXT
+                             + ARM_UCONTEXT_SIGCONTEXT
+                             + ARM_SIGCONTEXT_R0);
+}
+
+static void
+arm_linux_restart_syscall_init (const struct tramp_frame *self,
+                               struct frame_info *this_frame,
+                               struct trad_frame_cache *this_cache,
+                               CORE_ADDR func)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  CORE_ADDR pc = get_frame_memory_unsigned (this_frame, sp, 4);
+  CORE_ADDR cpsr = get_frame_register_unsigned (this_frame, ARM_PS_REGNUM);
+  ULONGEST t_bit = arm_psr_thumb_bit (gdbarch);
+  int sp_offset;
+
+  /* There are two variants of this trampoline; with older kernels, the
+     stub is placed on the stack, while newer kernels use the stub from
+     the vector page.  They are identical except that the older version
+     increments SP by 12 (to skip stored PC and the stub itself), while
+     the newer version increments SP only by 4 (just the stored PC).  */
+  if (self->insn[1].bytes == ARM_LDR_PC_SP_4)
+    sp_offset = 4;
+  else
+    sp_offset = 12;
+
+  /* Update Thumb bit in CPSR.  */
+  if (pc & 1)
+    cpsr |= t_bit;
+  else
+    cpsr &= ~t_bit;
+
+  /* Remove Thumb bit from PC.  */
+  pc = gdbarch_addr_bits_remove (gdbarch, pc);
+
+  /* Save previous register values.  */
+  trad_frame_set_reg_value (this_cache, ARM_SP_REGNUM, sp + sp_offset);
+  trad_frame_set_reg_value (this_cache, ARM_PC_REGNUM, pc);
+  trad_frame_set_reg_value (this_cache, ARM_PS_REGNUM, cpsr);
+
+  /* Save a frame ID.  */
+  trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
+static struct tramp_frame arm_linux_sigreturn_tramp_frame = {
+  SIGTRAMP_FRAME,
+  4,
+  {
+    { ARM_LINUX_SIGRETURN_INSTR, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_sigreturn_init
+};
+
+static struct tramp_frame arm_linux_rt_sigreturn_tramp_frame = {
+  SIGTRAMP_FRAME,
+  4,
+  {
+    { ARM_LINUX_RT_SIGRETURN_INSTR, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_rt_sigreturn_init
+};
+
+static struct tramp_frame arm_eabi_linux_sigreturn_tramp_frame = {
+  SIGTRAMP_FRAME,
+  4,
+  {
+    { ARM_SET_R7_SIGRETURN, -1 },
+    { ARM_EABI_SYSCALL, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_sigreturn_init
+};
+
+static struct tramp_frame arm_eabi_linux_rt_sigreturn_tramp_frame = {
+  SIGTRAMP_FRAME,
+  4,
+  {
+    { ARM_SET_R7_RT_SIGRETURN, -1 },
+    { ARM_EABI_SYSCALL, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_rt_sigreturn_init
+};
+
+static struct tramp_frame thumb2_eabi_linux_sigreturn_tramp_frame = {
+  SIGTRAMP_FRAME,
+  2,
+  {
+    { THUMB2_SET_R7_SIGRETURN1, -1 },
+    { THUMB2_SET_R7_SIGRETURN2, -1 },
+    { THUMB2_EABI_SYSCALL, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_sigreturn_init
+};
+
+static struct tramp_frame thumb2_eabi_linux_rt_sigreturn_tramp_frame = {
+  SIGTRAMP_FRAME,
+  2,
+  {
+    { THUMB2_SET_R7_RT_SIGRETURN1, -1 },
+    { THUMB2_SET_R7_RT_SIGRETURN2, -1 },
+    { THUMB2_EABI_SYSCALL, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_rt_sigreturn_init
+};
+
+static struct tramp_frame arm_linux_restart_syscall_tramp_frame = {
+  NORMAL_FRAME,
+  4,
+  {
+    { ARM_OABI_SYSCALL_RESTART_SYSCALL, -1 },
+    { ARM_LDR_PC_SP_12, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_restart_syscall_init
+};
+
+static struct tramp_frame arm_kernel_linux_restart_syscall_tramp_frame = {
+  NORMAL_FRAME,
+  4,
+  {
+    { ARM_OABI_SYSCALL_RESTART_SYSCALL, -1 },
+    { ARM_LDR_PC_SP_4, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_restart_syscall_init
+};
+
+/* Core file and register set support.  */
+
+#define ARM_LINUX_SIZEOF_GREGSET (18 * INT_REGISTER_SIZE)
+
+void
+arm_linux_supply_gregset (const struct regset *regset,
+                         struct regcache *regcache,
+                         int regnum, const void *gregs_buf, size_t len)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  const gdb_byte *gregs = gregs_buf;
+  int regno;
+  CORE_ADDR reg_pc;
+  gdb_byte pc_buf[INT_REGISTER_SIZE];
+
+  for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      regcache_raw_supply (regcache, regno,
+                          gregs + INT_REGISTER_SIZE * regno);
+
+  if (regnum == ARM_PS_REGNUM || regnum == -1)
+    {
+      if (arm_apcs_32)
+       regcache_raw_supply (regcache, ARM_PS_REGNUM,
+                            gregs + INT_REGISTER_SIZE * ARM_CPSR_GREGNUM);
+      else
+       regcache_raw_supply (regcache, ARM_PS_REGNUM,
+                            gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM);
+    }
+
+  if (regnum == ARM_PC_REGNUM || regnum == -1)
+    {
+      reg_pc = extract_unsigned_integer (gregs
+                                        + INT_REGISTER_SIZE * ARM_PC_REGNUM,
+                                        INT_REGISTER_SIZE, byte_order);
+      reg_pc = gdbarch_addr_bits_remove (gdbarch, reg_pc);
+      store_unsigned_integer (pc_buf, INT_REGISTER_SIZE, byte_order, reg_pc);
+      regcache_raw_supply (regcache, ARM_PC_REGNUM, pc_buf);
+    }
+}
+
+void
+arm_linux_collect_gregset (const struct regset *regset,
+                          const struct regcache *regcache,
+                          int regnum, void *gregs_buf, size_t len)
+{
+  gdb_byte *gregs = gregs_buf;
+  int regno;
+
+  for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      regcache_raw_collect (regcache, regno,
+                           gregs + INT_REGISTER_SIZE * regno);
+
+  if (regnum == ARM_PS_REGNUM || regnum == -1)
+    {
+      if (arm_apcs_32)
+       regcache_raw_collect (regcache, ARM_PS_REGNUM,
+                             gregs + INT_REGISTER_SIZE * ARM_CPSR_GREGNUM);
+      else
+       regcache_raw_collect (regcache, ARM_PS_REGNUM,
+                             gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM);
+    }
+
+  if (regnum == ARM_PC_REGNUM || regnum == -1)
+    regcache_raw_collect (regcache, ARM_PC_REGNUM,
+                         gregs + INT_REGISTER_SIZE * ARM_PC_REGNUM);
+}
+
+/* Support for register format used by the NWFPE FPA emulator.  */
+
+#define typeNone               0x00
+#define typeSingle             0x01
+#define typeDouble             0x02
+#define typeExtended           0x03
+
+void
+supply_nwfpe_register (struct regcache *regcache, int regno,
+                      const gdb_byte *regs)
+{
+  const gdb_byte *reg_data;
+  gdb_byte reg_tag;
+  gdb_byte buf[FP_REGISTER_SIZE];
+
+  reg_data = regs + (regno - ARM_F0_REGNUM) * FP_REGISTER_SIZE;
+  reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET];
+  memset (buf, 0, FP_REGISTER_SIZE);
+
+  switch (reg_tag)
+    {
+    case typeSingle:
+      memcpy (buf, reg_data, 4);
+      break;
+    case typeDouble:
+      memcpy (buf, reg_data + 4, 4);
+      memcpy (buf + 4, reg_data, 4);
+      break;
+    case typeExtended:
+      /* We want sign and exponent, then least significant bits,
+        then most significant.  NWFPE does sign, most, least.  */
+      memcpy (buf, reg_data, 4);
+      memcpy (buf + 4, reg_data + 8, 4);
+      memcpy (buf + 8, reg_data + 4, 4);
+      break;
+    default:
+      break;
+    }
+
+  regcache_raw_supply (regcache, regno, buf);
+}
+
+void
+collect_nwfpe_register (const struct regcache *regcache, int regno,
+                       gdb_byte *regs)
+{
+  gdb_byte *reg_data;
+  gdb_byte reg_tag;
+  gdb_byte buf[FP_REGISTER_SIZE];
+
+  regcache_raw_collect (regcache, regno, buf);
+
+  /* NOTE drow/2006-06-07: This code uses the tag already in the
+     register buffer.  I've preserved that when moving the code
+     from the native file to the target file.  But this doesn't
+     always make sense.  */
+
+  reg_data = regs + (regno - ARM_F0_REGNUM) * FP_REGISTER_SIZE;
+  reg_tag = regs[(regno - ARM_F0_REGNUM) + NWFPE_TAGS_OFFSET];
+
+  switch (reg_tag)
+    {
+    case typeSingle:
+      memcpy (reg_data, buf, 4);
+      break;
+    case typeDouble:
+      memcpy (reg_data, buf + 4, 4);
+      memcpy (reg_data + 4, buf, 4);
+      break;
+    case typeExtended:
+      memcpy (reg_data, buf, 4);
+      memcpy (reg_data + 4, buf + 8, 4);
+      memcpy (reg_data + 8, buf + 4, 4);
+      break;
+    default:
+      break;
+    }
+}
+
+void
+arm_linux_supply_nwfpe (const struct regset *regset,
+                       struct regcache *regcache,
+                       int regnum, const void *regs_buf, size_t len)
+{
+  const gdb_byte *regs = regs_buf;
+  int regno;
+
+  if (regnum == ARM_FPS_REGNUM || regnum == -1)
+    regcache_raw_supply (regcache, ARM_FPS_REGNUM,
+                        regs + NWFPE_FPSR_OFFSET);
+
+  for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      supply_nwfpe_register (regcache, regno, regs);
+}
+
+void
+arm_linux_collect_nwfpe (const struct regset *regset,
+                        const struct regcache *regcache,
+                        int regnum, void *regs_buf, size_t len)
+{
+  gdb_byte *regs = regs_buf;
+  int regno;
+
+  for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      collect_nwfpe_register (regcache, regno, regs);
+
+  if (regnum == ARM_FPS_REGNUM || regnum == -1)
+    regcache_raw_collect (regcache, ARM_FPS_REGNUM,
+                         regs + INT_REGISTER_SIZE * ARM_FPS_REGNUM);
+}
+
+/* Support VFP register format.  */
+
+#define ARM_LINUX_SIZEOF_VFP (32 * 8 + 4)
+
+static void
+arm_linux_supply_vfp (const struct regset *regset,
+                     struct regcache *regcache,
+                     int regnum, const void *regs_buf, size_t len)
+{
+  const gdb_byte *regs = regs_buf;
+  int regno;
+
+  if (regnum == ARM_FPSCR_REGNUM || regnum == -1)
+    regcache_raw_supply (regcache, ARM_FPSCR_REGNUM, regs + 32 * 8);
+
+  for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      regcache_raw_supply (regcache, regno,
+                          regs + (regno - ARM_D0_REGNUM) * 8);
+}
+
+static void
+arm_linux_collect_vfp (const struct regset *regset,
+                        const struct regcache *regcache,
+                        int regnum, void *regs_buf, size_t len)
+{
+  gdb_byte *regs = regs_buf;
+  int regno;
+
+  if (regnum == ARM_FPSCR_REGNUM || regnum == -1)
+    regcache_raw_collect (regcache, ARM_FPSCR_REGNUM, regs + 32 * 8);
+
+  for (regno = ARM_D0_REGNUM; regno <= ARM_D31_REGNUM; regno++)
+    if (regnum == -1 || regnum == regno)
+      regcache_raw_collect (regcache, regno,
+                           regs + (regno - ARM_D0_REGNUM) * 8);
+}
+
+static const struct regset arm_linux_gregset =
+  {
+    NULL, arm_linux_supply_gregset, arm_linux_collect_gregset
+  };
+
+static const struct regset arm_linux_fpregset =
+  {
+    NULL, arm_linux_supply_nwfpe, arm_linux_collect_nwfpe
+  };
+
+static const struct regset arm_linux_vfpregset =
+  {
+    NULL, arm_linux_supply_vfp, arm_linux_collect_vfp
+  };
+
+/* Iterate over core file register note sections.  */
+
+static void
+arm_linux_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", ARM_LINUX_SIZEOF_GREGSET, &arm_linux_gregset, NULL, cb_data);
+
+  if (tdep->have_vfp_registers)
+    cb (".reg-arm-vfp", ARM_LINUX_SIZEOF_VFP, &arm_linux_vfpregset,
+       "VFP floating-point", cb_data);
+  else if (tdep->have_fpa_registers)
+    cb (".reg2", ARM_LINUX_SIZEOF_NWFPE, &arm_linux_fpregset,
+       "FPA floating-point", cb_data);
+}
+
+/* Determine target description from core file.  */
 
-static struct minimal_symbol *
-find_minsym_and_objfile (char *name, struct objfile **objfile_p)
+static const struct target_desc *
+arm_linux_core_read_description (struct gdbarch *gdbarch,
+                                 struct target_ops *target,
+                                 bfd *abfd)
 {
-  struct objfile *objfile;
+  CORE_ADDR arm_hwcap = 0;
+
+  if (target_auxv_search (target, AT_HWCAP, &arm_hwcap) != 1)
+    return NULL;
 
-  ALL_OBJFILES (objfile)
+  if (arm_hwcap & HWCAP_VFP)
     {
-      struct minimal_symbol *msym;
+      /* NEON implies VFPv3-D32 or no-VFP unit.  Say that we only support
+         Neon with VFPv3-D32.  */
+      if (arm_hwcap & HWCAP_NEON)
+       return tdesc_arm_with_neon;
+      else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+       return tdesc_arm_with_vfpv3;
+      else
+       return tdesc_arm_with_vfpv2;
+    }
+
+  return NULL;
+}
 
-      ALL_OBJFILE_MSYMBOLS (objfile, msym)
+
+/* Copy the value of next pc of sigreturn and rt_sigrturn into PC,
+   return 1.  In addition, set IS_THUMB depending on whether we
+   will return to ARM or Thumb code.  Return 0 if it is not a
+   rt_sigreturn/sigreturn syscall.  */
+static int
+arm_linux_sigreturn_return_addr (struct frame_info *frame,
+                                unsigned long svc_number,
+                                CORE_ADDR *pc, int *is_thumb)
+{
+  /* Is this a sigreturn or rt_sigreturn syscall?  */
+  if (svc_number == 119 || svc_number == 173)
+    {
+      if (get_frame_type (frame) == SIGTRAMP_FRAME)
        {
-         if (DEPRECATED_SYMBOL_NAME (msym)
-             && strcmp (DEPRECATED_SYMBOL_NAME (msym), name) == 0)
-           {
-             *objfile_p = objfile;
-             return msym;
-           }
+         ULONGEST t_bit = arm_psr_thumb_bit (frame_unwind_arch (frame));
+         CORE_ADDR cpsr
+           = frame_unwind_register_unsigned (frame, ARM_PS_REGNUM);
+
+         *is_thumb = (cpsr & t_bit) != 0;
+         *pc = frame_unwind_caller_pc (frame);
+         return 1;
        }
     }
-
   return 0;
 }
 
+/* At a ptrace syscall-stop, return the syscall number.  This either
+   comes from the SWI instruction (OABI) or from r7 (EABI).
+
+   When the function fails, it should return -1.  */
+
+static LONGEST
+arm_linux_get_syscall_number (struct gdbarch *gdbarch,
+                             ptid_t ptid)
+{
+  struct regcache *regs = get_thread_regcache (ptid);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  ULONGEST pc;
+  ULONGEST cpsr;
+  ULONGEST t_bit = arm_psr_thumb_bit (gdbarch);
+  int is_thumb;
+  ULONGEST svc_number = -1;
+
+  regcache_cooked_read_unsigned (regs, ARM_PC_REGNUM, &pc);
+  regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &cpsr);
+  is_thumb = (cpsr & t_bit) != 0;
+
+  if (is_thumb)
+    {
+      regcache_cooked_read_unsigned (regs, 7, &svc_number);
+    }
+  else
+    {
+      enum bfd_endian byte_order_for_code = 
+       gdbarch_byte_order_for_code (gdbarch);
+
+      /* PC gets incremented before the syscall-stop, so read the
+        previous instruction.  */
+      unsigned long this_instr = 
+       read_memory_unsigned_integer (pc - 4, 4, byte_order_for_code);
+
+      unsigned long svc_operand = (0x00ffffff & this_instr);
+
+      if (svc_operand)
+       {
+          /* OABI */
+         svc_number = svc_operand - 0x900000;
+       }
+      else
+       {
+          /* EABI */
+         regcache_cooked_read_unsigned (regs, 7, &svc_number);
+       }
+    }
+
+  return svc_number;
+}
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+   instruction to be executed.  */
 
 static CORE_ADDR
-skip_hurd_resolver (CORE_ADDR pc)
+arm_linux_syscall_next_pc (struct frame_info *frame)
 {
-  /* The HURD dynamic linker is part of the GNU C library, so many
-     GNU/Linux distributions use it.  (All ELF versions, as far as I
-     know.)  An unresolved PLT entry points to "_dl_runtime_resolve",
-     which calls "fixup" to patch the PLT, and then passes control to
-     the function.
-
-     We look for the symbol `_dl_runtime_resolve', and find `fixup' in
-     the same objfile.  If we are at the entry point of `fixup', then
-     we set a breakpoint at the return address (at the top of the
-     stack), and continue.
-  
-     It's kind of gross to do all these checks every time we're
-     called, since they don't change once the executable has gotten
-     started.  But this is only a temporary hack --- upcoming versions
-     of GNU/Linux will provide a portable, efficient interface for
-     debugging programs that use shared libraries.  */
-
-  struct objfile *objfile;
-  struct minimal_symbol *resolver 
-    = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile);
-
-  if (resolver)
+  CORE_ADDR pc = get_frame_pc (frame);
+  CORE_ADDR return_addr = 0;
+  int is_thumb = arm_frame_is_thumb (frame);
+  ULONGEST svc_number = 0;
+
+  if (is_thumb)
     {
-      struct minimal_symbol *fixup
-       = lookup_minimal_symbol ("fixup", NULL, objfile);
+      svc_number = get_frame_register_unsigned (frame, 7);
+      return_addr = pc + 2;
+    }
+  else
+    {
+      struct gdbarch *gdbarch = get_frame_arch (frame);
+      enum bfd_endian byte_order_for_code = 
+       gdbarch_byte_order_for_code (gdbarch);
+      unsigned long this_instr = 
+       read_memory_unsigned_integer (pc, 4, byte_order_for_code);
+
+      unsigned long svc_operand = (0x00ffffff & this_instr);
+      if (svc_operand)  /* OABI.  */
+       {
+         svc_number = svc_operand - 0x900000;
+       }
+      else /* EABI.  */
+       {
+         svc_number = get_frame_register_unsigned (frame, 7);
+       }
 
-      if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc)
-       return (DEPRECATED_SAVED_PC_AFTER_CALL (get_current_frame ()));
+      return_addr = pc + 4;
     }
 
-  return 0;
-}      
+  arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
 
-/* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c.
-   This function:
-   1) decides whether a PLT has sent us into the linker to resolve
-      a function reference, and 
-   2) if so, tells us where to set a temporary breakpoint that will
-      trigger when the dynamic linker is done.  */
+  /* Addresses for calling Thumb functions have the bit 0 set.  */
+  if (is_thumb)
+    return_addr |= 1;
 
-CORE_ADDR
-arm_linux_skip_solib_resolver (CORE_ADDR pc)
+  return return_addr;
+}
+
+
+/* Insert a single step breakpoint at the next executed instruction.  */
+
+static int
+arm_linux_software_single_step (struct frame_info *frame)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct address_space *aspace = get_frame_address_space (frame);
+  CORE_ADDR next_pc;
+
+  if (arm_deal_with_atomic_sequence (frame))
+    return 1;
+
+  next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+
+  /* The Linux kernel offers some user-mode helpers in a high page.  We can
+     not read this page (as of 2.6.23), and even if we could then we couldn't
+     set breakpoints in it, and even if we could then the atomic operations
+     would fail when interrupted.  They are all called as functions and return
+     to the address in LR, so step to there instead.  */
+  if (next_pc > 0xffff0000)
+    next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+
+  arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+
+  return 1;
+}
+
+/* Support for displaced stepping of Linux SVC instructions.  */
+
+static void
+arm_linux_cleanup_svc (struct gdbarch *gdbarch,
+                      struct regcache *regs,
+                      struct displaced_step_closure *dsc)
 {
-  CORE_ADDR result;
+  CORE_ADDR from = dsc->insn_addr;
+  ULONGEST apparent_pc;
+  int within_scratch;
+
+  regcache_cooked_read_unsigned (regs, ARM_PC_REGNUM, &apparent_pc);
 
-  /* Plug in functions for other kinds of resolvers here.  */
-  result = skip_hurd_resolver (pc);
+  within_scratch = (apparent_pc >= dsc->scratch_base
+                   && apparent_pc < (dsc->scratch_base
+                                     + DISPLACED_MODIFIED_INSNS * 4 + 4));
+
+  if (debug_displaced)
+    {
+      fprintf_unfiltered (gdb_stdlog, "displaced: PC is apparently %.8lx after "
+                         "SVC step ", (unsigned long) apparent_pc);
+      if (within_scratch)
+        fprintf_unfiltered (gdb_stdlog, "(within scratch space)\n");
+      else
+        fprintf_unfiltered (gdb_stdlog, "(outside scratch space)\n");
+    }
+
+  if (within_scratch)
+    displaced_write_reg (regs, dsc, ARM_PC_REGNUM, from + 4, BRANCH_WRITE_PC);
+}
+
+static int
+arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs,
+                   struct displaced_step_closure *dsc)
+{
+  CORE_ADDR return_to = 0;
+
+  struct frame_info *frame;
+  unsigned int svc_number = displaced_read_reg (regs, dsc, 7);
+  int is_sigreturn = 0;
+  int is_thumb;
+
+  frame = get_current_frame ();
+
+  is_sigreturn = arm_linux_sigreturn_return_addr(frame, svc_number,
+                                                &return_to, &is_thumb);
+  if (is_sigreturn)
+    {
+         struct symtab_and_line sal;
+
+         if (debug_displaced)
+           fprintf_unfiltered (gdb_stdlog, "displaced: found "
+             "sigreturn/rt_sigreturn SVC call.  PC in frame = %lx\n",
+             (unsigned long) get_frame_pc (frame));
+
+         if (debug_displaced)
+           fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx.  "
+             "Setting momentary breakpoint.\n", (unsigned long) return_to);
+
+         gdb_assert (inferior_thread ()->control.step_resume_breakpoint
+                     == NULL);
+
+         sal = find_pc_line (return_to, 0);
+         sal.pc = return_to;
+         sal.section = find_pc_overlay (return_to);
+         sal.explicit_pc = 1;
+
+         frame = get_prev_frame (frame);
+
+         if (frame)
+           {
+             inferior_thread ()->control.step_resume_breakpoint
+               = set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
+                                           bp_step_resume);
+
+             /* set_momentary_breakpoint invalidates FRAME.  */
+             frame = NULL;
+
+             /* We need to make sure we actually insert the momentary
+                breakpoint set above.  */
+             insert_breakpoints ();
+           }
+         else if (debug_displaced)
+           fprintf_unfiltered (gdb_stderr, "displaced: couldn't find previous "
+                               "frame to set momentary breakpoint for "
+                               "sigreturn/rt_sigreturn\n");
+       }
+      else if (debug_displaced)
+       fprintf_unfiltered (gdb_stdlog, "displaced: sigreturn/rt_sigreturn "
+                           "SVC call not in signal trampoline frame\n");
+    
+
+  /* Preparation: If we detect sigreturn, set momentary breakpoint at resume
+                 location, else nothing.
+     Insn: unmodified svc.
+     Cleanup: if pc lands in scratch space, pc <- insn_addr + 4
+              else leave pc alone.  */
+
+
+  dsc->cleanup = &arm_linux_cleanup_svc;
+  /* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
+     instruction.  */
+  dsc->wrote_to_pc = 1;
 
-  if (result)
-    return result;
-  
   return 0;
 }
 
-/* The constants below were determined by examining the following files
-   in the linux kernel sources:
 
-      arch/arm/kernel/signal.c
-         - see SWI_SYS_SIGRETURN and SWI_SYS_RT_SIGRETURN
-      include/asm-arm/unistd.h
-         - see __NR_sigreturn, __NR_rt_sigreturn, and __NR_SYSCALL_BASE */
+/* The following two functions implement single-stepping over calls to Linux
+   kernel helper routines, which perform e.g. atomic operations on architecture
+   variants which don't support them natively.
 
-#define ARM_LINUX_SIGRETURN_INSTR      0xef900077
-#define ARM_LINUX_RT_SIGRETURN_INSTR   0xef9000ad
+   When this function is called, the PC will be pointing at the kernel helper
+   (at an address inaccessible to GDB), and r14 will point to the return
+   address.  Displaced stepping always executes code in the copy area:
+   so, make the copy-area instruction branch back to the kernel helper (the
+   "from" address), and make r14 point to the breakpoint in the copy area.  In
+   that way, we regain control once the kernel helper returns, and can clean
+   up appropriately (as if we had just returned from the kernel helper as it
+   would have been called from the non-displaced location).  */
+
+static void
+cleanup_kernel_helper_return (struct gdbarch *gdbarch,
+                             struct regcache *regs,
+                             struct displaced_step_closure *dsc)
+{
+  displaced_write_reg (regs, dsc, ARM_LR_REGNUM, dsc->tmp[0], CANNOT_WRITE_PC);
+  displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->tmp[0], BRANCH_WRITE_PC);
+}
+
+static void
+arm_catch_kernel_helper_return (struct gdbarch *gdbarch, CORE_ADDR from,
+                               CORE_ADDR to, struct regcache *regs,
+                               struct displaced_step_closure *dsc)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  dsc->numinsns = 1;
+  dsc->insn_addr = from;
+  dsc->cleanup = &cleanup_kernel_helper_return;
+  /* Say we wrote to the PC, else cleanup will set PC to the next
+     instruction in the helper, which isn't helpful.  */
+  dsc->wrote_to_pc = 1;
+
+  /* Preparation: tmp[0] <- r14
+                  r14 <- <scratch space>+4
+                 *(<scratch space>+8) <- from
+     Insn: ldr pc, [r14, #4]
+     Cleanup: r14 <- tmp[0], pc <- tmp[0].  */
+
+  dsc->tmp[0] = displaced_read_reg (regs, dsc, ARM_LR_REGNUM);
+  displaced_write_reg (regs, dsc, ARM_LR_REGNUM, (ULONGEST) to + 4,
+                      CANNOT_WRITE_PC);
+  write_memory_unsigned_integer (to + 8, 4, byte_order, from);
+
+  dsc->modinsn[0] = 0xe59ef004;  /* ldr pc, [lr, #4].  */
+}
+
+/* Linux-specific displaced step instruction copying function.  Detects when
+   the program has stepped into a Linux kernel helper routine (which must be
+   handled as a special case), falling back to arm_displaced_step_copy_insn()
+   if it hasn't.  */
+
+static struct displaced_step_closure *
+arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
+                                   CORE_ADDR from, CORE_ADDR to,
+                                   struct regcache *regs)
+{
+  struct displaced_step_closure *dsc
+    = xmalloc (sizeof (struct displaced_step_closure));
+
+  /* Detect when we enter an (inaccessible by GDB) Linux kernel helper, and
+     stop at the return location.  */
+  if (from > 0xffff0000)
+    {
+      if (debug_displaced)
+        fprintf_unfiltered (gdb_stdlog, "displaced: detected kernel helper "
+                           "at %.8lx\n", (unsigned long) from);
+
+      arm_catch_kernel_helper_return (gdbarch, from, to, regs, dsc);
+    }
+  else
+    {
+      /* Override the default handling of SVC instructions.  */
+      dsc->u.svc.copy_svc_os = arm_linux_copy_svc;
+
+      arm_process_displaced_insn (gdbarch, from, to, regs, dsc);
+    }
+
+  arm_displaced_init_closure (gdbarch, from, to, dsc);
+
+  return dsc;
+}
+
+/* Implementation of `gdbarch_stap_is_single_operand', as defined in
+   gdbarch.h.  */
+
+static int
+arm_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
+{
+  return (*s == '#' || *s == '$' || isdigit (*s) /* Literal number.  */
+         || *s == '[' /* Register indirection or
+                         displacement.  */
+         || isalpha (*s)); /* Register value.  */
+}
+
+/* This routine is used to parse a special token in ARM's assembly.
+
+   The special tokens parsed by it are:
+
+      - Register displacement (e.g, [fp, #-8])
+
+   It returns one if the special token has been parsed successfully,
+   or zero if the current token is not considered special.  */
+
+static int
+arm_stap_parse_special_token (struct gdbarch *gdbarch,
+                             struct stap_parse_info *p)
+{
+  if (*p->arg == '[')
+    {
+      /* Temporary holder for lookahead.  */
+      const char *tmp = p->arg;
+      char *endp;
+      /* Used to save the register name.  */
+      const char *start;
+      char *regname;
+      int len, offset;
+      int got_minus = 0;
+      long displacement;
+      struct stoken str;
+
+      ++tmp;
+      start = tmp;
+
+      /* Register name.  */
+      while (isalnum (*tmp))
+       ++tmp;
+
+      if (*tmp != ',')
+       return 0;
+
+      len = tmp - start;
+      regname = alloca (len + 2);
+
+      offset = 0;
+      if (isdigit (*start))
+       {
+         /* If we are dealing with a register whose name begins with a
+            digit, it means we should prefix the name with the letter
+            `r', because GDB expects this name pattern.  Otherwise (e.g.,
+            we are dealing with the register `fp'), we don't need to
+            add such a prefix.  */
+         regname[0] = 'r';
+         offset = 1;
+       }
+
+      strncpy (regname + offset, start, len);
+      len += offset;
+      regname[len] = '\0';
+
+      if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1)
+       error (_("Invalid register name `%s' on expression `%s'."),
+              regname, p->saved_arg);
+
+      ++tmp;
+      tmp = skip_spaces_const (tmp);
+      if (*tmp == '#' || *tmp == '$')
+       ++tmp;
+
+      if (*tmp == '-')
+       {
+         ++tmp;
+         got_minus = 1;
+       }
+
+      displacement = strtol (tmp, &endp, 10);
+      tmp = endp;
+
+      /* Skipping last `]'.  */
+      if (*tmp++ != ']')
+       return 0;
+
+      /* The displacement.  */
+      write_exp_elt_opcode (&p->pstate, OP_LONG);
+      write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long);
+      write_exp_elt_longcst (&p->pstate, displacement);
+      write_exp_elt_opcode (&p->pstate, OP_LONG);
+      if (got_minus)
+       write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+
+      /* The register name.  */
+      write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+      str.ptr = regname;
+      str.length = len;
+      write_exp_string (&p->pstate, str);
+      write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+
+      write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+
+      /* Casting to the expected type.  */
+      write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+      write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type));
+      write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+
+      write_exp_elt_opcode (&p->pstate, UNOP_IND);
+
+      p->arg = tmp;
+    }
+  else
+    return 0;
 
-/* arm_linux_in_sigtramp determines if PC points at one of the
-   instructions which cause control to return to the Linux kernel upon
-   return from a signal handler.  FUNC_NAME is unused.  */
+  return 1;
+}
+
+/* ARM process record-replay constructs: syscall, signal etc.  */
+
+struct linux_record_tdep arm_linux_record_tdep;
 
-int
-arm_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
+/* arm_canonicalize_syscall maps from the native arm Linux set
+   of syscall ids into a canonical set of syscall ids used by
+   process record.  */
+
+static enum gdb_syscall
+arm_canonicalize_syscall (int syscall)
 {
-  unsigned long inst;
+  enum { sys_process_vm_writev = 377 };
+
+  if (syscall <= gdb_sys_sched_getaffinity)
+    return syscall;
+  else if (syscall >= 243 && syscall <= 247)
+    return syscall + 2;
+  else if (syscall >= 248 && syscall <= 253)
+    return syscall + 4;
 
-  inst = read_memory_integer (pc, 4);
+  return -1;
+}
 
-  return (inst == ARM_LINUX_SIGRETURN_INSTR
-         || inst == ARM_LINUX_RT_SIGRETURN_INSTR);
+/* Record all registers but PC register for process-record.  */
+
+static int
+arm_all_but_pc_registers_record (struct regcache *regcache)
+{
+  int i;
+
+  for (i = 0; i < ARM_PC_REGNUM; i++)
+    {
+      if (record_full_arch_list_add_reg (regcache, ARM_A1_REGNUM + i))
+        return -1;
+    }
 
+  if (record_full_arch_list_add_reg (regcache, ARM_PS_REGNUM))
+    return -1;
+
+  return 0;
 }
 
-/* arm_linux_sigcontext_register_address returns the address in the
-   sigcontext of register REGNO given a stack pointer value SP and
-   program counter value PC.  The value 0 is returned if PC is not
-   pointing at one of the signal return instructions or if REGNO is
-   not saved in the sigcontext struct.  */
+/* Handler for arm system call instruction recording.  */
 
-CORE_ADDR
-arm_linux_sigcontext_register_address (CORE_ADDR sp, CORE_ADDR pc, int regno)
+static int
+arm_linux_syscall_record (struct regcache *regcache, unsigned long svc_number)
 {
-  unsigned long inst;
-  CORE_ADDR reg_addr = 0;
+  int ret = 0;
+  enum gdb_syscall syscall_gdb;
 
-  inst = read_memory_integer (pc, 4);
+  syscall_gdb = arm_canonicalize_syscall (svc_number);
 
-  if (inst == ARM_LINUX_SIGRETURN_INSTR
-      || inst == ARM_LINUX_RT_SIGRETURN_INSTR)
+  if (syscall_gdb < 0)
     {
-      CORE_ADDR sigcontext_addr;
-
-      /* The sigcontext structure is at different places for the two
-         signal return instructions.  For ARM_LINUX_SIGRETURN_INSTR,
-        it starts at the SP value.  For ARM_LINUX_RT_SIGRETURN_INSTR,
-        it is at SP+8.  For the latter instruction, it may also be
-        the case that the address of this structure may be determined
-        by reading the 4 bytes at SP, but I'm not convinced this is
-        reliable.
-
-        In any event, these magic constants (0 and 8) may be
-        determined by examining struct sigframe and struct
-        rt_sigframe in arch/arm/kernel/signal.c in the Linux kernel
-        sources.  */
-
-      if (inst == ARM_LINUX_RT_SIGRETURN_INSTR)
-       sigcontext_addr = sp + 8;
-      else /* inst == ARM_LINUX_SIGRETURN_INSTR */
-        sigcontext_addr = sp + 0;
-
-      /* The layout of the sigcontext structure for ARM GNU/Linux is
-         in include/asm-arm/sigcontext.h in the Linux kernel sources.
-
-        There are three 4-byte fields which precede the saved r0
-        field.  (This accounts for the 12 in the code below.)  The
-        sixteen registers (4 bytes per field) follow in order.  The
-        PSR value follows the sixteen registers which accounts for
-        the constant 19 below. */
-
-      if (0 <= regno && regno <= ARM_PC_REGNUM)
-       reg_addr = sigcontext_addr + 12 + (4 * regno);
-      else if (regno == ARM_PS_REGNUM)
-       reg_addr = sigcontext_addr + 19 * 4;
+      printf_unfiltered (_("Process record and replay target doesn't "
+                           "support syscall number %s\n"),
+                           plongest (svc_number));
+      return -1;
     }
 
-  return reg_addr;
+  if (syscall_gdb == gdb_sys_sigreturn
+      || syscall_gdb == gdb_sys_rt_sigreturn)
+   {
+     if (arm_all_but_pc_registers_record (regcache))
+       return -1;
+     return 0;
+   }
+
+  ret = record_linux_system_call (syscall_gdb, regcache,
+                                  &arm_linux_record_tdep);
+  if (ret != 0)
+    return ret;
+
+  /* Record the return value of the system call.  */
+  if (record_full_arch_list_add_reg (regcache, ARM_A1_REGNUM))
+    return -1;
+  /* Record LR.  */
+  if (record_full_arch_list_add_reg (regcache, ARM_LR_REGNUM))
+    return -1;
+  /* Record CPSR.  */
+  if (record_full_arch_list_add_reg (regcache, ARM_PS_REGNUM))
+    return -1;
+
+  return 0;
+}
+
+/* Implement the skip_trampoline_code gdbarch method.  */
+
+static CORE_ADDR
+arm_linux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
+{
+  CORE_ADDR target_pc = arm_skip_stub (frame, pc);
+
+  if (target_pc != 0)
+    return target_pc;
+
+  return find_solib_trampoline_target (frame, pc);
 }
 
 static void
 arm_linux_init_abi (struct gdbarch_info info,
                    struct gdbarch *gdbarch)
 {
+  static const char *const stap_integer_prefixes[] = { "#", "$", "", NULL };
+  static const char *const stap_register_prefixes[] = { "r", NULL };
+  static const char *const stap_register_indirection_prefixes[] = { "[",
+                                                                   NULL };
+  static const char *const stap_register_indirection_suffixes[] = { "]",
+                                                                   NULL };
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
+  linux_init_abi (info, gdbarch);
+
   tdep->lowest_pc = 0x8000;
-  tdep->arm_breakpoint = arm_linux_arm_le_breakpoint;
+  if (info.byte_order == BFD_ENDIAN_BIG)
+    {
+      if (tdep->arm_abi == ARM_ABI_AAPCS)
+       tdep->arm_breakpoint = eabi_linux_arm_be_breakpoint;
+      else
+       tdep->arm_breakpoint = arm_linux_arm_be_breakpoint;
+      tdep->thumb_breakpoint = arm_linux_thumb_be_breakpoint;
+      tdep->thumb2_breakpoint = arm_linux_thumb2_be_breakpoint;
+    }
+  else
+    {
+      if (tdep->arm_abi == ARM_ABI_AAPCS)
+       tdep->arm_breakpoint = eabi_linux_arm_le_breakpoint;
+      else
+       tdep->arm_breakpoint = arm_linux_arm_le_breakpoint;
+      tdep->thumb_breakpoint = arm_linux_thumb_le_breakpoint;
+      tdep->thumb2_breakpoint = arm_linux_thumb2_le_breakpoint;
+    }
   tdep->arm_breakpoint_size = sizeof (arm_linux_arm_le_breakpoint);
+  tdep->thumb_breakpoint_size = sizeof (arm_linux_thumb_le_breakpoint);
+  tdep->thumb2_breakpoint_size = sizeof (arm_linux_thumb2_le_breakpoint);
 
-  tdep->fp_model = ARM_FLOAT_FPA;
+  if (tdep->fp_model == ARM_FLOAT_AUTO)
+    tdep->fp_model = ARM_FLOAT_FPA;
 
-  tdep->jb_pc = ARM_LINUX_JB_PC;
+  switch (tdep->fp_model)
+    {
+    case ARM_FLOAT_FPA:
+      tdep->jb_pc = ARM_LINUX_JB_PC_FPA;
+      break;
+    case ARM_FLOAT_SOFT_FPA:
+    case ARM_FLOAT_SOFT_VFP:
+    case ARM_FLOAT_VFP:
+      tdep->jb_pc = ARM_LINUX_JB_PC_EABI;
+      break;
+    default:
+      internal_error
+       (__FILE__, __LINE__,
+         _("arm_linux_init_abi: Floating point model not supported"));
+      break;
+    }
   tdep->jb_elt_size = ARM_LINUX_JB_ELEMENT_SIZE;
 
-  set_gdbarch_deprecated_call_dummy_words (gdbarch, arm_linux_call_dummy_words);
-  set_gdbarch_deprecated_sizeof_call_dummy_words (gdbarch, sizeof (arm_linux_call_dummy_words));
+  set_solib_svr4_fetch_link_map_offsets
+    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
 
-  /* The following two overrides shouldn't be needed.  */
-  set_gdbarch_deprecated_extract_return_value (gdbarch, arm_linux_extract_return_value);
-  set_gdbarch_deprecated_push_arguments (gdbarch, arm_linux_push_arguments);
+  /* Single stepping.  */
+  set_gdbarch_software_single_step (gdbarch, arm_linux_software_single_step);
 
   /* Shared library handling.  */
-  set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
-  set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
+  set_gdbarch_skip_trampoline_code (gdbarch, arm_linux_skip_trampoline_code);
+  set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+  /* Enable TLS support.  */
+  set_gdbarch_fetch_tls_load_module_address (gdbarch,
+                                             svr4_fetch_objfile_link_map);
+
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_linux_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_linux_rt_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_eabi_linux_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_eabi_linux_rt_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &thumb2_eabi_linux_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &thumb2_eabi_linux_rt_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_linux_restart_syscall_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_kernel_linux_restart_syscall_tramp_frame);
+
+  /* Core file support.  */
+  set_gdbarch_iterate_over_regset_sections
+    (gdbarch, arm_linux_iterate_over_regset_sections);
+  set_gdbarch_core_read_description (gdbarch, arm_linux_core_read_description);
+
+  set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
+
+  /* Displaced stepping.  */
+  set_gdbarch_displaced_step_copy_insn (gdbarch,
+                                       arm_linux_displaced_step_copy_insn);
+  set_gdbarch_displaced_step_fixup (gdbarch, arm_displaced_step_fixup);
+  set_gdbarch_displaced_step_free_closure (gdbarch,
+                                          simple_displaced_step_free_closure);
+  set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point);
+
+  /* Reversible debugging, process record.  */
+  set_gdbarch_process_record (gdbarch, arm_process_record);
+
+  /* SystemTap functions.  */
+  set_gdbarch_stap_integer_prefixes (gdbarch, stap_integer_prefixes);
+  set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes);
+  set_gdbarch_stap_register_indirection_prefixes (gdbarch,
+                                         stap_register_indirection_prefixes);
+  set_gdbarch_stap_register_indirection_suffixes (gdbarch,
+                                         stap_register_indirection_suffixes);
+  set_gdbarch_stap_gdb_register_prefix (gdbarch, "r");
+  set_gdbarch_stap_is_single_operand (gdbarch, arm_stap_is_single_operand);
+  set_gdbarch_stap_parse_special_token (gdbarch,
+                                       arm_stap_parse_special_token);
+
+  tdep->syscall_next_pc = arm_linux_syscall_next_pc;
+
+  /* `catch syscall' */
+  set_xml_syscall_file_name ("syscalls/arm-linux.xml");
+  set_gdbarch_get_syscall_number (gdbarch, arm_linux_get_syscall_number);
+
+  /* Syscall record.  */
+  tdep->arm_syscall_record = arm_linux_syscall_record;
+
+  /* Initialize the arm_linux_record_tdep.  */
+  /* These values are the size of the type that will be used in a system
+     call.  They are obtained from Linux Kernel source.  */
+  arm_linux_record_tdep.size_pointer
+    = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+  arm_linux_record_tdep.size__old_kernel_stat = 32;
+  arm_linux_record_tdep.size_tms = 16;
+  arm_linux_record_tdep.size_loff_t = 8;
+  arm_linux_record_tdep.size_flock = 16;
+  arm_linux_record_tdep.size_oldold_utsname = 45;
+  arm_linux_record_tdep.size_ustat = 20;
+  arm_linux_record_tdep.size_old_sigaction = 140;
+  arm_linux_record_tdep.size_old_sigset_t = 128;
+  arm_linux_record_tdep.size_rlimit = 8;
+  arm_linux_record_tdep.size_rusage = 72;
+  arm_linux_record_tdep.size_timeval = 8;
+  arm_linux_record_tdep.size_timezone = 8;
+  arm_linux_record_tdep.size_old_gid_t = 2;
+  arm_linux_record_tdep.size_old_uid_t = 2;
+  arm_linux_record_tdep.size_fd_set = 128;
+  arm_linux_record_tdep.size_dirent = 268;
+  arm_linux_record_tdep.size_dirent64 = 276;
+  arm_linux_record_tdep.size_statfs = 64;
+  arm_linux_record_tdep.size_statfs64 = 84;
+  arm_linux_record_tdep.size_sockaddr = 16;
+  arm_linux_record_tdep.size_int
+    = gdbarch_int_bit (gdbarch) / TARGET_CHAR_BIT;
+  arm_linux_record_tdep.size_long
+    = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
+  arm_linux_record_tdep.size_ulong
+    = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
+  arm_linux_record_tdep.size_msghdr = 28;
+  arm_linux_record_tdep.size_itimerval = 16;
+  arm_linux_record_tdep.size_stat = 88;
+  arm_linux_record_tdep.size_old_utsname = 325;
+  arm_linux_record_tdep.size_sysinfo = 64;
+  arm_linux_record_tdep.size_msqid_ds = 88;
+  arm_linux_record_tdep.size_shmid_ds = 84;
+  arm_linux_record_tdep.size_new_utsname = 390;
+  arm_linux_record_tdep.size_timex = 128;
+  arm_linux_record_tdep.size_mem_dqinfo = 24;
+  arm_linux_record_tdep.size_if_dqblk = 68;
+  arm_linux_record_tdep.size_fs_quota_stat = 68;
+  arm_linux_record_tdep.size_timespec = 8;
+  arm_linux_record_tdep.size_pollfd = 8;
+  arm_linux_record_tdep.size_NFS_FHSIZE = 32;
+  arm_linux_record_tdep.size_knfsd_fh = 132;
+  arm_linux_record_tdep.size_TASK_COMM_LEN = 16;
+  arm_linux_record_tdep.size_sigaction = 140;
+  arm_linux_record_tdep.size_sigset_t = 8;
+  arm_linux_record_tdep.size_siginfo_t = 128;
+  arm_linux_record_tdep.size_cap_user_data_t = 12;
+  arm_linux_record_tdep.size_stack_t = 12;
+  arm_linux_record_tdep.size_off_t = arm_linux_record_tdep.size_long;
+  arm_linux_record_tdep.size_stat64 = 96;
+  arm_linux_record_tdep.size_gid_t = 2;
+  arm_linux_record_tdep.size_uid_t = 2;
+  arm_linux_record_tdep.size_PAGE_SIZE = 4096;
+  arm_linux_record_tdep.size_flock64 = 24;
+  arm_linux_record_tdep.size_user_desc = 16;
+  arm_linux_record_tdep.size_io_event = 32;
+  arm_linux_record_tdep.size_iocb = 64;
+  arm_linux_record_tdep.size_epoll_event = 12;
+  arm_linux_record_tdep.size_itimerspec
+    = arm_linux_record_tdep.size_timespec * 2;
+  arm_linux_record_tdep.size_mq_attr = 32;
+  arm_linux_record_tdep.size_siginfo = 128;
+  arm_linux_record_tdep.size_termios = 36;
+  arm_linux_record_tdep.size_termios2 = 44;
+  arm_linux_record_tdep.size_pid_t = 4;
+  arm_linux_record_tdep.size_winsize = 8;
+  arm_linux_record_tdep.size_serial_struct = 60;
+  arm_linux_record_tdep.size_serial_icounter_struct = 80;
+  arm_linux_record_tdep.size_hayes_esp_config = 12;
+  arm_linux_record_tdep.size_size_t = 4;
+  arm_linux_record_tdep.size_iovec = 8;
+
+  /* These values are the second argument of system call "sys_ioctl".
+     They are obtained from Linux Kernel source.  */
+  arm_linux_record_tdep.ioctl_TCGETS = 0x5401;
+  arm_linux_record_tdep.ioctl_TCSETS = 0x5402;
+  arm_linux_record_tdep.ioctl_TCSETSW = 0x5403;
+  arm_linux_record_tdep.ioctl_TCSETSF = 0x5404;
+  arm_linux_record_tdep.ioctl_TCGETA = 0x5405;
+  arm_linux_record_tdep.ioctl_TCSETA = 0x5406;
+  arm_linux_record_tdep.ioctl_TCSETAW = 0x5407;
+  arm_linux_record_tdep.ioctl_TCSETAF = 0x5408;
+  arm_linux_record_tdep.ioctl_TCSBRK = 0x5409;
+  arm_linux_record_tdep.ioctl_TCXONC = 0x540a;
+  arm_linux_record_tdep.ioctl_TCFLSH = 0x540b;
+  arm_linux_record_tdep.ioctl_TIOCEXCL = 0x540c;
+  arm_linux_record_tdep.ioctl_TIOCNXCL = 0x540d;
+  arm_linux_record_tdep.ioctl_TIOCSCTTY = 0x540e;
+  arm_linux_record_tdep.ioctl_TIOCGPGRP = 0x540f;
+  arm_linux_record_tdep.ioctl_TIOCSPGRP = 0x5410;
+  arm_linux_record_tdep.ioctl_TIOCOUTQ = 0x5411;
+  arm_linux_record_tdep.ioctl_TIOCSTI = 0x5412;
+  arm_linux_record_tdep.ioctl_TIOCGWINSZ = 0x5413;
+  arm_linux_record_tdep.ioctl_TIOCSWINSZ = 0x5414;
+  arm_linux_record_tdep.ioctl_TIOCMGET = 0x5415;
+  arm_linux_record_tdep.ioctl_TIOCMBIS = 0x5416;
+  arm_linux_record_tdep.ioctl_TIOCMBIC = 0x5417;
+  arm_linux_record_tdep.ioctl_TIOCMSET = 0x5418;
+  arm_linux_record_tdep.ioctl_TIOCGSOFTCAR = 0x5419;
+  arm_linux_record_tdep.ioctl_TIOCSSOFTCAR = 0x541a;
+  arm_linux_record_tdep.ioctl_FIONREAD = 0x541b;
+  arm_linux_record_tdep.ioctl_TIOCINQ = arm_linux_record_tdep.ioctl_FIONREAD;
+  arm_linux_record_tdep.ioctl_TIOCLINUX = 0x541c;
+  arm_linux_record_tdep.ioctl_TIOCCONS = 0x541d;
+  arm_linux_record_tdep.ioctl_TIOCGSERIAL = 0x541e;
+  arm_linux_record_tdep.ioctl_TIOCSSERIAL = 0x541f;
+  arm_linux_record_tdep.ioctl_TIOCPKT = 0x5420;
+  arm_linux_record_tdep.ioctl_FIONBIO = 0x5421;
+  arm_linux_record_tdep.ioctl_TIOCNOTTY = 0x5422;
+  arm_linux_record_tdep.ioctl_TIOCSETD = 0x5423;
+  arm_linux_record_tdep.ioctl_TIOCGETD = 0x5424;
+  arm_linux_record_tdep.ioctl_TCSBRKP = 0x5425;
+  arm_linux_record_tdep.ioctl_TIOCTTYGSTRUCT = 0x5426;
+  arm_linux_record_tdep.ioctl_TIOCSBRK = 0x5427;
+  arm_linux_record_tdep.ioctl_TIOCCBRK = 0x5428;
+  arm_linux_record_tdep.ioctl_TIOCGSID = 0x5429;
+  arm_linux_record_tdep.ioctl_TCGETS2 = 0x802c542a;
+  arm_linux_record_tdep.ioctl_TCSETS2 = 0x402c542b;
+  arm_linux_record_tdep.ioctl_TCSETSW2 = 0x402c542c;
+  arm_linux_record_tdep.ioctl_TCSETSF2 = 0x402c542d;
+  arm_linux_record_tdep.ioctl_TIOCGPTN = 0x80045430;
+  arm_linux_record_tdep.ioctl_TIOCSPTLCK = 0x40045431;
+  arm_linux_record_tdep.ioctl_FIONCLEX = 0x5450;
+  arm_linux_record_tdep.ioctl_FIOCLEX = 0x5451;
+  arm_linux_record_tdep.ioctl_FIOASYNC = 0x5452;
+  arm_linux_record_tdep.ioctl_TIOCSERCONFIG = 0x5453;
+  arm_linux_record_tdep.ioctl_TIOCSERGWILD = 0x5454;
+  arm_linux_record_tdep.ioctl_TIOCSERSWILD = 0x5455;
+  arm_linux_record_tdep.ioctl_TIOCGLCKTRMIOS = 0x5456;
+  arm_linux_record_tdep.ioctl_TIOCSLCKTRMIOS = 0x5457;
+  arm_linux_record_tdep.ioctl_TIOCSERGSTRUCT = 0x5458;
+  arm_linux_record_tdep.ioctl_TIOCSERGETLSR = 0x5459;
+  arm_linux_record_tdep.ioctl_TIOCSERGETMULTI = 0x545a;
+  arm_linux_record_tdep.ioctl_TIOCSERSETMULTI = 0x545b;
+  arm_linux_record_tdep.ioctl_TIOCMIWAIT = 0x545c;
+  arm_linux_record_tdep.ioctl_TIOCGICOUNT = 0x545d;
+  arm_linux_record_tdep.ioctl_TIOCGHAYESESP = 0x545e;
+  arm_linux_record_tdep.ioctl_TIOCSHAYESESP = 0x545f;
+  arm_linux_record_tdep.ioctl_FIOQSIZE = 0x5460;
+
+  /* These values are the second argument of system call "sys_fcntl"
+     and "sys_fcntl64".  They are obtained from Linux Kernel source.  */
+  arm_linux_record_tdep.fcntl_F_GETLK = 5;
+  arm_linux_record_tdep.fcntl_F_GETLK64 = 12;
+  arm_linux_record_tdep.fcntl_F_SETLK64 = 13;
+  arm_linux_record_tdep.fcntl_F_SETLKW64 = 14;
+
+  arm_linux_record_tdep.arg1 = ARM_A1_REGNUM + 1;
+  arm_linux_record_tdep.arg2 = ARM_A1_REGNUM + 2;
+  arm_linux_record_tdep.arg3 = ARM_A1_REGNUM + 3;
+  arm_linux_record_tdep.arg4 = ARM_A1_REGNUM + 3;
 }
 
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_arm_linux_tdep;
+
 void
 _initialize_arm_linux_tdep (void)
 {