Search code examples
c++armstartupcortex-mlinker-scripts

Array partially empty


I have written a startup and liker script for my C++ application, running on STM32F407VG.

The problem is i have an array of structure, where the structure field str is always zero despite the initialization. The other field in the struct are correctly initialized. I can-t understand what I'm doing wrong, I guess some part of initialization in the startup script is missing.

The array is declared like the following:

struct elem{
    uint32_t str;
    uint32_t value;
    uint32_t value2;
};

const struct elem array[]{
    {(uint32_t)(*(uint32_t*)"CM1"), 1, 1},
    {(uint32_t)(*(uint32_t*)"CM2"), 2, 2},
    {(uint32_t)(*(uint32_t*)"CM3"), 3, 3}
};

relevant section of startup script:

inline void static_init()
{
  for (void (**p)() = __preinit_array_start; p < __preinit_array_end; ++p)
    (*p)();

  for (void (**p)() = __init_array_start; p < __init_array_end; ++p)
    (*p)();
}

void reset_handler(void)
{

  unsigned long *source;
  unsigned long *destination;

  // Copying data from Flash to RAM
  source = &_data_flash;
  for (destination = &_data_begin; destination < &_data_end;)
  {
      *(destination++) = *(source++);
  }

  // default zero to undefined variables
  for (destination = &_bss_begin; destination < &_bss_end;)
  {
      *(destination++) = 0;
  }

  static_init();

#ifndef __NO_SYSTEM_INIT
  SystemInit();
#endif

  // starting main program
  main();
}

and the linker script:

/* Entry Point */
ENTRY(reset_handler)

_estack = 0x20010000; /* end of 128K RAM */
/* Specify the memory areas */
/*
0x08000000 until 0x08010000 is reserved for BOOTLOADER! (64k)
*/
MEMORY
{
  EEPROM (rwx)    : ORIGIN = 0x08010000, LENGTH = 64K /*fake EEPROM!*/
  FLASH (rx)      : ORIGIN = 0x08020000, LENGTH = 896K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
  RAM2 (rw)      : ORIGIN = 0x10000000, LENGTH = 64K
}

SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    __intvec_start__ = .;
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    _text = .;
      *(.text)           /* .text sections (code) */
    _text2 = .;
      *(.text*)          /* .text* sections (code) */
    _rodata = .;
      *(.rodata)         /* .rodata sections (constants, strings, etc.) */
      *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
      *(.glue_7)         /* glue arm to thumb code */
      *(.glue_7t)        /* glue thumb to arm code */
      *(.eh_frame)
    _init_data = .;
    KEEP (*(.init))
    KEEP (*(.fini))
    . = ALIGN(4);
      _etext = .;        /* define a global symbols at end of code */
  } > FLASH

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH

  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* used by the startup to initialize data */
  _data_flash = _sidata;

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data :
  {
    . = ALIGN(4);
    _data_begin = .;
    *(.data)
    *(.data*)

    . = ALIGN(4);
    _data_end = .;
  } >RAM AT> FLASH


  .bss (NOLOAD) :
  {
      . = ALIGN(4);
      _bss_begin = .;
      __bss_start__ = _bss_begin;
      *(.bss)
      *(.bss*)
      *(COMMON)
      . = ALIGN(4);
      _bss_end = .;
      __bss_end__ = _bss_end;
  } > RAM

  stack_size = 1024;
  __stack_end__ = ORIGIN(RAM)+LENGTH(RAM);
  __stack_start__ = __stack_end__ - stack_size;

  heap_size = 0;
  __heap_end__ = __stack_start__;
  __heap_start__ = __heap_end__ - heap_size;

  . = __stack_start__;
  ._stack :
  {
      PROVIDE ( end = . );
      . = . + stack_size;
      . = . + heap_size;
      . = ALIGN(4);
  } > RAM

  _siccmram = LOADADDR(.ram2);
  .ram2 (NOLOAD) :
  {
    . = ALIGN(4);
    *(.ram2);
          *(.ram2*);
    . = ALIGN(4);
  } > RAM2 AT> FLASH

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
      libc.a ( * )
      libm.a ( * )
      libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

Solution

  • You might use multicharacter literal: see (6.) of character_literal.

    Notice single quotes:

    const struct elem array[]{
        {'CM1', 1, 1},
        {'CM2', 2, 2},
        {'CM3', 3, 3}
    };
    

    You can see how gcc evaluate multicharacter literal:

    https://gcc.gnu.org/onlinedocs/cpp/Implementation-defined-behavior.html#Implementation-defined-behavior

    The compiler evaluates a multi-character character constant a character at a time, shifting the previous value left by the number of bits per target character, and then or-ing in the bit-pattern of the new character truncated to the width of a target character. The final bit-pattern is given type int, and is therefore signed, regardless of whether single characters are signed or not. If there are more characters in the constant than would fit in the target int the compiler issues a warning, and the excess leading characters are ignored.