def __init__(self, snapshot, ctl_parser, config=None, final=False, defb_size=8, defb_mod=1, zfill=False, defm_width=66, asm_hex=False, asm_lower=False): ctl_parser.apply_asm_data_directives(snapshot) self.disassembler = Disassembler(snapshot, defb_size, defb_mod, zfill, defm_width, asm_hex, asm_lower) self.ctl_parser = ctl_parser if asm_hex: if asm_lower: self.address_fmt = '{0:04x}' else: self.address_fmt = '{0:04X}' else: self.address_fmt = '{0}' self.entry_map = {} self.config = config or {} self.build(final)
def _get_code_blocks(snapshot, start, end, fname): if os.path.isdir(fname): raise SkoolKitError('{0} is a directory'.format(fname)) try: size = os.path.getsize(fname) except OSError as e: if e.errno == 2: raise SkoolKitError('{0}: file not found'.format(fname)) raise SkoolKitError('Failed to get size of {}: {}'.format( fname, e.strerror)) if size == 8192: # Assume this is a Z80 map file sys.stderr.write('Reading {0}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) address = start & 65528 for b in data[start // 8:end // 8 + 1]: for i in range(8): if b & 1 and start <= address < end: addresses.append(address) b >>= 1 address += 1 elif size == 65536: # Assume this is a SpecEmu map file sys.stderr.write('Reading {}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) for address in range(start, end): if data[address] & 1: addresses.append(address) else: sys.stderr.write('Reading {0}: '.format(fname)) sys.stderr.flush() with open_file(fname) as f: addresses = _get_addresses(f, fname, size, start, end) sys.stderr.write('\n') code_blocks = [] disassembler = Disassembler(snapshot) for address in addresses: size = disassembler.disassemble(address, address + 1)[0].size() if code_blocks and address <= sum(code_blocks[-1]): if address == sum(code_blocks[-1]): code_blocks[-1][1] += size else: code_blocks.append([address, size]) return code_blocks
def __init__(self, snapshot, sftfile, zfill=False, asm_hex=False, asm_lower=False): self.snapshot = snapshot self.disassembler = Disassembler(snapshot, zfill=zfill, asm_hex=asm_hex, asm_lower=asm_lower) self.sftfile = sftfile self.address_fmt = get_address_format(asm_hex, asm_lower) self.stack = [] self.disassemble = True
def _generate_ctls_with_code_map(snapshot, start, end, code_map): # (1) Use the code map to create an initial set of 'c' ctls, and mark all # unexecuted blocks as 'U' (unknown) # (2) Where a 'c' block doesn't end with a RET/JP/JR, extend it up to the # next RET/JP/JR in the following 'U' blocks, or up to the next 'c' # block # (3) Mark entry points in 'U' blocks that are CALLed or JPed to from 'c' # blocks with 'c' # (4) Split 'c' blocks on RET/JP/JR # (5) Scan the disassembly for pairs of adjacent blocks where the start # address of the second block is JRed or JPed to from the first block, # and join such pairs # (6) Examine the remaining 'U' blocks for text # (7) Mark data blocks of all zeroes with 's' # (1) Mark all executed blocks as 'c' and unexecuted blocks as 'U' # (unknown) ctls = {start: 'U', end: 'i'} for address, length in _get_code_blocks(snapshot, start, end, code_map): ctls[address] = 'c' if address + length < end: ctls[address + length] = 'U' # (2) Where a 'c' block doesn't end with a RET/JP/JR, extend it up to the # next RET/JP/JR in the following 'U' blocks, or up to the next 'c' block disassembler = Disassembler(snapshot) while 1: done = True for ctl, b_start, b_end in _get_blocks(ctls): if ctl == 'c': if _is_terminal_instruction( disassembler.disassemble(b_start, b_end)[-1]): continue if _find_terminal_instruction(disassembler, ctls, b_end, end) < end: done = False break if done: break # (3) Mark entry points in 'U' blocks that are CALLed or JPed to from 'c' # blocks with 'c' ctl_parser = CtlParser(ctls) disassembly = Disassembly(snapshot, ctl_parser) while 1: disassembly.build(True) done = True for entry in disassembly.entries: if entry.ctl == 'U': for instruction in entry.instructions: for referrer in instruction.referrers: if ctls[referrer.address] == 'c': ctls[instruction.address] = 'c' if entry.next: e_end = entry.next.address else: e_end = 65536 _find_terminal_instruction(disassembler, ctls, instruction.address, e_end, entry.ctl) disassembly.remove_entry(entry.address) done = False break if not done: break if not done: break if done: break # (4) Split 'c' blocks on RET/JP/JR for ctl, b_address, b_end in _get_blocks(ctls): if ctl == 'c': next_address = _find_terminal_instruction(disassembler, ctls, b_address, b_end, 'c') if next_address < b_end: disassembly.remove_entry(b_address) while next_address < b_end: next_address = _find_terminal_instruction( disassembler, ctls, next_address, b_end, 'c') # (5) Scan the disassembly for pairs of adjacent blocks where the start # address of the second block is JRed or JPed to from the first block, and # join such pairs while 1: disassembly.build() done = True for entry in disassembly.entries[:-1]: if entry.ctl == 'c': for instruction in entry.instructions: operation = instruction.operation if operation[:2] in ('JR', 'JP') and operation[-5:] == str( entry.next.address): del ctls[entry.next.address] disassembly.remove_entry(entry.address) disassembly.remove_entry(entry.next.address) done = False break if done: break # (6) Examine the 'U' blocks for text/data for ctl, b_start, b_end in _get_blocks(ctls): if ctl == 'U': ctls[b_start] = 'b' for t_start, t_end in _get_text_blocks(snapshot, b_start, b_end): ctls[t_start] = 't' if t_end < b_end: ctls[t_end] = 'b' # (7) Mark data blocks of all zeroes with 's' for ctl, b_start, b_end in _get_blocks(ctls): if ctl == 'b': z_end = b_start while z_end < b_end and snapshot[z_end] == 0: z_end += 1 if z_end > b_start: ctls[b_start] = 's' if z_end < b_end: ctls[z_end] = 'b' return ctls