def ir2C(ir_arch, irbloc, lbl_done, gen_exception_code=False, log_mn=False, log_regs=False): out = [] # print "TRANS" # print irbloc out.append(["%s:" % irbloc.label.name]) #out.append(['printf("%s:\n");' % irbloc.label.name]) assert len(irbloc.irs) == len(irbloc.lines) for l, exprs in zip(irbloc.lines, irbloc.irs): if l.offset not in lbl_done: e = set_pc(ir_arch, l.offset & mask_int) s1 = "%s" % TranslatorC.from_expr(patch_c_id(ir_arch.arch, e)) s1 += ';\n Resolve_dst(BlockDst, 0x%X, 0)' % (l.offset & mask_int) out.append([pre_instr_test_exception % (s1)]) lbl_done.add(l.offset) if log_regs: out.append([r'dump_gpregs(vmcpu);']) if log_mn: out.append(['printf("%.8X %s\\n");' % (l.offset, str(l))]) # print l # gen pc update post_instr = "" c_code, post_instr, _ = Expr2C(ir_arch, l, exprs, gen_exception_code) out.append(c_code + post_instr) out.append([goto_local_code]) return out
def gen_irdst(ir_arch, e): out = [] if isinstance(e, m2_expr.ExprCond): dst_cond_c = TranslatorC.from_expr(patch_c_id(ir_arch.arch, e.cond)) out.append("if (%s)" % dst_cond_c) out.append(' %s;' % (gen_resolve_dst_simple(ir_arch, e.src1))) out.append("else") out.append(' %s;' % (gen_resolve_dst_simple(ir_arch, e.src2))) else: out.append('%s;' % (gen_resolve_dst_simple(ir_arch, e))) return out
def Expr2C(ir_arch, l, exprs, gen_exception_code=False): id_to_update = [] out = ["// %s" % (l)] out_pc = [] dst_dict = {} src_mem = {} prefect_index = {8: 0, 16: 0, 32: 0, 64: 0} new_expr = [] e = set_pc(ir_arch, l.offset & mask_int) #out.append("%s;" % patch_c_id(ir_arch.arch, e))) pc_is_dst = False fetch_mem = False set_exception_flags = False for e in exprs: assert isinstance(e, m2_expr.ExprAff) assert not isinstance(e.dst, m2_expr.ExprOp) if isinstance(e.dst, m2_expr.ExprId): if not e.dst in dst_dict: dst_dict[e.dst] = [] dst_dict[e.dst].append(e) else: new_expr.append(e) # test exception flags ops = m2_expr.get_expr_ops(e) if set(['umod', 'udiv']).intersection(ops): set_exception_flags = True if e.dst == exception_flags: set_exception_flags = True # TODO XXX test function whose set exception_flags # search mem lookup for generate mem read prefetch rs = e.src.get_r(mem_read=True) for r in rs: if (not isinstance(r, m2_expr.ExprMem)) or r in src_mem: continue fetch_mem = True index = prefect_index[r.size] prefect_index[r.size] += 1 pfmem = prefetch_id_size[r.size][index] src_mem[r] = pfmem for dst, exs in dst_dict.items(): if len(exs) == 1: new_expr += exs continue exs = [expr_simp(x) for x in exs] log_to_c_h.debug('warning: detected multi dst to same id') log_to_c_h.debug('\t'.join([str(x) for x in exs])) new_expr += exs out_mem = [] # first, generate mem prefetch mem_k = src_mem.keys() mem_k.sort() for k in mem_k: str_src = TranslatorC.from_expr(patch_c_id(ir_arch.arch, k)) str_dst = TranslatorC.from_expr(patch_c_id(ir_arch.arch, src_mem[k])) out.append('%s = %s;' % (str_dst, str_src)) src_w_len = {} for k, v in src_mem.items(): src_w_len[k] = v for e in new_expr: src, dst = e.src, e.dst # reload src using prefetch src = src.replace_expr(src_w_len) if dst is ir_arch.IRDst: out += gen_irdst(ir_arch, src) continue str_src = TranslatorC.from_expr(patch_c_id(ir_arch.arch, src)) str_dst = TranslatorC.from_expr(patch_c_id(ir_arch.arch, dst)) if isinstance(dst, m2_expr.ExprId): id_to_update.append(dst) str_dst = patch_c_new_id(ir_arch.arch, dst) if dst in ir_arch.arch.regs.regs_flt_expr: # dont mask float affectation out.append('%s = (%s);' % (str_dst, str_src)) else: out.append('%s = (%s)&0x%X;' % (str_dst, str_src, my_size_mask[src.size])) elif isinstance(dst, m2_expr.ExprMem): fetch_mem = True str_dst = str_dst.replace('MEM_LOOKUP', 'MEM_WRITE') out_mem.append('%s, %s);' % (str_dst[:-1], str_src)) if e.dst == ir_arch.arch.pc[ir_arch.attrib]: pc_is_dst = True out_pc += ["return;"] # if len(id_to_update) != len(set(id_to_update)): # raise ValueError('Not implemented: multi dst to same id!', str([str(x) # for x in exprs])) out += out_mem if gen_exception_code: if fetch_mem: e = set_pc(ir_arch, l.offset & mask_int) s1 = "%s" % TranslatorC.from_expr(patch_c_id(ir_arch.arch, e)) s1 += ';\n Resolve_dst(BlockDst, 0x%X, 0)' % (l.offset & mask_int) out.append(code_exception_fetch_mem_at_instr_noautomod % s1) if set_exception_flags: e = set_pc(ir_arch, l.offset & mask_int) s1 = "%s" % TranslatorC.from_expr(patch_c_id(ir_arch.arch, e)) s1 += ';\n Resolve_dst(BlockDst, 0x%X, 0)' % (l.offset & mask_int) out.append(code_exception_at_instr_noautomod % s1) for i in id_to_update: if i is ir_arch.IRDst: continue out.append( '%s = %s;' % (patch_c_id(ir_arch.arch, i), patch_c_new_id(ir_arch.arch, i))) post_instr = [] # test stop exec #### if gen_exception_code: if set_exception_flags: if pc_is_dst: post_instr.append("if (vm_mngr->exception_flags) { " + "/*pc = 0x%X; */return; }" % (l.offset)) else: e = set_pc(ir_arch, l.offset & mask_int) s1 = "%s" % TranslatorC.from_expr(patch_c_id(ir_arch.arch, e)) s1 += ';\n Resolve_dst(BlockDst, 0x%X, 0)' % (l.offset & mask_int) e = set_pc(ir_arch, (l.offset + l.l) & mask_int) s2 = "%s" % TranslatorC.from_expr(patch_c_id(ir_arch.arch, e)) s2 += ';\n Resolve_dst(BlockDst, 0x%X, 0)' % ( (l.offset + l.l) & mask_int) post_instr.append(code_exception_post_instr_noautomod % (s1, s2)) if fetch_mem: if l.additional_info.except_on_instr: offset = l.offset else: offset = l.offset + l.l e = set_pc(ir_arch, offset & mask_int) s1 = "%s" % TranslatorC.from_expr(patch_c_id(ir_arch.arch, e)) s1 += ';\n Resolve_dst(BlockDst, 0x%X, 0)' % (offset & mask_int) post_instr.append(code_exception_fetch_mem_post_instr_noautomod % (s1)) # pc manip after all modifications return out, post_instr, post_instr + out_pc
def gen_resolve_other(ir_arch, e): return 'Resolve_dst(BlockDst, %s, 0)' % (TranslatorC.from_expr( patch_c_id(ir_arch.arch, e)))
class CGen(object): """ Helper to generate C code for a given AsmBlock """ """ Translate native assembly block to C """ CODE_EXCEPTION_MEM_AT_INSTR = r""" // except fetch mem at instr noauto if ((VM_exception_flag & ~EXCEPT_CODE_AUTOMOD) & EXCEPT_DO_NOT_UPDATE_PC) { %s = %s; BlockDst->address = %s; return JIT_RET_EXCEPTION; } """ CODE_EXCEPTION_AT_INSTR = r""" if (CPU_exception_flag_at_instr) { %s = %s; BlockDst->address = %s; return JIT_RET_EXCEPTION; } """ CODE_RETURN_EXCEPTION = r""" return JIT_RET_EXCEPTION; """ CODE_RETURN_NO_EXCEPTION = r""" %s: %s = %s; BlockDst->address = %s; return JIT_RET_NO_EXCEPTION; """ CODE_CPU_EXCEPTION_POST_INSTR = r""" if (CPU_exception_flag) { %s = DST_value; BlockDst->address = DST_value; return JIT_RET_EXCEPTION; } """ CODE_VM_EXCEPTION_POST_INSTR = r""" check_memory_breakpoint(&(jitcpu->pyvm->vm_mngr)); check_invalid_code_blocs(&(jitcpu->pyvm->vm_mngr)); if (VM_exception_flag) { %s = DST_value; BlockDst->address = DST_value; return JIT_RET_EXCEPTION; } """ CODE_INIT = r""" int DST_case; unsigned long long DST_value; vm_cpu_t* mycpu = (vm_cpu_t*)jitcpu->cpu; goto %s; """ CODE_BAD_BLOCK = r""" // Unknown mnemonic CPU_exception_flag = EXCEPT_UNK_MNEMO; """ + CODE_RETURN_EXCEPTION def __init__(self, ir_arch): self.ir_arch = ir_arch self.PC = self.ir_arch.pc self.translator = TranslatorC(self.ir_arch.loc_db) self.init_arch_C() def init_arch_C(self): """Iinitialize jitter internals""" self.id_to_c_id = {} for reg in self.ir_arch.arch.regs.all_regs_ids: self.id_to_c_id[reg] = ExprId('mycpu->%s' % reg, reg.size) self.C_PC = self.id_to_c(self.PC) def dst_to_c(self, src): """Translate Expr @src into C code""" if not isinstance(src, Expr): src = ExprInt(src, self.PC.size) return self.id_to_c(src) def patch_c_id(self, expr): """Replace ExprId in @expr with corresponding C variables""" return expr.replace_expr(self.id_to_c_id) def id_to_c(self, expr): """Translate Expr @expr into corresponding C code""" return self.translator.from_expr(self.patch_c_id(expr)) def add_label_index(self, dst2index, loc_key): """Insert @lbl to the dictionnary @dst2index with a uniq value @dst2index: LocKey -> uniq value @loc_key: LocKey istance""" if loc_key not in dst2index: dst2index[loc_key] = len(dst2index) def assignblk_to_irbloc(self, instr, assignblk): """ Ensure IRDst is always set in the head @assignblk of the @instr @instr: an instruction instance @assignblk: Assignblk instance """ new_assignblk = dict(assignblk) if self.ir_arch.IRDst not in assignblk: offset = instr.offset + instr.l loc_key = self.ir_arch.loc_db.get_or_create_offset_location(offset) dst = ExprLoc(loc_key, self.ir_arch.IRDst.size) new_assignblk[self.ir_arch.IRDst] = dst irs = [AssignBlock(new_assignblk, instr)] return IRBlock(self.ir_arch.get_loc_key_for_instr(instr), irs) def block2assignblks(self, block): """ Return the list of irblocks for a native @block @block: AsmBlock """ irblocks_list = [] for instr in block.lines: assignblk_head, assignblks_extra = self.ir_arch.instr2ir(instr) # Keep result in ordered list as first element is the assignblk head # The remainings order is not really important irblock_head = self.assignblk_to_irbloc(instr, assignblk_head) irblocks = [irblock_head] + assignblks_extra for irblock in irblocks: assert irblock.dst is not None irblocks_list.append(irblocks) return irblocks_list def add_local_var(self, dst_var, dst_index, expr): """ Add local varaible used to store temporay result @dst_var: dictionnary of Expr -> local_var_expr @dst_index : dictionnary of size -> local var count @expr: Expression source """ size = expr.size if size < 8: size = 8 if size not in dst_index: raise RuntimeError("Unsupported operand size %s", size) var_num = dst_index[size] dst = ExprId("var_%.2d_%.2d" % (size, var_num), size) dst_index[size] += 1 dst_var[expr] = dst return dst def get_mem_prefetch(self, assignblk): """ Generate temporary variables used to fetch memory used in the @assignblk Return a dictionnary: ExprMem -> temporary variable @assignblk: AssignBlock instance """ mem_index = {8: 0, 16: 0, 32: 0, 64: 0, 128: 0} mem_var = {} # Prefetch memory read for expr in assignblk.get_r(mem_read=True): if not isinstance(expr, ExprMem): continue var_num = mem_index[expr.size] mem_index[expr.size] += 1 var = ExprId("prefetch_%.2d_%.2d" % (expr.size, var_num), expr.size) mem_var[expr] = var # Generate memory prefetch return mem_var def gen_c_assignments(self, assignblk): """ Return C informations used to generate the C code of the @assignblk @assignblk: an AssignBlock instance """ c_var = [] c_main = [] c_mem = [] c_updt = [] c_prefetch = [] dst_index = {8: 0, 16: 0, 32: 0, 64: 0, 128: 0} dst_var = {} prefetchers = self.get_mem_prefetch(assignblk) for expr, prefetcher in sorted(prefetchers.iteritems()): str_src = self.id_to_c(expr) str_dst = self.id_to_c(prefetcher) c_prefetch.append('%s = %s;' % (str_dst, str_src)) for var in prefetchers.itervalues(): c_var.append("uint%d_t %s;" % (var.size, var)) for dst, src in sorted(assignblk.iteritems()): src = src.replace_expr(prefetchers) if dst == self.ir_arch.IRDst: pass elif isinstance(dst, ExprId): new_dst = self.add_local_var(dst_var, dst_index, dst) if dst in self.ir_arch.arch.regs.regs_flt_expr: # Dont mask float affectation c_main.append('%s = (%s);' % (self.id_to_c(new_dst), self.id_to_c(src))) else: c_main.append('%s = (%s)&%s;' % (self.id_to_c(new_dst), self.id_to_c(src), SIZE_TO_MASK[src.size])) elif isinstance(dst, ExprMem): ptr = dst.arg.replace_expr(prefetchers) new_dst = ExprMem(ptr, dst.size) str_dst = self.id_to_c(new_dst).replace( 'MEM_LOOKUP', 'MEM_WRITE') c_mem.append('%s, %s);' % (str_dst[:-1], self.id_to_c(src))) else: raise ValueError("Unknown dst") for dst, new_dst in dst_var.iteritems(): if dst == self.ir_arch.IRDst: continue c_updt.append('%s = %s;' % (self.id_to_c(dst), self.id_to_c(new_dst))) c_var.append("uint%d_t %s;" % (new_dst.size, new_dst)) return c_prefetch, c_var, c_main, c_mem, c_updt def gen_check_memory_exception(self, address): """Generate C code to check memory exceptions @address: address of the faulty instruction""" dst = self.dst_to_c(address) return (self.CODE_EXCEPTION_MEM_AT_INSTR % (self.C_PC, dst, dst)).split('\n') def gen_check_cpu_exception(self, address): """Generate C code to check cpu exceptions @address: address of the faulty instruction""" dst = self.dst_to_c(address) return (self.CODE_EXCEPTION_AT_INSTR % (self.C_PC, dst, dst)).split('\n') def traverse_expr_dst(self, expr, dst2index): """ Generate the index of the destination label for the @expr @dst2index: dictionnary to link label to its index """ if isinstance(expr, ExprCond): cond = self.id_to_c(expr.cond) src1, src1b = self.traverse_expr_dst(expr.src1, dst2index) src2, src2b = self.traverse_expr_dst(expr.src2, dst2index) return ("((%s)?(%s):(%s))" % (cond, src1, src2), "((%s)?(%s):(%s))" % (cond, src1b, src2b)) if isinstance(expr, ExprInt): offset = int(expr) loc_key = self.ir_arch.loc_db.get_or_create_offset_location(offset) self.add_label_index(dst2index, loc_key) return ("%s" % dst2index[loc_key], hex(offset)) if expr.is_loc(): loc_key = expr.loc_key offset = self.ir_arch.loc_db.get_location_offset(expr.loc_key) if offset is not None: self.add_label_index(dst2index, loc_key) return ("%s" % dst2index[loc_key], hex(offset)) self.add_label_index(dst2index, loc_key) return ("%s" % dst2index[loc_key], "0") dst2index[expr] = -1 return ("-1", self.id_to_c(expr)) def gen_assignblk_dst(self, dst): """Generate C code to handle instruction destination @dst: instruction destination Expr""" dst2index = {} (ret, retb) = self.traverse_expr_dst(dst, dst2index) ret = "DST_case = %s;" % ret retb = "DST_value = %s;" % retb return ['// %s' % dst2index, '%s' % ret, '%s' % retb], dst2index def gen_post_instr_checks(self, attrib): """Generate C code for handling potential exceptions @attrib: Attributes instance""" out = [] if attrib.mem_read | attrib.mem_write: out += (self.CODE_VM_EXCEPTION_POST_INSTR % (self.C_PC)).split('\n') if attrib.set_exception: out += (self.CODE_CPU_EXCEPTION_POST_INSTR % (self.C_PC)).split('\n') if attrib.mem_read | attrib.mem_write: out.append("reset_memory_access(&(jitcpu->pyvm->vm_mngr));") return out def gen_pre_code(self, instr_attrib): """Callback to generate code BEFORE the instruction execution @instr_attrib: Attributes instance""" out = [] if instr_attrib.log_mn: out.append('printf("%.8X %s\\n");' % (instr_attrib.instr.offset, instr_attrib.instr.to_string(self.ir_arch.loc_db))) return out def gen_post_code(self, attrib): """Callback to generate code AFTER the instruction execution @attrib: Attributes instance""" out = [] if attrib.log_regs: out.append('dump_gpregs(jitcpu->cpu);') return out def gen_goto_code(self, attrib, instr_offsets, dst): """Generate C code for a potential destination @dst @attrib: instruction Attributes @instr_offsets: instructions offsets list @dst: potential instruction destination""" out = [] if isinstance(dst, Expr): out += self.gen_post_code(attrib) out.append('BlockDst->address = DST_value;') out += self.gen_post_instr_checks(attrib) out.append('\t\treturn JIT_RET_NO_EXCEPTION;') return out assert isinstance(dst, LocKey) offset = self.ir_arch.loc_db.get_location_offset(dst) if offset is None: # Generate goto for local labels return ['goto %s;' % dst] if (offset > attrib.instr.offset and offset in instr_offsets): # Only generate goto for next instructions. # (consecutive instructions) out += self.gen_post_code(attrib) out += self.gen_post_instr_checks(attrib) out.append('goto %s;' % dst) else: out += self.gen_post_code(attrib) out.append('BlockDst->address = DST_value;') out += self.gen_post_instr_checks(attrib) out.append('\t\treturn JIT_RET_NO_EXCEPTION;') return out def gen_dst_goto(self, attrib, instr_offsets, dst2index): """ Generate code for possible @dst2index. @attrib: an Attributes instance @instr_offsets: list of instructions offsets @dst2index: link from destination to index """ if not dst2index: return [] out = [] out.append('switch(DST_case) {') stopcase = False for dst, index in sorted(dst2index.iteritems(), key=lambda lblindex: lblindex[1]): if index == -1: # Handle '-1' case only once if not stopcase: stopcase = True else: continue out.append('\tcase %d:' % index) out += self.gen_goto_code(attrib, instr_offsets, dst) out.append('\t\tbreak;') out.append('};') return out def gen_c_code(self, attrib, c_dst, c_assignmnts): """ Generate the C code for assignblk. @attrib: Attributes instance @c_dst: irdst C code """ c_prefetch, c_var, c_main, c_mem, c_updt = c_assignmnts out = [] out.append("{") out.append("// var") out += c_var out.append("// Prefetch") out += c_prefetch out.append("// Dst") out += c_dst out.append("// Main") out += c_main out.append("// Check op/mem exceptions") # Check memory access if assignblk has memory read if c_prefetch: out += self.gen_check_memory_exception(attrib.instr.offset) out.append("// Mem updt") out += c_mem out.append("// Check exception Mem write") # Check memory write exceptions if attrib.mem_write: out += self.gen_check_memory_exception(attrib.instr.offset) out.append("// Updt") out += c_updt out.append("// Checks exception") # Check post assignblk exception flags if attrib.set_exception: out += self.gen_check_cpu_exception(attrib.instr.offset) out.append("}") return out def get_caracteristics(self, assignblk, attrib): """ Set the carateristics in @attrib according to the @assignblk @assignblk: an AssignBlock instance @attrib: an Attributes instance """ # Check explicit exception raising attrib.set_exception = self.ir_arch.arch.regs.exception_flags in assignblk element_read = assignblk.get_r(mem_read=True) # Check mem read attrib.mem_read = any( isinstance(expr, ExprMem) for expr in element_read) # Check mem write attrib.mem_write = any(isinstance(dst, ExprMem) for dst in assignblk) def get_attributes(self, instr, irblocks, log_mn=False, log_regs=False): """ Get the carateristics of each @irblocks. Returns the corresponding attributes object. @irblock: a list of irbloc instance @log_mn: generate code to log instructions @log_regs: generate code to log registers states """ instr_attrib = Attributes(log_mn, log_regs) instr_attrib.instr = instr irblocks_attributes = [] for irblock in irblocks: attributes = [] irblocks_attributes.append(attributes) for assignblk in irblock: attrib = Attributes(log_mn, log_regs) attributes.append(attrib) self.get_caracteristics(assignblk, attrib) attrib.instr = instr instr_attrib.mem_read |= attrib.mem_read instr_attrib.mem_write |= attrib.mem_write instr_attrib.set_exception |= attrib.set_exception return instr_attrib, irblocks_attributes def gen_bad_block(self): """ Generate the C code for a bad_block instance """ return self.CODE_BAD_BLOCK.split("\n") def get_block_post_label(self, block): """Get label next to the @block @block: AsmBlock instance""" last_instr = block.lines[-1] offset = last_instr.offset + last_instr.l return self.ir_arch.loc_db.get_or_create_offset_location(offset) def gen_init(self, block): """ Generate the init C code for a @block @block: an asm_bloc instance """ instr_offsets = [line.offset for line in block.lines] post_label = self.get_block_post_label(block) post_offset = self.ir_arch.loc_db.get_location_offset(post_label) instr_offsets.append(post_offset) lbl_start = block.loc_key return (self.CODE_INIT % lbl_start).split("\n"), instr_offsets def gen_irblock(self, instr_attrib, attributes, instr_offsets, irblock): """ Generate the C code for an @irblock @irblock: an irbloc instance @attributes: an Attributes instance list """ out = [] dst2index = None for index, assignblk in enumerate(irblock): if index == irblock.dst_linenb: c_dst, dst2index = self.gen_assignblk_dst(irblock.dst) else: c_dst = [] c_assignmnts = self.gen_c_assignments(assignblk) out += self.gen_c_code(attributes[index], c_dst, c_assignmnts) if dst2index: out.append("// Set irdst") # Gen goto on irdst set out += self.gen_dst_goto(instr_attrib, instr_offsets, dst2index) return out def gen_finalize(self, block): """ Generate the C code for the final block instruction """ loc_key = self.get_block_post_label(block) offset = self.ir_arch.loc_db.get_location_offset(loc_key) dst = self.dst_to_c(offset) code = self.CODE_RETURN_NO_EXCEPTION % (loc_key, self.C_PC, dst, dst) return code.split('\n') def gen_c(self, block, log_mn=False, log_regs=False): """ Generate the C code for the @block and return it as a list of lines @log_mn: log mnemonics @log_regs: log registers """ if isinstance(block, AsmBlockBad): return self.gen_bad_block() irblocks_list = self.block2assignblks(block) out, instr_offsets = self.gen_init(block) assert len(block.lines) == len(irblocks_list) for instr, irblocks in zip(block.lines, irblocks_list): instr_attrib, irblocks_attributes = self.get_attributes( instr, irblocks, log_mn, log_regs) for index, irblock in enumerate(irblocks): new_irblock = self.ir_arch.irbloc_fix_regs_for_mode( irblock, self.ir_arch.attrib) label = str(new_irblock.loc_key) out.append("%-40s // %.16X %s" % (label + ":", instr.offset, instr)) if index == 0: out += self.gen_pre_code(instr_attrib) out += self.gen_irblock(instr_attrib, irblocks_attributes[index], instr_offsets, new_irblock) out += self.gen_finalize(block) return ['\t' + line for line in out]
""" Module to generate C code for a given native @block """ from miasm2.expression.expression import Expr, ExprId, ExprLoc, ExprInt, \ ExprMem, ExprCond, LocKey from miasm2.ir.ir import IRBlock, AssignBlock from miasm2.ir.translators.C import TranslatorC from miasm2.core.asmblock import AsmBlockBad TRANSLATOR_NO_SYMBOL = TranslatorC(loc_db=None) SIZE_TO_MASK = { size: TRANSLATOR_NO_SYMBOL.from_expr(ExprInt(0, size).mask) for size in (1, 2, 3, 7, 8, 16, 32, 64, 128) } class Attributes(object): """ Store an irblock attributes """ def __init__(self, log_mn=False, log_regs=False): self.mem_read = False self.mem_write = False self.set_exception = False self.log_mn = log_mn self.log_regs = log_regs self.instr = None
class CGen(object): """ Helper to generate C code for a given AsmBlock """ """ Translate native assembly block to C """ CODE_EXCEPTION_MEM_AT_INSTR = r""" // except fetch mem at instr noauto if ((VM_exception_flag & ~EXCEPT_CODE_AUTOMOD) & EXCEPT_DO_NOT_UPDATE_PC) { %s = %s; BlockDst->address = %s; return JIT_RET_EXCEPTION; } """ CODE_EXCEPTION_AT_INSTR = r""" if (CPU_exception_flag_at_instr) { %s = %s; BlockDst->address = %s; return JIT_RET_EXCEPTION; } """ CODE_RETURN_EXCEPTION = r""" return JIT_RET_EXCEPTION; """ CODE_RETURN_NO_EXCEPTION = r""" %s: %s = %s; BlockDst->address = %s; return JIT_RET_NO_EXCEPTION; """ CODE_CPU_EXCEPTION_POST_INSTR = r""" if (CPU_exception_flag) { %s = DST_value; BlockDst->address = DST_value; return JIT_RET_EXCEPTION; } """ CODE_VM_EXCEPTION_POST_INSTR = r""" check_memory_breakpoint(&(jitcpu->pyvm->vm_mngr)); check_invalid_code_blocs(&(jitcpu->pyvm->vm_mngr)); if (VM_exception_flag) { %s = DST_value; BlockDst->address = DST_value; return JIT_RET_EXCEPTION; } """ CODE_INIT = r""" int DST_case; uint64_t DST_value; vm_cpu_t* mycpu = (vm_cpu_t*)jitcpu->cpu; goto %s; """ CODE_BAD_BLOCK = r""" // Unknown mnemonic CPU_exception_flag = EXCEPT_UNK_MNEMO; """ + CODE_RETURN_EXCEPTION def __init__(self, ir_arch): self.ir_arch = ir_arch self.PC = self.ir_arch.pc self.translator = TranslatorC(self.ir_arch.loc_db) self.init_arch_C() def init_arch_C(self): """Iinitialize jitter internals""" self.id_to_c_id = {} for reg in self.ir_arch.arch.regs.all_regs_ids: self.id_to_c_id[reg] = ExprId('mycpu->%s' % reg, reg.size) self.C_PC = self.id_to_c(self.PC) def dst_to_c(self, src): """Translate Expr @src into C code""" if not isinstance(src, Expr): src = ExprInt(src, self.PC.size) return self.id_to_c(src) def patch_c_id(self, expr): """Replace ExprId in @expr with corresponding C variables""" return expr.replace_expr(self.id_to_c_id) def id_to_c(self, expr): """Translate Expr @expr into corresponding C code""" return self.translator.from_expr(self.patch_c_id(expr)) def add_label_index(self, dst2index, loc_key): """Insert @lbl to the dictionnary @dst2index with a uniq value @dst2index: LocKey -> uniq value @loc_key: LocKey istance""" if loc_key not in dst2index: dst2index[loc_key] = len(dst2index) def assignblk_to_irbloc(self, instr, assignblk): """ Ensure IRDst is always set in the head @assignblk of the @instr @instr: an instruction instance @assignblk: Assignblk instance """ new_assignblk = dict(assignblk) if self.ir_arch.IRDst not in assignblk: offset = instr.offset + instr.l loc_key = self.ir_arch.loc_db.get_or_create_offset_location(offset) dst = ExprLoc(loc_key, self.ir_arch.IRDst.size) new_assignblk[self.ir_arch.IRDst] = dst irs = [AssignBlock(new_assignblk, instr)] return IRBlock(self.ir_arch.get_loc_key_for_instr(instr), irs) def block2assignblks(self, block): """ Return the list of irblocks for a native @block @block: AsmBlock """ irblocks_list = [] for instr in block.lines: assignblk_head, assignblks_extra = self.ir_arch.instr2ir(instr) # Keep result in ordered list as first element is the assignblk head # The remainings order is not really important irblock_head = self.assignblk_to_irbloc(instr, assignblk_head) irblocks = [irblock_head] + assignblks_extra # Simplify high level operators out = [] for irblock in irblocks: new_irblock = self.ir_arch.irbloc_fix_regs_for_mode(irblock, self.ir_arch.attrib) new_irblock = new_irblock.simplify(expr_simp_high_to_explicit)[1] out.append(new_irblock) irblocks = out for irblock in irblocks: assert irblock.dst is not None irblocks_list.append(irblocks) return irblocks_list def add_local_var(self, dst_var, dst_index, expr): """ Add local varaible used to store temporay result @dst_var: dictionnary of Expr -> local_var_expr @dst_index : dictionnary of size -> local var count @expr: Expression source """ size = expr.size if size < 8: size = 8 if size not in dst_index: raise RuntimeError("Unsupported operand size %s", size) var_num = dst_index[size] dst = ExprId("var_%.2d_%.2d" % (size, var_num), size) dst_index[size] += 1 dst_var[expr] = dst return dst def get_mem_prefetch(self, assignblk): """ Generate temporary variables used to fetch memory used in the @assignblk Return a dictionnary: ExprMem -> temporary variable @assignblk: AssignBlock instance """ mem_index = {8: 0, 16: 0, 32: 0, 64: 0, 128:0} mem_var = {} # Prefetch memory read for expr in assignblk.get_r(mem_read=True): if not isinstance(expr, ExprMem): continue var_num = mem_index[expr.size] mem_index[expr.size] += 1 var = ExprId( "prefetch_%.2d_%.2d" % (expr.size, var_num), expr.size ) mem_var[expr] = var # Generate memory prefetch return mem_var def gen_c_assignments(self, assignblk): """ Return C informations used to generate the C code of the @assignblk @assignblk: an AssignBlock instance """ c_var = [] c_main = [] c_mem = [] c_updt = [] c_prefetch = [] dst_index = {8: 0, 16: 0, 32: 0, 64: 0, 128:0} dst_var = {} prefetchers = self.get_mem_prefetch(assignblk) for expr, prefetcher in sorted(prefetchers.iteritems()): str_src = self.id_to_c(expr) str_dst = self.id_to_c(prefetcher) c_prefetch.append('%s = %s;' % (str_dst, str_src)) for var in prefetchers.itervalues(): if var.size <= self.translator.NATIVE_INT_MAX_SIZE: c_var.append("uint%d_t %s;" % (var.size, var)) else: c_var.append("bn_t %s; // %d" % (var, var.size)) for dst, src in sorted(assignblk.iteritems()): src = src.replace_expr(prefetchers) if dst == self.ir_arch.IRDst: pass elif isinstance(dst, ExprId): new_dst = self.add_local_var(dst_var, dst_index, dst) if dst in self.ir_arch.arch.regs.regs_flt_expr: # Dont mask float affectation c_main.append( '%s = (%s);' % (self.id_to_c(new_dst), self.id_to_c(src))) elif new_dst.size <= self.translator.NATIVE_INT_MAX_SIZE: c_main.append( '%s = (%s)&%s;' % (self.id_to_c(new_dst), self.id_to_c(src), SIZE_TO_MASK[src.size])) else: c_main.append( '%s = bignum_mask(%s, %d);' % ( self.id_to_c(new_dst), self.id_to_c(src), src.size ) ) elif isinstance(dst, ExprMem): ptr = dst.ptr.replace_expr(prefetchers) if ptr.size <= self.translator.NATIVE_INT_MAX_SIZE: new_dst = ExprMem(ptr, dst.size) str_dst = self.id_to_c(new_dst).replace('MEM_LOOKUP', 'MEM_WRITE') c_mem.append('%s, %s);' % (str_dst[:-1], self.id_to_c(src))) else: ptr_str = self.id_to_c(ptr) if ptr.size <= self.translator.NATIVE_INT_MAX_SIZE: c_mem.append('%s, %s);' % (str_dst[:-1], self.id_to_c(src))) else: if src.size <= self.translator.NATIVE_INT_MAX_SIZE: c_mem.append('MEM_WRITE_BN_INT(jitcpu, %d, %s, %s);' % ( src.size, ptr_str, self.id_to_c(src)) ) else: c_mem.append('MEM_WRITE_BN_BN(jitcpu, %d, %s, %s);' % ( src.size, ptr_str, self.id_to_c(src)) ) else: raise ValueError("Unknown dst") for dst, new_dst in dst_var.iteritems(): if dst == self.ir_arch.IRDst: continue c_updt.append('%s = %s;' % (self.id_to_c(dst), self.id_to_c(new_dst))) if dst.size <= self.translator.NATIVE_INT_MAX_SIZE: c_var.append("uint%d_t %s;" % (new_dst.size, new_dst)) else: c_var.append("bn_t %s; // %d" % (new_dst, new_dst.size)) return c_prefetch, c_var, c_main, c_mem, c_updt def gen_check_memory_exception(self, address): """Generate C code to check memory exceptions @address: address of the faulty instruction""" dst = self.dst_to_c(address) return (self.CODE_EXCEPTION_MEM_AT_INSTR % (self.C_PC, dst, dst)).split('\n') def gen_check_cpu_exception(self, address): """Generate C code to check cpu exceptions @address: address of the faulty instruction""" dst = self.dst_to_c(address) return (self.CODE_EXCEPTION_AT_INSTR % (self.C_PC, dst, dst)).split('\n') def traverse_expr_dst(self, expr, dst2index): """ Generate the index of the destination label for the @expr @dst2index: dictionnary to link label to its index """ if isinstance(expr, ExprCond): src1, src1b = self.traverse_expr_dst(expr.src1, dst2index) src2, src2b = self.traverse_expr_dst(expr.src2, dst2index) cond = self.id_to_c(expr.cond) if not expr.cond.size <= self.translator.NATIVE_INT_MAX_SIZE: cond = "(!bignum_is_zero(%s))" % cond return ("((%s)?(%s):(%s))" % (cond, src1, src2), "((%s)?(%s):(%s))" % (cond, src1b, src2b)) if isinstance(expr, ExprInt): offset = int(expr) loc_key = self.ir_arch.loc_db.get_or_create_offset_location(offset) self.add_label_index(dst2index, loc_key) out = hex(offset) return ("%s" % dst2index[loc_key], out) if expr.is_loc(): loc_key = expr.loc_key offset = self.ir_arch.loc_db.get_location_offset(expr.loc_key) if offset is not None: self.add_label_index(dst2index, loc_key) out = hex(offset) return ("%s" % dst2index[loc_key], out) self.add_label_index(dst2index, loc_key) out = hex(0) return ("%s" % dst2index[loc_key], out) dst2index[expr] = -1 return ("-1", self.id_to_c(expr)) def gen_assignblk_dst(self, dst): """Generate C code to handle instruction destination @dst: instruction destination Expr""" dst2index = {} (ret, retb) = self.traverse_expr_dst(dst, dst2index) ret = "DST_case = %s;" % ret retb = 'DST_value = %s;' % retb return ['// %s' % dst2index, '%s' % ret, '%s' % retb], dst2index def gen_post_instr_checks(self, attrib): """Generate C code for handling potential exceptions @attrib: Attributes instance""" out = [] if attrib.mem_read | attrib.mem_write: out += (self.CODE_VM_EXCEPTION_POST_INSTR % (self.C_PC)).split('\n') if attrib.set_exception: out += (self.CODE_CPU_EXCEPTION_POST_INSTR % (self.C_PC)).split('\n') if attrib.mem_read | attrib.mem_write: out.append("reset_memory_access(&(jitcpu->pyvm->vm_mngr));") return out def gen_pre_code(self, instr_attrib): """Callback to generate code BEFORE the instruction execution @instr_attrib: Attributes instance""" out = [] if instr_attrib.log_mn: out.append( 'printf("%.8X %s\\n");' % ( instr_attrib.instr.offset, instr_attrib.instr.to_string(self.ir_arch.loc_db) ) ) return out def gen_post_code(self, attrib): """Callback to generate code AFTER the instruction execution @attrib: Attributes instance""" out = [] if attrib.log_regs: out.append('dump_gpregs(jitcpu->cpu);') return out def gen_goto_code(self, attrib, instr_offsets, dst): """Generate C code for a potential destination @dst @attrib: instruction Attributes @instr_offsets: instructions offsets list @dst: potential instruction destination""" out = [] if isinstance(dst, Expr): out += self.gen_post_code(attrib) out.append('BlockDst->address = DST_value;') out += self.gen_post_instr_checks(attrib) out.append('\t\treturn JIT_RET_NO_EXCEPTION;') return out assert isinstance(dst, LocKey) offset = self.ir_arch.loc_db.get_location_offset(dst) if offset is None: # Generate goto for local labels return ['goto %s;' % dst] if (offset > attrib.instr.offset and offset in instr_offsets): # Only generate goto for next instructions. # (consecutive instructions) out += self.gen_post_code(attrib) out += self.gen_post_instr_checks(attrib) out.append('goto %s;' % dst) else: out += self.gen_post_code(attrib) out.append('BlockDst->address = DST_value;') out += self.gen_post_instr_checks(attrib) out.append('\t\treturn JIT_RET_NO_EXCEPTION;') return out def gen_dst_goto(self, attrib, instr_offsets, dst2index): """ Generate code for possible @dst2index. @attrib: an Attributes instance @instr_offsets: list of instructions offsets @dst2index: link from destination to index """ if not dst2index: return [] out = [] out.append('switch(DST_case) {') stopcase = False for dst, index in sorted(dst2index.iteritems(), key=lambda lblindex: lblindex[1]): if index == -1: # Handle '-1' case only once if not stopcase: stopcase = True else: continue out.append('\tcase %d:' % index) out += self.gen_goto_code(attrib, instr_offsets, dst) out.append('\t\tbreak;') out.append('};') return out def gen_c_code(self, attrib, c_dst, c_assignmnts): """ Generate the C code for assignblk. @attrib: Attributes instance @c_dst: irdst C code """ c_prefetch, c_var, c_main, c_mem, c_updt = c_assignmnts out = [] out.append("{") out.append("// var") out += c_var out.append("// Prefetch") out += c_prefetch out.append("// Dst") out += c_dst out.append("// Main") out += c_main out.append("// Check op/mem exceptions") # Check memory access if assignblk has memory read if c_prefetch: out += self.gen_check_memory_exception(attrib.instr.offset) out.append("// Mem updt") out += c_mem out.append("// Check exception Mem write") # Check memory write exceptions if attrib.mem_write: out += self.gen_check_memory_exception(attrib.instr.offset) out.append("// Updt") out += c_updt out.append("// Checks exception") # Check post assignblk exception flags if attrib.set_exception: out += self.gen_check_cpu_exception(attrib.instr.offset) out.append("}") return out def get_caracteristics(self, assignblk, attrib): """ Set the carateristics in @attrib according to the @assignblk @assignblk: an AssignBlock instance @attrib: an Attributes instance """ # Check explicit exception raising attrib.set_exception = self.ir_arch.arch.regs.exception_flags in assignblk element_read = assignblk.get_r(mem_read=True) # Check mem read attrib.mem_read = any(isinstance(expr, ExprMem) for expr in element_read) # Check mem write attrib.mem_write = any(isinstance(dst, ExprMem) for dst in assignblk) def get_attributes(self, instr, irblocks, log_mn=False, log_regs=False): """ Get the carateristics of each @irblocks. Returns the corresponding attributes object. @irblock: a list of irbloc instance @log_mn: generate code to log instructions @log_regs: generate code to log registers states """ instr_attrib = Attributes(log_mn, log_regs) instr_attrib.instr = instr irblocks_attributes = [] for irblock in irblocks: attributes = [] irblocks_attributes.append(attributes) for assignblk in irblock: attrib = Attributes(log_mn, log_regs) attributes.append(attrib) self.get_caracteristics(assignblk, attrib) attrib.instr = instr instr_attrib.mem_read |= attrib.mem_read instr_attrib.mem_write |= attrib.mem_write instr_attrib.set_exception |= attrib.set_exception return instr_attrib, irblocks_attributes def gen_bad_block(self): """ Generate the C code for a bad_block instance """ return self.CODE_BAD_BLOCK.split("\n") def get_block_post_label(self, block): """Get label next to the @block @block: AsmBlock instance""" last_instr = block.lines[-1] offset = last_instr.offset + last_instr.l return self.ir_arch.loc_db.get_or_create_offset_location(offset) def gen_init(self, block): """ Generate the init C code for a @block @block: an asm_bloc instance """ instr_offsets = [line.offset for line in block.lines] post_label = self.get_block_post_label(block) post_offset = self.ir_arch.loc_db.get_location_offset(post_label) instr_offsets.append(post_offset) lbl_start = block.loc_key return (self.CODE_INIT % lbl_start).split("\n"), instr_offsets def gen_irblock(self, instr_attrib, attributes, instr_offsets, irblock): """ Generate the C code for an @irblock @irblock: an irbloc instance @attributes: an Attributes instance list """ out = [] dst2index = None for index, assignblk in enumerate(irblock): if index == irblock.dst_linenb: c_dst, dst2index = self.gen_assignblk_dst(irblock.dst) else: c_dst = [] c_assignmnts = self.gen_c_assignments(assignblk) out += self.gen_c_code(attributes[index], c_dst, c_assignmnts) if dst2index: out.append("// Set irdst") # Gen goto on irdst set out += self.gen_dst_goto(instr_attrib, instr_offsets, dst2index) return out def gen_finalize(self, block): """ Generate the C code for the final block instruction """ loc_key = self.get_block_post_label(block) offset = self.ir_arch.loc_db.get_location_offset(loc_key) dst = self.dst_to_c(offset) code = self.CODE_RETURN_NO_EXCEPTION % (loc_key, self.C_PC, dst, dst) return code.split('\n') def gen_c(self, block, log_mn=False, log_regs=False): """ Generate the C code for the @block and return it as a list of lines @log_mn: log mnemonics @log_regs: log registers """ if isinstance(block, AsmBlockBad): return self.gen_bad_block() irblocks_list = self.block2assignblks(block) out, instr_offsets = self.gen_init(block) assert len(block.lines) == len(irblocks_list) for instr, irblocks in zip(block.lines, irblocks_list): instr_attrib, irblocks_attributes = self.get_attributes(instr, irblocks, log_mn, log_regs) for index, irblock in enumerate(irblocks): label = str(irblock.loc_key) out.append("%-40s // %.16X %s" % (label + ":", instr.offset, instr)) if index == 0: out += self.gen_pre_code(instr_attrib) out += self.gen_irblock(instr_attrib, irblocks_attributes[index], instr_offsets, irblock) out += self.gen_finalize(block) return ['\t' + line for line in out]
""" Module to generate C code for a given native @block """ from miasm2.expression.expression import Expr, ExprId, ExprLoc, ExprInt, \ ExprMem, ExprCond, LocKey from miasm2.ir.ir import IRBlock, AssignBlock from miasm2.ir.translators.C import TranslatorC from miasm2.core.asmblock import AsmBlockBad from miasm2.expression.simplifications import expr_simp_high_to_explicit TRANSLATOR_NO_SYMBOL = TranslatorC(loc_db=None) SIZE_TO_MASK = {size: TRANSLATOR_NO_SYMBOL.from_expr(ExprInt(0, size).mask) for size in (1, 2, 3, 7, 8, 16, 32, 64)} class Attributes(object): """ Store an irblock attributes """ def __init__(self, log_mn=False, log_regs=False): self.mem_read = False self.mem_write = False
def translationTest(self, expr, expected): from miasm2.ir.translators.C import TranslatorC self.assertEqual(TranslatorC.from_expr(expr), expected)