#!/usr/bin/env python3 """Emit a spec-correct 32-bit LE ELF (e_machine=EM_NDS32) from a MediaTek Connac2 WiFi firmware container, one PT_LOAD segment per region at true load addresses. Structure is validated by readelf; disassembly requires Ghidra (official NDS32 module) or an Andes nds32le-elf toolchain — Debian binutils dropped NDS32. Entry point is unknown pre-RE (e_entry=0); set it in your tool after import. NON_DL regions are included as loadable segments by default; they are host-side data per the driver, not downloaded to the MCU. """ import struct import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from mtk_fw_extract import PATCH_HDR, PATCH_SEC, REGION, TRAILER # noqa: E402 EM_NDS32 = 167 EHDR = struct.Struct('<16sHHIIIIIHHHHHH') # 52 bytes PHDR = struct.Struct(' bytes: regions = [(a, dat) for a, dat in regions if dat] n = len(regions) phoff = EHDR.size data_off = phoff + n * PHDR.size eh = EHDR.pack( b'\x7fELF' + bytes([1, 1, 1, 0]) + b'\x00' * 8, # ELFCLASS32/LSB/v1 2, EM_NDS32, 1, regions[0][0] if regions else 0, # e_entry (first region; see doc) phoff, 0, 0, EHDR.size, PHDR.size, n, 40, 0, 0) out = bytearray(eh) offset = data_off for addr, dat in regions: out += PHDR.pack(PT_LOAD, offset, addr, addr, len(dat), len(dat), PF_RWX, 0x1000) offset += len(dat) for _, dat in regions: out += dat return bytes(out) def main(): if len(sys.argv) < 2: sys.exit(f'usage: {sys.argv[0]} [...] [-o outdir]') args, outdir = [], Path('extracted') it = iter(sys.argv[1:]) for a in it: if a == '-o': outdir = Path(next(it)) else: args.append(a) for path in args: p = Path(path) d = p.read_bytes() regions = patch_regions(d) if 'patch' in p.name else ram_regions(d) elf = build_elf(regions) out = outdir / p.stem / f'{p.stem}.elf' out.parent.mkdir(parents=True, exist_ok=True) out.write_bytes(elf) n_seg = struct.unpack_from(' {out} ({len(elf)} bytes, {n_seg} PT_LOAD)') if __name__ == '__main__': main()