mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
201 lines
7.3 KiB
Python
Executable File
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)
|