[rfa] Skip _from_arm and _from_thumb stubs

Daniel Jacobowitz drow@false.org
Sat Sep 1 20:42:00 GMT 2007


This patch lets us step from an ARM function into a Thumb function
when using GNU ld and ARM v4T.  The linker synthesizes interworking
stubs, but they don't have debug info.  Without this patch, GDB
decides we have stepped into a function without debug info,
and automatically does a "finish".

There's more relevant stubs that I tried to support but encountered
hiccups.  GCC has -mcaller-super-interworking, but that's rarely used
nowadays.  It's problematic because we have to be able to unwind from
_arm_return; there's a nop before _arm_return so that the unwind info
is correct, but it's after the symbol, so GDB's prologue analyzer gets
all confused.  And GDB doesn't use the ARM unwind tables directly,
and gas doesn't generate .debug_frame from them.

Also GCC generates out-of-line "bx" instructions for indirect function
calls on v4T.  GDB can't currently figure out that they're in Thumb
mode, for whatever reason, so it has some trouble detecting them.

Anyway, this patch is a strict improvement.  Tested on arm-none-eabi
ARM and Thumb modes and by hand.  OK to commit?

-- 
Daniel Jacobowitz
CodeSourcery

2007-09-01  Daniel Jacobowitz  <dan@codesourcery.com>

	PR gdb/2103
	* arm-tdep.c (arm_in_call_stub): Delete.
	(arm_skip_stub): Handle from_arm and from_thumb stubs.

Index: arm-tdep.c
===================================================================
RCS file: /scratch/gcc/repos/src/src/gdb/arm-tdep.c,v
retrieving revision 1.241
diff -u -p -r1.241 arm-tdep.c
--- arm-tdep.c	23 Aug 2007 18:08:26 -0000	1.241
+++ arm-tdep.c	1 Sep 2007 17:27:24 -0000
@@ -2358,38 +2358,24 @@ arm_get_longjmp_target (struct frame_inf
   return 1;
 }
 
-/* Return non-zero if the PC is inside a thumb call thunk.  */
-
-int
-arm_in_call_stub (CORE_ADDR pc, char *name)
-{
-  CORE_ADDR start_addr;
-
-  /* Find the starting address of the function containing the PC.  If
-     the caller didn't give us a name, look it up at the same time.  */
-  if (0 == find_pc_partial_function (pc, name ? NULL : &name, 
-				     &start_addr, NULL))
-    return 0;
-
-  return strncmp (name, "_call_via_r", 11) == 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
-   _called_via_xx, where x is the register name.  The possible names
-   are r0-r9, sl, fp, ip, sp, and lr.  */
+/* Recognize GCC and GNU ld's trampolines.  If we are in a trampoline,
+   return the target PC.  Otherwise return 0.  */
 
 CORE_ADDR
 arm_skip_stub (struct frame_info *frame, CORE_ADDR pc)
 {
   char *name;
+  int namelen;
   CORE_ADDR start_addr;
 
   /* 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;
 
-  /* Call thunks always start with "_call_via_".  */
+  /* 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
+     _call_via_xx, where x is the register name.  The possible names
+     are r0-r9, sl, fp, ip, sp, and lr.  */
   if (strncmp (name, "_call_via_", 10) == 0)
     {
       /* Use the name suffix to determine which register contains the
@@ -2399,12 +2385,49 @@ arm_skip_stub (struct frame_info *frame,
        "r8", "r9", "sl", "fp", "ip", "sp", "lr"
       };
       int regno;
+      int offset = strlen (name) - 2;
 
       for (regno = 0; regno <= 14; regno++)
-	if (strcmp (&name[10], table[regno]) == 0)
+	if (strcmp (&name[offset], table[regno]) == 0)
 	  return get_frame_register_unsigned (frame, regno);
     }
 
+  /* GNU ld generates __foo_from_arm or __foo_from_thumb for
+     non-interworking calls to foo.  We could decode the stubs
+     to find the target but it's easier to use the symbol table.  */
+  namelen = strlen (name);
+  if (name[0] == '_' && name[1] == '_'
+      && ((namelen > 2 + strlen ("_from_thumb")
+	   && strncmp (name + namelen - strlen ("_from_thumb"), "_from_thumb",
+		       strlen ("_from_thumb")) == 0)
+	  || (namelen > 2 + strlen ("_from_arm")
+	      && strncmp (name + namelen - strlen ("_from_arm"), "_from_arm",
+			  strlen ("_from_arm")) == 0)))
+    {
+      char *target_name;
+      int target_len = namelen - 2;
+      struct minimal_symbol *minsym;
+      struct objfile *objfile;
+      struct obj_section *sec;
+
+      if (name[namelen - 1] == 'b')
+	target_len -= strlen ("_from_thumb");
+      else
+	target_len -= strlen ("_from_arm");
+
+      target_name = alloca (target_len + 1);
+      memcpy (target_name, name + 2, target_len);
+      target_name[target_len] = '\0';
+
+      sec = find_pc_section (pc);
+      objfile = (sec == NULL) ? NULL : sec->objfile;
+      minsym = lookup_minimal_symbol (target_name, NULL, objfile);
+      if (minsym != NULL)
+	return SYMBOL_VALUE_ADDRESS (minsym);
+      else
+	return 0;
+    }
+
   return 0;			/* not a stub */
 }
 



More information about the Gdb-patches mailing list