2016-10-04 00:16:22 -06:00
|
|
|
from Stream import *
|
|
|
|
|
import os
|
2017-05-28 17:17:25 -06:00
|
|
|
import struct
|
2016-10-04 00:16:22 -06:00
|
|
|
|
|
|
|
|
def list_as_hex(list):
|
|
|
|
|
return [("0x%08X" % value) for value in list]
|
|
|
|
|
|
|
|
|
|
def extend_sign_bit(value, numBits):
|
|
|
|
|
signBit = 1 << (numBits - 1)
|
|
|
|
|
return (value & (signBit - 1)) - (value & signBit)
|
|
|
|
|
|
|
|
|
|
# Relocation type definitions
|
|
|
|
|
R_INVALID = -1
|
|
|
|
|
R_PPC_NONE = 0
|
|
|
|
|
R_PPC_ADDR32 = 1
|
|
|
|
|
R_PPC_ADDR24 = 2
|
|
|
|
|
R_PPC_ADDR16 = 3
|
|
|
|
|
R_PPC_ADDR16_LO = 4
|
|
|
|
|
R_PPC_ADDR16_HI = 5
|
|
|
|
|
R_PPC_ADDR16_HA = 6
|
|
|
|
|
R_PPC_ADDR14 = 7
|
|
|
|
|
R_PPC_ADDR14_BRTAKEN = 8
|
|
|
|
|
R_PPC_ADDR14_BRNTAKEN = 9
|
|
|
|
|
R_PPC_REL24 = 10
|
|
|
|
|
R_PPC_REL14 = 11
|
|
|
|
|
R_DOLPHIN_NOP = 201
|
|
|
|
|
R_DOLPHIN_SECTION = 202
|
|
|
|
|
R_DOLPHIN_END = 203
|
|
|
|
|
|
|
|
|
|
class DolFile:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.valid = False
|
|
|
|
|
|
|
|
|
|
def read(self, filename):
|
|
|
|
|
self.filename = filename
|
|
|
|
|
stream = InputStream(filename, BIG_ENDIAN)
|
|
|
|
|
|
|
|
|
|
# Read DOL Header
|
|
|
|
|
self.textSecOffsets = []
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
self.textSecOffsets.append( stream.read_long() )
|
|
|
|
|
|
|
|
|
|
self.dataSecOffsets = []
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
self.dataSecOffsets.append( stream.read_long() )
|
|
|
|
|
|
|
|
|
|
self.textSecAddresses = []
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
self.textSecAddresses.append( stream.read_long() )
|
|
|
|
|
|
|
|
|
|
self.dataSecAddresses = []
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
self.dataSecAddresses.append( stream.read_long() )
|
|
|
|
|
|
|
|
|
|
self.textSecSizes = []
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
self.textSecSizes.append( stream.read_long() )
|
|
|
|
|
|
|
|
|
|
self.dataSecSizes = []
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
self.dataSecSizes.append( stream.read_long() )
|
|
|
|
|
|
|
|
|
|
self.bssAddress = stream.read_long()
|
|
|
|
|
self.bssSize = stream.read_long()
|
|
|
|
|
self.entryPoint = stream.read_long()
|
|
|
|
|
self.valid = True
|
|
|
|
|
|
|
|
|
|
# Fetch game build number
|
|
|
|
|
buildStrOffset = stream.data.find(b'Build v') + 7
|
|
|
|
|
buildStrEnd = stream.data.find(b' ', buildStrOffset)
|
|
|
|
|
self.buildVersionStr = stream.data[buildStrOffset:buildStrEnd].decode()
|
|
|
|
|
self.buildVersion = float(self.buildVersionStr)
|
|
|
|
|
|
2019-04-04 00:10:08 -07:00
|
|
|
def is_patched(self, bootstrapSectionIdx):
|
|
|
|
|
return self.textSecOffsets[bootstrapSectionIdx] != 0
|
2017-05-28 17:17:25 -06:00
|
|
|
|
|
|
|
|
def patch_rel24(self, buffer, offset, basePatchAddress, targetAddress):
|
|
|
|
|
instruction = struct.unpack(">I", buffer[offset:offset+4])[0] & ~0x3FFFFFC
|
|
|
|
|
relAddr = targetAddress - (basePatchAddress + offset)
|
|
|
|
|
instruction |= (relAddr & 0x3FFFFFC)
|
|
|
|
|
buffer[offset:offset+4] = struct.pack(">I", instruction)
|
|
|
|
|
|
|
|
|
|
def patch_hi_lo(self, buffer, offsetHi, offsetLo, targetAddress):
|
|
|
|
|
instrHi = struct.unpack(">I", buffer[offsetHi:offsetHi+4])[0] & 0xFFFF0000
|
|
|
|
|
instrLo = struct.unpack(">I", buffer[offsetLo:offsetLo+4])[0] & 0xFFFF0000
|
|
|
|
|
addrHi = (targetAddress & 0xFFFF0000)
|
|
|
|
|
addrLo = (targetAddress & 0x0000FFFF)
|
|
|
|
|
|
|
|
|
|
if addrLo >= 0x8000:
|
|
|
|
|
addrHi += 0x10000
|
|
|
|
|
|
|
|
|
|
instrHi |= (addrHi >> 16)
|
|
|
|
|
instrLo |= addrLo
|
|
|
|
|
|
|
|
|
|
buffer[offsetHi:offsetHi+4] = struct.pack(">I", instrHi)
|
|
|
|
|
buffer[offsetLo:offsetLo+4] = struct.pack(">I", instrLo)
|
2016-10-04 00:16:22 -06:00
|
|
|
|
2019-04-04 00:10:08 -07:00
|
|
|
def apply_patch(self, bootstrapHookTarget, bootstrapSectionIdx, bootstrapAddr, outputModuleName, patchFile, outFile):
|
2017-05-28 17:17:25 -06:00
|
|
|
# Find the addresses of functions we need to call from the patch code
|
|
|
|
|
addrDVDOpen = self.get_symbol("DVDOpen")
|
|
|
|
|
addrDVDReadAsyncPrio = self.get_symbol("DVDReadAsyncPrio")
|
|
|
|
|
addrDVDClose = self.get_symbol("DVDClose")
|
|
|
|
|
addrOSLink = self.get_symbol("OSLink")
|
|
|
|
|
addrNew = self.get_symbol("__nwa__FUlPCcPCc")
|
2019-04-04 00:10:08 -07:00
|
|
|
addrHook = self.get_symbol(bootstrapHookTarget)
|
2017-05-28 17:17:25 -06:00
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
if addrDVDOpen == None: failed.append("DVDOpen")
|
|
|
|
|
if addrDVDReadAsyncPrio == None: failed.append("DVDReadAsyncPrio")
|
|
|
|
|
if addrDVDClose == None: failed.append("DVDClose")
|
|
|
|
|
if addrOSLink == None: failed.append("OSLink")
|
|
|
|
|
if addrNew == None: failed.append("__nwa__FUlPCcPCc")
|
2019-04-04 00:10:08 -07:00
|
|
|
if addrHook == None: failed.append(bootstrapHookTarget)
|
2017-05-28 17:17:25 -06:00
|
|
|
|
|
|
|
|
if len(failed) > 0:
|
2019-04-04 00:10:08 -07:00
|
|
|
print("Failed to apply DOL bootstrap patch! The following symbols are missing: %s" % ", ".join(failed))
|
2017-05-28 17:17:25 -06:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Read patch data into memory
|
2016-10-04 00:16:22 -06:00
|
|
|
assert(self.valid)
|
|
|
|
|
patchFile = open(patchFile, "rb")
|
|
|
|
|
patchData = bytearray( patchFile.read() )
|
|
|
|
|
amtToPad = ((len(patchData) + 31) & ~31) - len(patchData)
|
|
|
|
|
patchData.extend(bytearray(amtToPad))
|
|
|
|
|
patchFile.close()
|
|
|
|
|
|
2017-05-28 17:17:25 -06:00
|
|
|
# Pick address to write the patch code to
|
2019-04-04 00:10:08 -07:00
|
|
|
address = bootstrapAddr & ~31
|
2017-05-28 17:17:25 -06:00
|
|
|
print("Adding patch to 0x%08X" % address)
|
|
|
|
|
|
|
|
|
|
# Relocate addresses in the patch code to account for the address it's placed at in memory
|
2019-04-04 00:10:08 -07:00
|
|
|
self.patch_hi_lo(patchData, 0x04, 0x08, address + 0xD0)
|
|
|
|
|
self.patch_hi_lo(patchData, 0x20, 0x24, address + 0xE0)
|
|
|
|
|
self.patch_hi_lo(patchData, 0x40, 0x44, address + 0xD1)
|
2017-05-28 17:17:25 -06:00
|
|
|
self.patch_hi_lo(patchData, 0x64, 0x68, address)
|
2019-04-04 00:10:08 -07:00
|
|
|
self.patch_hi_lo(patchData, 0x74, 0x78, address + 0xD0)
|
|
|
|
|
self.patch_hi_lo(patchData, 0x90, 0x94, address + 0xD9)
|
2017-05-28 17:17:25 -06:00
|
|
|
self.patch_rel24(patchData, 0x2C, address, addrDVDOpen)
|
|
|
|
|
self.patch_rel24(patchData, 0x4C, address, addrNew)
|
|
|
|
|
self.patch_rel24(patchData, 0x70, address, addrDVDReadAsyncPrio)
|
|
|
|
|
self.patch_rel24(patchData, 0x88, address, addrDVDClose)
|
|
|
|
|
self.patch_rel24(patchData, 0x9C, address, addrNew)
|
|
|
|
|
self.patch_rel24(patchData, 0xAC, address, addrOSLink)
|
2019-04-04 00:10:08 -07:00
|
|
|
self.patch_rel24(patchData, 0xCC, address, addrHook)
|
|
|
|
|
|
|
|
|
|
# Place rel filename in the buffer at the end of the patch and remove any unused space
|
|
|
|
|
relFilename = outputModuleName + ".rel" + '\0'
|
|
|
|
|
patchData[0xE0:] = relFilename.encode()
|
2017-05-28 17:17:25 -06:00
|
|
|
|
|
|
|
|
# Update DOL header for write
|
2019-04-04 00:10:08 -07:00
|
|
|
patchOffset = 0
|
|
|
|
|
|
|
|
|
|
for i in range(bootstrapSectionIdx-1, -1, -1):
|
|
|
|
|
if self.textSecOffsets[i] > 0 and self.textSecOffsets[i] > patchOffset:
|
|
|
|
|
patchOffset = self.textSecOffsets[i] + self.textSecSizes[i]
|
|
|
|
|
|
|
|
|
|
patchOffset = ((patchOffset + 31) & ~31)
|
|
|
|
|
patchSize = ((len(patchData) + 31) & ~31)
|
|
|
|
|
padSize = patchSize - len(patchData)
|
|
|
|
|
self.textSecOffsets[bootstrapSectionIdx] = patchOffset
|
|
|
|
|
self.textSecSizes[bootstrapSectionIdx] = patchSize
|
|
|
|
|
self.textSecAddresses[bootstrapSectionIdx] = address
|
2016-10-04 00:16:22 -06:00
|
|
|
|
|
|
|
|
srcStream = InputStream(self.filename, BIG_ENDIAN)
|
|
|
|
|
outStream = OutputStream(BIG_ENDIAN)
|
|
|
|
|
|
|
|
|
|
# Write DOL header
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
offset = self.textSecOffsets[textSecIdx]
|
|
|
|
|
|
2019-04-04 00:10:08 -07:00
|
|
|
if textSecIdx > bootstrapSectionIdx and offset != 0:
|
|
|
|
|
offset += patchSize
|
2016-10-04 00:16:22 -06:00
|
|
|
|
|
|
|
|
outStream.write_long(offset)
|
|
|
|
|
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
offset = self.dataSecOffsets[dataSecIdx]
|
|
|
|
|
|
|
|
|
|
if offset != 0:
|
2019-04-04 00:10:08 -07:00
|
|
|
offset += patchSize
|
2016-10-04 00:16:22 -06:00
|
|
|
|
|
|
|
|
outStream.write_long(offset)
|
|
|
|
|
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
outStream.write_long(self.textSecAddresses[textSecIdx])
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
outStream.write_long(self.dataSecAddresses[dataSecIdx])
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
outStream.write_long(self.textSecSizes[textSecIdx])
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
outStream.write_long(self.dataSecSizes[dataSecIdx])
|
|
|
|
|
|
|
|
|
|
outStream.write_long(self.bssAddress)
|
|
|
|
|
outStream.write_long(self.bssSize)
|
|
|
|
|
outStream.write_long(self.entryPoint)
|
|
|
|
|
|
|
|
|
|
for padIdx in range(0, 7):
|
|
|
|
|
outStream.write_long(0)
|
|
|
|
|
|
|
|
|
|
# Write section data
|
2019-04-04 00:10:08 -07:00
|
|
|
foundAnyCallers = False
|
|
|
|
|
foundMultipleCallers = False
|
|
|
|
|
|
2016-10-04 00:16:22 -06:00
|
|
|
for textSecIdx in range(0, 7):
|
2019-04-04 00:10:08 -07:00
|
|
|
if textSecIdx == bootstrapSectionIdx:
|
2016-10-04 00:16:22 -06:00
|
|
|
outStream.write_bytes(patchData)
|
2019-04-04 00:10:08 -07:00
|
|
|
for i in range(0, padSize):
|
|
|
|
|
outStream.write_byte(0)
|
2016-10-04 00:16:22 -06:00
|
|
|
else:
|
|
|
|
|
srcStream.goto(self.textSecOffsets[textSecIdx])
|
|
|
|
|
data = srcStream.read_bytes(self.textSecSizes[textSecIdx])
|
2017-05-28 17:17:25 -06:00
|
|
|
|
2019-04-04 00:10:08 -07:00
|
|
|
if textSecIdx <= 1:
|
|
|
|
|
# Look for instructions calling the target function so we can
|
|
|
|
|
# patch it to call our bootstrap function instead.
|
|
|
|
|
patchedSection = False
|
|
|
|
|
baseAddr = self.textSecAddresses[textSecIdx]
|
|
|
|
|
buffer = bytearray(data)
|
|
|
|
|
|
|
|
|
|
for offset in range(0, len(buffer), 4):
|
|
|
|
|
instr = struct.unpack(">I", buffer[offset:offset+4])[0]
|
|
|
|
|
opcode = (instr >> 26) & 0x3F
|
|
|
|
|
|
|
|
|
|
if opcode is 18:
|
|
|
|
|
LI = instr & 0x3FFFFFC
|
|
|
|
|
AA = True if ((instr & 0x2) != 0) else False
|
|
|
|
|
branchAddr = (baseAddr + offset + LI) if not AA else LI
|
|
|
|
|
|
|
|
|
|
if branchAddr == addrHook:
|
|
|
|
|
if foundAnyCallers:
|
|
|
|
|
foundMultipleCallers = True
|
|
|
|
|
else:
|
|
|
|
|
# The bootstrap function starts at offset 0x10 within the patch data
|
|
|
|
|
self.patch_rel24(buffer, offset, baseAddr, address + 0x10)
|
|
|
|
|
foundAnyCallers = True
|
|
|
|
|
patchedSection = True
|
|
|
|
|
|
|
|
|
|
if patchedSection:
|
|
|
|
|
data = bytes(buffer)
|
|
|
|
|
|
2016-10-04 00:16:22 -06:00
|
|
|
outStream.write_bytes(data)
|
|
|
|
|
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
srcStream.goto(self.dataSecOffsets[dataSecIdx])
|
|
|
|
|
data = srcStream.read_bytes(self.dataSecSizes[dataSecIdx])
|
|
|
|
|
outStream.write_bytes(data)
|
|
|
|
|
|
2019-04-04 00:10:08 -07:00
|
|
|
# Check if there was a problem with the caller patching
|
|
|
|
|
if not foundAnyCallers:
|
|
|
|
|
print("Patch failed; no callers of bootstrap hook found.")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
elif foundMultipleCallers:
|
|
|
|
|
print("Warning: Multiple callers of bootstrap hook were found. Only the first will be patched.");
|
|
|
|
|
|
2017-05-28 17:17:25 -06:00
|
|
|
# Save
|
2016-10-04 00:16:22 -06:00
|
|
|
outStream.save_file(outFile)
|
2017-05-28 17:17:25 -06:00
|
|
|
return True
|
2016-10-04 00:16:22 -06:00
|
|
|
|
|
|
|
|
def load_symbols(self, symbolDir):
|
|
|
|
|
# Load symbols
|
|
|
|
|
symbolFileName = "%s\\v%s.lst" % (symbolDir, self.buildVersionStr)
|
|
|
|
|
if not os.path.isfile(symbolFileName):
|
|
|
|
|
print("Failed to find a symbol list file for the provided build of the game (v%s)" % self.buildVersionStr)
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
symbolFile = open("%s\\v%s.lst" % (symbolDir, self.buildVersionStr))
|
|
|
|
|
self.symbols = {}
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
line = symbolFile.readline()
|
|
|
|
|
if not line: break
|
|
|
|
|
|
|
|
|
|
split = line.strip().split(" ")
|
|
|
|
|
self.symbols[split[1]] = int(split[0], 0)
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def generate_patches(self, origSymName, newSymName):
|
|
|
|
|
# Read all instructions in all text sections and find branches to the given address.
|
|
|
|
|
# Only unconditional branch instructions supported for now. More later.
|
|
|
|
|
# Note: newSymbolName should be unmangled.
|
|
|
|
|
PPC_OPCODE_bx = 18
|
|
|
|
|
|
|
|
|
|
out = []
|
|
|
|
|
|
|
|
|
|
# Find the address of the symbol.
|
|
|
|
|
if origSymName in self.symbols:
|
|
|
|
|
origSymAddress = self.symbols[origSymName]
|
|
|
|
|
|
|
|
|
|
# Check for potential unmangled name, which can happen if extern "C" is used (line in SDK libraries)
|
|
|
|
|
else:
|
|
|
|
|
funcNameEnd = origSymName.find("__F")
|
|
|
|
|
if funcNameEnd != -1:
|
|
|
|
|
unmangledName = origSymName[0:funcNameEnd]
|
|
|
|
|
|
|
|
|
|
if unmangledName in self.symbols:
|
|
|
|
|
origSymAddress = self.symbols[unmangledName]
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
print("*** Warning: Failed to resolve symbol '%s'. There will be no relocations generated for it. ***" % origSymName)
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
stream = InputStream(self.filename)
|
|
|
|
|
origSymAddress = self.symbols[origSymName]
|
|
|
|
|
|
|
|
|
|
for textSecIdx in range(0, 7):
|
|
|
|
|
# Grab offset/address, check if valid
|
|
|
|
|
secOffset = self.textSecOffsets[textSecIdx]
|
|
|
|
|
if secOffset is 0: continue
|
|
|
|
|
|
|
|
|
|
secAddress = self.textSecAddresses[textSecIdx]
|
|
|
|
|
secSize = self.textSecSizes[textSecIdx]
|
|
|
|
|
secEnd = secOffset + secSize
|
|
|
|
|
stream.goto(secOffset)
|
|
|
|
|
|
|
|
|
|
# Parse all instructions in this code block
|
|
|
|
|
while stream.tell() < secEnd and not stream.eof():
|
|
|
|
|
address = secAddress + (stream.tell() - secOffset)
|
|
|
|
|
instruction = stream.read_long()
|
|
|
|
|
opcode = (instruction >> 26) & 0x3F
|
|
|
|
|
|
|
|
|
|
# bx
|
|
|
|
|
if opcode == PPC_OPCODE_bx:
|
|
|
|
|
LI = extend_sign_bit(instruction & 0x3FFFFFC, 24)
|
|
|
|
|
AA = (instruction >> 1) & 0x1
|
|
|
|
|
target = LI
|
|
|
|
|
if AA is 0: target = target + address
|
|
|
|
|
|
|
|
|
|
if target == origSymAddress:
|
|
|
|
|
patch = {}
|
|
|
|
|
patch['address'] = address
|
|
|
|
|
patch['type'] = R_PPC_REL24
|
|
|
|
|
patch['symbol'] = newSymName
|
|
|
|
|
out.append(patch)
|
|
|
|
|
|
|
|
|
|
for dataSecIdx in range(0, 11):
|
|
|
|
|
# Grab offset/address, check if valid
|
|
|
|
|
secOffset = self.dataSecOffsets[dataSecIdx]
|
|
|
|
|
if secOffset is 0: continue
|
|
|
|
|
|
|
|
|
|
secAddress = self.dataSecAddresses[dataSecIdx]
|
|
|
|
|
secSize = self.dataSecSizes[dataSecIdx]
|
|
|
|
|
secEnd = secOffset + secSize
|
|
|
|
|
stream.goto(secOffset)
|
|
|
|
|
|
|
|
|
|
# Look for 32-bit address references
|
|
|
|
|
while stream.tell() < secEnd and not stream.eof():
|
|
|
|
|
address = secAddress + (stream.tell() - secOffset)
|
|
|
|
|
value = stream.read_long()
|
|
|
|
|
|
|
|
|
|
if value == origSymAddress:
|
|
|
|
|
patch = {}
|
|
|
|
|
patch['address'] = address
|
|
|
|
|
patch['type'] = R_PPC_ADDR32
|
|
|
|
|
patch['symbol'] = newSymName
|
|
|
|
|
out.append(patch)
|
|
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
def get_symbol(self, symName):
|
|
|
|
|
if symName in self.symbols:
|
|
|
|
|
return self.symbols[symName]
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def get_section_index(self, address):
|
|
|
|
|
allSecAddresses = self.textSecAddresses + self.dataSecAddresses
|
|
|
|
|
allSecSizes = self.textSecSizes + self.dataSecSizes
|
|
|
|
|
|
|
|
|
|
for secIdx in range(0, 18):
|
|
|
|
|
secBegin = allSecAddresses[secIdx]
|
|
|
|
|
secEnd = secBegin + allSecSizes[secIdx]
|
|
|
|
|
if address >= secBegin and address < secEnd:
|
|
|
|
|
return secIdx
|
|
|
|
|
|
|
|
|
|
# If we're at this point then the address is invalid
|
2017-05-27 21:20:18 -06:00
|
|
|
assert(False)
|
2016-10-04 00:16:22 -06:00
|
|
|
return -1
|
|
|
|
|
|
|
|
|
|
def print_header_info(self):
|
|
|
|
|
print("Text Section Offsets: %s" % list_as_hex(self.textSecOffsets))
|
|
|
|
|
print("Data Section Offsets: %s" % list_as_hex(self.dataSecOffsets))
|
|
|
|
|
print("Text Section Addresses: %s" % list_as_hex(self.textSecAddresses))
|
|
|
|
|
print("Data Section Addresses: %s" % list_as_hex(self.dataSecAddresses))
|
|
|
|
|
print("BSS Address: 0x%08X" % self.bssAddress)
|
|
|
|
|
print("BSS Size: 0x%08X" % self.bssSize)
|
|
|
|
|
print("Entry Point: 0x%08X" % self.entryPoint)
|