This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH] Fix prologue skipping on ppc32 for variadic functions
- From: Kwok Cheung Yeung <kcy at codesourcery dot com>
- To: <gdb-patches at sourceware dot org>
- Date: Wed, 15 May 2013 15:18:00 +0100
- Subject: [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