Dear Community,
I am developing firmware for STM32l476VGT using Gnu Make Builder and "Ac6 STM32 MCU GCC" Toolchain. I encounter a memory related run time error during the startup (before main) of my controller. The error occurs during the execution of the LoopFillZeroBss section:
LoopFillZerobss: ldr r3, = _ebss cmp r2, r3 bcc FillZerobss /* Call the clock system intitialization function.*/ bl SystemInit /* Call static constructors */ bl __libc_init_array /* Call the application's entry point.*/ bl main LoopForever: b LoopForever .size Reset_Handler, .-Reset_Handler /** * @brief This is the code that gets called when the processor receives an * unexpected interrupt. This simply enters an infinite loop, preserving * the system state for examination by a debugger. * * @param None * @retval : None */ .section .text.Default_Handler,"ax",%progbits Default_Handler: Infinite_Loop: b Infinite_Loop .size Default_Handler, .-Default_Handler
After the execution of bl __libc_init_array I end up in the Infinite_Loop of the Default_Handler. It is worth noting, that this error can be influenced by the amount memory which is statically allocated. In my source code file there is an array
static uint8_t array[SIZE] = {0};
If SIZE is smaller than threshold1, I can enter main() without any problems. If SIZE is between threshold1 and threshold2 (with threshold1 < threshold2) then the behaviour is as described above. If SIZE exceeds threshold2, a linker error (section '.bss' will not fit in region 'RAM') is generated (as expected).
I have the following questions:
1. What exactly happens, when 'bl __libc_init_array' is executed?
2. How could the fail of this command possibly related to the memory usage?
Any thoughts or helpful sources on this topic would be greatly appreciated as well.
Kind regards
Oliver
Hey Oliver,
without seeing the actual contents of your compiled elf I can just guess that your
static uint8_t array[SIZE] = {0}
overwrites parts of the __init_array section when the array is too big.__libc_init_array iterates through that section and calls the function pointers placed there to alloc memory for static C++ classes or functions marked with __attribute__((constructor)) if you're just using plain C. If you don't recall using that attribute: such functions might be part of linked libs.
While this usually shouldn't happen, since __init_array should be placed in Flash, it might be that your linker script isn't set up right.
But from your description of the problem without more info I'd say that's what's going on...Regards,Sebastian
Dear Sebastian,
That is an interesting idea. However, I've checked the *.elf file and the linker script and it appears that __init_array is placed in the Flash Section.
You can find the content of my linker file at the very bottom, if it is of any use for you.
Is there a possibility to debug deeper into bl __libc_init_array?
Kind regards,
/* Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = 0x20018000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ _Min_Heap_Size = 0x150; /* required amount of heap */ _Min_Stack_Size = 0x500; /* required amount of stack */ /* Specify the memory areas */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K SRAM2 (xrw) : ORIGIN = 0x10000000, LENGTH = 32K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1022K FLASH2 (xrw) : ORIGIN = 0x080FF800, LENGTH = 2K } /* Define output sections */ SECTIONS { /* The startup code goes first into FLASH */ .isr_vector : { . = ALIGN(8); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(8); } >FLASH /* The program code and other data goes into FLASH */ .text : { . = ALIGN(8); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(8); _etext = .; /* define a global symbols at end of code */ } >FLASH /* Constant data goes into FLASH */ .rodata : { . = ALIGN(8); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(8); } >FLASH .ARM.extab : { . = ALIGN(8); *(.ARM.extab* .gnu.linkonce.armextab.*) . = ALIGN(8); } >FLASH .ARM : { . = ALIGN(8); __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; . = ALIGN(8); } >FLASH .preinit_array : { . = ALIGN(8); PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); . = ALIGN(8); } >FLASH .init_array : { . = ALIGN(8); PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); . = ALIGN(8); } >FLASH .fini_array : { . = ALIGN(8); PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); . = ALIGN(8); } >FLASH /* program data to be stored in FLASH */ .flash_storage : { . = ALIGN(8); KEEP(*(.flash_storage)) /* flash storage code */ . = ALIGN(8); } >FLASH2 /* used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections goes into RAM, load LMA copy after code */ .data : { . = ALIGN(8); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ . = ALIGN(8); _edata = .; /* define a global symbol at data end */ } >RAM AT> FLASH _sisram2 = LOADADDR(.sram2); /* SRAM2 section * * IMPORTANT NOTE! * If initialized variables will be placed in this section, * the startup code needs to be modified to copy the init-values. */ .sram2 : { . = ALIGN(8); _ssram2 = .; /* create a global symbol at sram2 start */ *(.sram2) *(.sram2*) . = ALIGN(8); _esram2 = .; /* create a global symbol at sram2 end */ } >SRAM2 AT> FLASH /* Uninitialized data section */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM /* Remove information from the standard libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } }
Hey Oliver,well that's the source code of __libc_init_array()
void __libc_init_array (void) { size_t count; size_t i; count = __preinit_array_end - __preinit_array_start; for (i = 0; i < count; i++) __preinit_array_start[i] (); _init (); count = __init_array_end - __init_array_start; for (i = 0; i < count; i++) __init_array_start[i] (); }
Since your __preinit_array is 0 bytes and _init is also empty with nosys.specs the only thing that gets executed is the __init_array part.From the "Details" section in your screenshot I take, that the first entry in your 8byte __init_array section is0x800001b5 and the second one is 0x0.Since libc_init_array calls both entries as function pointers, calling 0x0 will be a definite Bus/MemFault on the Cortex-M.
I also noticed you have an alignment of 8 bytes for the init_array sections. In my linker scripts I always use 4 since there is no need there to align to double word boundaries. So maybe change that to 4 in your script too.
And I've seen weird behaviours like yours too, when using inline assembler and the code was compiled with no optimization ("O0"), so I always have the
-falign-functions=4
in my default C-Flags.
Give it a try, I'm looking forward to hearing about your results!/Sebastian
One more little thing: I switched to the official ARM GCC toolchains a while ago because sometimes the ones maintained by the Chipmakers aren't up to date and so, when you have some extra time (just for the future) I'd suggest you migrate to the 7-2018-q2update...
Hey Sebastian,
thanks for your advice. I found out, that the error is difficult to reproduce. It didn't occur anymore until today, when I switched to another hardware sample. Now, with the new hardware I obtain a similar error as described above. The only difference is that I am now unable to get to the main function, no matter how severely I reduce my memory usage.
I cannot access the source code of __libc_init_array() since we are currently using this toolchain
https://www.st.com/en/development-tools/sw4stm32.html
and I can only access the libc.a file. Some progress on my side:
1. The flag
didn't fix the problem.
2. Setting the compiler optimization level from -O3 down to -O2 seems to be a workaround for my problem.
I still have some questions:
1. Do you have an explanation or an educated guess why this workaround works?
2. Do you think changing to the official ARM GCC toolchain might fix this issue, since there might be a unresolved bug in the libs that we are currently using?
Best,
sorry for the late reply, somehow I didn't see your message until now...
First of all: the STM Toolchain uses gcc, too so your __libc_init_array source is the one I posted earlier.If you step through the disassembly you'll see. Now, regarding the align-functions flag - did you also change the alignment from 8 to 4 here?
.init_array : { . = ALIGN(8);
Also, when your code works with -O2 it could be an option to try and narrow things down by adding the single flags from O3 one by one and see which one breaks it. You can find the gcc flags for each level here: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
I'd also try and step through the startup code in disassembly view (`lay asm`) with single instruction steps (`si`) and see where it breaks/hangs...
Last not least, when it comes to toolchains, the only real option to be on the safe side (and I hate to be saying this) is to buy a compiler that comes with some "warranty" and support (like IAR or Keil). As long as you're working with gcc you'll find yourself in situations like this every now and then again.But on the positive side, You learn a lot this way - believe me, been there done that ;)Sorry I can't be of more help right now - but it's really difficult to remote-diagnose such things.
Sebastian
Greetings,
I encountered this issue this week and found this topic as similar.
I bet that your __init_array has a proper size (2 words as function pointers), but second is 0x0 because __init_array_start is somehow shifted !! Im currently tackling this problem and I found that my first element in __init_array_start is just 1 word above from where it should be. Seems like a compiler issue because __init_array_start and __init_array_end are both 1 word shifted, which results in a correct size calculation within Reset_Handler() but leading to a call of the last element which is indeed empty.
In my case, i had to change my startup code to iterate from __init_array_start[-1] to __init_array_start[count - 2] but unfortunately, this shift is related to a code payload (i.e number of chars in debug messages, because they are padding final image size).
Any news after this thread has started?
Ernest