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