GCC 7.2.1 with LTO reports "Error: offset out of range"

Bug #1763050 reported by aoandr
16
This bug affects 3 people
Affects Status Importance Assigned to Milestone
GNU Arm Embedded Toolchain
Won't Fix
Undecided
Unassigned

Bug Description

Version: GNU Tools for Arm Embedded Processors 7-2017-q4-major
Original build, downloaded from developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
Running on Ubuntu 16.04.4 LTS (xenial), x86_64

The test case is attached to the bug report.
To run the test case:
- $ tar xaf "the_test_archive.tar.xz"
- $ cd test/
- $ ./build.sh

The output contains the error message like:

/tmp/ccXrBd4D.s:27: Error: offset out of range

The archive contains the full output as captured on my host, see test/out.txt
Also, the ccXrBd4D.s file is stored in the same directory for reference.

This problem does not show up when running GCC 6.3.1 (6-2017-q2-update).
Without -flto flag the original project compiles and runs fine (by GCC 7 or 6).
Adding the "-ffunction-sections" parameter to the linking stage seems to eliminate the problem.

The offending line 27 of the assembler code is:
ldr.w r0, =0xE000ED88

The line comes from the FreeRTOS "naked" function vPortEnableVFP(), which contains an __asm() statement.

In the original project I also encountered the same error triggered at another line:
ldr r0, =0xE000ED08
this asm instruction is from the __asm() statement in function prvPortStartFirstTask().

My original project triggered error messages at both lines, but one of the errors was gone during source reduction.

The provided test code is radically reduced with creduce after pre-processing. So, the type definitions in different files are not aligned. Minor changes to the source code make the error disappear. When I tried to synchronize the type definitions in different files, the problem went away.

Revision history for this message
aoandr (t-launchpad-nbs-eng-ru) wrote :
summary: - GCC 7.2.1 with LTO reports
+ GCC 7.2.1 with LTO reports "Error: offset out of range"
Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

The problem is not LTO specific in my opinion. I think the LTO factor here is that the function with this LDR gets too large and the assembler generates a pc-relative ldr to a literal pool entry which is too far from the ldr. It should be reproducible by setting a big enough value for the function being assembled.

Changed in gcc-arm-embedded:
status: New → Confirmed
description: updated
Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

Hi,

the LDR r0, =cst construct lets the assembler create a literal pool. However since GCC can create its own using in an inline asm can lead to some conflicts between the two and the construct is really aimed at full assembly file instead. Beside there is not much the assembler can do to alleviate the offset issue.

Instead I would suggest to use something like:

asm volatile ("ldr r1, [%0]" :: "r" (0xE000ED88) : "r1", "memory");

Or if you don't care about which register the load is made into:

int output;
sm volatile ("ldr %1, [%0]" : "=r" (output) : "r" (0xE000ED88) : "memory");

Best regards.

Changed in gcc-arm-embedded:
status: Confirmed → Won't Fix
Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

Or even better as a colleague pointed out: (void)*(volatile int*)0xE000ED88; which avoids the inline asm altogether.

Revision history for this message
aoandr (t-launchpad-nbs-eng-ru) wrote :

Thank you for your efforts!

This code comes from the FreeRTOS project.
The actual functions look like:

static void prvPortStartFirstTask( void )
{
__asm volatile(
  " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */
  " ldr r0, [r0] \n"
  " ldr r0, [r0] \n"
  " msr msp, r0 \n" /* Set the msp back to the start of the stack. */
  " cpsie i \n" /* Globally enable interrupts. */
  " cpsie f \n"
  " dsb \n"
  " isb \n"
  " svc 0 \n" /* System call to start first task. */
  " nop \n");
}

and

static void vPortEnableVFP( void )
{
__asm volatile (
  " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */
  " ldr r1, [r0] \n"
  " \n"
  " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */
  " str r1, [r0] \n"
  " bx r14 ");
}

So, it looks like __asm() is essential here.
Will the proposed solution "ldr r1, [%0]" :: "r" (0xE000ED88) : "r1", "memory" work in this context? (Sorry, I have very little understanding of ARM assembly language.)

Thanks in advance!

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

How about:

volatile int *cpasr_ptr = 0xE000ED88;

static void prvPortStartFirstTask( void )
{
  intptr_t stack_addr = **cpasr_ptr;
  asm ("msr msp, %0\n"
       "cpsie i\n"
       "cpsie f\n"
       "dsb\n"
       "isb\n"
       "svc 0\n" /* System call to start first task. */
       "nop\n" : "r"(stack_addr));
}

static void vPortEnableVFP( void )
{
  *cpasr_ptr = *cpasr_ptr | (0xf << 20);
}

Revision history for this message
Philip Karlsson Gisslow (philip-karlsson-gisslow) wrote :

Hi, I have also encountered this issue. As the problem seems to be related to the ldr generating a too long jump to the literal pool a simple solution is to add a .ltorg at the end of each of the two inlined functions to ensure that the pool gets placed there.

static void prvPortStartFirstTask( void )
{
__asm volatile(
  " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */
  " ldr r0, [r0] \n"
  " ldr r0, [r0] \n"
  " msr msp, r0 \n" /* Set the msp back to the start of the stack. */
  " cpsie i \n" /* Globally enable interrupts. */
  " cpsie f \n"
  " dsb \n"
  " isb \n"
  " svc 0 \n" /* System call to start first task. */
  " nop \n"
  " .ltorg \n");
}

static void vPortEnableVFP( void )
{
__asm volatile (
  " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */
  " ldr r1, [r0] \n"
  " \n"
  " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */
  " str r1, [r0] \n"
  " bx r14 \n"
  " .ltorg ");
}

Looking at the generated machine code, the ldr offset is now in the range of 20 bytes.

BR,
Philip

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Related questions

Bug attachments

Remote bug watches

Bug watches keep track of this bug in other bug trackers.