From 77b384c53f2229b05513fb4b297d4c93da04f01a Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Thu, 17 Nov 2016 20:12:13 +0000 Subject: [PATCH] Fix PR77933: stack corruption on ARM when using high registers and LR 2016-11-17 Thomas Preud'homme 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 --- gcc/ChangeLog | 8 ++++ gcc/config/arm/arm.c | 29 +++++++++------ gcc/testsuite/ChangeLog | 6 +++ gcc/testsuite/gcc.target/arm/pr77933-1.c | 46 +++++++++++++++++++++++ gcc/testsuite/gcc.target/arm/pr77933-2.c | 47 ++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/gcc.target/arm/pr77933-1.c create mode 100644 gcc/testsuite/gcc.target/arm/pr77933-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 86c664bf7a9..1cf436d1612 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +2016-11-17 Thomas Preud'homme + + 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 * config/i386/i386.md (cmpstrnsi): New test to bail out if neither diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 239117ff7da..d7ce87c27ee 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -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; } } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index cce390bb744..55f7d837aad 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2016-11-17 Thomas Preud'homme + + PR target/77933 + * gcc.target/arm/pr77933-1.c: New test. + * gcc.target/arm/pr77933-2.c: Likewise. + 2016-11-17 Jakub Jelinek PR middle-end/78201 diff --git a/gcc/testsuite/gcc.target/arm/pr77933-1.c b/gcc/testsuite/gcc.target/arm/pr77933-1.c new file mode 100644 index 00000000000..95cf68ea753 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr77933-1.c @@ -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; +} diff --git a/gcc/testsuite/gcc.target/arm/pr77933-2.c b/gcc/testsuite/gcc.target/arm/pr77933-2.c new file mode 100644 index 00000000000..9028c4fcab4 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr77933-2.c @@ -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; +} -- 2.30.2