[1/3] Skip 'bx reg' trampoline on arm-none-eabi

Message ID 1402279983-1907-2-git-send-email-yao@codesourcery.com
State Committed
Headers

Commit Message

Yao Qi June 9, 2014, 2:13 a.m. UTC
  After this patch
<https://gcc.gnu.org/ml/gcc-patches/2005-01/msg00813.html> applied to
GCC, a new trampoline is generated but GDB doesn't recognize it.  This
patch is to teach GDB to understand this trampoline.  See details
about this trampoline and the heuristics in the comments.

gdb:

2014-06-09  Yao Qi  <yao@codesourcery.com>

	* arm-tdep.c (arm_skip_bx_reg): New function.
	(arm_skip_stub): Call arm_skip_bx_reg.
---
 gdb/arm-tdep.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 67 insertions(+), 1 deletion(-)
  

Comments

Joel Brobecker June 23, 2014, 1:17 p.m. UTC | #1
> 2014-06-09  Yao Qi  <yao@codesourcery.com>
> 
> 	* arm-tdep.c (arm_skip_bx_reg): New function.
> 	(arm_skip_stub): Call arm_skip_bx_reg.

Sorry about the delay, Yao. This is OK.  Perhaps consider the idea
of moving this function up a bit so that you don't have to add an
prototype in patch #2.

> ---
>  gdb/arm-tdep.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 67 insertions(+), 1 deletion(-)
> 
> diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
> index 74942b1..2430a86 100644
> --- a/gdb/arm-tdep.c
> +++ b/gdb/arm-tdep.c
> @@ -9214,6 +9214,64 @@ arm_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
>    return 1;
>  }
>  
> +/* 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;
> +}
> +
>  /* Recognize GCC and GNU ld's trampolines.  If we are in a trampoline,
>     return the target PC.  Otherwise return 0.  */
>  
> @@ -9226,7 +9284,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
> -- 
> 1.9.0
  
Yao Qi June 24, 2014, 1:34 a.m. UTC | #2
On 06/23/2014 09:17 PM, Joel Brobecker wrote:
> Sorry about the delay, Yao. This is OK.  Perhaps consider the idea
> of moving this function up a bit so that you don't have to add an
> prototype in patch #2.

Sure, I move arm_skip_bx_reg up and push these three patches in.
Don't post patches again as changes are quite trivial.  Thanks
for the review.
  

Patch

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 74942b1..2430a86 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -9214,6 +9214,64 @@  arm_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
   return 1;
 }
 
+/* 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;
+}
+
 /* Recognize GCC and GNU ld's trampolines.  If we are in a trampoline,
    return the target PC.  Otherwise return 0.  */
 
@@ -9226,7 +9284,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