Files

201 lines
7.3 KiB
Python
Executable File

#!/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(
"<HHIIIHH", d, 0)
if magic != MAGIC:
raise CoffError("%s: magic %#06x, expected %#06x"
% (path, magic, MAGIC))
opt = None
if opthdr:
if 20 + opthdr > 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("<HHIIIIII", d, 20)
base = 20 + opthdr
secs = []
for i in range(nscns):
off = base + i * 40
if off + 40 > 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("<IIIIIIHHI", d, off + 8)
relocs = []
for r in range(nreloc):
ro = relptr + r * 10
if ro + 10 > len(d):
raise CoffError("%s: relocation %d past EOF" % (path, r))
raddr, symidx, rtype = struct.unpack_from("<IIH", d, ro)
relocs.append((raddr, symidx, rtype))
secs.append(dict(index=i + 1, name=name, paddr=paddr, vaddr=vaddr,
size=size, scnptr=scnptr, relptr=relptr,
lnnoptr=lnnoptr, nreloc=nreloc, nlnno=nlnno,
flags=sflags, relocs=relocs, raw=raw))
syms = []
i = 0
while i < nsyms:
off = symptr + i * 18
if off + 18 > 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("<IHhBB", e, 8)
aux = d[off + 18:off + 18 + naux * 18]
syms.append(dict(index=i, name=name, value=val, secno=secno,
type=stype, sclass=sclass, naux=naux, aux=aux))
i += 1 + naux
return dict(path=path, size=len(d), magic=magic, nscns=nscns,
timdat=timdat, symptr=symptr, nsyms=nsyms, opthdr=opthdr,
flags=flags, opt=opt, sections=secs, symbols=syms, data=d)
def summary(path):
c = parse(path)
print("== %s (%d bytes)" % (path, c["size"]))
print(" file hdr: magic=%#06x nscns=%d symptr=%#x nsyms=%d "
"opthdr=%d flags=%#06x"
% (c["magic"], c["nscns"], c["symptr"], c["nsyms"], c["opthdr"],
c["flags"]))
if c["opt"]:
magic, vstamp, tsize, dsize, bsize, entry, tstart, dstart = c["opt"]
print(" optional: magic=%#06x vstamp=%#06x text=%#x bits "
"data=%#x bits bss=%#x bits entry=%#010x textstart=%#010x "
"datastart=%#010x"
% (magic, vstamp, tsize, dsize, bsize, entry, tstart, dstart))
print(" optional: sizes in bytes text=%d data=%d bss=%d; "
"entry in bits=%s"
% (tsize // 8, dsize // 8, bsize // 8,
"0 (no -e global symbol)" if entry == 0 else hex(entry)))
for s in c["sections"]:
fl = ",".join(v for k, v in SECTION_FLAGS.items() if s["flags"] & k)
print(" sec %d %-8s paddr=%010x vaddr=%010x size=%#x bits (%d B) "
"scnptr=%#x nreloc=%d flags=%#x%s"
% (s["index"], s["name"], s["paddr"], s["vaddr"], s["size"],
s["size"] // 8, s["scnptr"], s["nreloc"], s["flags"],
(" [" + fl + "]") if fl else ""))
for raddr, symidx, rtype in s["relocs"]:
print(" reloc at %#x sym=%d type=%#x %s"
% (raddr, symidx, rtype,
RELOC_NAMES.get(rtype, "?")))
for sym in c["symbols"]:
if sym["naux"]:
print(" sym[%d] %-10s value=%010x sec=%d class=%d naux=%d"
% (sym["index"], sym["name"], sym["value"], sym["secno"],
sym["sclass"], sym["naux"]))
else:
print(" sym[%d] %-10s value=%010x sec=%d class=%d"
% (sym["index"], sym["name"], sym["value"], sym["secno"],
sym["sclass"]))
print()
def section_bytes(c, name):
for s in c["sections"]:
if s["name"] == name:
return s, c["data"][s["scnptr"]:s["scnptr"] + s["size"] // 8]
return None, None
def raw(paths):
if len(paths) != 2:
raise SystemExit("coff.py raw: need exactly two files")
a, b = parse(paths[0]), parse(paths[1])
for name in (".text", ".data", ".bss"):
sa, ba = section_bytes(a, name)
sb, bb = section_bytes(b, name)
if sa is None or sb is None:
continue
print("== %s: %d vs %d bytes" % (name, len(ba), len(bb)))
n = min(len(ba), len(bb))
bad = [i for i in range(n) if ba[i] != bb[i]]
print(" differing bytes: %d" % (len(bad) + abs(len(ba) - len(bb))))
for i in bad[:24]:
print(" %#06x: %02x -> %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)