/* GNU/Linux on ARM target support.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009, 2010 Free Software Foundation, Inc.
+ 2009, 2010, 2011 Free Software Foundation, Inc.
This file is part of GDB.
static const char arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
-/* Description of the longjmp buffer. */
+/* Description of the longjmp buffer. The buffer is treated as an array of
+ elements of size ARM_LINUX_JB_ELEMENT_SIZE.
+
+ 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.
+
+ 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 21
+#define ARM_LINUX_JB_PC_FPA 21
+#define ARM_LINUX_JB_PC_EABI 9
/*
Dynamic Linking on ARM GNU/Linux
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.
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)
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]:
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].
return NULL;
}
+/* Copy the value of next pc of sigreturn and rt_sigrturn into PC,
+ and return 1. 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)
+{
+ /* Is this a sigreturn or rt_sigreturn syscall? */
+ if (svc_number == 119 || svc_number == 173)
+ {
+ if (get_frame_type (frame) == SIGTRAMP_FRAME)
+ {
+ *pc = frame_unwind_caller_pc (frame);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* When FRAME is at a syscall instruction, return the PC of the next
+ instruction to be executed. */
+
+static CORE_ADDR
+arm_linux_syscall_next_pc (struct frame_info *frame)
+{
+ CORE_ADDR pc = get_frame_pc (frame);
+ CORE_ADDR return_addr = 0;
+ int is_thumb = arm_frame_is_thumb (frame);
+ ULONGEST svc_number = 0;
+ int is_sigreturn = 0;
+
+ if (is_thumb)
+ {
+ svc_number = get_frame_register_unsigned (frame, 7);
+ }
+ 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);
+ }
+ }
+
+ is_sigreturn = arm_linux_sigreturn_return_addr (frame, svc_number,
+ &return_addr);
+
+ if (is_sigreturn)
+ return return_addr;
+
+ if (is_thumb)
+ {
+ return_addr = pc + 2;
+ /* Addresses for calling Thumb functions have the bit 0 set. */
+ return_addr |= 1;
+ }
+ else
+ {
+ return_addr = pc + 4;
+ }
+
+ return return_addr;
+}
+
+
/* Insert a single step breakpoint at the next executed instruction. */
static int
/* Support for displaced stepping of Linux SVC instructions. */
static void
-arm_linux_cleanup_svc (struct gdbarch *gdbarch ATTRIBUTE_UNUSED,
+arm_linux_cleanup_svc (struct gdbarch *gdbarch,
struct regcache *regs,
struct displaced_step_closure *dsc)
{
struct regcache *regs, struct displaced_step_closure *dsc)
{
CORE_ADDR from = dsc->insn_addr;
+ CORE_ADDR return_to = 0;
+
struct frame_info *frame;
unsigned int svc_number = displaced_read_reg (regs, from, 7);
+ int is_sigreturn = 0;
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying Linux svc insn %.8lx\n",
frame = get_current_frame ();
- /* Is this a sigreturn or rt_sigreturn syscall? Note: these are only useful
- for EABI. */
- if (svc_number == 119 || svc_number == 173)
+ is_sigreturn = arm_linux_sigreturn_return_addr(frame, svc_number,
+ &return_to);
+ if (is_sigreturn)
{
- if (get_frame_type (frame) == SIGTRAMP_FRAME)
- {
- CORE_ADDR return_to;
struct symtab_and_line sal;
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: found "
- "sigreturn/rt_sigreturn SVC call. PC in frame = %lx\n",
+ "sigreturn/rt_sigreturn SVC call. PC in frame = %lx\n",
(unsigned long) get_frame_pc (frame));
- return_to = frame_unwind_caller_pc (frame);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx. "
+ fprintf_unfiltered (gdb_stdlog, "displaced: unwind pc = %lx. "
"Setting momentary breakpoint.\n", (unsigned long) return_to);
- gdb_assert (inferior_thread ()->step_resume_breakpoint == NULL);
+ gdb_assert (inferior_thread ()->control.step_resume_breakpoint
+ == NULL);
sal = find_pc_line (return_to, 0);
sal.pc = return_to;
if (frame)
{
- inferior_thread ()->step_resume_breakpoint
+ inferior_thread ()->control.step_resume_breakpoint
= set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
bp_step_resume);
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.
would have been called from the non-displaced location). */
static void
-cleanup_kernel_helper_return (struct gdbarch *gdbarch ATTRIBUTE_UNUSED,
+cleanup_kernel_helper_return (struct gdbarch *gdbarch,
struct regcache *regs,
struct displaced_step_closure *dsc)
{
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ linux_init_abi (info, gdbarch);
+
tdep->lowest_pc = 0x8000;
if (info.byte_order == BFD_ENDIAN_BIG)
{
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_solib_svr4_fetch_link_map_offsets
set_gdbarch_displaced_step_free_closure (gdbarch,
simple_displaced_step_free_closure);
set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point);
+
+
+ tdep->syscall_next_pc = arm_linux_syscall_next_pc;
}
/* Provide a prototype to silence -Wmissing-prototypes. */