Task 3: Linker Script
March 31, 2026 · View on GitHub
Overview
File: linker/esp32c6.ld
A linker script that matches the segment layout required by the 2nd stage bootloader (SOC_MMU_DI_VADDR_SHARED).
Memory Map
| Region | Attributes | Start Address | Size | Purpose |
|---|---|---|---|---|
iram | RWX | 0x40800000 | 512KB | .data/.bss (bootloader copies from Flash to RAM) |
irom | RX | 0x42000020 | ~2MB | .text (executed via Flash MMU) |
drom | R | 0x42010020 | ~2MB | .rodata (read via Flash MMU) |
lp_ram | RW | 0x50000000 | 16KB | LP memory (retained during deep sleep) |
Section Placement
irom (0x42000020) ← Flash MMU mapped
└── .text Code
drom (0x42010020) ← Flash MMU mapped
└── .rodata Read-only data, Swift metadata
iram (0x40800000) ← Bootloader copies from Flash to RAM
├── .data Initialized data
├── .got Global Offset Table
├── .bss Uninitialized data (zero-cleared)
├── .stack Stack area (16KB)
└── heap _heap_start ~ _heap_end (0x40880000)
Heap Region
The heap occupies the remaining IRAM space after .bss and .stack:
_heap_start = .; (after .stack, ALIGN(8))
_heap_end = ORIGIN(iram) + LENGTH(iram); (0x40880000)
These are linker-defined symbols (not variables). The bump allocator in RuntimeStubs.swift uses linkerSymbolAddress() to obtain their addresses via GOT entries. Allocations that would exceed _heap_end return ENOMEM (12).
Bootloader Compatibility
Two-Segment Requirement
The 2nd stage bootloader expects exactly 2 Flash MMU segments in the 0x42000000 range (matching the SOC_MMU_DI_VADDR_SHARED convention):
Therefore, .text is placed in irom and .rodata in drom (at a separate address 0x42010020).
Reason for the 0x20 Offset
The 0x18-byte file header + 0x08-byte segment header added by elf2image.swift (same layout as esptool.py) must satisfy the Flash MMU constraint paddr % 64KB == vaddr % 64KB.
Entry Point
The main symbol generated by @main is used as the entry point.
Specified via -Xlinker -e -Xlinker main in toolset.json (the linker script's ENTRY is evaluated before --defsym and thus has no effect).