https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122146

            Bug ID: 122146
           Summary: Compiler orders loop in assembly incorrectly, if the
                    conditional value is from linker script
           Product: gcc
           Version: 12.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: flowank at gmx dot de
  Target Milestone: ---

Created attachment 62492
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=62492&action=edit
*.i file that contains the full code and some further compilation info

Hi,

during writing an embedded startup code in C I found the following bug /
unexpected behavior.

GCC orders loop in assembly incorrectly, if
the conditional value is from linker script. The reordering leads to
incorrect loop behavior in case of condition is 0, leading to number
overflow / hard fault on hardware target. An implemented head controlled loop
(for loop) gets compiled to assembler foot controlled loop. In example code a
bss init function is implemented and the bss section is actually 0. Running the
incorrect ordered code will lead to hard fault on embedded device, because
unavailable memory regions get accessed.

(1) Used GCC : arm-none-eabi-gcc (15:12.2.rel1-1) 12.2.1 20221205
(2) Using the following OS: Debian GNU/Linux 12 (bookworm), Release: 12
(3) Compiler Flags : COMPILER_FLAGS := -march=armv6-m -mcpu=cortex-m0 -
mthumb -mlittle-endian -O1 -fmessage-length=0 -fsigned-char -ffunction-
sections -fdata-sections -g3 -std=gnu11 -fanalyzer -Wall -Wextra
(for more detailed information see the attached make file)

(4) Find the related makefile, source, linker, and startup.i file
attached

(5) Detailed information with code excerpt:

**********************************************************************
Working code, using calculation the bss size in source code (no bss, so
difference is 0):

 Startup_initMemorySection((u8 *)&_sbss, 0u, (u32)&_ebss-(u32)&_sbss) :

 8000108:       4a09            ldr     r2, [pc, #36]   @ (8000130
 800010a:       4b0a            ldr     r3, [pc, #40]   @ (8000134

    for(u32 l_idx_u32 = 0; l_idx_u32 < f_bytesToSet_u32; l_idx_u32++)
 800010c:       429a            cmp     r2, r3
 800010e:       d004            beq.n   800011a
        f_pDestStart_u8[l_idx_u32] = f_valueToSet_u8;
 8000110:       2100            movs    r1, #0
 8000112:       7019            strb    r1, [r3, #0]
/
    for(u32 l_idx_u32 = 0; l_idx_u32 < f_bytesToSet_u32; l_idx_u32++)
 8000114:       3301            adds    r3, #1
 8000116:       429a            cmp     r2, r3
 8000118:       d1fb            bne.n   

**********************************************************************
None Working Code, using calculation of bss size in linker script
(_bss_size = 0):
Startup_initMemorySection((u8 *)&_sbss, 0u, (u32)&_bss_size):

    for(u32 l_idx_u32 = 0; l_idx_u32 < f_bytesToSet_u32; l_idx_u32++)
 8000108:       4b08            ldr     r3, [pc, #32]   @ (800012c
 800010a:       4a09            ldr     r2, [pc, #36]   @ (8000130
 800010c:       189a            adds    r2, r3, r2
        f_pDestStart_u8[l_idx_u32] = f_valueToSet_u8;
 800010e:       2100            movs    r1, #0
 8000110:       7019            strb    r1, [r3, #0]
    for(u32 l_idx_u32 = 0; l_idx_u32 < f_bytesToSet_u32; l_idx_u32++)
 8000112:       3301            adds    r3, #1
 8000114:       429a            cmp     r2, r3
 8000116:       d1fb            bne.n   8000110

As can be seen in the none working version, the limit check is
reordered and compared after increment (what leads to increment until
overflow in case of 0, but have hard fault on hardware due to access of
non existing memory).

**********************************************************************
The following linker script is used (I think it is helpful to reproduce the
bug, since the compiler bug happens using linker symbols) :

/* linkerfile stm32f091rc.ld */

ENTRY(Application_ResetHandler)

MEMORY
{
   FLASH(rx)       : ORIGIN = 0x08000000, LENGTH = 256K
   SYSMEM(rw)      : ORIGIN = 0x1FFFD800, LENGTH = 8K
   OPTIONBYTES(rw) : ORIGIN = 0x1FFFF800, LENGTH = 2K
   SRAM(rwx)       : ORIGIN = 0x20000000, LENGTH = 32K
}

SECTIONS
{
   .text : 
   {   
      _start_of_flash = .;  
      _stext = .;
      _isr_start = .;

      KEEP(*(.isr_vector))
      . = ALIGN(4);   
      *(.text)
      *(.text*)    
      *(.rodata)    
      . = ALIGN(4);  

      _etext = .;
   }> FLASH


   .data :
   {

      _sdata = .;

      *(.data)
      *(.data*)
      *(.ramtext)
      . = ALIGN(4);
      *(.code_ram)
      . = ALIGN(4);

      _edata = .;
   } > SRAM AT>FLASH


   _data_size = _edata - _sdata;
   _data_loadaddr = LOADADDR(.data);

   .bss :
   {
      _sbss = .;

      *(.bss)
      *(.bss*)
      *(COMMON)
      . = ALIGN(4);

      _ebss = .;
   } > SRAM AT>FLASH   

   _bss_size = _ebss - _sbss;


   .stack :
   {
     . = ALIGN(8);
     _estack = .;
     *(.stack)     
   } > SRAM AT>FLASH

   _sstack = 0x20008000;
   _stack_size = _sstack - _estack;

}

Regards

Florian

Reply via email to