1.6. Startup Code

Submitted by admin on Sat, 05/08/2021 - 19:20

1. Introduction

This tutorial addresses something called the startup code (a.k.a. bootstrap). The startup code is the code that is executed by the MCU before main() function is called. This code is implemented in the very first function being executed which is the reset handler function. Moreover, it should be strongly associated with the linker script for some reasons we will discuss.

For now, just open the blink2 project as you've left it after the previous tutorial (Running the Debugger).

2. Meaning of object file ‘Size’

Perform a fresh rebuild. For that, you'll need to clean first, and then build the project again from scratch. You can use the contextual menu for that:

The console reports build information and the output of MCU ARM GCC size function.

19:34:44 **** Build of configuration Debug for project blink2 ****
make all 
arm-none-eabi-gcc "../main.c" -mcpu=cortex-m0 -std=gnu11 -g3 -c -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -MMD -MP -MF"main.d" -MT"main.o" --specs=nano.specs -mfloat-abi=soft -mthumb -o "main.o"
arm-none-eabi-gcc -o "blink2.elf" @"objects.list"   -mcpu=cortex-m0 -T"C:\STM\workspace_tuto\blink2\linkerscript.ld" --specs=nosys.specs -Wl,-Map="blink2.map" -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -Wl,--end-group
Finished building target: blink2.elf
arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    256	      0	      0	    256	    100	blink2.elf
Finished building: default.size.stdout
arm-none-eabi-objdump -h -S  blink2.elf  > "blink2.list"
Finished building: blink2.list
arm-none-eabi-objcopy  -O binary  blink2.elf  "blink2.bin"
Finished building: blink2.bin

19:34:44 Build Finished. 0 errors, 0 warnings. (took 620ms)

What’s the meaning of these reported sizes?

Let start with general definitions:

  • text → Refers to the application code size (bytes). It is stored in the FLASH memory. Here we have 256 bytes.
  • bss → Refers to ‘uninitialized variables’. Those variables have reserved locations in RAM memory
  • data → Refers to ‘initialized variables’. Those variables have reserved location in RAM memory and their initial value is stored in the FLASH memory. In short, an initialized variable occupies 2x its own size (one in RAM for the variable, one in FLASH for its initial value).

Take a look at the Build Analyzer window, Memory Regions tab:

We got the same numbers, i.e. 256 bytes for the code in FLASH, and nothing in RAM. You can display more details on what is where using the Memory Details tab:

First observation:

/* Main program */
int main(void)
    int i = 0, j = 0;
    unsigned char state = 0;


Our main() function declares 3 variables:

  • 2 32-bit integers: i, j
  • 1 8-bit integer: state

These variables are reported nowhere in the build report, neither in the bss nor in the data section. Why is that?

This is because these variable are local to the main() function. RAM space, for variables that are defined inside functions, is dynamically allocated at runtime, into a segment of available RAM called the stack. The physical RAM is temporarily associated to local variables and then cleaned up (freed) when the function exits, making it available for other variables of other functions. These variables, and associated RAM requirements are therefore NOT reported into the size of the object file. This is an important issue with projects requiring large amount of RAM (e.g. to store big arrays) with no clue of that in the build report when declared locally.

The object file only includes variables with statically allocated memory. That means:

  • Global variables
  • Static variables within functions

Let us experiment this!


3. Experiment on size and variables initialization

Let us make j an unititialized global variable:

int j;

/* Main program */
int main(void)
    int i = 0;
    unsigned char state = 0;


Save all  and build  the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    260	      0	      4	    264	    108	blink2.elf
Finished building: default.size.stdout

Note that code size text inflates by 4 bytes (i.e. 32-bit) and we have an additional 4 bytes of bss uninitialized variable (in RAM) allocated for j global variable. Take a look in the Memory Details. Now j is reported in the bss section.

Start the debugger  and note the initial values of both i and j variables:

As you may expect from uninitialized variables, the value of j is something random. Less expected, i is also ramdom altrough it was initialized when declared. Ok then...

If you look at C99 standard, chapter 6.7.8 Initialization, you’ll find this:

“If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

If an object that has static storage duration is not initialized explicitly, then:

  • if it has pointer type, it is initialized to a null pointer;
  • if it has arithmetic type, it is initialized to (positive or unsigned) zero;
  • if it is an aggregate, every member is initialized (recursively) according to these rules;
  • if it is a union, the first named member is initialized (recursively) according to these rules"


This means that according to C99 standard, a global variable that is not explicitly initialized should be initialized to zero somewhere. It is obviously not the case here. Therefore, our code is not C99 compliant… That’s dangerous…

Let us try to explicitly initialize j now:

int j = 0;

If what’s before is true, the allocated 4 bytes should move from the bss to the data section, now that j is initialized.

Save main.c save_edit and build build_exec the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    260	      0	      4	    264	    108	blink2.elf
Finished building: default.size.stdout

Nothing has changed... Thats weird!

At least, let see if it is well initialized. Launch the debugger…


It's not either... More weird!

Let’s try this:

int j=5;

Save main.c save_edit and build build_exec the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    260	      4	      0	    264	    108	blink2.elf
Finished building: default.size.stdout

Good! 4 bytes have been shifted from bss to data section. So, if the initialization value is not 0, then the variable appears in the initialized data variable section, at last!

As it was mentioned before, now that j is initialized to a non-zero value, you'll notice that j occupies 4 bytes in RAM (for the variable itself) and also 4 bytes in FLASH (to store the initial value, which is 5 here).

Well, now let make sure it is indeed initialized to 5. Start the debugger…

Still not correctly initialized! It seems that nothing can be done here to get something correctly initialized...


Let’s now try to turn i into an uninitialized variable:

int j=5;

/* Main program */
int main(void)
    int i;
    unsigned char state = 0;

Save main.c save_edit and build build_exec the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    256	      4	      0	    260	    104	blink2.elf
Finished building: default.size.stdout

The code size has reduced by 4 bytes, otherwise, nothing has changed.


Then change i into a static int variable:

int j=5;

/* Main program */
int main(void)
    static int i;
    unsigned char state = 0;

Save main.c save_edit and build build_exec the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    276	      4	      4	    284	    11c	blink2.elf
Finished building: default.size.stdout

Note that bss section gained 4 bytes compared to the previous build. We have now 4 bytes for initialized data variables j and 4 bytes again for uninitialized bss variables i. That makes sense. Memory is statically allocated for static variables, and are therefore reported just like global variables. Note that text size has an additionnal 20 bytes also...


The ‘random’ value of i has changed. It is likely that the physical RAM location associated to the variable i has changed. That’s understandable since i is no more stored on the stack. Nothing new regarding j...

Another attempt:

int j=5;

/* Main program */
int main(void)
    static int i=10;
    unsigned char state = 0;

Save main.c save_edit and build build_exec the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    276	      8	      0	    284	    11c	blink2.elf
Finished building: default.size.stdout

Well, both i and count are now part of the data section as expected since we have explicit initialization.


But neither are effectively initialized…

One last attempt. Set i back as a regular initialized local variable:

int j=5;

/* Main program */
int main(void)
    int i=10;
    unsigned char state = 0;

Save main.c save_edit and build build_exec the project:

arm-none-eabi-size   blink2.elf 
   text	   data	    bss	    dec	    hex	filename
    260	      4	      0	    264	    108	blink2.elf
Finished building: default.size.stdout

As expected, i has disappeared from both data and bss sections, but the code is bigger by 4 bytes than the same one with no i initialization.


Neither i nor j are initialized, but looking closer into the very first assembly code of main() function we find this:

These are the two machine cycles that set i to 10 (producing four additional bytes in the text section).

In conclusion, it would have been the same to write:

        int i;
	i = 10;

There is no benefit (memory-wise) to initialize local variable at the declaration.

Try to inspect the assembly code at the beginning of main() in case i is declared as static. You'll see that the initialization code is no more there. That means that initialization for global or static variable is assumed to be done somewhere else.

For now, you can remember that initializing a local variable at declaration is strictly the same as setting the variable apart from the declaration. This is not the same for global and static variables since no code is produced in this case.

Static and global variables initialization must therefore rely on another process that is clearly not working in our project. Sad...


4. Debrief

Initialization of global or static variables is neither automatic nor magic. It has to be done by the CPU before main() is called. And a CPU does only what he is told to… In other words, you must include a startup code in your project that performs these initializations (and more)! This is usually something done within the reset handler function. In our example, the reset handler is nothing more than a call to main(). That’s the reason why we didn’t get any variable initialization.

/* Reset handler */
void reset_handler (void)

A well-written startup code must perform the following:

  • Fill the interrupt vector table. The address of the reset handler code being the first one.
  • Implement the reset handler function, which includes:
    • Initialize the stack pointer register. For this, it needs to know where the available RAM starts (after space for global and static variables has been allocated)
    • Copy the initial values of data variables from the FLASH, to the RAM. For this, it needs to know where variables are in RAM, and where initial values are in FLASH
    • Fill with zeros the bss segment in order to reset all the ‘uninitialized’ variables. For this, it needs to know where the bss is in RAM.
    • Call other initialization functions (clock for instance)
    • Call the main function

Because it is better to go fast during the initialization, the startup code might be written in assembler.

Note that explicitly initializing a global or static variable to 0 would use space in FLASH to store 0 as initial value. Because all bss variables are initialized to 0 by the startup code, its better doing nothing. That’s the reason why the linker considers that a zero-initialized variable is of bss type.

Historically (1950) bss meant “Block Started by Symbol”.

"Some people like to remember it as 'Better Save Space.' Since the BSS segment only holds variables that don't have any value yet, it doesn't actually need to store the image of these variables. The size that BSS will require at runtime is recorded in the object file, but BSS (unlike the data segment) doesn't take up any actual space in the object file." https://en.wikipedia.org/wiki/.bss

All the addresses, in both the RAM and the FLASH, required by the startup code to perform initializations must be known. That's the purpose of the linker script. Well, not OUR linker script, but a well-written one… In the following, we’re going to use linker scripts and startup code provided by ST libraries… At least, you understand now the reason why. And everything should work has expected…

Below are code example for both the startup code and the linker script. Just take a look. You will not understand everything, but you can locate the parts that address the issues discussed in this tutorial.

The one important thing you need to remember is that startup code and linker scripts works together, and should be in sync regarding address labels. That's a common cause of problems when you pick up startup code and linker scripts files from differents sources on the web. That doesn't work that way.


5. Annex A: startup_stm32f072xb.s

  * @file      startup_stm32f072xb.s
  * @author    MCD Application Team
  * @brief     STM32F072x8/STM32F072xB devices vector table for Atollic TrueSTUDIO toolchain.
  *            This module performs:
  *                - Set the initial SP
  *                - Set the initial PC == Reset_Handler,
  *                - Set the vector table entries with the exceptions ISR address
  *                - Branches to main in the C library (which eventually
  *                  calls main()).
  *            After Reset the Cortex-M0 processor is in Thread mode,
  *            priority is Privileged, and the Stack is set to Main.
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.

  .syntax unified
  .cpu cortex-m0
  .fpu softvfp

.global g_pfnVectors
.global Default_Handler

/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss

  .section .text.Reset_Handler
  .weak Reset_Handler
  .type Reset_Handler, %function

  ldr   r0, =_estack
  mov   sp, r0          /* set stack pointer */

/* Copy the data segment initializers from flash to SRAM */
  movs r1, #0
  b LoopCopyDataInit

  ldr r3, =_sidata
  ldr r3, [r3, r1]
  str r3, [r0, r1]
  adds r1, r1, #4

  ldr r0, =_sdata
  ldr r3, =_edata
  adds r2, r0, r1
  cmp r2, r3
  bcc CopyDataInit
  ldr r2, =_sbss
  b LoopFillZerobss

/* Zero fill the bss segment. */
  movs r3, #0
  str  r3, [r2]
  adds r2, r2, #4

  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

    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
  b Infinite_Loop
  .size Default_Handler, .-Default_Handler

* The minimal vector table for a Cortex M0.  Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
   .section .isr_vector,"a",%progbits
  .type g_pfnVectors, %object
  .size g_pfnVectors, .-g_pfnVectors

  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  0
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler
  .word  WWDG_IRQHandler                   /* Window WatchDog              */
  .word  PVD_VDDIO2_IRQHandler             /* PVD and VDDIO2 through EXTI Line detect */
  .word  RTC_IRQHandler                    /* RTC through the EXTI line    */
  .word  FLASH_IRQHandler                  /* FLASH                        */
  .word  RCC_CRS_IRQHandler                /* RCC and CRS                  */
  .word  EXTI0_1_IRQHandler                /* EXTI Line 0 and 1            */
  .word  EXTI2_3_IRQHandler                /* EXTI Line 2 and 3            */
  .word  EXTI4_15_IRQHandler               /* EXTI Line 4 to 15            */
  .word  TSC_IRQHandler                    /* TSC                          */
  .word  DMA1_Channel1_IRQHandler          /* DMA1 Channel 1               */
  .word  DMA1_Channel2_3_IRQHandler        /* DMA1 Channel 2 and Channel 3 */
  .word  DMA1_Channel4_5_6_7_IRQHandler    /* DMA1 Channel 4, Channel 5, Channel 6 and Channel 7*/
  .word  ADC1_COMP_IRQHandler              /* ADC1, COMP1 and COMP2         */
  .word  TIM1_BRK_UP_TRG_COM_IRQHandler    /* TIM1 Break, Update, Trigger and Commutation */
  .word  TIM1_CC_IRQHandler                /* TIM1 Capture Compare         */
  .word  TIM2_IRQHandler                   /* TIM2                         */
  .word  TIM3_IRQHandler                   /* TIM3                         */
  .word  TIM6_DAC_IRQHandler               /* TIM6 and DAC                 */
  .word  TIM7_IRQHandler                   /* TIM7                         */
  .word  TIM14_IRQHandler                  /* TIM14                        */
  .word  TIM15_IRQHandler                  /* TIM15                        */
  .word  TIM16_IRQHandler                  /* TIM16                        */
  .word  TIM17_IRQHandler                  /* TIM17                        */
  .word  I2C1_IRQHandler                   /* I2C1                         */
  .word  I2C2_IRQHandler                   /* I2C2                         */
  .word  SPI1_IRQHandler                   /* SPI1                         */
  .word  SPI2_IRQHandler                   /* SPI2                         */
  .word  USART1_IRQHandler                 /* USART1                       */
  .word  USART2_IRQHandler                 /* USART2                       */
  .word  USART3_4_IRQHandler               /* USART3 and USART4            */
  .word  CEC_CAN_IRQHandler                /* CEC and CAN                  */
  .word  USB_IRQHandler                    /* USB                          */
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.

  .weak      NMI_Handler
  .thumb_set NMI_Handler,Default_Handler

  .weak      HardFault_Handler
  .thumb_set HardFault_Handler,Default_Handler

  .weak      SVC_Handler
  .thumb_set SVC_Handler,Default_Handler

  .weak      PendSV_Handler
  .thumb_set PendSV_Handler,Default_Handler

  .weak      SysTick_Handler
  .thumb_set SysTick_Handler,Default_Handler

  .weak      WWDG_IRQHandler
  .thumb_set WWDG_IRQHandler,Default_Handler

  .weak      PVD_VDDIO2_IRQHandler
  .thumb_set PVD_VDDIO2_IRQHandler,Default_Handler

  .weak      RTC_IRQHandler
  .thumb_set RTC_IRQHandler,Default_Handler

  .weak      FLASH_IRQHandler
  .thumb_set FLASH_IRQHandler,Default_Handler

  .weak      RCC_CRS_IRQHandler
  .thumb_set RCC_CRS_IRQHandler,Default_Handler

  .weak      EXTI0_1_IRQHandler
  .thumb_set EXTI0_1_IRQHandler,Default_Handler

  .weak      EXTI2_3_IRQHandler
  .thumb_set EXTI2_3_IRQHandler,Default_Handler

  .weak      EXTI4_15_IRQHandler
  .thumb_set EXTI4_15_IRQHandler,Default_Handler

  .weak      TSC_IRQHandler
  .thumb_set TSC_IRQHandler,Default_Handler

  .weak      DMA1_Channel1_IRQHandler
  .thumb_set DMA1_Channel1_IRQHandler,Default_Handler

  .weak      DMA1_Channel2_3_IRQHandler
  .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler

  .weak      DMA1_Channel4_5_6_7_IRQHandler
  .thumb_set DMA1_Channel4_5_6_7_IRQHandler,Default_Handler

  .weak      ADC1_COMP_IRQHandler
  .thumb_set ADC1_COMP_IRQHandler,Default_Handler

  .weak      TIM1_BRK_UP_TRG_COM_IRQHandler
  .thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler

  .weak      TIM1_CC_IRQHandler
  .thumb_set TIM1_CC_IRQHandler,Default_Handler

  .weak      TIM2_IRQHandler
  .thumb_set TIM2_IRQHandler,Default_Handler

  .weak      TIM3_IRQHandler
  .thumb_set TIM3_IRQHandler,Default_Handler

  .weak      TIM6_DAC_IRQHandler
  .thumb_set TIM6_DAC_IRQHandler,Default_Handler

  .weak      TIM7_IRQHandler
  .thumb_set TIM7_IRQHandler,Default_Handler

  .weak      TIM14_IRQHandler
  .thumb_set TIM14_IRQHandler,Default_Handler

  .weak      TIM15_IRQHandler
  .thumb_set TIM15_IRQHandler,Default_Handler

  .weak      TIM16_IRQHandler
  .thumb_set TIM16_IRQHandler,Default_Handler

  .weak      TIM17_IRQHandler
  .thumb_set TIM17_IRQHandler,Default_Handler

  .weak      I2C1_IRQHandler
  .thumb_set I2C1_IRQHandler,Default_Handler

  .weak      I2C2_IRQHandler
  .thumb_set I2C2_IRQHandler,Default_Handler

  .weak      SPI1_IRQHandler
  .thumb_set SPI1_IRQHandler,Default_Handler

  .weak      SPI2_IRQHandler
  .thumb_set SPI2_IRQHandler,Default_Handler

  .weak      USART1_IRQHandler
  .thumb_set USART1_IRQHandler,Default_Handler

  .weak      USART2_IRQHandler
  .thumb_set USART2_IRQHandler,Default_Handler

  .weak      USART3_4_IRQHandler
  .thumb_set USART3_4_IRQHandler,Default_Handler

  .weak      CEC_CAN_IRQHandler
  .thumb_set CEC_CAN_IRQHandler,Default_Handler

  .weak      USB_IRQHandler
  .thumb_set USB_IRQHandler,Default_Handler

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


6. Annex B: LinkerScript.ld

**  File        : LinkerScript.ld
**  Abstract    : Linker script for STM32F072RBTx Device with
**                128KByte FLASH, 16KByte RAM
**                Set heap size, stack size and stack location according
**                to application requirements.
**                Set memory bank area and size if external memory is used.
**  Target      : STMicroelectronics STM32
**  Distribution: The file is distributed as is, without any warranty
**                of any kind.
**  (c)Copyright Ac6.
**  You may use this file as-is or modify it according to the needs of your
**  project. Distribution of this file (unmodified or modified) is not
**  permitted. Ac6 permit registered System Workbench for MCU users the
**  rights to distribute the assembled, compiled & linked contents of this
**  file as part of an application binary file, provided that it is built
**  using the System Workbench for MCU toolchain.

/* Entry Point */

/* Highest address of the user mode stack */
_estack = 0x20004000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 16K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 128K

/* Define output sections */
  /* The startup code goes first into FLASH */
  .isr_vector :
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  .text :
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    __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);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */

  /* 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;

    . = 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 */
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )

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