-Os optimisation causes hard fault

Bug #1955320 reported by Andrew Coad
10
This bug affects 1 person
Affects Status Importance Assigned to Milestone
GNU Arm Embedded Toolchain
New
Undecided
Unassigned

Bug Description

Toolchain: GNU Arm Embedded Toolchain 10-2020-q4-major (not rebuilt)
Host: Mac BigSur
IDE: Eclipse 4.21.0
Target: Cortex-M33 (Nordic nrf5340)

With optimisation level -Os (optimise for size), the following startup code for nrf5340 from Nordic fails:

void Reset_Handler(void)
{
  unsigned long *pSrc, *pDst;

  //SEGGER_RTT_WriteString(0, "Boo\r\n");

  /* Copy data segment initializers from flash to SRAM. */
  pSrc = &__text_end__;
  pDst = &__data_start__;
  while (pDst < &__data_end__)
  {
    *pDst++ = *pSrc++;
  }

The first two assignments (pSrc = and pDst = ) are optimised away but the while loop is not. Hence a hard fault occurs when the while loop is executed (occurs immediately after reset so no code is actually executed)

Regards,
skajam66

Revision history for this message
Liviu Ionescu (ilg) wrote :

Unfortunately such pointer operations are considered to have undefined behavior and the compiler is free to take whatever actions it thinks more appropriate.

I saw other such cases during startup when the compiler used float instructions before having the chance for the FPU to be initialised, also resulting in crashes.

Revision history for this message
Andrew Coad (skajam66) wrote :

Hi Liviu,

Thank you for your comments. I have two related questions:
1. If the compiler can remove the pointer assignments, surely it must also remove code that references those assignments?
2. You say that such pointer operations are undefined. Can you offer any guidance on how that code snippet should be written to avoid any undefined behaviour?

Kind regards,
skajam66

Revision history for this message
Liviu Ionescu (ilg) wrote :

I was a fervent suporter of writing the startup in C/C++, but the reality is that at this point the only absolute safe code is in assembly. The second choice would be to use memset/memcpy, if you can call them without making pointer operations.

All other opearations involving pointers that are not pointing to the same structure or the same array should be considered with caution.

Revision history for this message
David Brown (davidbrown) wrote :

The reason this is undefined behaviour is that the three symbols __text_end__, __data_start__ and __data_end__ will be declared as extern objects. Even if they are declared as arrays, if you start with a pointer that points to one variable, and increment it, you can never end up with a pointer that points at another variable. This can lead to unexpected optimisation behaviour in some cases.

However, I don't see how the compiler could end up removing the assignments at the start of the function. I would want to see the declarations of the external symbols here, and know the command line flags used.

A very useful tool for this is the online compiler at <https://godbolt.org>. You can select "ARM gcc 10.2.1 (none)" as your compiler here and add the flags such as "-mcpu=cortex-m33 -O2 -Wall -Wextra" and see the assembly results. You can also use "share" to get a link, and put that in a comment here, so that others can see exactly the effect you are facing.

In general, I disagree with Liviu here - C and C++ are perfectly good languages for writing startup code and there is no need to resort to assembly. But you do need to understand the limitations for using C and C++ before main(), and you need to be careful with linker symbols that might not be quite right for C.

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

Other bug subscribers

Remote bug watches

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