Map stock firmware GPIO use from disassembly
Disassembled the SV1.3 app image and confirmed the shift-register (HSPI 13/14 + latch 15), WS2812 (12), LDR (A0) and button pins, the 16-bit bit map and the tube order. The stock firmware has no I2C code and drives a DFPlayer on UART0. Adds the ELF/xref helper tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9ffcfb95d2
commit
8efa810e4e
4 files changed
+598
-15
No files matched your search
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wrap the segments of an ESP8266 (v1) app image in a minimal ELF so binutils can read it.
|
||||
|
||||
Reads only the app image (the 0x1000 slot of a full flash dump), never SPIFFS.
|
||||
|
||||
python tools/esp8266_image_to_elf.py <full_dump.bin> <out.elf> [--offset 0x1000]
|
||||
|
||||
Each image segment becomes one section (.irom0, .iram0_N, .dram0_N) at its load address,
|
||||
so `xtensa-lx106-elf-objdump -d out.elf` disassembles with real addresses.
|
||||
"""
|
||||
import argparse
|
||||
import struct
|
||||
|
||||
APP_SLOT_MAX = 0x100000 # never read past the 1 MB app slot (SPIFFS starts at 0x200000)
|
||||
|
||||
|
||||
def read_segments(image):
|
||||
magic, nseg, _mode, _sizefreq, entry = struct.unpack_from("<BBBBI", image, 0)
|
||||
if magic != 0xE9:
|
||||
raise SystemExit(f"bad image magic 0x{magic:02x}")
|
||||
off = 8
|
||||
segs = []
|
||||
for _ in range(nseg):
|
||||
addr, size = struct.unpack_from("<II", image, off)
|
||||
off += 8
|
||||
segs.append((addr, image[off:off + size]))
|
||||
off += size
|
||||
return entry, segs
|
||||
|
||||
|
||||
def section_name(addr, counters):
|
||||
if 0x40200000 <= addr < 0x40300000:
|
||||
kind = "irom0"
|
||||
elif 0x40100000 <= addr < 0x40108000:
|
||||
kind = "iram0"
|
||||
else:
|
||||
kind = "dram0"
|
||||
counters[kind] = counters.get(kind, 0) + 1
|
||||
return f".{kind}_{counters[kind] - 1}"
|
||||
|
||||
|
||||
def build_elf(entry, segs):
|
||||
counters = {}
|
||||
names = [section_name(a, counters) for a, _ in segs]
|
||||
shstrtab = b"\0"
|
||||
name_off = []
|
||||
for n in names + [".shstrtab"]:
|
||||
name_off.append(len(shstrtab))
|
||||
shstrtab += n.encode() + b"\0"
|
||||
|
||||
ehsize, phentsize, shentsize = 52, 32, 40
|
||||
nseg = len(segs)
|
||||
data_off = ehsize + phentsize * nseg
|
||||
body = b""
|
||||
offsets = []
|
||||
for _, data in segs:
|
||||
while (data_off + len(body)) % 4:
|
||||
body += b"\0"
|
||||
offsets.append(data_off + len(body))
|
||||
body += data
|
||||
shstr_off = data_off + len(body)
|
||||
body += shstrtab
|
||||
while (data_off + len(body)) % 4:
|
||||
body += b"\0"
|
||||
shoff = data_off + len(body)
|
||||
shnum = nseg + 2
|
||||
|
||||
out = bytearray()
|
||||
out += b"\x7fELF" + bytes([1, 1, 1]) + bytes(9)
|
||||
out += struct.pack("<HHIIIIIHHHHHH", 2, 94, 1, entry, ehsize, shoff, 0x300,
|
||||
ehsize, phentsize, nseg, shentsize, shnum, shnum - 1)
|
||||
for (addr, data), o in zip(segs, offsets):
|
||||
flags = 5 if addr >= 0x40000000 else 6 # R+X for code, R+W for data
|
||||
out += struct.pack("<IIIIIIII", 1, o, addr, addr, len(data), len(data), flags, 4)
|
||||
out += body
|
||||
out += bytes(shentsize) # null section
|
||||
for i, ((addr, data), o) in enumerate(zip(segs, offsets)):
|
||||
code = addr >= 0x40000000
|
||||
sh_flags = 0x2 | (0x4 if code else 0x1) # ALLOC + EXECINSTR / WRITE
|
||||
out += struct.pack("<IIIIIIIIII", name_off[i], 1, sh_flags, addr, o, len(data), 0, 0, 4, 0)
|
||||
out += struct.pack("<IIIIIIIIII", name_off[-1], 3, 0, 0, shstr_off, len(shstrtab), 0, 0, 1, 0)
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
ap.add_argument("dump")
|
||||
ap.add_argument("out")
|
||||
ap.add_argument("--offset", type=lambda s: int(s, 0), default=0x1000)
|
||||
args = ap.parse_args()
|
||||
with open(args.dump, "rb") as f:
|
||||
f.seek(args.offset)
|
||||
image = f.read(APP_SLOT_MAX - args.offset)
|
||||
entry, segs = read_segments(image)
|
||||
with open(args.out, "wb") as f:
|
||||
f.write(build_elf(entry, segs))
|
||||
for addr, data in segs:
|
||||
print(f"0x{addr:08x} 0x{len(data):06x}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user