Bug 88167 - [ARM] Function __builtin_return_address returns invalid address
Summary: [ARM] Function __builtin_return_address returns invalid address
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 7.3.1
: P3 normal
Target Milestone: 10.0
Assignee: Mihail Ionescu
Keywords: wrong-code
Depends on:
Reported: 2018-11-23 13:16 UTC by Mihail Ionescu
Modified: 2019-05-08 14:40 UTC (History)
1 user (show)

See Also:
Target: arm
Known to work:
Known to fail:
Last reconfirmed: 2018-11-23 00:00:00


Note You need to log in before you can comment on or make changes to this bug.
Description Mihail Ionescu 2018-11-23 13:16:32 UTC
Used compiler options are: -mcpu=cortex-m0 -mthumb -O2.

Consider the following code:

void *retaddr;

void xxxxtest(void) {
  retaddr = __builtin_return_address(0);

  /* Used for enforcing registers stacking.*/
  asm volatile("" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
                        "r8", "r9", "r10", "r11", "r12");

It produces the following asm:

 8001940: b5f0 push {r4, r5, r6, r7, lr}
 8001942: 46de mov lr, fp <=========== HERE LR is used as a scratchpad
 8001944: 4657 mov r7, sl
 8001946: 464e mov r6, r9
 8001948: 4645 mov r5, r8
 800194a: 4672 mov r2, lr <=========== HERE LR is accessed for return address
 800194c: 4b04 ldr r3, [pc, #16] ; (8001960 <xxxxtest+0x20>)
 800194e: b5e0 push {r5, r6, r7, lr}
 8001950: 601a str r2, [r3, #0]
 8001952: bc3c pop {r2, r3, r4, r5}
 8001954: 4690 mov r8, r2
 8001956: 4699 mov r9, r3
 8001958: 46a2 mov sl, r4
 800195a: 46ab mov fp, r5
 800195c: bdf0 pop {r4, r5, r6, r7, pc}

The problem is in the function entry code, where callee-saved registers are stacked. LR is used as scratchpad @8001942 before the return address is taken @800194a.

This problem broke the ChibiOS port for Cortex-M0 using the latest compilers, the builtin is used for enforcing context switch after nested ISRs execution, a very critical bit of code, there is no easy way to workaround this.
Comment 1 Richard Earnshaw 2019-05-08 14:36:46 UTC
Author: rearnsha
Date: Wed May  8 14:36:15 2019
New Revision: 271012

URL: https://gcc.gnu.org/viewcvs?rev=271012&root=gcc&view=rev
[arm][PR88167] Fix __builtin_return_address returns invalid address

This patch fixes a problem with the thumb1 prologue code where the link
register could be unconditionally used as a scratch register even if the
return value was still live at the end of the prologue.

Additionally, the patch improves the code generated when we are not
using many low call-saved registers to make use of any unused call
clobbered registers to help with the saving of high registers that
cannot be pushed directly (quite rare in normal code as the register
allocator correctly prefers low registers).

2019-05-08  Mihail Ionescu  <mihail.ionescu@arm.com>
	    Richard Earnshaw  <rearnsha@arm.com>


	PR target/88167
	* config/arm/arm.c (thumb1_prologue_unused_call_clobbered_lo_regs): New
	(thumb1_epilogue_unused_call_clobbered_lo_regs): New function.
	(thumb1_compute_save_core_reg_mask): Don't force a spare work
	register if both the epilogue and prologue can use call-clobbered
	(thumb1_unexpanded_epilogue): Use
	thumb1_epilogue_unused_call_clobbered_lo_regs.  Reverse the logic for
	picking temporaries for restoring high regs to match that of the
	prologue where possible.
	(thumb1_expand_prologue): Add any usable call-clobbered low registers to
	the list of work registers.  Detect if the return address is still live
	at the end of the prologue and avoid using it for a work register if so.
	If the return address is not live, add LR to the list of pushable regs
	after the first pass.


	PR target/88167
	* gcc.target/arm/pr88167-1.c: New test.
	* gcc.target/arm/pr88167-2.c: New test.

Comment 2 Richard Earnshaw 2019-05-08 14:40:21 UTC
Fixed on trunk.