This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfc] Unwind the ARM CPSR


This patch is based on a suggestion that Mark made around March 2006,
and another related problem that Paul Brook and Vladimir Prus
discovered.  I would love comments on it; it works, but there is
one place that I'm unhappy with (in frame unwinding and the DWARF-2
unwinder).

Many ARM processors support two execution modes (ARM and Thumb) with
different instruction sets.  We can use the symbol table to figure out
which mode a function requires, but there are cases where it's more
useful to consult the processor state.  The state is normally stored
in the CPSR (processor status register).  But the state can change
across function calls / returns, so it is also encoded on the stack
as the low bit of LR.

We treat the stack copy of LR as the resume address.  This isn't quite
right, because that low bit is only valid in LR or on the stack; it's
never set in the PC register.  The GDB simulator lets you set it and
still works more or less OK.  Another simulator we test with (QEMU)
does not allow this.  Nor does the architecture.  So going up a frame,
printing $pc, and seeing its low bit set is surprising.  It also
causes trouble for the "return" command.

Most of CPSR is not saved by normal function calls.  But the nicest
way by far to handle Thumb-ness checking for frames is to unwind that
frame's CPSR and check its T (Thumb state) bit, which requires the
unwinders to synthesize a saved CPSR from the next frame's CPSR
and the saved return address.  Pretty straightforward for the prologue
unwinder, but harder for the DWARF-2 frame unwinder.

To handle that, I added a new DWARF2_FRAME_REG_FN which supplies a
default unwinder.  For the PC, I originally added
DWARF2_FRAME_REG_RA_MASK instead to mask out the low bit, but
that wasn't enough for the CPSR.  And I discovered that
there is no easy way, given the arguments to an unwinder's
prev_register method, to request the unwound value of another
register from the same frame!  That's why the patch below had
to make dwarf2_frame_prev_register global, and pass its opaque
THIS_CACHE to the function supplied by DWARF2_FRAME_REG_FN.

During a prev_register method there are three frames of interest.  The
NEXT frame is passed; the CURRENT frame's cache is passed and some
knowledge of the CURRENT frame is implicit in the called function; and
the PREVIOUS frame's register value is calculated.  Given a frame
there are functions to get its register values or the values of the
previous frame; since we start with NEXT that means we can use generic
frame interfaces to get only NEXT or CURRENT registers.  But to
calculate PREVIOUS's CPSR we need PREVIOUS's LR so we have to call
dwarf2_frame_prev_register directly.

Most prev_register methods work by looking at their cache, and if they
can not find anything there, using an unwind method on the NEXT frame.
That returns the CURRENT frame's registers, i.e., the right values if
this frame did not save the register.  We're never interested in using
the other set of methods, which given the NEXT frame return NEXT's
registers.  And we can not use get_prev_frame (NEXT) to get CURRENT,
because in some cases that may return NULL, like if we're around main
where the backtrace cuts off.  But that frame does always exist.

So why shouldn't the argument be the CURRENT (i.e. THIS) frame
instead?  Then we can call frame_register (CURRENT) instead of
frame_unwind_register (NEXT) to get the same result, plus we'll
have the option of calling frame_unwind_register (CURRENT) when
we need it.

Of course this would be a pain to change all at once since there are
so many unwinders.  I'd introduce a new method instead
(unwind->prev_register_this?).  What do you think?  Is there some
reason I didn't think of why it would be naughty to use the current
frame instead of the next one?

Meanwhile the patch below has been tested on arm-sim/-mthumb, both
with and without the DWARF-2 unwinder.

-- 
Daniel Jacobowitz
CodeSourcery

2007-10-11  Daniel Jacobowitz  <dan@codesourcery.com>

	* arm-linux-tdep.h (ARM_CPSR_REGNUM): Delete definition.
	* arm-tdep.c (arm_pc_is_thumb): Clarify comment.
	(arm_frame_is_thumb): New.
	(arm_addr_bits_remove): Only remove one bit.
	(thumb_analyze_prologue): Remove PC special case.
	(thumb_scan_prologue): Take a block_addr argument.  Use it for
	find_pc_partial_function.  Remove unused variables.
	(arm_scan_prologue): Use arm_frame_is_thumb.  Use the unwound
	block address for find_pc_partial_function.  Remove PC special
	case.
	(arm_prologue_prev_register): Add special handling for PC and LR.
	(arm_dwarf2_prev_register, arm_dwarf2_frame_init_reg): New.
	(arm_get_next_pc): Use arm_frame_is_thumb.
	(arm_write_pc): Use CPSR_T instead of 0x20.
	(arm_gdbarch_init): Call dwarf2_frame_set_init_reg.
	* arm-tdep.h (enum gdb_regnum): Add ARM_CPSR_REGNUM.
	(CPSR_T): Define.
	* dwarf2-frame.c (dwarf2_frame_prev_register): Make global.
	Handle DWARF2_FRAME_REG_FN.
	* dwarf2-frame.h (enum dwarf2_frame_reg_rule): Add
	DWARF2_FRAME_REG_FN.
	(struct dwarf2_frame_state_reg): Add FN to loc union.
	(dwarf2_frame_prev_register): Declare.

2007-10-11  Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.arch/thumb-prologue.exp: Do not expect a saved PC.

Index: arm-linux-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/arm-linux-tdep.h,v
retrieving revision 1.3
diff -u -p -r1.3 arm-linux-tdep.h
--- arm-linux-tdep.h	23 Aug 2007 18:08:26 -0000	1.3
+++ arm-linux-tdep.h	11 Oct 2007 14:16:49 -0000
@@ -20,8 +20,6 @@
 struct regset;
 struct regcache;
 
-#define		ARM_CPSR_REGNUM		16
-
 #define ARM_LINUX_SIZEOF_NWFPE (8 * FP_REGISTER_SIZE \
 				+ 2 * INT_REGISTER_SIZE \
 				+ 8 + INT_REGISTER_SIZE)
Index: arm-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/arm-tdep.c,v
retrieving revision 1.245
diff -u -p -r1.245 arm-tdep.c
--- arm-tdep.c	10 Oct 2007 14:04:53 -0000	1.245
+++ arm-tdep.c	11 Oct 2007 14:16:49 -0000
@@ -211,7 +211,8 @@ struct arm_prologue_cache
 int arm_apcs_32 = 1;
 
 /* Determine if the program counter specified in MEMADDR is in a Thumb
-   function.  */
+   function.  This function should be called for addresses unrelated to
+   any executing frame; otherwise, prefer arm_frame_is_thumb.  */
 
 static int
 arm_pc_is_thumb (CORE_ADDR memaddr)
@@ -234,12 +235,33 @@ arm_pc_is_thumb (CORE_ADDR memaddr)
     }
 }
 
+/* Determine if FRAME is executing in Thumb mode.  It does not have to
+   be the current frame.  If UNWIND is non-zero, check the frame previous
+   to FRAME instead.  */
+
+static int
+arm_frame_is_thumb (struct frame_info *frame, int unwind)
+{
+  CORE_ADDR cpsr;
+
+  /* Every ARM frame unwinder can unwind the CPSR, either directly
+     (from a signal frame or dummy frame) or by interpreting the saved
+     LR (from a prologue or DWARF frame).  So consult it and trust
+     the unwinders.  */
+  if (unwind)
+    cpsr = frame_unwind_register_unsigned (frame, ARM_PS_REGNUM);
+  else
+    cpsr = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+
+  return (cpsr & CPSR_T) != 0;
+}
+
 /* Remove useless bits from addresses in a running program.  */
 static CORE_ADDR
 arm_addr_bits_remove (CORE_ADDR val)
 {
   if (arm_apcs_32)
-    return (val & (arm_pc_is_thumb (val) ? 0xfffffffe : 0xfffffffc));
+    return UNMAKE_THUMB_ADDR (val);
   else
     return (val & 0x03fffffc);
 }
@@ -272,14 +294,6 @@ thumb_analyze_prologue (struct gdbarch *
   stack = make_pv_area (ARM_SP_REGNUM);
   back_to = make_cleanup_free_pv_area (stack);
 
-  /* The call instruction saved PC in LR, and the current PC is not
-     interesting.  Due to this file's conventions, we want the value
-     of LR at this function's entry, not at the call site, so we do
-     not record the save of the PC - when the ARM prologue analyzer
-     has also been converted to the pv mechanism, we could record the
-     save here and remove the hack in prev_register.  */
-  regs[ARM_PC_REGNUM] = pv_unknown ();
-
   while (start < limit)
     {
       unsigned short insn;
@@ -533,22 +547,15 @@ arm_skip_prologue (CORE_ADDR pc)
 /* *INDENT-ON* */
 
 static void
-thumb_scan_prologue (CORE_ADDR prev_pc, struct arm_prologue_cache *cache)
+thumb_scan_prologue (CORE_ADDR prev_pc, CORE_ADDR block_addr,
+		     struct arm_prologue_cache *cache)
 {
   CORE_ADDR prologue_start;
   CORE_ADDR prologue_end;
   CORE_ADDR current_pc;
-  /* Which register has been copied to register n?  */
-  int saved_reg[16];
-  /* findmask:
-     bit 0 - push { rlist }
-     bit 1 - mov r7, sp  OR  add r7, sp, #imm  (setting of r7)
-     bit 2 - sub sp, #simm  OR  add sp, #simm  (adjusting of sp)
-  */
-  int findmask = 0;
-  int i;
 
-  if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end))
+  if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+				&prologue_end))
     {
       struct symtab_and_line sal = find_pc_line (prologue_start, 0);
 
@@ -643,6 +650,8 @@ arm_scan_prologue (struct frame_info *ne
   int regno;
   CORE_ADDR prologue_start, prologue_end, current_pc;
   CORE_ADDR prev_pc = frame_pc_unwind (next_frame);
+  CORE_ADDR block_addr = frame_unwind_address_in_block (next_frame,
+							NORMAL_FRAME);
   pv_t regs[ARM_FPS_REGNUM];
   struct pv_area *stack;
   struct cleanup *back_to;
@@ -653,15 +662,16 @@ arm_scan_prologue (struct frame_info *ne
   cache->framesize = 0;
 
   /* Check for Thumb prologue.  */
-  if (arm_pc_is_thumb (prev_pc))
+  if (arm_frame_is_thumb (next_frame, 1))
     {
-      thumb_scan_prologue (prev_pc, cache);
+      thumb_scan_prologue (prev_pc, block_addr, cache);
       return;
     }
 
   /* Find the function prologue.  If we can't find the function in
      the symbol table, peek in the stack frame to find the PC.  */
-  if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end))
+  if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+				&prologue_end))
     {
       /* One way to find the end of the prologue (which works well
          for unoptimized code) is to do the following:
@@ -750,8 +760,6 @@ arm_scan_prologue (struct frame_info *ne
   stack = make_pv_area (ARM_SP_REGNUM);
   back_to = make_cleanup_free_pv_area (stack);
 
-  regs[ARM_PC_REGNUM] = pv_unknown ();
-
   for (current_pc = prologue_start;
        current_pc < prologue_end;
        current_pc += 4)
@@ -989,10 +997,31 @@ arm_prologue_prev_register (struct frame
   cache = *this_cache;
 
   /* If we are asked to unwind the PC, then we need to return the LR
-     instead.  The saved value of PC points into this frame's
-     prologue, not the next frame's resume location.  */
+     instead.  The prologue may save PC, but it will point into this
+     frame's prologue, not the next frame's resume location.  Also
+     strip the saved T bit.  A valid LR may have the low bit set, but
+     a valid PC never does.  */
   if (prev_regnum == ARM_PC_REGNUM)
-    prev_regnum = ARM_LR_REGNUM;
+    {
+      if (valuep)
+	{
+	  CORE_ADDR pc;
+
+	  trad_frame_get_prev_register (next_frame, cache->saved_regs,
+					ARM_LR_REGNUM, optimized, lvalp, addrp,
+					realnump, valuep);
+	  pc = extract_unsigned_integer (valuep, INT_REGISTER_SIZE);
+	  pc = arm_addr_bits_remove (pc);
+	  store_unsigned_integer (valuep, INT_REGISTER_SIZE, pc);
+	}
+
+      /* The PC is never an lvalue.  */
+      *optimized = 0;
+      *lvalp = not_lval;
+      *addrp = 0;
+      *realnump = -1;
+      return;
+    }
 
   /* SP is generally not saved to the stack, but this frame is
      identified by NEXT_FRAME's stack pointer at the time of the call.
@@ -1005,6 +1034,39 @@ arm_prologue_prev_register (struct frame
       return;
     }
 
+  /* The CPSR may have been changed by the call instruction and by the
+     called function.  The only bit we can reconstruct is the T bit,
+     by checking the low bit of LR as of the call.  This is a reliable
+     indicator of Thumb-ness except for some ARM v4T pre-interworking
+     Thumb code, which could get away with a clear low bit as long as
+     the called function did not use bx.  Guess that all other
+     bits are unchanged; the condition flags are presumably lost,
+     but the processor status is likely valid.  */
+  if (prev_regnum == ARM_PS_REGNUM)
+    {
+      if (valuep)
+	{
+	  CORE_ADDR lr, cpsr;
+
+	  cpsr = frame_unwind_register_unsigned (next_frame, prev_regnum);
+
+	  trad_frame_get_prev_register (next_frame, cache->saved_regs,
+					ARM_LR_REGNUM, optimized, lvalp, addrp,
+					realnump, valuep);
+	  lr = extract_unsigned_integer (valuep, INT_REGISTER_SIZE);
+	  if (IS_THUMB_ADDR (lr))
+	    cpsr |= CPSR_T;
+	  else
+	    cpsr &= ~CPSR_T;
+	  store_unsigned_integer (valuep, INT_REGISTER_SIZE, cpsr);
+	}
+
+      *optimized = 0;
+      *lvalp = not_lval;
+      *addrp = 0;
+      *realnump = -1;
+    }
+
   trad_frame_get_prev_register (next_frame, cache->saved_regs, prev_regnum,
 				optimized, lvalp, addrp, realnump, valuep);
 }
@@ -1123,6 +1185,93 @@ arm_unwind_sp (struct gdbarch *gdbarch, 
   return frame_unwind_register_unsigned (this_frame, ARM_SP_REGNUM);
 }
 
+static void
+arm_dwarf2_prev_register (struct frame_info *next_frame, void **this_cache,
+			  int regnum, int *optimized, enum lval_type *lvalp,
+			  CORE_ADDR *addrp, int *realnump,
+			  gdb_byte *valuep)
+{
+  switch (regnum)
+    {
+    case ARM_PC_REGNUM:
+      /* The PC is normally copied from the return column, which
+	 describes saves of LR.  However, that version may have an
+	 extra bit set to indicate Thumb state.  The bit is not
+	 part of the PC.  */
+      if (valuep)
+	{
+	  CORE_ADDR pc;
+
+	  dwarf2_frame_prev_register (next_frame, this_cache,
+				      ARM_LR_REGNUM, optimized, lvalp,
+				      addrp, realnump, valuep);
+	  pc = extract_unsigned_integer (valuep, INT_REGISTER_SIZE);
+	  pc = arm_addr_bits_remove (pc);
+	  store_unsigned_integer (valuep, INT_REGISTER_SIZE, pc);
+	}
+
+      *optimized = 0;
+      *lvalp = not_lval;
+      *addrp = 0;
+      *realnump = -1;
+      break;
+
+    case ARM_PS_REGNUM:
+      /* The CPSR may have been changed by the call instruction and by
+	 the called function.  The only bit we can reconstruct is the
+	 T bit, by checking the low bit of LR as of the call.  This is
+	 a reliable indicator of Thumb-ness except for some ARM v4T
+	 pre-interworking Thumb code, which could get away with a
+	 clear low bit as long as the called function did not use bx.
+	 Guess that all other bits are unchanged; the condition flags
+	 are presumably lost, but the processor status is likely
+	 valid.  */
+      if (valuep)
+	{
+	  CORE_ADDR lr, cpsr;
+
+	  cpsr = frame_unwind_register_unsigned (next_frame, ARM_PS_REGNUM);
+	  dwarf2_frame_prev_register (next_frame, this_cache,
+				      ARM_LR_REGNUM, optimized, lvalp,
+				      addrp, realnump, valuep);
+	  lr = extract_unsigned_integer (valuep, INT_REGISTER_SIZE);
+	  if (IS_THUMB_ADDR (lr))
+	    cpsr |= CPSR_T;
+	  else
+	    cpsr &= ~CPSR_T;
+	  store_unsigned_integer (valuep, INT_REGISTER_SIZE, cpsr);
+	}
+
+      *optimized = 0;
+      *lvalp = not_lval;
+      *addrp = 0;
+      *realnump = -1;
+      break;
+
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Unexpected register %d"), regnum);
+    }
+}
+
+static void
+arm_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
+			   struct dwarf2_frame_state_reg *reg,
+			   struct frame_info *next_frame)
+{
+  switch (regnum)
+    {
+    case ARM_PC_REGNUM:
+    case ARM_PS_REGNUM:
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = arm_dwarf2_prev_register;
+      break;
+    case ARM_SP_REGNUM:
+      reg->how = DWARF2_FRAME_REG_CFA;
+      break;
+    }
+}
+
 /* When arguments must be pushed onto the stack, they go on in reverse
    order.  The code below implements a FILO (stack) to do this.  */
 
@@ -1701,7 +1850,7 @@ arm_get_next_pc (struct frame_info *fram
   unsigned long status;
   CORE_ADDR nextpc;
 
-  if (arm_pc_is_thumb (pc))
+  if (arm_frame_is_thumb (frame, 0))
     return thumb_get_next_pc (frame, pc);
 
   pc_val = (unsigned long) pc;
@@ -2669,10 +2818,10 @@ arm_write_pc (struct regcache *regcache,
       ULONGEST val;
       regcache_cooked_read_unsigned (regcache, ARM_PS_REGNUM, &val);
       if (arm_pc_is_thumb (pc))
-	regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | 0x20);
+	regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | CPSR_T);
       else
 	regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
-					val & ~(ULONGEST) 0x20);
+					val & ~(ULONGEST) CPSR_T);
     }
 }
 
@@ -3036,6 +3185,8 @@ arm_gdbarch_init (struct gdbarch_info in
   /* Hook in the ABI-specific overrides, if they have been registered.  */
   gdbarch_init_osabi (info, gdbarch);
 
+  dwarf2_frame_set_init_reg (gdbarch, arm_dwarf2_frame_init_reg);
+
   /* Add some default predicates.  */
   frame_unwind_append_sniffer (gdbarch, arm_stub_unwind_sniffer);
   frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
Index: arm-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/arm-tdep.h,v
retrieving revision 1.28
diff -u -p -r1.28 arm-tdep.h
--- arm-tdep.h	10 Oct 2007 14:04:53 -0000	1.28
+++ arm-tdep.h	11 Oct 2007 14:16:49 -0000
@@ -38,6 +38,7 @@ enum gdb_regnum {
   ARM_F7_REGNUM = 23, 		/* last floating point register */
   ARM_FPS_REGNUM = 24,		/* floating point status register */
   ARM_PS_REGNUM = 25,		/* Contains processor status */
+  ARM_CPSR_REGNUM = ARM_PS_REGNUM,
   ARM_WR0_REGNUM,		/* WMMX data registers.  */
   ARM_WR15_REGNUM = ARM_WR0_REGNUM + 15,
   ARM_WC0_REGNUM,		/* WMMX control registers.  */
@@ -107,6 +108,8 @@ enum gdb_regnum {
 #define FLAG_C		0x20000000
 #define FLAG_V		0x10000000
 
+#define CPSR_T		0x20
+
 /* Type of floating-point code in use by inferior.  There are really 3 models
    that are traditionally supported (plus the endianness issue), but gcc can
    only generate 2 of those.  The third is APCS_FLOAT, where arguments to
Index: dwarf2-frame.c
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2-frame.c,v
retrieving revision 1.77
diff -u -p -r1.77 dwarf2-frame.c
--- dwarf2-frame.c	8 Oct 2007 12:46:09 -0000	1.77
+++ dwarf2-frame.c	11 Oct 2007 14:16:49 -0000
@@ -1022,7 +1022,7 @@ dwarf2_signal_frame_this_id (struct fram
 			       frame_func_unwind (next_frame, SIGTRAMP_FRAME));
 }
 
-static void
+void
 dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache,
 			    int regnum, int *optimizedp,
 			    enum lval_type *lvalp, CORE_ADDR *addrp,
@@ -1168,6 +1168,11 @@ dwarf2_frame_prev_register (struct frame
         }
       break;
 
+    case DWARF2_FRAME_REG_FN:
+      cache->reg[regnum].loc.fn (next_frame, this_cache, regnum, optimizedp,
+				 lvalp, addrp, realnump, valuep);
+      break;
+
     default:
       internal_error (__FILE__, __LINE__, _("Unknown register rule."));
     }
Index: dwarf2-frame.h
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2-frame.h,v
retrieving revision 1.16
diff -u -p -r1.16 dwarf2-frame.h
--- dwarf2-frame.h	23 Aug 2007 18:08:28 -0000	1.16
+++ dwarf2-frame.h	11 Oct 2007 14:16:49 -0000
@@ -55,6 +55,7 @@ enum dwarf2_frame_reg_rule
 
   /* These aren't defined by the DWARF2 CFI specification, but are
      used internally by GDB.  */
+  DWARF2_FRAME_REG_FN,		/* Call a registered function.  */
   DWARF2_FRAME_REG_RA,		/* Return Address.  */
   DWARF2_FRAME_REG_RA_OFFSET,	/* Return Address with offset.  */
   DWARF2_FRAME_REG_CFA,		/* Call Frame Address.  */
@@ -71,6 +72,10 @@ struct dwarf2_frame_state_reg
     LONGEST offset;
     ULONGEST reg;
     unsigned char *exp;
+    void (*fn) (struct frame_info *next_frame, void **this_cache,
+		int regnum, int *optimizedp,
+		enum lval_type *lvalp, CORE_ADDR *addrp, int *realnump,
+		gdb_byte *valuep);
   } loc;
   ULONGEST exp_len;
   enum dwarf2_frame_reg_rule how;
@@ -116,4 +121,10 @@ extern const struct frame_base *
 
 void dwarf2_frame_build_info (struct objfile *objfile);
 
+void dwarf2_frame_prev_register (struct frame_info *next_frame, void
+				 **this_cache,
+				 int regnum, int *optimizedp,
+				 enum lval_type *lvalp, CORE_ADDR *addrp,
+				 int *realnump, gdb_byte *valuep);
+
 #endif /* dwarf2-frame.h */
Index: testsuite/gdb.arch/thumb-prologue.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.arch/thumb-prologue.exp,v
retrieving revision 1.5
diff -u -p -r1.5 thumb-prologue.exp
--- testsuite/gdb.arch/thumb-prologue.exp	23 Aug 2007 18:14:16 -0000	1.5
+++ testsuite/gdb.arch/thumb-prologue.exp	11 Oct 2007 14:16:50 -0000
@@ -57,5 +57,5 @@ gdb_test "backtrace 10" \
 	"backtrace in TPCS"
 
 gdb_test "info frame" \
-	".*Saved registers:.*r7 at.*r10 at.*r11 at.*lr at.*pc at .*" \
+	".*Saved registers:.*r7 at.*r10 at.*r11 at.*lr at.*" \
 	"saved registers in TPCS"


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]