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
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)
"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. "
"Setting momentary breakpoint.\n", (unsigned long) return_to);
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.
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. */
/* Determine if FRAME is executing in Thumb mode. */
-static int
+int
arm_frame_is_thumb (struct frame_info *frame)
{
CORE_ADDR cpsr;
else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
{
unsigned long cond = bits (inst1, 8, 11);
- if (cond != 0x0f && condition_true (cond, status)) /* 0x0f = SWI */
+ if (cond == 0x0f) /* 0x0f = SWI */
+ {
+ struct gdbarch_tdep *tdep;
+ tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->syscall_next_pc != NULL)
+ nextpc = tdep->syscall_next_pc (frame);
+
+ }
+ else if (cond != 0x0f && condition_true (cond, status))
nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
}
else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */
case 0xc:
case 0xd:
case 0xe: /* coproc ops */
+ break;
case 0xf: /* SWI */
+ {
+ struct gdbarch_tdep *tdep;
+ tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->syscall_next_pc != NULL)
+ nextpc = tdep->syscall_next_pc (frame);
+
+ }
break;
default:
struct type *arm_ext_type;
struct type *neon_double_type;
struct type *neon_quad_type;
+
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
};
/* Structures used for displaced stepping. */
CORE_ADDR arm_skip_stub (struct frame_info *, CORE_ADDR);
CORE_ADDR arm_get_next_pc (struct frame_info *, CORE_ADDR);
int arm_software_single_step (struct frame_info *);
+int arm_frame_is_thumb (struct frame_info *frame);
extern struct displaced_step_closure *
arm_displaced_step_copy_insn (struct gdbarch *, CORE_ADDR, CORE_ADDR,