#!/usr/bin/env python3 """coff.py -- TI TMS34010 GSP COFF reader for the VT1200 loader work. GSPLNK 2.00 emits the "TI COFF" described in the 1987 Assembler Tools guide, Appendix A. All section addresses and sizes in the section headers are in BITS (the TMS34010 is bit-addressed); file offsets (scnptr/relptr/symptr) are in BYTES. Commands coff.py summary FILE... dump header, optional header, sections, symbols (with aux entries) and relocations coff.py raw ABS1 ABS2 extract each section from two absolute outputs and report byte differences coff.py manifest FILE print shell-ish KEY=VALUE lines for one file The loader itself parses the same structures in assembly; this script is the reference and the evidence generator. """ import struct import sys MAGIC = 0x0090 OPTHDR_MAGIC = 0x0108 RELOC_NAMES = { 0x00: "R-ABS", 0x10: "R-RELWORD", 0x11: "R-RELLONG", 0x18: "R-OCRLONG", 0x19: "R-GSPPCR16", } SECTION_FLAGS = { 0x20: "text", 0x40: "data", 0x80: "bss", } class CoffError(Exception): pass def parse(path): d = open(path, "rb").read() if len(d) < 20: raise CoffError("%s: shorter than a COFF file header" % path) magic, nscns, timdat, symptr, nsyms, opthdr, flags = struct.unpack_from( " len(d): raise CoffError("%s: optional header past EOF" % path) # TI COFF optional header: magic, vstamp, textsize, datasize, bsssize, # entry, textstart, datastart (all sizes/addresses in bits). opt = struct.unpack_from(" len(d): raise CoffError("%s: section header %d past EOF" % (path, i)) raw = d[off:off + 40] name = raw[:8].rstrip(b"\0").decode("latin1") (paddr, vaddr, size, scnptr, relptr, lnnoptr, nreloc, nlnno, sflags) = struct.unpack_from(" len(d): raise CoffError("%s: relocation %d past EOF" % (path, r)) raddr, symidx, rtype = struct.unpack_from(" len(d): raise CoffError("%s: symbol %d past EOF" % (path, i)) e = d[off:off + 18] name = e[:8].rstrip(b"\0").decode("latin1", "replace") val, secno, stype, sclass, naux = struct.unpack_from(" %02x" % (i, ba[i], bb[i])) print() def manifest(path): c = parse(path) print("COFF=%s" % path) print("SIZE=%d" % c["size"]) print("NSECTIONS=%d" % c["nscns"]) if c["opt"]: _, _, tsize, dsize, bsize, entry, tstart, dstart = c["opt"] print("ENTRY=%#010x" % entry) print("TEXTSTART=%#010x" % tstart) print("DATASTART=%#010x" % dstart) print("TEXTSIZE_BITS=%d" % tsize) print("DATASIZE_BITS=%d" % dsize) print("BSSSIZE_BITS=%d" % bsize) for s in c["sections"]: key = s["name"].lstrip(".").upper() or ("SEC%d" % s["index"]) print("%s_PADDR=%#010x" % (key, s["paddr"])) print("%s_SIZE_BITS=%d" % (key, s["size"])) print("%s_NRELOC=%d" % (key, s["nreloc"])) def main(argv): if len(argv) < 3: raise SystemExit(__doc__) cmd = argv[1] if cmd == "summary": for p in argv[2:]: summary(p) elif cmd == "raw": raw(argv[2:]) elif cmd == "manifest": manifest(argv[2]) else: raise SystemExit(__doc__) if __name__ == "__main__": main(sys.argv)