[GH-ISSUE #3483] Feature request: Xtensa ELF lane (ESP32 / ESP32-S3) via Ghidra #1297

Open
opened 2026-10-16 21:03:54 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @michaelgtnet on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3483

Summary

Request: an Xtensa ELF lane through Ghidra, analogous to the existing
RISC-V and MIPS lanes. Xtensa (LX6/LX7) is the
CPU family of ESP32 / ESP32-S2 / ESP32-S3 — a large class of IoT / automotive /
OBD firmware targets. Today REA cannot analyze them at all, on any host.

Current behavior (measured; rea 6.4.0 + Ghidra 12.1.4, Linux)

Given a well-formed little-endian ELF32 ET_EXEC with e_machine = 94
(EM_XTENSA), whose PT_LOAD program headers map a real ESP-IDF app image:

$ rea inspect fw-s3.elf --provider ghidra --format json
{"error":"Analysis failed","code":"target_unavailable","category":"unavailable",
 "message":"Cannot open artifact: unsupported ELF architecture.",
 "details":{"reason":"unsupported ELF architecture"}}

Trace: src/domain/binaryTarget.ts → elfArchitecture() switches on
3, 62, 8, 40, 183, 243; 94 is not recognized, so parseElf returns
unsupported ELF architecture. The raw-binary path is also closed
(architecture ∈ {x86, x86_64, arm, arm64, 6502}), and Windows Ghidra P0 is
PE-only, so no route admits the target on any host.

Why this is viable

Ghidra 12.1.4 ships Xtensa processor modules
(Ghidra/Processors/Xtensa: xtensa_le.sla, xtensa_be.sla, xtensa.opinion).
Driving analyzeHeadless directly on the same wrapper:

INFO  Using Language/Compiler: Xtensa:LE:32:default:default (ProgramLoader)
INFO  REPORT: Import succeeded

Auto-analysis produced 5,908 functions / 3,919 defined strings for an
ESP32-S3 image and 4,638 / 2,132 for an ESP32 (LX6) image. So the engine
support already exists — only the REA admission boundary is missing.

Proposed lane (mirroring ghidra-riscv)

  • Recognize EM_XTENSA (94) as a provider-neutral xtensa family in
    elfArchitecture().
  • Admit little-endian ELF32 ET_EXEC, pin Xtensa:LE:32:default with the
    gcc compiler spec (windowed ABI is the ESP-IDF default). Record the Xtensa
    ABI declaration from e_flags as lane metadata (windowed vs call0).
  • Refuse with the declared value: other e_type (ET_REL/ET_DYN), non-EA32
    classes, and any reserved e_flags bits. Big-endian could be admitted too
    (xtensa_be.sla exists) or refused explicitly — either is fine, just state it.
  • Document that a raw ESP-IDF .bin has no ELF header and must be wrapped:
    each image segment becomes a PT_LOAD at its declared load address and
    e_entry = the image entry. esptool image-info prints the exact segment
    table (load addr / file offset / length). The original .bin is never modified.
  • Windows P0 stays out of scope (PE-only); like RISC-V/MIPS this lane is
    Linux/macOS.

Minimal reproduction of the wrapper

  1. esptool image-info fw.bin → segment table.
  2. Emit ELF32 LE (e_ident = 7f 45 4c 46 01 01 01 00, e_type = ET_EXEC,
    e_machine = 94, e_phoff = 52), one PT_LOAD per segment copying the
    exact source bytes, e_entry = image entry.

~40 lines of stdlib Python; no toolchain, no modification of original bytes.

Impact

Unlocks the whole ESP32 family for the same Evidence-backed native workflow the
RISC-V and MIPS lanes provide (IoT, K-line/CAN gateways, OBD devices, etc.).

Originally created by @michaelgtnet on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3483 ## Summary Request: an **Xtensa ELF lane** through Ghidra, analogous to the existing [RISC-V](ghidra-riscv.md) and [MIPS](ghidra-mips.md) lanes. Xtensa (LX6/LX7) is the CPU family of **ESP32 / ESP32-S2 / ESP32-S3** — a large class of IoT / automotive / OBD firmware targets. Today REA cannot analyze them at all, on any host. ## Current behavior (measured; rea 6.4.0 + Ghidra 12.1.4, Linux) Given a well-formed little-endian `ELF32 ET_EXEC` with `e_machine = 94` (`EM_XTENSA`), whose `PT_LOAD` program headers map a real ESP-IDF app image: ``` $ rea inspect fw-s3.elf --provider ghidra --format json {"error":"Analysis failed","code":"target_unavailable","category":"unavailable", "message":"Cannot open artifact: unsupported ELF architecture.", "details":{"reason":"unsupported ELF architecture"}} ``` Trace: `src/domain/binaryTarget.ts` → `elfArchitecture()` switches on `3, 62, 8, 40, 183, 243`; `94` is not recognized, so `parseElf` returns `unsupported ELF architecture`. The `raw-binary` path is also closed (`architecture ∈ {x86, x86_64, arm, arm64, 6502}`), and Windows Ghidra P0 is PE-only, so no route admits the target on any host. ## Why this is viable Ghidra 12.1.4 ships Xtensa processor modules (`Ghidra/Processors/Xtensa`: `xtensa_le.sla`, `xtensa_be.sla`, `xtensa.opinion`). Driving `analyzeHeadless` directly on the same wrapper: ``` INFO Using Language/Compiler: Xtensa:LE:32:default:default (ProgramLoader) INFO REPORT: Import succeeded ``` Auto-analysis produced **5,908 functions / 3,919 defined strings** for an ESP32-S3 image and **4,638 / 2,132** for an ESP32 (LX6) image. So the engine support already exists — only the REA admission boundary is missing. ## Proposed lane (mirroring `ghidra-riscv`) - Recognize `EM_XTENSA` (94) as a provider-neutral `xtensa` family in `elfArchitecture()`. - Admit **little-endian `ELF32 ET_EXEC`**, pin `Xtensa:LE:32:default` with the `gcc` compiler spec (windowed ABI is the ESP-IDF default). Record the Xtensa ABI declaration from `e_flags` as lane metadata (windowed vs `call0`). - Refuse with the declared value: other `e_type` (`ET_REL`/`ET_DYN`), non-EA32 classes, and any reserved `e_flags` bits. Big-endian could be admitted too (`xtensa_be.sla` exists) or refused explicitly — either is fine, just state it. - Document that a raw ESP-IDF `.bin` has **no ELF header** and must be wrapped: each image segment becomes a `PT_LOAD` at its declared load address and `e_entry` = the image entry. `esptool image-info` prints the exact segment table (load addr / file offset / length). The original `.bin` is never modified. - Windows P0 stays out of scope (PE-only); like RISC-V/MIPS this lane is Linux/macOS. ## Minimal reproduction of the wrapper 1. `esptool image-info fw.bin` → segment table. 2. Emit `ELF32 LE` (`e_ident = 7f 45 4c 46 01 01 01 00`, `e_type = ET_EXEC`, `e_machine = 94`, `e_phoff = 52`), one `PT_LOAD` per segment copying the exact source bytes, `e_entry` = image entry. ~40 lines of stdlib Python; no toolchain, no modification of original bytes. ## Impact Unlocks the whole ESP32 family for the same Evidence-backed native workflow the RISC-V and MIPS lanes provide (IoT, K-line/CAN gateways, OBD devices, etc.).
Author
Owner

@ShahbazCoder1 commented on GitHub (Oct 14, 2026):

I’d like to work on this issue. Could you please assign it to me?

I plan to add Xtensa ELF detection and validation for EM_XTENSA (e_machine = 94), configure the Ghidra Xtensa language/compiler, add the necessary tests, and document the ELF-wrapper workflow for raw ESP32 .bin files. I’ll keep the initial scope focused on 32-bit little-endian ET_EXEC files and preserve the existing Windows limitations.

<!-- gh-comment-id:6169336369 --> @ShahbazCoder1 commented on GitHub (Oct 14, 2026): I’d like to work on this issue. Could you please assign it to me? I plan to add Xtensa ELF detection and validation for EM_XTENSA (e_machine = 94), configure the Ghidra Xtensa language/compiler, add the necessary tests, and document the ELF-wrapper workflow for raw ESP32 .bin files. I’ll keep the initial scope focused on 32-bit little-endian ET_EXEC files and preserve the existing Windows limitations.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1297
No description provided.