mirror of
https://frontier.innolan.net/github/amigaos-cross-toolchain6.git
synced 2024-10-19 10:29:55 +00:00
589 lines
14 KiB
Python
589 lines
14 KiB
Python
import os
|
|
import struct
|
|
import textwrap
|
|
|
|
from collections import defaultdict, namedtuple
|
|
from contextlib import contextmanager
|
|
|
|
import util
|
|
|
|
|
|
HunkMap = {
|
|
'HUNK_UNIT': 999,
|
|
'HUNK_NAME': 1000,
|
|
'HUNK_CODE': 1001,
|
|
'HUNK_DATA': 1002,
|
|
'HUNK_BSS': 1003,
|
|
'HUNK_RELOC32': 1004,
|
|
'HUNK_RELOC16': 1005,
|
|
'HUNK_RELOC8': 1006,
|
|
'HUNK_EXT': 1007,
|
|
'HUNK_SYMBOL': 1008,
|
|
'HUNK_DEBUG': 1009,
|
|
'HUNK_END': 1010,
|
|
'HUNK_HEADER': 1011,
|
|
'HUNK_OVERLAY': 1013,
|
|
'HUNK_BREAK': 1014,
|
|
'HUNK_DREL32': 1015,
|
|
'HUNK_DREL16': 1016,
|
|
'HUNK_DREL8': 1017,
|
|
'HUNK_LIB': 1018,
|
|
'HUNK_INDEX': 1019,
|
|
'HUNK_RELOC32SHORT': 1020,
|
|
'HUNK_RELRELOC32': 1021,
|
|
'HUNK_ABSRELOC16': 1022
|
|
}
|
|
|
|
HunkExtMap = {
|
|
'EXT_SYMB': 0, # symbol table
|
|
'EXT_DEF': 1, # relocatable definition
|
|
'EXT_ABS': 2, # Absolute definition
|
|
'EXT_RES': 3, # no longer supported
|
|
'EXT_REF32': 129, # 32 bit absolute reference to symbol
|
|
'EXT_COMMON': 130, # 32 bit absolute reference to COMMON block
|
|
'EXT_REF16': 131, # 16 bit PC-relative reference to symbol
|
|
'EXT_REF8': 132, # 8 bit PC-relative reference to symbol
|
|
'EXT_DEXT32': 133, # 32 bit data relative reference
|
|
'EXT_DEXT16': 134, # 16 bit data relative reference
|
|
'EXT_DEXT8': 135, # 8 bit data relative reference
|
|
'EXT_RELREF32': 136, # 32 bit PC-relative reference to symbol
|
|
'EXT_RELCOMMON': 137, # 32 bit PC-relative reference to COMMON block
|
|
'EXT_ABSREF16': 138, # 16 bit absolute reference to symbol
|
|
'EXT_ABSREF8': 139 # 8 bit absolute reference to symbol
|
|
}
|
|
|
|
# Any hunks that have the HUNKB_ADVISORY bit set will be ignored if they
|
|
# aren't understood. When ignored, they're treated like HUNK_DEBUG hunks.
|
|
# NOTE: this handling of HUNKB_ADVISORY started as of V39 dos.library! If
|
|
# lading such executables is attempted under <V39 dos, it will fail with a
|
|
# bad hunk type.
|
|
HUNKB_ADVISORY = 29
|
|
HUNKB_CHIP = 30
|
|
HUNKB_FAST = 31
|
|
|
|
HunkFlagsMap = {
|
|
'HUNKF_ADVISORY': 1 << HUNKB_ADVISORY,
|
|
'HUNKF_CHIP': 1 << HUNKB_CHIP,
|
|
'HUNKF_FAST': 1 << HUNKB_FAST
|
|
}
|
|
|
|
|
|
Symbol = namedtuple('Symbol', 'name size refs')
|
|
|
|
|
|
class Hunk(object):
|
|
def __init__(self, type_):
|
|
self.type = type_
|
|
|
|
@staticmethod
|
|
def getType(number):
|
|
number &= 0x1fffffff
|
|
|
|
for name, value in HunkMap.items():
|
|
if value == number:
|
|
return name
|
|
|
|
raise ValueError('Unknown Hunk: %d' % number)
|
|
|
|
@staticmethod
|
|
def getFlags(number):
|
|
return [name for name, value in HunkFlagsMap.items() if value & number]
|
|
|
|
|
|
class HunkSep(Hunk):
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
type_, _ = hf.readHunk('HUNK_END', 'HUNK_BREAK')
|
|
return cls(type_)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
|
|
|
|
class HunkStr(Hunk):
|
|
def __init__(self, type_, name=''):
|
|
Hunk.__init__(self, type_)
|
|
self.name = name
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
type_, _ = hf.readHunk('HUNK_NAME', 'HUNK_UNIT')
|
|
return cls(type_, hf.readString())
|
|
|
|
def dump(self):
|
|
print self.type
|
|
print ' ' + repr(self.name)
|
|
|
|
|
|
class HunkBinary(Hunk):
|
|
def __init__(self, type_, flags=None, data=''):
|
|
Hunk.__init__(self, type_)
|
|
self.flags = flags or []
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
type_, flags = hf.readHunk('HUNK_DATA', 'HUNK_CODE')
|
|
return cls(type_, flags, hf.readBytes())
|
|
|
|
def dump(self):
|
|
print '{0} {1}'.format(self.type, ', '.join(self.flags))
|
|
if self.data:
|
|
util.hexdump(self.data)
|
|
else:
|
|
print ' [empty]'
|
|
|
|
|
|
class HunkDebug(Hunk):
|
|
# HUNK_DEBUG:
|
|
# magic-number=0x10b symtabsize strtabsize symtabdata
|
|
# [length=symtabsize] strtabdata [length=strtabsize] [pad bytes]
|
|
|
|
def __init__(self, fmt='?', data=''):
|
|
Hunk.__init__(self, 'HUNK_DEBUG')
|
|
self.fmt = fmt
|
|
self.data = data
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
hf.readHunk('HUNK_DEBUG')
|
|
length = hf.readLong() * 4
|
|
|
|
with hf.rollback():
|
|
fmt = hf.readLong()
|
|
|
|
if fmt == 0x10b:
|
|
hf.skip(4)
|
|
return cls('GNU stubs', hf.readBytes(length - 4))
|
|
|
|
return cls('?', hf.readBytes(length))
|
|
|
|
def dump(self):
|
|
print '{0} (format: {1!r})'.format(self.type, self.fmt)
|
|
util.hexdump(self.data)
|
|
|
|
|
|
class HunkOverlay(Hunk):
|
|
def __init__(self):
|
|
Hunk.__init__(self, 'HUNK_OVERLAY')
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
hf.readHunk('HUNK_OVERLAY')
|
|
hf.skip(hf.readLong() * 4 + 4)
|
|
return cls()
|
|
|
|
|
|
class HunkBss(Hunk):
|
|
def __init__(self, flags=None, size=0):
|
|
Hunk.__init__(self, 'HUNK_BSS')
|
|
self.flags = flags or []
|
|
self.size = size
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
_, flags = hf.readHunk('HUNK_BSS')
|
|
return cls(flags, hf.readLong() * 4)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
print ' {0} bytes'.format(self.size)
|
|
|
|
|
|
class HunkLib(Hunk):
|
|
def __init__(self, size=0):
|
|
Hunk.__init__(self, 'HUNK_LIB')
|
|
self.size = size
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
_, flags = hf.readHunk('HUNK_LIB')
|
|
return cls(hf.readLong() * 4)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
|
|
|
|
class HunkReloc(Hunk):
|
|
def __init__(self, type_, relocs=None):
|
|
Hunk.__init__(self, type_)
|
|
self.relocs = relocs or {}
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
type_, _ = hf.readHunk('HUNK_RELOC32', 'HUNK_RELOC16', 'HUNK_RELOC8',
|
|
'HUNK_DREL32', 'HUNK_DREL16', 'HUNK_DREL8')
|
|
|
|
if hf.type is 'executable' and type_ in ['HUNK_DREL32', 'HUNK_DREL16',
|
|
'HUNK_DREL8']:
|
|
relocs = hf.readShortRelocs()
|
|
else:
|
|
relocs = hf.readRelocs()
|
|
|
|
return cls(type_, relocs)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
for k, nums in self.relocs.items():
|
|
prefix = ' %d: ' % k
|
|
print textwrap.fill('[' + ', '.join(str(n) for n in nums) + ']',
|
|
width=68, initial_indent=prefix,
|
|
subsequent_indent=' ' * (len(prefix) + 1))
|
|
|
|
|
|
class HunkSymbol(Hunk):
|
|
def __init__(self, symbols=None):
|
|
Hunk.__init__(self, 'HUNK_SYMBOL')
|
|
self.symbols = symbols or []
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
hf.readHunk('HUNK_SYMBOL')
|
|
return cls(hf.readSymbols())
|
|
|
|
def dump(self):
|
|
print self.type
|
|
|
|
l = max(len(s.name) for s in self.symbols) + 1
|
|
|
|
for s in sorted(self.symbols, key=lambda s: s.name):
|
|
print ' {0}: {1}'.format(s.name.ljust(l, ' '), s.refs)
|
|
|
|
|
|
class HunkHeader(Hunk):
|
|
def __init__(self, residents=None, hunks=0, first=0, last=0,
|
|
specifiers=None):
|
|
Hunk.__init__(self, 'HUNK_HEADER')
|
|
self.residents = residents or []
|
|
self.hunks = hunks
|
|
self.first = first
|
|
self.last = last
|
|
self.specifiers = specifiers or []
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
hf.readHunk('HUNK_HEADER')
|
|
|
|
residents = []
|
|
|
|
while True:
|
|
longs = hf.readLong()
|
|
|
|
if not longs:
|
|
break
|
|
|
|
residents.append(hf.readString(longs * 4))
|
|
|
|
hunks, first, last = hf.readLong(), hf.readLong(), hf.readLong()
|
|
specifiers = [hf.readLong() for i in range(last - first + 1)]
|
|
|
|
return cls(residents, hunks, first, last, specifiers)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
print ' hunks={0}, first={1}, last={2}'.format(self.hunks, self.first,
|
|
self.last)
|
|
print ' residents : ' + repr(self.residents)
|
|
print ' specifiers : ' + repr(self.specifiers)
|
|
|
|
|
|
class HunkExt(Hunk):
|
|
def __init__(self, hunks=None):
|
|
Hunk.__init__(self, 'HUNK_EXT')
|
|
self.hunks = hunks or defaultdict(list)
|
|
|
|
@staticmethod
|
|
def getType(number):
|
|
for name, value in HunkExtMap.items():
|
|
if value == number:
|
|
return name
|
|
|
|
raise ValueError('Unknown HunkExt: %d' % number)
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
hf.readHunk('HUNK_EXT')
|
|
|
|
hunks = defaultdict(list)
|
|
|
|
while True:
|
|
longs = hf.readLong()
|
|
|
|
if not longs:
|
|
break
|
|
|
|
length = (longs & 0xffffff) * 4
|
|
extName = HunkExt.getType(longs >> 24)
|
|
|
|
if extName in ['EXT_DEF', 'EXT_ABS', 'EXT_REL']:
|
|
symbol = hf.readSymbol(length)
|
|
elif extName in ['EXT_REF32', 'EXT_REF16', 'EXT_REF8',
|
|
'EXT_DEXT32', 'EXT_DEXT16', 'EXT_DEXT8']:
|
|
symbol = hf.readString(length)
|
|
count = hf.readLong()
|
|
refs = [hf.readLong() for i in range(count)]
|
|
symbol = Symbol(symbol, None, refs)
|
|
elif extName in ['EXT_COMMON']:
|
|
name = hf.readString(length)
|
|
size = hf.readLong()
|
|
refs_num = hf.readLong()
|
|
refs = [hf.readLong() for i in range(refs_num)]
|
|
symbol = Symbol(name, size, refs)
|
|
else:
|
|
raise NotImplementedError('%s not handled.' % extName)
|
|
|
|
hunks[extName].append(symbol)
|
|
|
|
return cls(hunks)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
|
|
for name, symbols in self.hunks.items():
|
|
print ' ', name
|
|
sl = max(len(s.name) for s in symbols)
|
|
for symbol, size, value in symbols:
|
|
if value:
|
|
print ' ', symbol.ljust(sl, ' '), '=', value
|
|
else:
|
|
print ' ', symbol.ljust(sl, ' '), ':', size
|
|
|
|
|
|
class HunkIndex(Hunk):
|
|
def __init__(self, units=None):
|
|
Hunk.__init__(self, 'HUNK_INDEX')
|
|
self.units = units or []
|
|
|
|
@classmethod
|
|
def parse(cls, hf):
|
|
hf.readHunk('HUNK_INDEX')
|
|
|
|
length = hf.readLong() * 4
|
|
last = hf.tell() + length
|
|
|
|
strsize = hf.readWord()
|
|
strdata = hf.readBytes(strsize)
|
|
names = {}
|
|
|
|
s = 0
|
|
|
|
while True:
|
|
e = strdata.find('\0', s, strsize)
|
|
|
|
if e == -1:
|
|
names[s] = strdata[s:]
|
|
break
|
|
|
|
if e > s:
|
|
names[s] = strdata[s:e]
|
|
|
|
s = e + 1
|
|
|
|
units = []
|
|
|
|
while hf.tell() < last:
|
|
unit_name = names[hf.readWord()]
|
|
first_hunk = hf.readWord() * 4
|
|
|
|
hunks_count = hf.readWord()
|
|
hunks = []
|
|
|
|
for i in range(hunks_count):
|
|
h_name = names[hf.readWord()]
|
|
h_size = hf.readWord() * 4
|
|
h_type = hf.readWord()
|
|
|
|
refs_count = hf.readWord()
|
|
refs = []
|
|
|
|
for i in range(refs_count):
|
|
n = hf.readWord()
|
|
try:
|
|
refs.append(names[n])
|
|
except KeyError:
|
|
refs.append(names[n + 1])
|
|
|
|
symbols_count = hf.readWord()
|
|
symbols = []
|
|
|
|
for i in range(symbols_count):
|
|
s_name = names[hf.readWord()]
|
|
s_value = hf.readWord()
|
|
s_type = hf.readWord()
|
|
symbols.append((s_name, s_value, s_type))
|
|
|
|
hunks.append((h_name, h_size, h_type, refs, symbols))
|
|
|
|
units.append((unit_name, first_hunk, hunks))
|
|
|
|
return cls(units)
|
|
|
|
def dump(self):
|
|
print self.type
|
|
|
|
for u in self.units:
|
|
print ' ', 'UNIT', repr(u[0]), u[1]
|
|
for h in u[2]:
|
|
print ' ', Hunk.getType(h[2]), repr(h[0]), h[1]
|
|
if h[3]:
|
|
print ' ', 'REFS'
|
|
for s in sorted(h[3]):
|
|
print ' ', s
|
|
if h[4]:
|
|
print ' ', 'DEFS'
|
|
l = max(len(s[0]) for s in h[4])
|
|
for s in sorted(h[4], key=lambda x: x[1]):
|
|
print ' ', s[0].ljust(l), '=', s[1]
|
|
print ''
|
|
|
|
|
|
class HunkFile(file):
|
|
def __init__(self, *args, **kwargs):
|
|
file.__init__(self, *args, **kwargs)
|
|
|
|
self.size = os.stat(self.name).st_size
|
|
self.type = 'object'
|
|
|
|
@contextmanager
|
|
def rollback(self):
|
|
pos = self.tell()
|
|
yield self
|
|
self.seek(pos, os.SEEK_SET)
|
|
|
|
def readWord(self):
|
|
return struct.unpack_from('>H', self.read(2))[0]
|
|
|
|
def readLong(self):
|
|
return struct.unpack_from('>I', self.read(4))[0]
|
|
|
|
def readInt(self):
|
|
return struct.unpack_from('>i', self.read(4))[0]
|
|
|
|
def readBytes(self, n=None):
|
|
if n is None:
|
|
n = self.readLong() * 4
|
|
return self.read(n)
|
|
|
|
def readString(self, n=None):
|
|
return self.readBytes(n).strip('\0')
|
|
|
|
def readSymbol(self, length):
|
|
symbol = self.readString(length)
|
|
value = self.readInt()
|
|
return Symbol(symbol, None, value)
|
|
|
|
def readSymbols(self):
|
|
symbols = []
|
|
|
|
while True:
|
|
length = self.readLong() * 4
|
|
|
|
if not length:
|
|
break
|
|
|
|
symbols.append(self.readSymbol(length))
|
|
|
|
return symbols
|
|
|
|
def readHunk(self, *types):
|
|
hunkId = self.readLong()
|
|
hunkType = Hunk.getType(hunkId)
|
|
hunkFlags = Hunk.getFlags(hunkId)
|
|
|
|
if types:
|
|
if hunkType not in types:
|
|
raise ValueError('Unexpected hunk type: %s', hunkType)
|
|
|
|
return hunkType, hunkFlags
|
|
|
|
def readRelocs(self):
|
|
relocs = {}
|
|
|
|
while True:
|
|
longs = self.readLong()
|
|
|
|
if not longs:
|
|
break
|
|
|
|
hunkRef = self.readLong()
|
|
offsets = [self.readLong() for i in range(longs)]
|
|
relocs[hunkRef] = offsets
|
|
|
|
return relocs
|
|
|
|
def readShortRelocs(self):
|
|
start = self.tell()
|
|
relocs = {}
|
|
|
|
while True:
|
|
words = self.readWord()
|
|
|
|
if not words:
|
|
break
|
|
|
|
hunkRef = self.readWord()
|
|
offsets = [self.readWord() for i in range(words)]
|
|
relocs[hunkRef] = offsets
|
|
|
|
if (self.tell() - start) & 3:
|
|
self.skip(2)
|
|
|
|
return relocs
|
|
|
|
def skip(self, n):
|
|
self.seek(n, os.SEEK_CUR)
|
|
|
|
def eof(self):
|
|
return self.tell() == self.size
|
|
|
|
|
|
HunkClassMap = {
|
|
'HUNK_END': HunkSep,
|
|
'HUNK_BREAK': HunkSep,
|
|
'HUNK_EXT': HunkExt,
|
|
'HUNK_SYMBOL': HunkSymbol,
|
|
'HUNK_HEADER': HunkHeader,
|
|
'HUNK_INDEX': HunkIndex,
|
|
'HUNK_NAME': HunkStr,
|
|
'HUNK_UNIT': HunkStr,
|
|
'HUNK_CODE': HunkBinary,
|
|
'HUNK_DATA': HunkBinary,
|
|
'HUNK_OVERLAY': HunkOverlay,
|
|
'HUNK_BSS': HunkBss,
|
|
'HUNK_LIB': HunkLib,
|
|
'HUNK_RELOC32': HunkReloc,
|
|
'HUNK_RELOC16': HunkReloc,
|
|
'HUNK_RELOC8': HunkReloc,
|
|
'HUNK_DREL32': HunkReloc,
|
|
'HUNK_DREL16': HunkReloc,
|
|
'HUNK_DREL8': HunkReloc,
|
|
'HUNK_DEBUG': HunkDebug
|
|
}
|
|
|
|
|
|
def HunkParser(path):
|
|
with HunkFile(path) as hf:
|
|
hunks = []
|
|
|
|
while not hf.eof():
|
|
with hf.rollback():
|
|
hunkId = hf.readLong()
|
|
|
|
type_ = Hunk.getType(hunkId)
|
|
|
|
if type_ is 'HUNK_HEADER':
|
|
hf.type = 'executable'
|
|
|
|
hunk = HunkClassMap.get(type_, None)
|
|
|
|
if not hunk:
|
|
raise NotImplementedError('%s not handled.' % type_)
|
|
|
|
try:
|
|
hunks.append(hunk.parse(hf))
|
|
except ValueError:
|
|
print 'Parse error at position 0x%x.' % hf.tell()
|
|
util.hexdump(hf.read())
|
|
|
|
return hunks
|