diff options
Diffstat (limited to 'miasm2/jitter/jitcore.py')
| -rw-r--r-- | miasm2/jitter/jitcore.py | 189 |
1 files changed, 109 insertions, 80 deletions
diff --git a/miasm2/jitter/jitcore.py b/miasm2/jitter/jitcore.py index f2b1375d..fc5cf35e 100644 --- a/miasm2/jitter/jitcore.py +++ b/miasm2/jitter/jitcore.py @@ -16,66 +16,69 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # from hashlib import md5 +import warnings -from miasm2.core import asmblock +from miasm2.core.asmblock import disasmEngine, AsmBlockBad from miasm2.core.interval import interval from miasm2.core.utils import BoundedDict +from miasm2.expression.expression import LocKey from miasm2.jitter.csts import * - class JitCore(object): "JiT management. This is an abstract class" + # Jitted function's name + FUNCNAME = "block_entry" + jitted_block_delete_cb = None jitted_block_max_size = 10000 - def __init__(self, ir_arch, bs=None): + def __init__(self, ir_arch, bin_stream): """Initialise a JitCore instance. @ir_arch: ir instance for current architecture - @bs: bitstream + @bin_stream: bin_stream instance """ - + # Arch related self.ir_arch = ir_arch + self.ircfg = self.ir_arch.new_ircfg() self.arch_name = "%s%s" % (self.ir_arch.arch.name, self.ir_arch.attrib) - self.bs = bs - self.known_blocs = {} - self.lbl2jitbloc = BoundedDict(self.jitted_block_max_size, + + # Structures for block tracking + self.offset_to_jitted_func = BoundedDict(self.jitted_block_max_size, delete_cb=self.jitted_block_delete_cb) - self.lbl2bloc = {} + self.loc_key_to_block = {} + self.blocks_mem_interval = interval() + + # Logging & options self.log_mn = False self.log_regs = False self.log_newbloc = False - self.segm_to_do = set() - self.jitcount = 0 - self.addr2obj = {} - self.addr2objref = {} - self.blocs_mem_interval = interval() - self.disasm_cb = None - self.split_dis = set() self.options = {"jit_maxline": 50, # Maximum number of line jitted "max_exec_per_call": 0 # 0 means no limit } - self.mdis = asmblock.disasmEngine(ir_arch.arch, ir_arch.attrib, bs, - lines_wd=self.options["jit_maxline"], - symbol_pool=ir_arch.symbol_pool, - follow_call=False, - dontdis_retcall=False, - split_dis=self.split_dis, - dis_block_callback=self.disasm_cb) + # Disassembly Engine + self.split_dis = set() + self.mdis = disasmEngine( + ir_arch.arch, ir_arch.attrib, bin_stream, + lines_wd=self.options["jit_maxline"], + loc_db=ir_arch.loc_db, + follow_call=False, + dontdis_retcall=False, + split_dis=self.split_dis, + ) def set_options(self, **kwargs): "Set options relative to the backend" - self.options.update(kwargs) def clear_jitted_blocks(self): "Reset all jitted blocks" - self.lbl2jitbloc.clear() - self.lbl2bloc.clear() - self.blocs_mem_interval = interval() + self.offset_to_jitted_func.clear() + self.loc_key_to_block.clear() + self.blocks_mem_interval = interval() def add_disassembly_splits(self, *args): """The disassembly engine will stop on address in args if they @@ -90,7 +93,7 @@ class JitCore(object): "Initialise the Jitter" raise NotImplementedError("Abstract class") - def get_bloc_min_max(self, cur_block): + def set_block_min_max(self, cur_block): "Update cur_block to set min/max address" if cur_block.lines: @@ -98,94 +101,107 @@ class JitCore(object): cur_block.ad_max = cur_block.lines[-1].offset + cur_block.lines[-1].l else: # 1 byte block for unknown mnemonic - cur_block.ad_min = cur_block.label.offset - cur_block.ad_max = cur_block.label.offset+1 + offset = ir_arch.loc_db.get_location_offset(cur_block.loc_key) + cur_block.ad_min = offset + cur_block.ad_max = offset+1 - def add_bloc_to_mem_interval(self, vm, block): + def add_block_to_mem_interval(self, vm, block): "Update vm to include block addresses in its memory range" - - self.blocs_mem_interval += interval([(block.ad_min, block.ad_max - 1)]) + self.blocks_mem_interval += interval([(block.ad_min, block.ad_max - 1)]) vm.reset_code_bloc_pool() - for a, b in self.blocs_mem_interval: + for a, b in self.blocks_mem_interval: vm.add_code_bloc(a, b + 1) - def jitirblocs(self, label, irblocks): + def jit_irblocks(self, label, irblocks): """JiT a group of irblocks. @label: the label of the irblocks - @irblocks: a gorup of irblocks + @irblocks: a group of irblocks """ raise NotImplementedError("Abstract class") - def add_bloc(self, block): + def add_block(self, block): """Add a block to JiT and JiT it. @block: asm_bloc to add """ - - irblocks = self.ir_arch.add_block(block, gen_pc_updt = True) + irblocks = self.ir_arch.add_asmblock_to_ircfg(block, self.ircfg, gen_pc_updt = True) block.blocks = irblocks - self.jitirblocs(block.label, irblocks) + self.jit_irblocks(block.loc_key, irblocks) - def disbloc(self, addr, vm): + def disasm_and_jit_block(self, addr, vm): """Disassemble a new block and JiT it - @addr: address of the block to disassemble (AsmLabel or int) + @addr: address of the block to disassemble (LocKey or int) @vm: VmMngr instance """ # Get the block - if isinstance(addr, asmblock.AsmLabel): - addr = addr.offset + if isinstance(addr, LocKey): + addr = self.ir_arch.loc_db.get_location_offset(addr) + if addr is None: + raise RuntimeError("Unknown offset for LocKey") # Prepare disassembler self.mdis.lines_wd = self.options["jit_maxline"] - self.mdis.dis_block_callback = self.disasm_cb # Disassemble it - try: - cur_block = self.mdis.dis_block(addr) - except IOError: - # vm_exception_flag is set - label = self.ir_arch.symbol_pool.getby_offset_create(addr) - cur_block = asmblock.AsmBlockBad(label) - + cur_block = self.mdis.dis_block(addr) + if isinstance(cur_block, AsmBlockBad): + return cur_block # Logging if self.log_newbloc: - print cur_block + print cur_block.to_string(self.mdis.loc_db) # Update label -> block - self.lbl2bloc[cur_block.label] = cur_block + self.loc_key_to_block[cur_block.loc_key] = cur_block # Store min/max block address needed in jit automod code - self.get_bloc_min_max(cur_block) + self.set_block_min_max(cur_block) # JiT it - self.add_bloc(cur_block) + self.add_block(cur_block) # Update jitcode mem range - self.add_bloc_to_mem_interval(vm, cur_block) - - def runbloc(self, cpu, lbl, breakpoints): - """Run the block starting at lbl. + self.add_block_to_mem_interval(vm, cur_block) + return cur_block + + def run_at(self, cpu, offset, stop_offsets): + """Run from the starting address @offset. + Execution will stop if: + - max_exec_per_call option is reached + - a new, yet unknown, block is reached after the execution of block at + address @offset + - an address in @stop_offsets is reached @cpu: JitCpu instance - @lbl: target label + @offset: starting address (int) + @stop_offsets: set of address on which the jitter must stop """ - if lbl is None: - lbl = getattr(cpu, self.ir_arch.pc.name) + if offset is None: + offset = getattr(cpu, self.ir_arch.pc.name) - if not lbl in self.lbl2jitbloc: + if offset not in self.offset_to_jitted_func: # Need to JiT the block - self.disbloc(lbl, cpu.vmmngr) + cur_block = self.disasm_and_jit_block(offset, cpu.vmmngr) + if isinstance(cur_block, AsmBlockBad): + errno = cur_block.errno + if errno == AsmBlockBad.ERROR_IO: + cpu.vmmngr.set_exception(EXCEPT_ACCESS_VIOL) + elif errno == AsmBlockBad.ERROR_CANNOT_DISASM: + cpu.set_exception(EXCEPT_UNK_MNEMO) + else: + raise RuntimeError("Unhandled disasm result %r" % errno) + return offset # Run the block and update cpu/vmmngr state - return self.exec_wrapper(lbl, cpu, self.lbl2jitbloc.data, breakpoints, + return self.exec_wrapper(offset, cpu, self.offset_to_jitted_func.data, + stop_offsets, self.options["max_exec_per_call"]) - def blocs2memrange(self, blocks): + def blocks_to_memrange(self, blocks): """Return an interval instance standing for blocks addresses - @blocks: list of asm_bloc instances + @blocks: list of AsmBlock instances """ mem_range = interval() @@ -204,10 +220,10 @@ class JitCore(object): vm.reset_code_bloc_pool() # Add blocks in the pool - for start, stop in self.blocs_mem_interval: + for start, stop in self.blocks_mem_interval: vm.add_code_bloc(start, stop + 1) - def del_bloc_in_range(self, ad1, ad2): + def del_block_in_range(self, ad1, ad2): """Find and remove jitted block in range [ad1, ad2]. Return the list of block removed. @ad1: First address @@ -216,7 +232,7 @@ class JitCore(object): # Find concerned blocks modified_blocks = set() - for block in self.lbl2bloc.values(): + for block in self.loc_key_to_block.values(): if not block.lines: continue if block.ad_max <= ad1 or block.ad_min >= ad2: @@ -227,26 +243,28 @@ class JitCore(object): modified_blocks.add(block) # Generate interval to delete - del_interval = self.blocs2memrange(modified_blocks) + del_interval = self.blocks_to_memrange(modified_blocks) # Remove interval from monitored interval list - self.blocs_mem_interval -= del_interval + self.blocks_mem_interval -= del_interval # Remove modified blocks for block in modified_blocks: try: for irblock in block.blocks: # Remove offset -> jitted block link - if irblock.label.offset in self.lbl2jitbloc: - del(self.lbl2jitbloc[irblock.label.offset]) + offset = self.ir_arch.loc_db.get_location_offset(irblock.loc_key) + if offset in self.offset_to_jitted_func: + del(self.offset_to_jitted_func[offset]) except AttributeError: # The block has never been translated in IR - if block.label.offset in self.lbl2jitbloc: - del(self.lbl2jitbloc[block.label.offset]) + offset = self.ir_arch.loc_db.get_location_offset(block.loc_key) + if offset in self.offset_to_jitted_func: + del(self.offset_to_jitted_func[offset]) # Remove label -> block link - del(self.lbl2bloc[block.label]) + del(self.loc_key_to_block[block.loc_key]) return modified_blocks @@ -256,7 +274,7 @@ class JitCore(object): @mem_range: list of start/stop addresses """ for addr_start, addr_stop in mem_range: - self.del_bloc_in_range(addr_start, addr_stop) + self.del_block_in_range(addr_start, addr_stop) self.__updt_jitcode_mem_range(vm) vm.reset_memory_access() @@ -275,9 +293,20 @@ class JitCore(object): @block: asmblock """ block_raw = "".join(line.b for line in block.lines) - block_hash = md5("%X_%s_%s_%s_%s" % (block.label.offset, + offset = self.ir_arch.loc_db.get_location_offset(block.loc_key) + block_hash = md5("%X_%s_%s_%s_%s" % (offset, self.arch_name, self.log_mn, self.log_regs, block_raw)).hexdigest() return block_hash + + @property + def disasm_cb(self): + warnings.warn("Deprecated API: use .mdis.dis_block_callback") + return self.mdis.dis_block_callback + + @disasm_cb.setter + def disasm_cb(self, value): + warnings.warn("Deprecated API: use .mdis.dis_block_callback") + self.mdis.dis_block_callback = value |