Fix PR77933: stack corruption on ARM when using high registers and LR

2016-11-17  Thomas Preud'homme  <thomas.preudhomme@arm.com>

    gcc/
    PR target/77933
    * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr
    being live in the function and lr needing to be saved.  Distinguish
    between already saved pushable registers and registers to push.
    Check for LR being an available pushable register.

    gcc/testsuite/
    PR target/77933
    * gcc.target/arm/pr77933-1.c: New test.
    * gcc.target/arm/pr77933-2.c: Likewise.

From-SVN: r242559
This commit is contained in:
Thomas Preud'homme 2016-11-17 20:12:13 +00:00 committed by Thomas Preud'homme
parent d9df71be90
commit 77b384c53f
5 changed files with 124 additions and 12 deletions

View file

@ -1,3 +1,11 @@
2016-11-17 Thomas Preud'homme <thomas.preudhomme@arm.com>
PR target/77933
* config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr
being live in the function and lr needing to be saved. Distinguish
between already saved pushable registers and registers to push.
Check for LR being an available pushable register.
2016-11-17 Aaron Sawdey <acsawdey@linux.vnet.ibm.com>
* config/i386/i386.md (cmpstrnsi): New test to bail out if neither

View file

@ -23593,6 +23593,7 @@ thumb1_expand_prologue (void)
unsigned long live_regs_mask;
unsigned long l_mask;
unsigned high_regs_pushed = 0;
bool lr_needs_saving;
func_type = arm_current_func_type ();
@ -23615,6 +23616,7 @@ thumb1_expand_prologue (void)
offsets = arm_get_frame_offsets ();
live_regs_mask = offsets->saved_regs_mask;
lr_needs_saving = live_regs_mask & (1 << LR_REGNUM);
/* Extract a mask of the ones we can give to the Thumb's push instruction. */
l_mask = live_regs_mask & 0x40ff;
@ -23681,6 +23683,7 @@ thumb1_expand_prologue (void)
{
insn = thumb1_emit_multi_reg_push (l_mask, l_mask);
RTX_FRAME_RELATED_P (insn) = 1;
lr_needs_saving = false;
offset = bit_count (l_mask) * UNITS_PER_WORD;
}
@ -23745,12 +23748,13 @@ thumb1_expand_prologue (void)
be a push of LR and we can combine it with the push of the first high
register. */
else if ((l_mask & 0xff) != 0
|| (high_regs_pushed == 0 && l_mask))
|| (high_regs_pushed == 0 && lr_needs_saving))
{
unsigned long mask = l_mask;
mask |= (1 << thumb1_extra_regs_pushed (offsets, true)) - 1;
insn = thumb1_emit_multi_reg_push (mask, mask);
RTX_FRAME_RELATED_P (insn) = 1;
lr_needs_saving = false;
}
if (high_regs_pushed)
@ -23768,7 +23772,9 @@ thumb1_expand_prologue (void)
/* Here we need to mask out registers used for passing arguments
even if they can be pushed. This is to avoid using them to stash the high
registers. Such kind of stash may clobber the use of arguments. */
pushable_regs = l_mask & (~arg_regs_mask) & 0xff;
pushable_regs = l_mask & (~arg_regs_mask);
if (lr_needs_saving)
pushable_regs &= ~(1 << LR_REGNUM);
if (pushable_regs == 0)
pushable_regs = 1 << thumb_find_work_register (live_regs_mask);
@ -23776,8 +23782,9 @@ thumb1_expand_prologue (void)
while (high_regs_pushed > 0)
{
unsigned long real_regs_mask = 0;
unsigned long push_mask = 0;
for (regno = LAST_LO_REGNUM; regno >= 0; regno --)
for (regno = LR_REGNUM; regno >= 0; regno --)
{
if (pushable_regs & (1 << regno))
{
@ -23786,6 +23793,7 @@ thumb1_expand_prologue (void)
high_regs_pushed --;
real_regs_mask |= (1 << next_hi_reg);
push_mask |= (1 << regno);
if (high_regs_pushed)
{
@ -23795,23 +23803,20 @@ thumb1_expand_prologue (void)
break;
}
else
{
pushable_regs &= ~((1 << regno) - 1);
break;
}
break;
}
}
/* If we had to find a work register and we have not yet
saved the LR then add it to the list of regs to push. */
if (l_mask == (1 << LR_REGNUM))
if (lr_needs_saving)
{
pushable_regs |= l_mask;
real_regs_mask |= l_mask;
l_mask = 0;
push_mask |= 1 << LR_REGNUM;
real_regs_mask |= 1 << LR_REGNUM;
lr_needs_saving = false;
}
insn = thumb1_emit_multi_reg_push (pushable_regs, real_regs_mask);
insn = thumb1_emit_multi_reg_push (push_mask, real_regs_mask);
RTX_FRAME_RELATED_P (insn) = 1;
}
}

View file

@ -1,3 +1,9 @@
2016-11-17 Thomas Preud'homme <thomas.preudhomme@arm.com>
PR target/77933
* gcc.target/arm/pr77933-1.c: New test.
* gcc.target/arm/pr77933-2.c: Likewise.
2016-11-17 Jakub Jelinek <jakub@redhat.com>
PR middle-end/78201

View file

@ -0,0 +1,46 @@
/* { dg-do run } */
/* { dg-options "-O2" } */
__attribute__ ((noinline, noclone)) void
clobber_lr_and_highregs (void)
{
__asm__ volatile ("" : : : "r8", "r9", "lr");
}
int
main (void)
{
int ret;
__asm volatile ("mov\tr4, #0xf4\n\t"
"mov\tr5, #0xf5\n\t"
"mov\tr6, #0xf6\n\t"
"mov\tr7, #0xf7\n\t"
"mov\tr0, #0xf8\n\t"
"mov\tr8, r0\n\t"
"mov\tr0, #0xfa\n\t"
"mov\tr10, r0"
: : : "r0", "r4", "r5", "r6", "r7", "r8", "r10");
clobber_lr_and_highregs ();
__asm volatile ("cmp\tr4, #0xf4\n\t"
"bne\tfail\n\t"
"cmp\tr5, #0xf5\n\t"
"bne\tfail\n\t"
"cmp\tr6, #0xf6\n\t"
"bne\tfail\n\t"
"cmp\tr7, #0xf7\n\t"
"bne\tfail\n\t"
"mov\tr0, r8\n\t"
"cmp\tr0, #0xf8\n\t"
"bne\tfail\n\t"
"mov\tr0, r10\n\t"
"cmp\tr0, #0xfa\n\t"
"bne\tfail\n\t"
"mov\t%0, #1\n"
"fail:\n\t"
"sub\tr0, #1"
: "=r" (ret) : :);
return ret;
}

View file

@ -0,0 +1,47 @@
/* { dg-do run } */
/* { dg-skip-if "" { ! { arm_thumb1_ok || arm_thumb2_ok } } } */
/* { dg-options "-mthumb -O2 -mtpcs-leaf-frame" } */
__attribute__ ((noinline, noclone)) void
clobber_lr_and_highregs (void)
{
__asm__ volatile ("" : : : "r8", "r9", "lr");
}
int
main (void)
{
int ret;
__asm volatile ("mov\tr4, #0xf4\n\t"
"mov\tr5, #0xf5\n\t"
"mov\tr6, #0xf6\n\t"
"mov\tr7, #0xf7\n\t"
"mov\tr0, #0xf8\n\t"
"mov\tr8, r0\n\t"
"mov\tr0, #0xfa\n\t"
"mov\tr10, r0"
: : : "r0", "r4", "r5", "r6", "r7", "r8", "r10");
clobber_lr_and_highregs ();
__asm volatile ("cmp\tr4, #0xf4\n\t"
"bne\tfail\n\t"
"cmp\tr5, #0xf5\n\t"
"bne\tfail\n\t"
"cmp\tr6, #0xf6\n\t"
"bne\tfail\n\t"
"cmp\tr7, #0xf7\n\t"
"bne\tfail\n\t"
"mov\tr0, r8\n\t"
"cmp\tr0, #0xf8\n\t"
"bne\tfail\n\t"
"mov\tr0, r10\n\t"
"cmp\tr0, #0xfa\n\t"
"bne\tfail\n\t"
"mov\t%0, #1\n"
"fail:\n\t"
"sub\tr0, #1"
: "=r" (ret) : :);
return ret;
}