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]

[PATCH] Fix prologue skipping on ppc32 for variadic functions


GDB currently has a problem with setting breakpoints on or stepping into variadic functions on 32-bit PowerPC architectures. e.g.

void f(int x, ...)
{
}

int main(void)
{
  f (0, 1);
  f (0, 1.0);
}

compiles (with gcc -g -O0) to:

10000478 <f>:
void f(int x, ...)
{
10000478:       94 21 ff 80     stwu    r1,-128(r1)
1000047c:       93 e1 00 7c     stw     r31,124(r1)
10000480:       7c 3f 0b 78     mr      r31,r1
10000484:       90 9f 00 0c     stw     r4,12(r31)
10000488:       90 bf 00 10     stw     r5,16(r31)
1000048c:       90 df 00 14     stw     r6,20(r31)
10000490:       90 ff 00 18     stw     r7,24(r31)
10000494:       91 1f 00 1c     stw     r8,28(r31)
10000498:       91 3f 00 20     stw     r9,32(r31)
1000049c:       91 5f 00 24     stw     r10,36(r31)
100004a0:       40 86 00 24     bne     cr1,100004c4 <f+0x4c>
100004a4:       d8 3f 00 28     stfd    f1,40(r31)
100004a8:       d8 5f 00 30     stfd    f2,48(r31)
100004ac:       d8 7f 00 38     stfd    f3,56(r31)
100004b0:       d8 9f 00 40     stfd    f4,64(r31)
100004b4:       d8 bf 00 48     stfd    f5,72(r31)
100004b8:       d8 df 00 50     stfd    f6,80(r31)
100004bc:       d8 ff 00 58     stfd    f7,88(r31)
100004c0:       d9 1f 00 60     stfd    f8,96(r31)
100004c4:       90 7f 00 68     stw     r3,104(r31)
}
...
100004d8 <main>:

int main(void)
{
...
  f (0, 1);
100004ec:       38 60 00 00     li      r3,0
100004f0:       38 80 00 01     li      r4,1
100004f4:       4c c6 31 82     crclr   4*cr1+eq
100004f8:       4b ff ff 81     bl      10000478 <f>
  f (0, 1.0);
100004fc:       3d 20 10 00     lis     r9,4096
10000500:       c8 09 07 08     lfd     f0,1800(r9)
10000504:       38 60 00 00     li      r3,0
10000508:       fc 20 00 90     fmr     f1,f0
1000050c:       4c c6 32 42     crset   4*cr1+eq
10000510:       4b ff ff 69     bl      10000478 <f>
}
...

When this is loaded into GDB and a 'break f' issued, a breakpoint is placed at 0x100004a4. Consider the first call site at 0x10000f48 - at this point, cr1 will be cleared, so when execution is resumed the branch at 0x100004a0 will be taken, and the breakpoint will be skipped over. GDB has lost control over program execution at this point, and unless another breakpoint is set elsewhere or f is called without the branch taken (in this case, the second call), the program will run to completion.

This branch in the prologue seems to be generated by GCC as an optimisation to avoid saving the floating-point registers unless necessary (in the GCC source, this is done by setup_incoming_varargs() in gcc/config/rs6000/rs6000.c). This is controlled by the caller, which sets cr1 as required before branching to the function.

When debug information is available, the breakpoint is placed at 0x100004a4 due to rs6000_skip_prologue() using skip_prologue_using_sal(). The branch at 0x100004a0 marks the end of a basic block, and so line info has been placed there. skip_prologue_using_sal() sees two line entries for the same line at the beginning of the function, and so picks the address associated with the second, which is just after the branch in the prologue.

This patch fixes this by checking instructions between the beginning of the function and the address returned by skip_prologue_using_sal() for the 'bne cr1, <target>' instruction. If it finds it, then after confirming that the instructions between the instruction and the target are all floating-point save instructions, the first line info item with an address greater-or-equal to the target address will be used as the start of the function body.

When debug information is not available, the breakpoint is placed at 0x100004a0 due to skip_prologue() stopping at a branch instruction. While control over the program is not lost in this case, the prologue still has not ended at this point. This patch adds the 'bne cr1, <target>' instruction to the list of recognised prologue instructions.

Kwok

gdb/ChangeLog:

	* rs6000-tdep.c (skip_prologue): Add test for instruction used
	to skip saving of the floating-point registers.
	(GET_DEST_REG): New macro.
	(BNE_CR1_MASK, BNE_CR1_INSTRUCTION, BNE_TARGET_MASK): New defines.
	(SFP_MASK, SFP_R31_INSTRUCTION): New defines.
	(skip_prologue_fp_reg_save): New.
	(rs6000_skip_prologue): Use result of skip_prologue_fp_reg_save
	when using SAL information to skip prologue.
	
Index: gdb/rs6000-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/rs6000-tdep.c,v
retrieving revision 1.363
diff -u -p -u -p -r1.363 rs6000-tdep.c
--- gdb/rs6000-tdep.c	8 Apr 2013 19:59:09 -0000	1.363
+++ gdb/rs6000-tdep.c	15 May 2013 14:07:02 -0000
@@ -1756,6 +1756,10 @@ skip_prologue (struct gdbarch *gdbarch, 
 	  fdata->used_bl = 1;
 	  continue;
 	}
+      /* The prologue of variadic functions may contain a branch instruction
+	 to skip the saving of floating-point registers.  */
+      else if ((op & 0xffff0003) == 0x40860000)   /* bne cr1,SIMM */
+	continue;
       /* update stack pointer */
       else if ((op & 0xfc1f0000) == 0x94010000)
 	{		/* stu rX,NUM(r1) ||  stwu rX,NUM(r1) */
@@ -2093,6 +2097,96 @@ skip_prologue (struct gdbarch *gdbarch, 
   return last_prologue_pc;
 }
 
+
+#define GET_DEST_REG(x) (((x) >> 16) & 0x1f)
+
+#define BNE_CR1_MASK            0xffff0003
+#define BNE_CR1_INSTRUCTION     0x40860000
+#define BNE_TARGET_MASK         0x0000fffc
+#define STF_MASK                0xf4000000
+#define STF_INSTRUCTION         0xd0000000
+
+/* This function inspects the instructions between the start of a
+   function and PC, looking for code indicative of a floating-point
+   save sequence used by variadic functions.  If found, an address
+   that occurs after the save sequence will be returned, otherwise
+   PC is returned.  */
+
+static CORE_ADDR
+skip_prologue_fp_reg_save (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  gdb_byte buf[PPC_INSN_SIZE];
+  unsigned long opcode;
+  CORE_ADDR branch_addr, addr;
+  CORE_ADDR target_addr = 0;
+  CORE_ADDR start_pc, end_pc;
+  struct symtab_and_line prologue_sal;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  find_pc_partial_function (pc, NULL, &start_pc, &end_pc);
+
+  /* Search for a bne cr1,SIMM instruction.  */
+  for (branch_addr = start_pc;
+       branch_addr < pc;
+       branch_addr += PPC_INSN_SIZE)
+    {
+      if (target_read_memory (branch_addr, buf, PPC_INSN_SIZE))
+        return pc;
+      opcode = extract_unsigned_integer (buf, PPC_INSN_SIZE, byte_order);
+
+      if ((opcode & BNE_CR1_MASK) == BNE_CR1_INSTRUCTION)
+        {
+          target_addr = branch_addr + (opcode & BNE_TARGET_MASK);
+          break;
+        }
+    }
+
+  if (target_addr <= pc)
+    return pc;
+
+  /* All instructions between BRANCH_ADDR + PPC_INSN_SIZE and
+     TARGET_ADDR - PPC_INSN_SIZE should be floating-point
+     store instructions that write to an address relative to
+     R1 or R31.  */
+  for (addr = branch_addr + PPC_INSN_SIZE;
+       addr < target_addr;
+       addr += PPC_INSN_SIZE)
+    {
+      unsigned int insn;
+
+      if (target_read_memory (addr, buf, PPC_INSN_SIZE))
+        return pc;
+      insn = extract_unsigned_integer (buf, PPC_INSN_SIZE, byte_order);
+      if ((insn & STF_MASK) != STF_INSTRUCTION
+          || (GET_DEST_REG (insn) != 1 && GET_DEST_REG (insn) != 31))
+        return pc;
+    }
+
+  prologue_sal = find_pc_line (target_addr, 0);
+
+  /* Look for the first line that starts on or after TARGET_ADDR.  */
+  if (prologue_sal.symtab->language != language_asm)
+    {
+      struct linetable *linetable = LINETABLE (prologue_sal.symtab);
+      int idx = 0;
+
+      /* Skip any earlier lines, and any end-of-sequence marker
+         from a previous function.  */
+      while (linetable->item[idx].pc != prologue_sal.pc
+             || linetable->item[idx].line == 0)
+        idx++;
+
+      for (; idx < linetable->nitems; idx++)
+        {
+          if (linetable->item[idx].line != 0
+              && linetable->item[idx].pc >= target_addr)
+            return linetable->item[idx].pc;
+        }
+    }
+
+  return target_addr;
+}
+
 static CORE_ADDR
 rs6000_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
 {
@@ -2107,7 +2201,11 @@ rs6000_skip_prologue (struct gdbarch *gd
       CORE_ADDR post_prologue_pc
 	= skip_prologue_using_sal (gdbarch, func_addr);
       if (post_prologue_pc != 0)
-	return max (pc, post_prologue_pc);
+        {
+          post_prologue_pc = skip_prologue_fp_reg_save (gdbarch,
+                                                        post_prologue_pc);
+          return max (pc, post_prologue_pc);
+        }
     }
 
   /* Can't determine prologue from the symbol table, need to examine

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