#!/usr/bin/env python3 """ Verify ELF symbols against firmware exports. Rerunnable from skill. Usage: python3 scripts/verify_symbols.py [--elf Apps/BibleVerse/build/cmake-build-esp32s3/BibleVerse.app.elf] [--firmware ../firmware] Requires: xtensa-esp32s3-elf-nm in PATH after sourcing ~/esp/esp-idf/export.sh """ import argparse, pathlib, re, subprocess, sys def get_undef(elf_path: pathlib.Path): r = subprocess.run( ["zsh","-c", f"source /Users/adolforeyna/esp/esp-idf/export.sh >/dev/null 2>&1; xtensa-esp32s3-elf-nm -D {elf_path}"], capture_output=True, text=True ) undef=[] for line in r.stdout.splitlines(): parts=line.split() if len(parts)>=2 and parts[-2]=='U': undef.append(parts[-1]) return undef, r.stdout def get_exports(firmware_dir: pathlib.Path): pat=re.compile(r'(?:ESP_ELFSYM_EXPORT|DEFINE_MODULE_SYMBOL)\s*\(\s*([A-Za-z0-9_]+)\s*\)') exp=set() for fp in firmware_dir.rglob("*.*"): if 'build' in str(fp): continue if fp.suffix not in ('.c','.h','.cpp'): continue try: exp.update(m.group(1) for m in pat.finditer(fp.read_text(errors='ignore'))) except: pass return exp def main(): p=argparse.ArgumentParser() p.add_argument('--elf', default='Apps/BibleVerse/build/cmake-build-esp32s3/BibleVerse.app.elf') p.add_argument('--firmware', default='../firmware') args=p.parse_args() elf=pathlib.Path(args.elf) fw=pathlib.Path(args.firmware) if not elf.exists(): print(f"ELF not found: {elf}", file=sys.stderr) sys.exit(2) undef, raw = get_undef(elf) exp = get_exports(fw) missing=[s for s in undef if s not in exp] print(f"Undefined: {len(undef)}") print(f"Exported: {len(exp)}") if missing: print(f"Missing {len(missing)}:") for s in sorted(missing): print(f" - {s}") sys.exit(1) print("✅ All symbols resolved") sys.exit(0) if __name__ == '__main__': main()