armhub.dev
All tools

Linker script builder.

Pick a preset or define memory regions, pick where the stack and vector table live, and get a completelinker.ld you can drop into a GCC build — with .isr_vector, .data in flash → RAM, and proper C++ ctor arrays.

Preset
Memory regions
total 640K
KB
KB
Layout
Generated linker.ld
/* Linker script generated by armhub.dev linker-script tool
 * CPU: cortex-m4
 * Stack region: RAM (4K)
 * Heap size: 2K
 * Vector table region: FLASH
 */

ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM);

/* Required amounts of heap and stack */
_Min_Heap_Size  = 0x800;  /* 2 KiB */
_Min_Stack_Size = 0x1000;  /* 4 KiB */

MEMORY
{
  FLASH   (rx ) : ORIGIN = 0x08000000, LENGTH = 512K
  RAM     (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

SECTIONS
{
  /* --- Vector table + code + read-only data --- */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector))
    . = ALIGN(4);
  } >FLASH

  .text :
  {
    . = ALIGN(4);
    *(.text)
    *(.text*)
    *(.glue_7)         /* ARM/Thumb interworking veneers */
    *(.glue_7t)
    *(.eh_frame)

    /* C++ static constructors */
    . = ALIGN(4);
    KEEP(*(.init))
    KEEP(*(.fini))
    . = ALIGN(4);
    __preinit_array_start = .;
    KEEP(*(.preinit_array))
    __preinit_array_end = .;
    . = ALIGN(4);
    __init_array_start = .;
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array))
    __init_array_end = .;
    . = ALIGN(4);
    __fini_array_start = .;
    KEEP(*(SORT(.fini_array.*)))
    KEEP(*(.fini_array))
    __fini_array_end = .;

    . = ALIGN(4);
    _etext = .;
  } >FLASH

  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)
    *(.rodata*)
    . = ALIGN(4);
  } >FLASH

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

  /* --- Initialized data: load address in flash, run address in RAM --- */
  _sidata = LOADADDR(.data);

  .data :
  {
    . = ALIGN(4);
    _sdata = .;
    *(.data)
    *(.data*)
    *(.RamFunc)        /* code that runs from RAM */
    *(.RamFunc*)
    . = ALIGN(4);
    _edata = .;
  } >RAM AT> FLASH

  /* --- Zero-initialized BSS --- */
  .bss :
  {
    . = ALIGN(4);
    _sbss = .;
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    _ebss = .;
    __bss_end__ = _ebss;
  } >RAM

  /* --- User heap & stack: keep them in the stack region, fail at link if too small --- */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}
How to use
  1. Save as linker.ld in your project.
  2. Pass it to GCC with -T linker.ld.
  3. In your startup file, set _estack as the initial SP in the vector table — it's defined here as the top of the stack region.
  4. Your reset handler must zero .bss (from _sbss to _ebss) and copy .data from _sidata to _sdata..._edata.
Stack ends at top of RAM — grows down toward _end.