reverse-finish: turn internal error into normal error
[deliverable/binutils-gdb.git] / gdb / arm-tdep.c
index 74942b1fe82c5d31aa357b745b93edc53afeb200..8cc60a4bb1322115c2fea803689fe3cc7f9e1286 100644 (file)
@@ -2870,6 +2870,64 @@ struct frame_unwind arm_exidx_unwind = {
   arm_exidx_unwind_sniffer
 };
 
+/* Recognize GCC's trampoline for thumb call-indirect.  If we are in a
+   trampoline, return the target PC.  Otherwise return 0.
+
+   void call0a (char c, short s, int i, long l) {}
+
+   int main (void)
+   {
+     (*pointer_to_call0a) (c, s, i, l);
+   }
+
+   Instead of calling a stub library function  _call_via_xx (xx is
+   the register name), GCC may inline the trampoline in the object
+   file as below (register r2 has the address of call0a).
+
+   .global main
+   .type main, %function
+   ...
+   bl .L1
+   ...
+   .size main, .-main
+
+   .L1:
+   bx r2
+
+   The trampoline 'bx r2' doesn't belong to main.  */
+
+static CORE_ADDR
+arm_skip_bx_reg (struct frame_info *frame, CORE_ADDR pc)
+{
+  /* The heuristics of recognizing such trampoline is that FRAME is
+     executing in Thumb mode and the instruction on PC is 'bx Rm'.  */
+  if (arm_frame_is_thumb (frame))
+    {
+      gdb_byte buf[2];
+
+      if (target_read_memory (pc, buf, 2) == 0)
+       {
+         struct gdbarch *gdbarch = get_frame_arch (frame);
+         enum bfd_endian byte_order_for_code
+           = gdbarch_byte_order_for_code (gdbarch);
+         uint16_t insn
+           = extract_unsigned_integer (buf, 2, byte_order_for_code);
+
+         if ((insn & 0xff80) == 0x4700)  /* bx <Rm> */
+           {
+             CORE_ADDR dest
+               = get_frame_register_unsigned (frame, bits (insn, 3, 6));
+
+             /* Clear the LSB so that gdb core sets step-resume
+                breakpoint at the right address.  */
+             return UNMAKE_THUMB_ADDR (dest);
+           }
+       }
+    }
+
+  return 0;
+}
+
 static struct arm_prologue_cache *
 arm_make_stub_cache (struct frame_info *this_frame)
 {
@@ -2906,12 +2964,19 @@ arm_stub_unwind_sniffer (const struct frame_unwind *self,
 {
   CORE_ADDR addr_in_block;
   gdb_byte dummy[4];
+  CORE_ADDR pc, start_addr;
+  const char *name;
 
   addr_in_block = get_frame_address_in_block (this_frame);
+  pc = get_frame_pc (this_frame);
   if (in_plt_section (addr_in_block)
       /* We also use the stub winder if the target memory is unreadable
         to avoid having the prologue unwinder trying to read it.  */
-      || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
+      || target_read_memory (pc, dummy, 4) != 0)
+    return 1;
+
+  if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0
+      && arm_skip_bx_reg (this_frame, pc) != 0)
     return 1;
 
   return 0;
@@ -9226,7 +9291,15 @@ arm_skip_stub (struct frame_info *frame, CORE_ADDR pc)
 
   /* Find the starting address and name of the function containing the PC.  */
   if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0)
-    return 0;
+    {
+      /* Trampoline 'bx reg' doesn't belong to any functions.  Do the
+        check here.  */
+      start_addr = arm_skip_bx_reg (frame, pc);
+      if (start_addr != 0)
+       return start_addr;
+
+      return 0;
+    }
 
   /* If PC is in a Thumb call or return stub, return the address of the
      target PC, which is in a register.  The thunk functions are called
This page took 0.025872 seconds and 4 git commands to generate.