This is the mail archive of the gdb-cvs@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]

[binutils-gdb] ppc: Detect when LR is saved through frame pointer


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=dd6d677f0b06341dce90d259785c9d513d2e3935

commit dd6d677f0b06341dce90d259785c9d513d2e3935
Author: Pedro Franco de Carvalho <pedromfc@linux.vnet.ibm.com>
Date:   Thu Mar 22 10:22:45 2018 -0300

    ppc: Detect when LR is saved through frame pointer
    
    This patch extends rs6000-tdep.c:skip_prologue so that it can detect
    when the Link Register is saved using the frame pointer (usually r31)
    in adition to the stack pointer (r1).
    
    The frame pointer offset from the frame base is tracked separately
    from the stack pointer offset for cases when the frame pointer is not
    in synch with the stack pointer at the moment of the LR save.
    
    Previously, "stq" could also be detected as an instruction that saves
    LR or CR. Because this was likely unintentional, this patch also
    restricts the matches to stw/stwu/std/stdu.
    
    gdb/ChangeLog:
    
    	* rs6000-tdep.c (store_insn_p): New function.
    	(skip_prologue): New variable alloca_reg_offset. Set lr_reg
    	and cr_reg to their unshifted values. Use store_insn_p to
    	match LR saves using either R1 or fdata->alloca_reg. Use
    	store_insn_p to match CR saves. Set alloca_reg_offset
    	when alloca_reg and framep are set. Remove lr_reg shift
    	when assigning to fdata->lr_register.

Diff:
---
 gdb/ChangeLog     | 10 ++++++++
 gdb/rs6000-tdep.c | 71 +++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 63 insertions(+), 18 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f169909..cf996bc 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,13 @@
+2018-03-22  Pedro Franco de Carvalho  <pedromfc@linux.vnet.ibm.com>
+
+	* rs6000-tdep.c (store_insn_p): New function.
+	(skip_prologue): New variable alloca_reg_offset. Set lr_reg
+	and cr_reg to their unshifted values. Use store_insn_p to
+	match LR saves using either R1 or fdata->alloca_reg. Use
+	store_insn_p to match CR saves. Set alloca_reg_offset
+	when alloca_reg and framep are set. Remove lr_reg shift
+	when assigning to fdata->lr_register.
+
 2018-03-22  Andreas Arnez  <arnez@linux.vnet.ibm.com>
 
 	* linux-tdep.c (linux_info_proc): For "info proc cmdline", print
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index a5b7091..d61fe58 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -1357,6 +1357,39 @@ bl_to_blrl_insn_p (CORE_ADDR pc, int insn, enum bfd_endian byte_order)
   return 0;
 }
 
+/* Return true if OP is a stw or std instruction with
+   register operands RS and RA and any immediate offset.
+
+   If WITH_UPDATE is true, also return true if OP is
+   a stwu or stdu instruction with the same operands.
+
+   Return false otherwise.
+   */
+static bool
+store_insn_p (unsigned long op, unsigned long rs,
+	      unsigned long ra, bool with_update)
+{
+  rs = rs << 21;
+  ra = ra << 16;
+
+  if (/* std RS, SIMM(RA) */
+      ((op & 0xffff0003) == (rs | ra | 0xf8000000)) ||
+      /* stw RS, SIMM(RA) */
+      ((op & 0xffff0000) == (rs | ra | 0x90000000)))
+    return true;
+
+  if (with_update)
+    {
+      if (/* stdu RS, SIMM(RA) */
+	  ((op & 0xffff0003) == (rs | ra | 0xf8000001)) ||
+	  /* stwu RS, SIMM(RA) */
+	  ((op & 0xffff0000) == (rs | ra | 0x94000000)))
+	return true;
+    }
+
+  return false;
+}
+
 /* Masks for decoding a branch-and-link (bl) instruction.
 
    BL_MASK and BL_INSTRUCTION are used in combination with each other.
@@ -1583,6 +1616,7 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
   gdb_byte buf[4];
   unsigned long op;
   long offset = 0;
+  long alloca_reg_offset = 0;
   long vr_saved_offset = 0;
   int lr_reg = -1;
   int cr_reg = -1;
@@ -1654,14 +1688,14 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 	     remember just the first one, but skip over additional
 	     ones.  */
 	  if (lr_reg == -1)
-	    lr_reg = (op & 0x03e00000);
+	    lr_reg = (op & 0x03e00000) >> 21;
           if (lr_reg == 0)
             r0_contains_arg = 0;
 	  continue;
 	}
       else if ((op & 0xfc1fffff) == 0x7c000026)
 	{			/* mfcr Rx */
-	  cr_reg = (op & 0x03e00000);
+	  cr_reg = (op & 0x03e00000) >> 21;
           if (cr_reg == 0)
             r0_contains_arg = 0;
 	  continue;
@@ -1738,14 +1772,17 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 
 	}
       else if (lr_reg >= 0 &&
-	       /* std Rx, NUM(r1) || stdu Rx, NUM(r1) */
-	       (((op & 0xffff0000) == (lr_reg | 0xf8010000)) ||
-		/* stw Rx, NUM(r1) */
-		((op & 0xffff0000) == (lr_reg | 0x90010000)) ||
-		/* stwu Rx, NUM(r1) */
-		((op & 0xffff0000) == (lr_reg | 0x94010000))))
-	{	/* where Rx == lr */
-	  fdata->lr_offset = offset;
+	       ((store_insn_p (op, lr_reg, 1, true)) ||
+		(framep &&
+		 (store_insn_p (op, lr_reg,
+				fdata->alloca_reg - tdep->ppc_gp0_regnum,
+				false)))))
+	{
+	  if (store_insn_p (op, lr_reg, 1, true))
+	    fdata->lr_offset = offset;
+	  else /* LR save through frame pointer. */
+	    fdata->lr_offset = alloca_reg_offset;
+
 	  fdata->nosavedpc = 0;
 	  /* Invalidate lr_reg, but don't set it to -1.
 	     That would mean that it had never been set.  */
@@ -1760,13 +1797,8 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 
 	}
       else if (cr_reg >= 0 &&
-	       /* std Rx, NUM(r1) || stdu Rx, NUM(r1) */
-	       (((op & 0xffff0000) == (cr_reg | 0xf8010000)) ||
-		/* stw Rx, NUM(r1) */
-		((op & 0xffff0000) == (cr_reg | 0x90010000)) ||
-		/* stwu Rx, NUM(r1) */
-		((op & 0xffff0000) == (cr_reg | 0x94010000))))
-	{	/* where Rx == cr */
+	       (store_insn_p (op, cr_reg, 1, true)))
+	{
 	  fdata->cr_offset = offset;
 	  /* Invalidate cr_reg, but don't set it to -1.
 	     That would mean that it had never been set.  */
@@ -1920,6 +1952,7 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 	  fdata->frameless = 0;
 	  framep = 1;
 	  fdata->alloca_reg = (tdep->ppc_gp0_regnum + 29);
+	  alloca_reg_offset = offset;
 	  continue;
 
 	  /* Another way to set up the frame pointer.  */
@@ -1930,6 +1963,7 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 	  fdata->frameless = 0;
 	  framep = 1;
 	  fdata->alloca_reg = (tdep->ppc_gp0_regnum + 31);
+	  alloca_reg_offset = offset;
 	  continue;
 
 	  /* Another way to set up the frame pointer.  */
@@ -1940,6 +1974,7 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 	  framep = 1;
 	  fdata->alloca_reg = (tdep->ppc_gp0_regnum
 			       + ((op & ~0x38010000) >> 21));
+	  alloca_reg_offset = offset;
 	  continue;
 	}
       /* AltiVec related instructions.  */
@@ -2180,7 +2215,7 @@ skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR lim_pc,
 #endif /* 0 */
 
   if (pc == lim_pc && lr_reg >= 0)
-    fdata->lr_register = lr_reg >> 21;
+    fdata->lr_register = lr_reg;
 
   fdata->offset = -fdata->offset;
   return last_prologue_pc;


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