Segments
The v5.0.3 image contains seven segments. DROM grew by 6,528 bytes and IROM by 996 bytes over v5.0.2; IRAM and the RTC segments are unchanged in size. Executable code lives in IROM (flash-mapped) and IRAM; constants and the frozen Python bytecode live in DROM.
Segments 1 and 6 address the same RTC FAST memory through different buses: seg 1
is the data-bus view (
.rtc.data) and seg 6 the instruction-bus view (.rtc.text).
RTC SLOW memory (0x50000000–0x50002000) is not used by any segment.
Structural details of the layout above:
- DROM starts with the OTA descriptor. The first 256 bytes of segment 0 are the
esp_app_desc_tapplication descriptor (build date, IDF version, ELF SHA-256), ahead of the rodata / qstr pool / frozen bytecode. +0x20load offset. Both flash-mapped segments (0x3f400020,0x400d0020) load0x20above their 64-KiB-aligned MMU page bases (0x3f400000/0x400d0000): the 24-byteesp_image_header_tand the 8-byte segment-0 header occupy the first0x20bytes in flash, ahead of the segment payload.- Segments 2 and 4 are contiguous in DRAM.
0x3ffbdb60 + 0xd08 = 0x3ffbe868(seg 4’s start), so the two initialized-data segments form one continuous region even though the IROM segment sits between them in image order. v5.0.2 split the same 23,248-byte region differently (0x2688+0x3448at0x3ffc01e8).
0x40081144, in IRAM (segment 5). The disassembled prologue is the
standard ESP32 startup: entry, a load into the exception vecbase, then a callx8
into early init. The entry routine itself is (inferred) the IDF call_start_cpu0 —
the rebuilt ELF has no symbol table, so it cannot be confirmed by symbol.
ESP32 address regions (reference)
Rebuilt ELF
For analysis, the seven segments are reassembled into anEM_XTENSA ELF with each
segment placed at its real load address and correct R/W/X permissions. That ELF loads
directly into Ghidra (with an Xtensa processor module) or IDA. Capstone 6.0
(CS_ARCH_XTENSA) also disassembles the code segments directly. See
methodology.