You've already forked ultrasm64-2
mirror of
https://github.com/HackerN64/ultrasm64-2.git
synced 2026-01-21 10:38:08 -08:00
939 lines
35 KiB
Python
939 lines
35 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# .mdebug section
|
|
#
|
|
|
|
"""
|
|
References:
|
|
https://www.cs.unibo.it/~solmi/teaching/arch_2002-2003/AssemblyLanguageProgDoc.pdf
|
|
https://web.archive.org/web/20010628021622/http://reality.sgi.com/davea/objectinfo.html
|
|
https://github.com/astrelsky/ghidra_mdebug
|
|
https://github.com/Rozelette/print-mdebug/blob/master/print_mdebug.py
|
|
https://opensource.apple.com/source/gcc_legacy/gcc_legacy-938/gcc/mips-tdump.c.auto.html
|
|
https://github.com/uhhpctools/openuh/blob/master/osprey-gcc-4.2.0/gcc/mips-tdump.c
|
|
https://github.com/bminor/binutils-gdb/blob/master/gdb/mdebugread.c
|
|
|
|
(stabs docs):
|
|
https://sourceware.org/gdb/current/onlinedocs/stabs.html
|
|
|
|
(ecoff docs):
|
|
https://web.archive.org/web/20160305114748/http://h41361.www4.hp.com/docs/base_doc/DOCUMENTATION/V50A_ACRO_SUP/OBJSPEC.PDF
|
|
https://chromium.googlesource.com/native_client/nacl-toolchain/+/refs/tags/gcc-4.4.3/binutils/gas/ecoff.c
|
|
https://kernel.googlesource.com/pub/scm/linux/kernel/git/hjl/binutils/+/hjl/secondary/include/coff/sym.h
|
|
https://kernel.googlesource.com/pub/scm/linux/kernel/git/hjl/binutils/+/hjl/secondary/include/coff/symconst.h
|
|
"""
|
|
|
|
from enum import IntEnum
|
|
import struct
|
|
|
|
class EcoffBt(IntEnum): # Basic Type
|
|
NIL = 0 #
|
|
ADR = 1 # pointer-sized integer type
|
|
CHAR = 2 # char
|
|
UCHAR = 3 # unsigned char
|
|
SHORT = 4 # short
|
|
USHORT = 5 # unsigned short
|
|
INT = 6 # int
|
|
UINT = 7 # unsigned int
|
|
LONG = 8 # long
|
|
ULONG = 9 # unsigned long
|
|
FLOAT = 10 # float
|
|
DOUBLE = 11 # double
|
|
STRUCT = 12 # struct
|
|
UNION = 13 # union
|
|
ENUM = 14 # enum
|
|
TYPEDEF = 15 # type definition
|
|
RANGE = 16 # subrange of int
|
|
SET = 17 # pascal set
|
|
COMPLEX = 18 # FORTRAN complex
|
|
DCOMPLEX = 19 # FORTRAN double com[plex
|
|
INDIRECT = 20 # forward or unnamed typedef
|
|
FIXEDDEC = 21 # Fixed point decimal
|
|
FLOATDEC = 22 # Floating point decimal
|
|
STRING = 23 # Varying length character string
|
|
BIT = 24 # Aligned bit tring
|
|
PICTURE = 25 # picture
|
|
VOID = 26 # void
|
|
LONGLONG = 27 # long long int
|
|
ULONGLONG = 28 # unsigned long long int
|
|
LONG64 = 30 #
|
|
ULONG64 = 31 #
|
|
LONGLONG64 = 32 #
|
|
ULONGLONG64 = 33 #
|
|
ADR64 = 34 #
|
|
INT64 = 35 #
|
|
UINT64 = 36 #
|
|
|
|
AGGREGATE = 63 # not a basic type
|
|
MAX = 64 #
|
|
|
|
class EcoffSc(IntEnum):
|
|
NIL = 0
|
|
TEXT = 1 # .text symbol
|
|
DATA = 2 # .data symbol
|
|
BSS = 3 # .bss symbol
|
|
REGISTER = 4 # value of symbol is register number
|
|
ABS = 5 # value of symbol is absolute
|
|
UNDEFINED = 6 # value of symbol is undefined
|
|
CDBLOCAL = 7 # variable value is in se->va.??
|
|
BITS = 8 # variable is a bit field
|
|
CDBSYSTEM = 9 # variable value is in cdb address space
|
|
REGIMAGE = 10 # register value is saved on stack
|
|
INFO = 11 # symbol contains debugger information
|
|
USERSTRUCT = 12 # address in struct user for current process
|
|
SDATA = 13 # load time only small data
|
|
SBSS = 14 # load time only small common
|
|
RDATA = 15 # load time only read-only data
|
|
VAR = 16 # var parameter (FORTRAN, Pascal)
|
|
COMMON = 17 # common variable
|
|
SCOMMON = 18 # small common
|
|
VARREGISTER = 19 # var parameter in a register
|
|
VARIANT = 20 # variant record
|
|
SUNDEFINED = 21 # small undefined (external) data
|
|
INIT = 22 # .init section symbol
|
|
BASEDVAR = 23 # FORTRAN or PL/1 ptr based var
|
|
XDATA = 24 # exception handling data
|
|
PDATA = 25 # procedure section
|
|
FINI = 26 # .fini section
|
|
RCONST = 27 # .rconst section
|
|
MAX = 32 #
|
|
|
|
class EcoffSt(IntEnum):
|
|
NIL = 0 #
|
|
GLOBAL = 1 # external symbol
|
|
STATIC = 2 # static symbol
|
|
PARAM = 3 # procedure argument
|
|
LOCAL = 4 # local variable
|
|
LABEL = 5 # label
|
|
PROC = 6 # procedure
|
|
BLOCK = 7 # beginning of block
|
|
END = 8 # end of something
|
|
MEMBER = 9 # member of struct/union/enum/..
|
|
TYPEDEF = 10 # type definition
|
|
FILE = 11 # filename
|
|
REGRELOC = 12 # register relocation
|
|
FORWARD = 13 # forwarding address
|
|
STATICPROC = 14 # load time only static procedures
|
|
# (CONSTANT and STAPARAM are in different orders between different sources...)
|
|
CONSTANT = 15 # constant
|
|
STAPARAM = 16 # FORTRAN static parameters
|
|
STRUCT = 26 # structure
|
|
UNION = 27 # union
|
|
ENUM = 28 # enum
|
|
INDIRECT = 34 #
|
|
|
|
class EcoffTq(IntEnum): # Type qualifier
|
|
NIL = 0 #
|
|
PTR = 1 # pointer
|
|
PROC = 2 # procedure
|
|
ARRAY = 3 # array
|
|
FAR = 4 # longer addressing
|
|
VOL = 5 # volatile
|
|
CONST = 6 # constant
|
|
MAX = 8 #
|
|
|
|
UNK7 = 7 # invalid
|
|
UNK9 = 9 # invalid
|
|
UNK10 = 10 # invalid
|
|
UNK11 = 11 # invalid
|
|
UNK12 = 12 # invalid
|
|
UNK13 = 13 # invalid
|
|
UNK14 = 14 # invalid
|
|
UNK = 15 # invalid
|
|
|
|
class EcoffLanguageCode(IntEnum):
|
|
C = 0
|
|
PASCAL = 1
|
|
FORTRAN = 2
|
|
ASM = 3
|
|
MACHINE = 4
|
|
NIL = 5
|
|
ADA = 6
|
|
PL1 = 7
|
|
COBOL = 8
|
|
STDC = 9
|
|
CPLUSPLUSV2 = 10
|
|
MAX = 11
|
|
|
|
def get_bitrange(value, start, length):
|
|
return (value >> start) & ((1 << length) - 1)
|
|
|
|
def sign_extend_16(value):
|
|
return (value & 0x7FFF) - (value & 0x8000)
|
|
|
|
def sign_extend_4(value):
|
|
return (value & 0x7) - (value & 0x8)
|
|
|
|
class EcoffLiner:
|
|
"""
|
|
ECOFF Line Numbers Mapping
|
|
|
|
typedef struct sLINER {
|
|
s32 count : 4;
|
|
s32 delta : 4;
|
|
} tLINER, *pLINER;
|
|
"""
|
|
|
|
def __init__(self, data) -> None:
|
|
self.count = get_bitrange(data[0], 0, 4) + 1
|
|
self.delta = get_bitrange(data[0], 4, 4)
|
|
|
|
if self.delta == 8:
|
|
self.is_extended = True
|
|
self.delta = sign_extend_16((data[1] << 8) | data[2])
|
|
self.data = data[:3]
|
|
else:
|
|
self.is_extended = False
|
|
self.delta = sign_extend_4(self.delta)
|
|
self.data = data[:1]
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffLiner =============
|
|
delta = {self.delta}
|
|
count = {self.count}
|
|
extended = {self.is_extended}"""
|
|
|
|
class EcoffTir:
|
|
"""
|
|
ECOFF Type Information Record
|
|
|
|
typedef struct {
|
|
#ifdef LITTLE_ENDIAN
|
|
u32 tq3 : 4;
|
|
u32 tq2 : 4;
|
|
u32 tq1 : 4; /* 6 type qualifiers - tqPtr, etc. */
|
|
u32 tq0 : 4;
|
|
/* ---- 16 bit boundary ---- */
|
|
u32 tq5 : 4;
|
|
u32 tq4 : 4;
|
|
u32 bt : 6; /* basic type */
|
|
u32 continued : 1; /* indicates additional TQ info in next AUX */
|
|
u32 fBitfield : 1; /* set if bit width is specified */
|
|
#else
|
|
u32 fBitfield : 1; /* set if bit width is specified */
|
|
u32 continued : 1; /* indicates additional TQ info in next AUX */
|
|
u32 bt : 6; /* basic type */
|
|
u32 tq4 : 4;
|
|
u32 tq5 : 4;
|
|
/* ---- 16 bit boundary ---- */
|
|
u32 tq0 : 4;
|
|
u32 tq1 : 4; /* 6 type qualifiers - tqPtr, etc. */
|
|
u32 tq2 : 4;
|
|
u32 tq3 : 4;
|
|
#endif
|
|
} TIR, *pTIR; // size = 4
|
|
"""
|
|
SIZE = 4
|
|
|
|
def __init__(self, data, endian) -> None:
|
|
if endian == 1:
|
|
self.tq3 = EcoffTq(get_bitrange(data, 0, 4))
|
|
self.tq2 = EcoffTq(get_bitrange(data, 4, 4))
|
|
self.tq1 = EcoffTq(get_bitrange(data, 8, 4))
|
|
self.tq0 = EcoffTq(get_bitrange(data, 12, 4))
|
|
self.tq5 = EcoffTq(get_bitrange(data, 16, 4))
|
|
self.tq4 = EcoffTq(get_bitrange(data, 20, 4))
|
|
self.bt = EcoffBt(get_bitrange(data, 24, 6))
|
|
self.continued = get_bitrange(data, 30, 1)
|
|
self.fBitfield = get_bitrange(data, 31, 1)
|
|
else:
|
|
self.fBitfield = get_bitrange(data, 0, 1)
|
|
self.continued = get_bitrange(data, 1, 1)
|
|
self.bt = EcoffBt(get_bitrange(data, 2, 6))
|
|
self.tq4 = EcoffTq(get_bitrange(data, 8, 4))
|
|
self.tq5 = EcoffTq(get_bitrange(data, 12, 4))
|
|
self.tq0 = EcoffTq(get_bitrange(data, 16, 4))
|
|
self.tq1 = EcoffTq(get_bitrange(data, 20, 4))
|
|
self.tq2 = EcoffTq(get_bitrange(data, 24, 4))
|
|
self.tq3 = EcoffTq(get_bitrange(data, 28, 4))
|
|
self.tqs = (self.tq0, self.tq1, self.tq2, self.tq3, self.tq4, self.tq5)
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffTIR ==============
|
|
fBitfield = {self.fBitfield}
|
|
continued = {self.continued}
|
|
bt = {self.bt.name}
|
|
tq4 = {self.tq4}
|
|
tq5 = {self.tq5}
|
|
tq0 = {self.tq0}
|
|
tq1 = {self.tq1}
|
|
tq2 = {self.tq2}
|
|
tq3 = {self.tq3}"""
|
|
|
|
class EcoffRNDXR:
|
|
"""
|
|
typedef struct {
|
|
#ifdef LITTLE_ENDIAN
|
|
u32 index : 20; /* index int sym/aux/iss tables */
|
|
u32 rfd : 12; /* index into the file indirect table */
|
|
#else
|
|
u32 rfd : 12; /* index into the file indirect table */
|
|
u32 index : 20; /* index int sym/aux/iss tables */
|
|
#endif
|
|
} RNDXR, *pRNDXR; // size = 4
|
|
"""
|
|
SIZE = 4
|
|
|
|
def __init__(self, data, endian) -> None:
|
|
if endian == 1:
|
|
self.index = get_bitrange(data, 0, 20)
|
|
self.rfd = get_bitrange(data, 20, 12)
|
|
else:
|
|
self.rfd = get_bitrange(data, 0, 12)
|
|
self.index = get_bitrange(data, 12, 20)
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffRNDXR ==============
|
|
index = {self.index}
|
|
rfd = {self.rfd}"""
|
|
|
|
class EcoffAux:
|
|
"""
|
|
typedef union __sgi_auxu_u {
|
|
TIR ti; /* type information record */
|
|
RNDXR rndx; /* relative index into symbol table */
|
|
long_i dnLow; /* low dimension of array */
|
|
long_i dnHigh; /* high dimension of array */
|
|
long_i isym; /* symbol table index (end of proc) */
|
|
long_i iss; /* index into string space (not used) */
|
|
long_i width; /* width for non-default sized struct fields */
|
|
long_i count; /* count of ranges for variant arm */
|
|
} AUXU, *pAUXU; // size = 4
|
|
"""
|
|
SIZE = 4
|
|
|
|
def __init__(self, fdr, data, endian) -> None:
|
|
data = struct.unpack((">" if endian == 1 else "<") + "I", data)[0]
|
|
self.ti = EcoffTir(data, endian)
|
|
self.rndx = EcoffRNDXR(data, endian)
|
|
self.dnLow = self.dnHigh = self.isym = self.iss = self.width = self.count = data
|
|
self.fdr = fdr
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffAux ==============
|
|
ti = {{
|
|
{self.ti}
|
|
}}
|
|
rndx = {{
|
|
{self.rndx}
|
|
}}
|
|
dnLow = {self.dnLow:04X}
|
|
dnHigh = {self.dnHigh:04X}
|
|
isym = {self.isym:04X}
|
|
iss = {self.iss:04X}
|
|
width = {self.width:04X}
|
|
count = {self.count:04X}"""
|
|
|
|
class EcoffSymr:
|
|
"""
|
|
ECOFF Local Symbol
|
|
|
|
typedef struct sSymr {
|
|
s32 iss; /* index into String Space of name */
|
|
s32 value; /* symbol value */
|
|
/* value can be an address, size or frame offset depending on symbol type */
|
|
EcoffSt st : 6; /* symbol type */
|
|
EcoffSc sc : 5; /* storage class - text, data, etc */
|
|
s32 _reserved : 1; /* reserved bit */
|
|
s32 index : 20; /* index into sym/aux table */
|
|
} tSymr, *pSymr; // size = 0xC
|
|
"""
|
|
SIZE = 0xC
|
|
|
|
def __init__(self, parent, idx, data):
|
|
self.idx = idx
|
|
self.parent = parent # can be either Fdr or Pdr
|
|
self.pdr = parent if type(parent) == EcoffPdr else None
|
|
self.fdr = self.pdr.parent if self.pdr is not None else parent
|
|
|
|
self.data = data[:EcoffSymr.SIZE]
|
|
|
|
self.iss, self.value, bits = struct.unpack(">III", self.data)
|
|
self.st = EcoffSt(get_bitrange(bits, 26, 6))
|
|
self.sc = EcoffSc(get_bitrange(bits, 21, 5))
|
|
self._reserved = get_bitrange(bits, 20, 1)
|
|
self.index = get_bitrange(bits, 0, 20)
|
|
|
|
self.name = self.fdr.read_string(self.iss)
|
|
self.type_name = None
|
|
|
|
self.c_repr = None
|
|
|
|
assert self._reserved == 0 # Sanity check
|
|
|
|
def link_syms(self):
|
|
if self.st == EcoffSt.END:
|
|
self.start_sym = self.fdr.symrs[self.index]
|
|
elif self.st in [EcoffSt.BLOCK, EcoffSt.FILE, EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]:
|
|
self.end_sym = self.fdr.symrs[self.index - 1]
|
|
elif self.st in [EcoffSt.PROC, EcoffSt.STATICPROC]:
|
|
aux = self.fdr.auxs[self.index]
|
|
self.end_sym = self.fdr.symrs[aux.isym - 1]
|
|
elif self.st in [EcoffSt.GLOBAL, EcoffSt.STATIC, EcoffSt.PARAM, EcoffSt.LOCAL, EcoffSt.MEMBER, EcoffSt.TYPEDEF, EcoffSt.FORWARD]:
|
|
pass
|
|
|
|
def late_init(self):
|
|
if self.st == EcoffSt.END:
|
|
"""
|
|
END symbols index the associated begin symbol
|
|
"""
|
|
self.start_sym = self.fdr.symrs[self.index]
|
|
if self.start_sym.st == EcoffSt.BLOCK:
|
|
self.c_repr = "}"
|
|
elif self.start_sym.st == EcoffSt.FILE:
|
|
self.c_repr = f"// end of file: \"{self.start_sym.name}\""
|
|
elif self.start_sym.st in [EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]:
|
|
self.c_repr = "}"
|
|
if len(self.start_sym.type_name) != 0:
|
|
self.c_repr += " " + self.start_sym.type_name
|
|
self.c_repr += f";"
|
|
elif self.st in [EcoffSt.BLOCK, EcoffSt.FILE, EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]:
|
|
"""
|
|
These symbols index the first symbol after their associated END symbol
|
|
"""
|
|
self.end_sym = self.fdr.symrs[self.index - 1]
|
|
if self.st == EcoffSt.BLOCK:
|
|
self.c_repr = "{"
|
|
elif self.st == EcoffSt.FILE:
|
|
self.c_repr = f"#line 1 \"{self.name}\""
|
|
elif self.st in [EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.ENUM]:
|
|
keyword = ""
|
|
self.type_name = ""
|
|
next_sym = self.fdr.symrs[self.index]
|
|
if next_sym.st == EcoffSt.TYPEDEF:
|
|
# possible typedef struct/union/enum
|
|
# TODO check this by ensuring the typedef symbol references this symbol
|
|
keyword += "typedef "
|
|
self.type_name = next_sym.name
|
|
|
|
if self.st == EcoffSt.UNION:
|
|
keyword += "union"
|
|
elif self.st == EcoffSt.ENUM:
|
|
keyword += "enum"
|
|
else:
|
|
keyword += "struct"
|
|
|
|
name = self.name
|
|
if len(name) != 0:
|
|
name = ' ' + name
|
|
|
|
self.c_repr = f"{keyword}{name} {{"
|
|
elif self.st in [EcoffSt.PROC, EcoffSt.STATICPROC]:
|
|
aux1 = self.fdr.auxs[self.index]
|
|
self.end_sym = self.fdr.symrs[aux1.isym - 1]
|
|
|
|
self.c_repr = ""
|
|
if self.st == EcoffSt.STATICPROC:
|
|
self.c_repr += "static "
|
|
|
|
self.return_type, _ = self.process_type_information(1)
|
|
self.c_repr += self.return_type
|
|
if len(self.return_type) != 0:
|
|
self.c_repr += " "
|
|
self.c_repr += self.name
|
|
self.c_repr += "()"
|
|
elif self.st in [EcoffSt.GLOBAL, EcoffSt.STATIC, EcoffSt.PARAM, EcoffSt.LOCAL, EcoffSt.MEMBER, EcoffSt.FORWARD]:
|
|
self.c_repr = ""
|
|
if self.st == EcoffSt.MEMBER:
|
|
# value of a stMember is the offset in bits
|
|
self.c_repr += f"/* 0x{self.value//8:X} */ "
|
|
|
|
type_str, bitwidth = self.process_type_information(0)
|
|
self.c_repr += type_str
|
|
if len(self.c_repr) != 0:
|
|
self.c_repr += " "
|
|
self.c_repr += f"{self.name}{f' : {bitwidth}' if bitwidth is not None else ''};"
|
|
elif self.st == EcoffSt.TYPEDEF:
|
|
# TODO the typedef may already be absorbed into a struct or similar, check before emitting
|
|
type_str, _ = self.process_type_information(0)
|
|
self.c_repr = f"typedef {type_str} {self.name};"
|
|
elif self.st == EcoffSt.LABEL:
|
|
self.c_repr = f"{self.name}:"
|
|
|
|
def process_type_information(self, ind):
|
|
c_bt_names = {
|
|
EcoffBt.NIL : None,
|
|
EcoffBt.ADR : None,
|
|
EcoffBt.CHAR : "signed char",
|
|
EcoffBt.UCHAR : "char",
|
|
EcoffBt.SHORT : "short",
|
|
EcoffBt.USHORT : "unsigned short",
|
|
EcoffBt.INT : "int",
|
|
EcoffBt.UINT : "unsigned int",
|
|
EcoffBt.LONG : "long",
|
|
EcoffBt.ULONG : "unsigned long",
|
|
EcoffBt.FLOAT : "float",
|
|
EcoffBt.DOUBLE : "double",
|
|
EcoffBt.STRUCT : "struct",
|
|
EcoffBt.UNION : "union",
|
|
EcoffBt.ENUM : "enum",
|
|
EcoffBt.TYPEDEF : "typedef",
|
|
EcoffBt.RANGE : None,
|
|
EcoffBt.SET : None,
|
|
EcoffBt.COMPLEX : "complex",
|
|
EcoffBt.DCOMPLEX : "double complex",
|
|
EcoffBt.INDIRECT : None,
|
|
EcoffBt.FIXEDDEC : None,
|
|
EcoffBt.FLOATDEC : None,
|
|
EcoffBt.STRING : "const char*",
|
|
EcoffBt.BIT : None,
|
|
EcoffBt.PICTURE : None,
|
|
EcoffBt.VOID : "void",
|
|
EcoffBt.LONGLONG : "long long",
|
|
EcoffBt.ULONGLONG : "unsigned long long",
|
|
EcoffBt.LONG64 : "long",
|
|
EcoffBt.ULONG64 : "unsigned long",
|
|
EcoffBt.LONGLONG64 : "long long",
|
|
EcoffBt.ULONGLONG64 : "unsigned long long",
|
|
EcoffBt.ADR64 : None,
|
|
EcoffBt.INT64 : None,
|
|
EcoffBt.UINT64 : None,
|
|
}
|
|
c_tq_str = {
|
|
EcoffTq.NIL : "",
|
|
EcoffTq.PTR : "*",
|
|
EcoffTq.PROC : "()",
|
|
EcoffTq.ARRAY : "[]",
|
|
EcoffTq.FAR : "/* FAR */",
|
|
EcoffTq.VOL : "volatile",
|
|
EcoffTq.CONST : "const",
|
|
}
|
|
|
|
if self.index == 0xFFFFF:
|
|
# no type info
|
|
return "", None
|
|
|
|
aux = self.fdr.auxs[self.index + ind]
|
|
ind += 1
|
|
type_str = ""
|
|
|
|
bit_width = None
|
|
if aux.ti.fBitfield == 1:
|
|
bit_width = self.fdr.auxs[self.index + ind].isym
|
|
ind += 1
|
|
|
|
if aux.ti.bt in [EcoffBt.STRUCT, EcoffBt.UNION, EcoffBt.ENUM, EcoffBt.TYPEDEF]:
|
|
type_ref_aux = self.fdr.auxs[self.index + ind]
|
|
ind += 1
|
|
|
|
fd_ref_idx = type_ref_aux.rndx.rfd
|
|
if fd_ref_idx == 4095:
|
|
fd_ref_idx = self.fdr.auxs[self.index + ind].isym
|
|
ind += 1
|
|
|
|
fdr_ref = self.fdr.parent.fdrs[fd_ref_idx]
|
|
sym_ref = fdr_ref.symrs[type_ref_aux.rndx.index]
|
|
# now we have the reference to the stStruct, stUnion, stEnum, or stTypeDef
|
|
type_str += f"{sym_ref.type_name if sym_ref.type_name is not None else sym_ref.name}"
|
|
else:
|
|
type_str += f"{c_bt_names[aux.ti.bt]}"
|
|
|
|
# TODO improve emitting qualified types
|
|
tqs = ""
|
|
for tq in aux.ti.tqs:
|
|
if tq == EcoffTq.NIL:
|
|
continue
|
|
if tq == EcoffTq.ARRAY:
|
|
ind += 2 # skips over some info such as the type of index (always int for C)
|
|
array_low_aux = self.fdr.auxs[self.index + ind]
|
|
array_high_aux = self.fdr.auxs[self.index + ind + 1]
|
|
stride_aux = self.fdr.auxs[self.index + ind + 2]
|
|
ind += 3
|
|
tqs += "["
|
|
if array_high_aux.dnHigh != 0xFFFFFFFF:
|
|
tqs += '%d' % (array_high_aux.dnHigh + 1)
|
|
tqs += "]"
|
|
else:
|
|
tqs += c_tq_str[tq]
|
|
tqs += " "
|
|
tqs = tqs.strip()
|
|
if len(tqs) != 0:
|
|
type_str += " " + tqs
|
|
|
|
return type_str, bit_width
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffSymr ============== {self.idx}
|
|
iss = 0x{self.iss:08X}
|
|
value = 0x{self.value:08X}
|
|
st = st{self.st.name}
|
|
sc = sc{self.sc.name}
|
|
_reserved = {self._reserved}
|
|
index = 0x{self.index:05X}
|
|
name = {self.name}"""
|
|
|
|
class EcoffPdr:
|
|
"""
|
|
ECOFF Procedure Descriptor
|
|
|
|
typedef struct sPDR {
|
|
s32 addr; /* memory address of start of procedure */
|
|
s32 isym; /* start of local symbol entries */
|
|
s32 iline; /* start of line number entries */
|
|
s32 regmask; /* save register mask */
|
|
s32 regoffset; /* save register offset */
|
|
s32 iopt; /* start of optimization symbol entries */
|
|
s32 fregmask; /* save floating point register mask */
|
|
s32 fregoffset; /* save floating point register offset */
|
|
s32 frameoffset; /* frame size */
|
|
u16 framereg; /* frame pointer register */
|
|
u16 pcreg; /* offset or reg of return pc */
|
|
s32 lnLow; /* lowest line in the procedure */
|
|
s32 lnHigh; /* highest line in the procedure */
|
|
s32 cbLineOffset; /* byte offset for this procedure from the fd base */
|
|
#ifdef 64_BIT
|
|
// TODO there's a bitfield in here
|
|
s32 gpPrologue; /* byte size of GP prologue */
|
|
s32 gpUsed; /* true if the procedure uses GP */
|
|
s32 regFrame; /* true if register frame procedure */
|
|
s32 prof; /* true if compiled with -pg */
|
|
s32 localOffset; /* offset of local variables from vfp */
|
|
#endif
|
|
} tPDR, *pPDR; // size = 0x34
|
|
"""
|
|
SIZE = 0x34
|
|
|
|
def __init__(self, fdr, data) -> None:
|
|
self.parent = fdr
|
|
self.data = data[:EcoffPdr.SIZE]
|
|
|
|
self.addr, self.isym, self.iline, self.regmask, \
|
|
self.regoffset, self.iopt, self.fregmask, self.fregoffset, \
|
|
self.frameoffset, self.framereg, self.pcreg, self.lnLow, \
|
|
self.lnHigh, self.cbLineOffset = struct.unpack(">IIIIIIIIIHHIII", self.data)
|
|
|
|
self.symrs = []
|
|
|
|
i = self.isym
|
|
symr = self.parent.symrs[i]
|
|
assert symr.st == EcoffSt.PROC or symr.st == EcoffSt.STATICPROC
|
|
# Inherit procedure name from procedure symbol
|
|
self.name = symr.name
|
|
|
|
self.symrs.append(symr)
|
|
while not (symr.st == EcoffSt.END and symr.name == self.symrs[0].name):
|
|
i += 1
|
|
symr = self.parent.symrs[i]
|
|
self.symrs.append(symr)
|
|
|
|
assert symr.st == EcoffSt.END and symr.sc == EcoffSc.TEXT
|
|
self.size = symr.value # value field of an stEND and scTEXT symbol is the procedure size
|
|
assert self.size % 4 == 0
|
|
|
|
# indexed by asm word offset from proc start
|
|
self.lines = []
|
|
|
|
# ilineMax = self.parent.parent.hdrr.ilineMax
|
|
# cbLine = self.parent.parent.hdrr.cbLine
|
|
# cbLineOffset = self.parent.parent.hdrr.cbLineOffset
|
|
# ilineBase = self.parent.ilineBase
|
|
# cline = self.parent.cline
|
|
# cbLineOffset = self.parent.cbLineOffset
|
|
# cbLine = self.parent.cbLine
|
|
# lnLow = self.lnLow
|
|
# lnHigh = self.lnHigh
|
|
# iline = self.iline
|
|
|
|
elf_data = self.parent.parent.parent.data
|
|
|
|
line_no = self.lnLow # first line in the procedure
|
|
line_data = self.parent.parent.hdrr.cbLineOffset + self.parent.cbLineOffset + self.cbLineOffset
|
|
# line_end = self.parent.parent.hdrr.cbLineOffset + self.parent.cbLineOffset + self.parent.cbLine
|
|
|
|
# print(self)
|
|
# print(f"{self.name} [{self.lnLow}:{self.lnHigh}]")
|
|
# print(self.size//4)
|
|
while len(self.lines) < self.size//4:
|
|
# assert line_data < line_end , "Overflow in line numbers table"
|
|
|
|
liner = EcoffLiner(elf_data[line_data:])
|
|
line_no += liner.delta
|
|
# if line_no < self.lnLow or line_no > self.lnHigh:
|
|
# break
|
|
|
|
# print(liner)
|
|
for i in range(liner.count):
|
|
# print(f"[{len(self.lines)}] {line_no}")
|
|
self.lines.append(line_no)
|
|
|
|
line_data += len(liner.data)
|
|
|
|
def lookup_sym(self, value, type=-1):
|
|
for sym in self.symrs:
|
|
if sym.value == value and (type == -1 or type == sym.st):
|
|
return sym
|
|
return None
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffPdr ===============
|
|
addr = 0x{self.addr:08X}
|
|
isym = 0x{self.isym:08X}
|
|
iline = 0x{self.iline:08X}
|
|
regmask = 0b{self.regmask:032b}
|
|
regoffset = 0x{self.regoffset:08X}
|
|
iopt = 0x{self.iopt:08X}
|
|
fregmask = 0b{self.fregmask:032b}
|
|
fregoffset = 0x{self.fregoffset:08X}
|
|
frameoffset = 0x{self.frameoffset:08X}
|
|
framereg = ${self.framereg}
|
|
pcreg = ${self.pcreg}
|
|
lnLow = {self.lnLow}
|
|
lnHigh = {self.lnHigh}
|
|
cbLineOffset = 0x{self.cbLineOffset:08X}
|
|
name = {self.name}"""
|
|
|
|
class EcoffFdr:
|
|
"""
|
|
ECOFF File Descriptor
|
|
|
|
typedef struct sFDR {
|
|
u32 adr; /* memory address of beginning of file */
|
|
s32 rss; /* file name (of source, if known) */
|
|
s32 issBase; /* file's string space */
|
|
s32 cbSs; /* number of bytes in the ss */
|
|
s32 isymBase; /* beginning of symbols */
|
|
s32 csym; /* count file's of symbols */
|
|
s32 ilineBase; /* file's line symbols */
|
|
s32 cline; /* count of file's line symbols */
|
|
s32 ioptBase; /* file's optimization entries */
|
|
s32 copt; /* count of file's optimization entries */
|
|
u16 ipdFirst; /* start of procedures for this file */
|
|
u16 cpd; /* count of procedures for this file */
|
|
s32 iauxBase; /* file's auxiliary entries */
|
|
s32 caux; /* count of file's auxiliary entries */
|
|
s32 rfdBase; /* index into the file indirect table */
|
|
s32 crfd; /* count file indirect entries */
|
|
EcoffLanguageCode lang : 5; /* language for this file */
|
|
u32 fMerge : 1; /* whether this file can be merged */
|
|
u32 fReadin : 1; /* true if it was read in (not just created) */
|
|
u32 fBigEndian : 1; /* true if AUXU's are big endian */
|
|
u32 glevel : 2; /* level this file was compiled with */
|
|
u32 _reserved : 20; /* reserved bits */
|
|
s32 cbLineOffset; /* byte offset from header for this file ln's */
|
|
s32 cbLine; /* size of lines for this file */
|
|
} tFDR, *pFDR; // size = 0x48
|
|
"""
|
|
SIZE = 0x48
|
|
|
|
@staticmethod
|
|
def from_binary(mdebug, i):
|
|
# Init
|
|
if 'init' not in EcoffFdr.from_binary.__dict__:
|
|
EcoffFdr.from_binary.cache = {}
|
|
EcoffFdr.from_binary.init = True
|
|
# Parent Init
|
|
if mdebug not in EcoffFdr.from_binary.cache:
|
|
EcoffFdr.from_binary.cache[mdebug] = {}
|
|
# Cache hit
|
|
if i in EcoffFdr.from_binary.cache[mdebug]:
|
|
return EcoffFdr.from_binary.cache[mdebug][i]
|
|
# Cache miss
|
|
cbFdOffset = mdebug.hdrr.cbFdOffset
|
|
elf_data = mdebug.parent.data
|
|
EcoffFdr.from_binary.cache[mdebug][i] = EcoffFdr(mdebug, elf_data[cbFdOffset+i*EcoffFdr.SIZE:cbFdOffset+(i+1)*EcoffFdr.SIZE])
|
|
return EcoffFdr.from_binary.cache[mdebug][i]
|
|
|
|
def __init__(self, mdebug, data) -> None:
|
|
self.parent = mdebug
|
|
self.data = data[:EcoffFdr.SIZE]
|
|
|
|
self.adr, self.rss, self.issBase, self.cbSs, \
|
|
self.isymBase, self.csym, self.ilineBase, self.cline, \
|
|
self.ioptBase, self.copt, self.ipdFirst, self.cpd, \
|
|
self.iauxBase, self.caux, self.rfdBase, self.crfd, \
|
|
bits, self.cbLineOffset, self.cbLine = struct.unpack(">IIIIIIIIIIHHIIIIIII", self.data)
|
|
|
|
self.lang = EcoffLanguageCode(get_bitrange(bits, 27, 5))
|
|
self.fMerge = get_bitrange(bits, 26, 1)
|
|
self.fReadin = get_bitrange(bits, 25, 1)
|
|
self.fBigEndian = get_bitrange(bits, 24, 1)
|
|
self.glevel = get_bitrange(bits, 22, 2)
|
|
self._reserved = get_bitrange(bits, 2, 20)
|
|
|
|
self.name = self.parent.read_string(self.issBase + self.rss)
|
|
|
|
# print(self)
|
|
|
|
hdrr = self.parent.hdrr
|
|
elf_data = self.parent.parent.data
|
|
|
|
# Aux Symbols
|
|
self.auxs = []
|
|
for i in range(self.caux):
|
|
i += self.iauxBase
|
|
assert i < hdrr.iauxMax , "Out of bounds in Auxiliary Symbol Table"
|
|
aux = EcoffAux(self, elf_data[hdrr.cbAuxOffset+i*EcoffAux.SIZE:][:EcoffAux.SIZE], self.fBigEndian)
|
|
self.auxs.append(aux)
|
|
|
|
# Symbols
|
|
self.symrs = []
|
|
for i in range(self.csym):
|
|
j = i + self.isymBase
|
|
assert j < hdrr.isymMax , "Out of bounds in Local Symbol Table"
|
|
symr = EcoffSymr(self, i, elf_data[hdrr.cbSymOffset+j*EcoffSymr.SIZE:hdrr.cbSymOffset+(j+1)*EcoffSymr.SIZE])
|
|
self.symrs.append(symr)
|
|
for symr in self.symrs:
|
|
symr.link_syms()
|
|
|
|
# PDRs
|
|
self.pdrs = []
|
|
for i in range(self.cpd):
|
|
i += self.ipdFirst
|
|
assert i < hdrr.ipdMax , "Out of bounds in Procedure Descriptor Table"
|
|
pdr = EcoffPdr(self, elf_data[hdrr.cbPdOffset+i*EcoffPdr.SIZE:hdrr.cbPdOffset+(i+1)*EcoffPdr.SIZE])
|
|
self.pdrs.append(pdr)
|
|
|
|
self.size = sum([pdr.size for pdr in self.pdrs])
|
|
|
|
def late_init(self):
|
|
for symr in self.symrs:
|
|
symr.late_init()
|
|
|
|
def pdr_forname(self, procedure_name):
|
|
for pdr in self.pdrs:
|
|
if pdr.name == procedure_name:
|
|
return pdr
|
|
return None
|
|
|
|
def pdr_foranyaddr(self, addr):
|
|
for pdr in self.pdrs:
|
|
if pdr.addr <= addr and pdr.addr + pdr.size > addr:
|
|
return pdr
|
|
return None
|
|
|
|
def pdr_foraddr(self, addr):
|
|
for pdr in self.pdrs:
|
|
if pdr.addr == addr:
|
|
return pdr
|
|
return None
|
|
|
|
def read_string(self, index):
|
|
return self.parent.read_string(self.issBase + index)
|
|
|
|
def c_str(self) -> str:
|
|
"""
|
|
C prettyprint file
|
|
"""
|
|
def print_symbol(symr):
|
|
return f"{symr.st.name} :: {symr.c_repr}"
|
|
|
|
indent = 0
|
|
out = f"File: {self.name}\n"
|
|
|
|
for symr in self.symrs:
|
|
if symr.st in [EcoffSt.END]:
|
|
indent -= 2
|
|
|
|
out += " " * indent
|
|
out += print_symbol(symr)
|
|
out += "\n"
|
|
|
|
if symr.st in [EcoffSt.FILE, EcoffSt.STRUCT, EcoffSt.UNION, EcoffSt.PROC, EcoffSt.STATICPROC, EcoffSt.BLOCK]:
|
|
indent += 2
|
|
|
|
return out
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffFdr ===============
|
|
adr = 0x{self.adr:08X}
|
|
rss = 0x{self.rss:08X}
|
|
issBase = 0x{self.issBase:08X}
|
|
cbSs = 0x{self.cbSs:08X}
|
|
isymBase = 0x{self.isymBase:08X}
|
|
csym = 0x{self.csym:08X}
|
|
ilineBase = 0x{self.ilineBase:08X}
|
|
cline = 0x{self.cline:08X}
|
|
ioptBase = 0x{self.ioptBase:08X}
|
|
copt = 0x{self.copt:08X}
|
|
ipdFirst = 0x{self.ipdFirst:08X}
|
|
cpd = 0x{self.cpd:08X}
|
|
iauxBase = 0x{self.iauxBase:08X}
|
|
caux = 0x{self.caux:08X}
|
|
rfdBase = 0x{self.rfdBase:08X}
|
|
crfd = 0x{self.crfd:08X}
|
|
lang = {self.lang.name}
|
|
fMerge = {bool(self.fMerge)}
|
|
fReadin = {bool(self.fReadin)}
|
|
fBigEndian = {bool(self.fBigEndian)}
|
|
glevel = {self.glevel}
|
|
_reserved = {self._reserved}
|
|
cbLineOffset = 0x{self.cbLineOffset:08X}
|
|
cbLine = 0x{self.cbLine:08X}
|
|
name = {self.name}"""
|
|
|
|
class EcoffHDRR:
|
|
"""
|
|
Symbolic Header
|
|
|
|
typedef struct sHDRR {
|
|
u16 magic; /* 0x7009 */
|
|
u16 vstamp; /* version stamp */
|
|
s32 ilineMax; /* number of line number entries */
|
|
s32 cbLine; /* number of bytes for line number entries */
|
|
s32 cbLineOffset; /* offset to start of line number entries */
|
|
s32 idnMax; /* max index into dense number table */
|
|
s32 cbDnOffset; /* offset to start dense number table */
|
|
s32 ipdMax; /* number of procedures */
|
|
s32 cbPdOffset; /* offset to procedure descriptor table */
|
|
s32 isymMax; /* number of local symbols */
|
|
s32 cbSymOffset; /* offset to start of local symbols */
|
|
s32 ioptMax; /* max index into optimization symbol entries */
|
|
s32 cbOptOffset; /* offset to optimization symbol entries */
|
|
s32 iauxMax; /* number of auxillary symbol entries */
|
|
s32 cbAuxOffset; /* offset to start of auxillary symbol entries */
|
|
s32 issMax; /* max index into local strings */
|
|
s32 cbSsOffset; /* offset to start of local strings */
|
|
s32 issExtMax; /* max index into external strings */
|
|
s32 cbSsExtOffset; /* offset to start of external strings */
|
|
s32 ifdMax; /* number of file descriptor entries */
|
|
s32 cbFdOffset; /* offset to file descriptor table */
|
|
s32 crfd; /* number of relative file descriptor entries */
|
|
s32 cbRfdOffset; /* offset to relative file descriptor table */
|
|
s32 iextMax; /* max index into external symbols */
|
|
s32 cbExtOffset; /* offset to start of external symbol entries */
|
|
} tHDRR, *pHDRR; // size = 0x60
|
|
"""
|
|
HDRR_MAGIC = 0x7009
|
|
SIZE = 0x60
|
|
|
|
def __init__(self, data) -> None:
|
|
self.data = data[:EcoffHDRR.SIZE]
|
|
|
|
self.magic, self.vstamp, self.ilineMax, self.cbLine, \
|
|
self.cbLineOffset, self.idnMax, self.cbDnOffset, self.ipdMax, \
|
|
self.cbPdOffset, self.isymMax, self.cbSymOffset, self.ioptMax, \
|
|
self.cbOptOffset, self.iauxMax, self.cbAuxOffset, self.issMax, \
|
|
self.cbSsOffset, self.issExtMax, self.cbSsExtOffset, self.ifdMax, \
|
|
self.cbFdOffset, self.crfd, self.cbRfdOffset, self.iextMax, \
|
|
self.cbExtOffset = struct.unpack(">HHIIIIIIIIIIIIIIIIIIIIIII", self.data)
|
|
|
|
assert self.magic == EcoffHDRR.HDRR_MAGIC , f"Symbolic Header magic value is incorrect. Got 0x{self.magic:04X}, expected 0x{EcoffHDRR.HDRR_MAGIC:04X}"
|
|
|
|
def __str__(self) -> str:
|
|
return f"""= EcoffHDRR ==============
|
|
magic = 0x{self.magic:04X}
|
|
vstamp = 0x{self.vstamp:04X}
|
|
ilineMax = 0x{self.ilineMax:08X}
|
|
cbLine = 0x{self.cbLine:08X}
|
|
cbLineOffset = 0x{self.cbLineOffset:08X}
|
|
idnMax = 0x{self.idnMax:08X}
|
|
cbDnOffset = 0x{self.cbDnOffset:08X}
|
|
ipdMax = 0x{self.ipdMax:08X}
|
|
cbPdOffset = 0x{self.cbPdOffset:08X}
|
|
isymMax = 0x{self.isymMax:08X}
|
|
cbSymOffset = 0x{self.cbSymOffset:08X}
|
|
ioptMax = 0x{self.ioptMax:08X}
|
|
cbOptOffset = 0x{self.cbOptOffset:08X}
|
|
iauxMax = 0x{self.iauxMax:08X}
|
|
cbAuxOffset = 0x{self.cbAuxOffset:08X}
|
|
issMax = 0x{self.issMax:08X}
|
|
cbSsOffset = 0x{self.cbSsOffset:08X}
|
|
issExtMax = 0x{self.issExtMax:08X}
|
|
cbSsExtOffset = 0x{self.cbSsExtOffset:08X}
|
|
ifdMax = 0x{self.ifdMax:08X}
|
|
cbFdOffset = 0x{self.cbFdOffset:08X}
|
|
crfd = 0x{self.crfd:08X}
|
|
cbRfdOffset = 0x{self.cbRfdOffset:08X}
|
|
iextMax = 0x{self.iextMax:08X}
|
|
cbExtOffset = 0x{self.cbExtOffset:08X}"""
|