ESP32-S3 Baremetal Support

How it boots

The ESP32-S3 ROM looks for an image at flash offset 0. Normally that's the ESP-IDF second-stage bootloader, which sets up the flash cache, reads the partition table, and loads the real app. This project is the image at offset 0, so the chain stops there:

reset → ROM loader → reads image header at flash 0x0
                   → copies segment 0 to 0x3FC90000  (rodata/data)
                   → copies segment 1 to 0x40378000  (code)
                   → jumps to _start

Consequences worth knowing:

  • Everything runs from SRAM. Flash is never memory-mapped, so there's no cache to configure and no XIP. That's why an IRAM_ATTR equivalent is unnecessary here — all the code is already in IRAM.
  • We run on the ROM's stack. ESP-IDF's own bootloader does exactly this. The ROM stack sits at 0x3FCE9710, well above anything this image uses.
  • We keep the ROM's exception vectors. VECBASE is left alone, which is what makes Xtensa register-window overflow and underflow keep working without a single handler of our own. It also means _start can be a plain C function rather than assembly.
  • Nothing zeroes .bss. The image only carries sections with contents, so _start zeroes it before anything reads it.