@@ -239,6 +239,7 @@ static const char arm_linux_thumb2_le_br
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,
@@ -355,10 +356,36 @@ arm_linux_restart_syscall_init (const st
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;
- trad_frame_set_reg_addr (this_cache, ARM_PC_REGNUM, sp);
- trad_frame_set_reg_value (this_cache, ARM_SP_REGNUM, sp + 12);
+ /* 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));
@@ -417,6 +444,17 @@ static struct tramp_frame arm_linux_rest
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)
@@ -1008,6 +1046,8 @@ arm_linux_init_abi (struct gdbarch_info
&arm_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_regset_from_core_section (gdbarch,
@@ -262,7 +262,7 @@ int arm_apcs_32 = 1;
/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
-static int
+int
arm_psr_thumb_bit (struct gdbarch *gdbarch)
{
if (gdbarch_tdep (gdbarch)->is_m)
@@ -310,6 +310,9 @@ extern void arm_displaced_step_fixup (st
struct displaced_step_closure *,
CORE_ADDR, CORE_ADDR, struct regcache *);
+/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
+extern int arm_psr_thumb_bit (struct gdbarch *);
+
/* Is the instruction at the given memory address a Thumb or ARM
instruction? */
extern int arm_pc_is_thumb (struct gdbarch *, CORE_ADDR);