diff options
84 files changed, 374 insertions, 160 deletions
diff --git a/example/asm/shellcode.py b/example/asm/shellcode.py index f31c88c2..3ff11489 100644..100755 --- a/example/asm/shellcode.py +++ b/example/asm/shellcode.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 from argparse import ArgumentParser from pdb import pm diff --git a/example/jitter/arm.py b/example/jitter/arm.py index 7bd354bc..eac6c0e6 100644..100755 --- a/example/jitter/arm.py +++ b/example/jitter/arm.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import logging from pdb import pm diff --git a/example/jitter/arm_sc.py b/example/jitter/arm_sc.py index ca765d31..6644a440 100644..100755 --- a/example/jitter/arm_sc.py +++ b/example/jitter/arm_sc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- from miasm2.analysis.sandbox import Sandbox_Linux_armb_str from miasm2.analysis.sandbox import Sandbox_Linux_arml_str diff --git a/example/jitter/example_types.py b/example/jitter/example_types.py index c37c3b84..7ed964c2 100644..100755 --- a/example/jitter/example_types.py +++ b/example/jitter/example_types.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 """This script is just a short example of common usages for miasm2.core.types. For a more complete view of what is possible, tests/core/types.py covers most of the module possibilities, and the module doc gives useful information diff --git a/example/jitter/mips32.py b/example/jitter/mips32.py index a03cac8e..7c8900f8 100644..100755 --- a/example/jitter/mips32.py +++ b/example/jitter/mips32.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- from argparse import ArgumentParser from miasm2.analysis import debugging diff --git a/example/jitter/msp430.py b/example/jitter/msp430.py index 89ecd930..5c8b7197 100644..100755 --- a/example/jitter/msp430.py +++ b/example/jitter/msp430.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- from argparse import ArgumentParser from miasm2.analysis import debugging diff --git a/miasm2/analysis/disasm_cb.py b/miasm2/analysis/disasm_cb.py index b6c4351d..f1f23377 100644 --- a/miasm2/analysis/disasm_cb.py +++ b/miasm2/analysis/disasm_cb.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import ExprInt, ExprId, ExprMem, MatchExpr diff --git a/miasm2/analysis/gdbserver.py b/miasm2/analysis/gdbserver.py index cbc8fe8d..dbf1b3b6 100644 --- a/miasm2/analysis/gdbserver.py +++ b/miasm2/analysis/gdbserver.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import socket diff --git a/miasm2/analysis/machine.py b/miasm2/analysis/machine.py index 6215a21b..7a6069c0 100644 --- a/miasm2/analysis/machine.py +++ b/miasm2/analysis/machine.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- diff --git a/miasm2/arch/aarch64/arch.py b/miasm2/arch/aarch64/arch.py index b495821b..c875d787 100644 --- a/miasm2/arch/aarch64/arch.py +++ b/miasm2/arch/aarch64/arch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import logging diff --git a/miasm2/arch/aarch64/ira.py b/miasm2/arch/aarch64/ira.py index 090ff6d0..3441483c 100644 --- a/miasm2/arch/aarch64/ira.py +++ b/miasm2/arch/aarch64/ira.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/aarch64/regs.py b/miasm2/arch/aarch64/regs.py index 6130d075..95527be3 100644 --- a/miasm2/arch/aarch64/regs.py +++ b/miasm2/arch/aarch64/regs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/arm/arch.py b/miasm2/arch/arm/arch.py index 04e47585..0e58008d 100644 --- a/miasm2/arch/arm/arch.py +++ b/miasm2/arch/arm/arch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import logging diff --git a/miasm2/arch/arm/ira.py b/miasm2/arch/arm/ira.py index 7d8e1838..d888d598 100644 --- a/miasm2/arch/arm/ira.py +++ b/miasm2/arch/arm/ira.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/arm/regs.py b/miasm2/arch/arm/regs.py index 1393c372..a44878a8 100644 --- a/miasm2/arch/arm/regs.py +++ b/miasm2/arch/arm/regs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/mips32/arch.py b/miasm2/arch/mips32/arch.py index 38ed2811..2ac16770 100644 --- a/miasm2/arch/mips32/arch.py +++ b/miasm2/arch/mips32/arch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import logging diff --git a/miasm2/arch/mips32/ira.py b/miasm2/arch/mips32/ira.py index ecbc1a7f..ec63c8c7 100644 --- a/miasm2/arch/mips32/ira.py +++ b/miasm2/arch/mips32/ira.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/mips32/regs.py b/miasm2/arch/mips32/regs.py index b64b40d5..974d3a2b 100644 --- a/miasm2/arch/mips32/regs.py +++ b/miasm2/arch/mips32/regs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import ExprId diff --git a/miasm2/arch/msp430/arch.py b/miasm2/arch/msp430/arch.py index ce0fe70f..a9f695ec 100644 --- a/miasm2/arch/msp430/arch.py +++ b/miasm2/arch/msp430/arch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import logging diff --git a/miasm2/arch/msp430/ira.py b/miasm2/arch/msp430/ira.py index 8e6aecee..bf777775 100644 --- a/miasm2/arch/msp430/ira.py +++ b/miasm2/arch/msp430/ira.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/msp430/sem.py b/miasm2/arch/msp430/sem.py index 92b005ad..4b52361d 100644 --- a/miasm2/arch/msp430/sem.py +++ b/miasm2/arch/msp430/sem.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import * diff --git a/miasm2/arch/sh4/arch.py b/miasm2/arch/sh4/arch.py index 7c76e003..d72e6945 100644 --- a/miasm2/arch/sh4/arch.py +++ b/miasm2/arch/sh4/arch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from pyparsing import * diff --git a/miasm2/arch/x86/arch.py b/miasm2/arch/x86/arch.py index 87e91756..90d0fcef 100644 --- a/miasm2/arch/x86/arch.py +++ b/miasm2/arch/x86/arch.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import re diff --git a/miasm2/arch/x86/ira.py b/miasm2/arch/x86/ira.py index 30dc92b0..d772d9fc 100644 --- a/miasm2/arch/x86/ira.py +++ b/miasm2/arch/x86/ira.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from miasm2.expression.expression import ExprAff, ExprOp, ExprId diff --git a/miasm2/arch/x86/sem.py b/miasm2/arch/x86/sem.py index 152208d4..ea5830e3 100644 --- a/miasm2/arch/x86/sem.py +++ b/miasm2/arch/x86/sem.py @@ -1732,9 +1732,11 @@ def float_pop(avoid_flt=None, popcount=1): if avoid_flt != float_list[i]: e.append(m2_expr.ExprAff(float_list[i], float_list[i + popcount])) + fill_value = m2_expr.ExprOp("int_64_to_double", + m2_expr.ExprInt(0, float_list[i].size)) for i in xrange(8 - popcount, 8): e.append(m2_expr.ExprAff(float_list[i], - m2_expr.ExprInt(0, float_list[i].size))) + fill_value)) e.append( m2_expr.ExprAff(float_stack_ptr, float_stack_ptr - m2_expr.ExprInt(popcount, 3))) @@ -2906,13 +2908,20 @@ def bittest_get(a, b): b_mask = {16: 4, 32: 5, 64: 6} b_decal = {16: 1, 32: 3, 64: 7} ptr = a.arg + segm = a.is_op_segm() + if segm: + ptr = ptr.args[1] + off_bit = b.zeroExtend( a.size) & m2_expr.ExprInt((1 << b_mask[a.size]) - 1, a.size) off_byte = ((b.zeroExtend(ptr.size) >> m2_expr.ExprInt(3, ptr.size)) & m2_expr.ExprInt(((1 << a.size) - 1) ^ b_decal[a.size], ptr.size)) - d = m2_expr.ExprMem(ptr + off_byte, a.size) + addr = ptr + off_byte + if segm: + addr = m2_expr.ExprOp("segm", a.arg.args[0], addr) + d = m2_expr.ExprMem(addr, a.size) else: off_bit = m2_expr.ExprOp('&', b, m2_expr.ExprInt(a.size - 1, a.size)) d = a diff --git a/miasm2/core/asmbloc.py b/miasm2/core/asmbloc.py index 730d6d7d..b68b1f1c 100644 --- a/miasm2/core/asmbloc.py +++ b/miasm2/core/asmbloc.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import logging diff --git a/miasm2/core/cpu.py b/miasm2/core/cpu.py index 7a2968f9..22f4c8ab 100644 --- a/miasm2/core/cpu.py +++ b/miasm2/core/cpu.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import re diff --git a/miasm2/core/parse_asm.py b/miasm2/core/parse_asm.py index b324d2cc..238306b3 100644 --- a/miasm2/core/parse_asm.py +++ b/miasm2/core/parse_asm.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import re diff --git a/miasm2/expression/__init__.py b/miasm2/expression/__init__.py index 2bf12b34..67f567f7 100644 --- a/miasm2/expression/__init__.py +++ b/miasm2/expression/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # Copyright (C) 2011 EADS France, Fabrice Desclaux <fabrice.desclaux@eads.net> # diff --git a/miasm2/expression/modint.py b/miasm2/expression/modint.py index a801c948..90dabfac 100644 --- a/miasm2/expression/modint.py +++ b/miasm2/expression/modint.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- class moduint(object): diff --git a/miasm2/ir/analysis.py b/miasm2/ir/analysis.py index c606d958..0913019b 100644 --- a/miasm2/ir/analysis.py +++ b/miasm2/ir/analysis.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import logging diff --git a/miasm2/ir/ir.py b/miasm2/ir/ir.py index 87a70205..0a7d68ce 100644 --- a/miasm2/ir/ir.py +++ b/miasm2/ir/ir.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- # diff --git a/miasm2/jitter/Jitllvm.c b/miasm2/jitter/Jitllvm.c index c176a4b2..b46f88e3 100644 --- a/miasm2/jitter/Jitllvm.c +++ b/miasm2/jitter/Jitllvm.c @@ -12,19 +12,66 @@ PyObject* llvm_exec_bloc(PyObject* self, PyObject* args) { - uint64_t func_addr; uint64_t (*func)(void*, void*, void*, uint8_t*); - uint64_t vm; + vm_cpu_t* cpu; + vm_mngr_t* vm; uint64_t ret; JitCpu* jitcpu; uint8_t status; - - if (!PyArg_ParseTuple(args, "KOK", &func_addr, &jitcpu, &vm)) + PyObject* func_py; + PyObject* lbl2ptr; + PyObject* breakpoints; + PyObject* retaddr = NULL; + uint64_t max_exec_per_call = 0; + uint64_t cpt; + int do_cpt; + + if (!PyArg_ParseTuple(args, "OOOO|K", + &retaddr, &jitcpu, &lbl2ptr, &breakpoints, + &max_exec_per_call)) return NULL; - vm_cpu_t* cpu = jitcpu->cpu; - func = (void *) (intptr_t) func_addr; - ret = func((void*) jitcpu, (void*)(intptr_t) cpu, (void*)(intptr_t) vm, &status); - return PyLong_FromUnsignedLongLong(ret); + + cpu = jitcpu->cpu; + vm = &(jitcpu->pyvm->vm_mngr); + /* The loop will decref retaddr always once */ + Py_INCREF(retaddr); + + if (max_exec_per_call == 0) { + do_cpt = 0; + cpt = 1; + } else { + do_cpt = 1; + cpt = max_exec_per_call; + } + + for (;;) { + // Handle cpt + if (cpt == 0) + return retaddr; + if (do_cpt) + cpt --; + + // Get the expected jitted function address + func_py = PyDict_GetItem(lbl2ptr, retaddr); + if (func_py) + func = PyLong_AsVoidPtr((PyObject*) func_py); + else + // retaddr is not jitted yet + return retaddr; + + // Execute it + ret = func((void*) jitcpu, (void*)(intptr_t) cpu, (void*)(intptr_t) vm, &status); + Py_DECREF(retaddr); + retaddr = PyLong_FromUnsignedLongLong(ret); + + // Check exception + if (status) + return retaddr; + + // Check breakpoint + if (PyDict_Contains(breakpoints, retaddr)) + return retaddr; + } } diff --git a/miasm2/jitter/arch/JitCore_x86.c b/miasm2/jitter/arch/JitCore_x86.c index 66c3fb56..461e2224 100644 --- a/miasm2/jitter/arch/JitCore_x86.c +++ b/miasm2/jitter/arch/JitCore_x86.c @@ -603,6 +603,9 @@ PyObject* get_gpreg_offset_all(void) get_reg_off(exception_flags); get_reg_off(float_stack_ptr); + get_reg_off(reg_float_cs); + get_reg_off(reg_float_eip); + get_reg_off(reg_float_control); return dict; } diff --git a/miasm2/jitter/csts.py b/miasm2/jitter/csts.py index 95cd34a8..8efd5626 100644 --- a/miasm2/jitter/csts.py +++ b/miasm2/jitter/csts.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- diff --git a/miasm2/jitter/jitcore.py b/miasm2/jitter/jitcore.py index f3a79bee..7e831280 100644 --- a/miasm2/jitter/jitcore.py +++ b/miasm2/jitter/jitcore.py @@ -165,33 +165,22 @@ class JitCore(object): # Update jitcode mem range self.add_bloc_to_mem_interval(vm, cur_bloc) - def jit_call(self, label, cpu, _vmmngr, breakpoints): - """Call the function label with cpu and vmmngr states - @label: function's label - @cpu: JitCpu instance - @breakpoints: Dict instance of used breakpoints - """ - return self.exec_wrapper(label, cpu, self.lbl2jitbloc.data, breakpoints, - self.options["max_exec_per_call"]) - - def runbloc(self, cpu, vm, lbl, breakpoints): + def runbloc(self, cpu, lbl, breakpoints): """Run the bloc starting at lbl. @cpu: JitCpu instance - @vm: VmMngr instance @lbl: target label """ if lbl is None: - lbl = cpu.get_gpreg()[self.ir_arch.pc.name] + lbl = getattr(cpu, self.ir_arch.pc.name) if not lbl in self.lbl2jitbloc: # Need to JiT the bloc - self.disbloc(lbl, vm) + self.disbloc(lbl, cpu.vmmngr) # Run the bloc and update cpu/vmmngr state - ret = self.jit_call(lbl, cpu, vm, breakpoints) - - return ret + return self.exec_wrapper(lbl, cpu, self.lbl2jitbloc.data, breakpoints, + self.options["max_exec_per_call"]) def blocs2memrange(self, blocs): """Return an interval instance standing for blocs addresses diff --git a/miasm2/jitter/jitcore_cc_base.py b/miasm2/jitter/jitcore_cc_base.py index baebc294..2c2d3d52 100644 --- a/miasm2/jitter/jitcore_cc_base.py +++ b/miasm2/jitter/jitcore_cc_base.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import os diff --git a/miasm2/jitter/jitcore_gcc.py b/miasm2/jitter/jitcore_gcc.py index 0d9d5778..ccccc37a 100644 --- a/miasm2/jitter/jitcore_gcc.py +++ b/miasm2/jitter/jitcore_gcc.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import os diff --git a/miasm2/jitter/jitcore_llvm.py b/miasm2/jitter/jitcore_llvm.py index 0f265073..8f58f1da 100644 --- a/miasm2/jitter/jitcore_llvm.py +++ b/miasm2/jitter/jitcore_llvm.py @@ -1,6 +1,7 @@ import os import importlib -import hashlib +import tempfile +from hashlib import md5 from miasm2.jitter.llvmconvert import * import miasm2.jitter.jitcore as jitcore import Jitllvm @@ -28,9 +29,18 @@ class JitCore_LLVM(jitcore.JitCore): }) self.exec_wrapper = Jitllvm.llvm_exec_bloc - self.exec_engines = [] self.ir_arch = ir_arch + # Cache temporary dir + self.tempdir = os.path.join(tempfile.gettempdir(), "miasm_cache") + try: + os.mkdir(self.tempdir, 0755) + except OSError: + pass + if not os.access(self.tempdir, os.R_OK | os.W_OK): + raise RuntimeError( + 'Cannot access cache directory %s ' % self.tempdir) + def load(self): # Library to load within Jit context @@ -60,43 +70,62 @@ class JitCore_LLVM(jitcore.JitCore): mod = importlib.import_module(mod_name) self.context.set_vmcpu(mod.get_gpreg_offset_all()) + # Enable caching + self.context.enable_cache() + def add_bloc(self, block): """Add a block to JiT and JiT it. @block: the block to add """ - # TODO: caching using hash + block_hash = self.hash_block(block) + fname_out = os.path.join(self.tempdir, "%s.bc" % block_hash) - # Build a function in the context - func = LLVMFunction(self.context, block.label.name) + if not os.access(fname_out, os.R_OK): + # Build a function in the context + func = LLVMFunction(self.context, block.label.name) - # Set log level - func.log_regs = self.log_regs - func.log_mn = self.log_mn + # Set log level + func.log_regs = self.log_regs + func.log_mn = self.log_mn - # Import asm block - func.from_asmblock(block) + # Import asm block + func.from_asmblock(block) - # Verify - if self.options["safe_mode"] is True: - func.verify() + # Verify + if self.options["safe_mode"] is True: + func.verify() - # Optimise - if self.options["optimise"] is True: - func.optimise() + # Optimise + if self.options["optimise"] is True: + func.optimise() - # Log - if self.options["log_func"] is True: - print func - if self.options["log_assembly"] is True: - print func.get_assembly() + # Log + if self.options["log_func"] is True: + print func + if self.options["log_assembly"] is True: + print func.get_assembly() + + # Use propagate the cache filename + self.context.set_cache_filename(func, fname_out) + + # Get a pointer on the function for JiT + ptr = func.get_function_pointer() + + else: + # The cache file exists: function can be loaded from cache + ptr = self.context.get_ptr_from_cache(fname_out, block.label.name) # Store a pointer on the function jitted code - self.lbl2jitbloc[block.label.offset] = func.get_function_pointer() + self.lbl2jitbloc[block.label.offset] = ptr - def jit_call(self, label, cpu, _vmmngr, breakpoints): - """Call the function label with cpu and vmmngr states - @label: function's label - @cpu: JitCpu instance - @breakpoints: Dict instance of used breakpoints + def hash_block(self, block): + """ + Build a hash of the block @block + @block: asmbloc """ - return self.exec_wrapper(self.lbl2jitbloc[label], cpu, cpu.vmmngr.vmmngr) + block_raw = "".join(line.b for line in block.lines) + block_hash = md5("%X_%s_%s_%s" % (block.label.offset, + self.log_mn, + self.log_regs, + block_raw)).hexdigest() + return block_hash diff --git a/miasm2/jitter/jitcore_python.py b/miasm2/jitter/jitcore_python.py index 87259f71..27666ab4 100644 --- a/miasm2/jitter/jitcore_python.py +++ b/miasm2/jitter/jitcore_python.py @@ -38,11 +38,12 @@ class JitCore_Python(jitcore.JitCore): @irblocs: a gorup of irblocs """ - def myfunc(cpu, vmmngr): + def myfunc(cpu): """Execute the function according to cpu and vmmngr states @cpu: JitCpu instance - @vm: VmMngr instance """ + # Get virtual memory handler + vmmngr = cpu.vmmngr # Keep current location in irblocs cur_label = label @@ -125,15 +126,15 @@ class JitCore_Python(jitcore.JitCore): # Associate myfunc with current label self.lbl2jitbloc[label.offset] = myfunc - def jit_call(self, label, cpu, vmmngr, _breakpoints): - """Call the function label with cpu and vmmngr states + def exec_wrapper(self, label, cpu, _lbl2jitbloc, _breakpoints, + _max_exec_per_call): + """Call the function @label with @cpu @label: function's label @cpu: JitCpu instance - @vm: VmMngr instance """ # Get Python function corresponding to @label fc_ptr = self.lbl2jitbloc[label] # Execute the function - return fc_ptr(cpu, vmmngr) + return fc_ptr(cpu) diff --git a/miasm2/jitter/jitcore_tcc.py b/miasm2/jitter/jitcore_tcc.py index 1ab7df4d..5b47bf6d 100644 --- a/miasm2/jitter/jitcore_tcc.py +++ b/miasm2/jitter/jitcore_tcc.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- import os import tempfile diff --git a/miasm2/jitter/jitload.py b/miasm2/jitter/jitload.py index b2416fd5..bc09e1f2 100644 --- a/miasm2/jitter/jitload.py +++ b/miasm2/jitter/jitload.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python import logging from functools import wraps @@ -297,7 +296,7 @@ class jitter: """Wrapper on JiT backend. Run the code at PC and return the next PC. @pc: address of code to run""" - return self.jit.runbloc(self.cpu, self.vm, pc, self.breakpoints_handler.callbacks) + return self.jit.runbloc(self.cpu, pc, self.breakpoints_handler.callbacks) def runiter_once(self, pc): """Iterator on callbacks results on code running from PC. diff --git a/miasm2/jitter/llvmconvert.py b/miasm2/jitter/llvmconvert.py index 08b1986b..4031d8f2 100644 --- a/miasm2/jitter/llvmconvert.py +++ b/miasm2/jitter/llvmconvert.py @@ -11,6 +11,7 @@ # # +import os from llvmlite import binding as llvm from llvmlite import ir as llvm_ir import miasm2.expression.expression as m2_expr @@ -228,6 +229,9 @@ class LLVMContext_JIT(LLVMContext): "args": [p8, itype, itype]}}) + self.add_fc({"cpuid": {"ret": itype, + "args": [itype, + itype]}}) for k in [8, 16]: self.add_fc({"bcdadd_%s" % k: {"ret": LLVMType.IntType(k), @@ -291,6 +295,57 @@ class LLVMContext_JIT(LLVMContext): value]) + @staticmethod + def cache_notify(module, buffer): + """Called when @module has been compiled to @buffer""" + if not hasattr(module, "fname_out"): + return + fname_out = module.fname_out + + if os.access(fname_out, os.R_OK): + # No need to overwrite + return + + open(fname_out, "w").write(buffer) + + @staticmethod + def cache_getbuffer(module): + """Return a compiled buffer for @module if available""" + if not hasattr(module, "fname_out"): + return None + + fname_out = module.fname_out + if os.access(fname_out, os.R_OK): + return open(fname_out).read() + return None + + def enable_cache(self): + "Enable cache of compiled object" + # Load shared libraries + for lib_fname in self.library_filenames: + self.add_shared_library(lib_fname) + + # Activate cache + self.exec_engine.set_object_cache(self.cache_notify, + self.cache_getbuffer) + + def set_cache_filename(self, func, fname_out): + "Set the filename @fname_out to use for cache for @func" + # Use a custom attribute to propagate the cache filename + func.as_llvm_mod().fname_out = fname_out + + def get_ptr_from_cache(self, file_name, func_name): + "Load @file_name and return a pointer on the jitter @func_name" + # We use an empty module to avoid loosing time on function building + empty_module = llvm.parse_assembly("") + empty_module.fname_out = file_name + + engine = self.exec_engine + engine.add_module(empty_module) + engine.finalize_object() + return engine.get_function_address(func_name) + + class LLVMContext_IRCompilation(LLVMContext): """Extend LLVMContext in order to handle memory management and custom @@ -327,6 +382,7 @@ class LLVMFunction(): # Operation translation ## Basics op_translate = {'parity': 'parity', + 'cpuid': 'cpuid', } ## Add the size as first argument op_translate_with_size = {'<<<': 'rot_left', @@ -418,9 +474,13 @@ class LLVMFunction(): ptr = builder.gep(self.local_vars["vmcpu"], [llvm_ir.Constant(LLVMType.IntType(), offset)]) - int_size = LLVMType.IntType(expr.size) + regs = self.llvm_context.ir_arch.arch.regs + if hasattr(regs, "float_list") and expr in regs.float_list: + pointee_type = llvm_ir.DoubleType() + else: + pointee_type = LLVMType.IntType(expr.size) ptr_casted = builder.bitcast(ptr, - llvm_ir.PointerType(int_size)) + llvm_ir.PointerType(pointee_type)) # Store in cache self.local_vars_pointers[name] = ptr_casted @@ -702,6 +762,20 @@ class LLVMFunction(): self.update_cache(expr, ret) return ret + if op in ["int_16_to_double", "int_32_to_double", "int_64_to_double", + "mem_16_to_double", "mem_32_to_double", "mem_64_to_double"]: + arg = self.add_ir(expr.args[0]) + ret = builder.uitofp(arg, llvm_ir.DoubleType()) + self.update_cache(expr, ret) + return ret + + if op in ["double_to_int_16", "double_to_int_32", "double_to_int_64", + "double_to_mem_16", "double_to_mem_32", "double_to_mem_64"]: + arg = self.add_ir(expr.args[0]) + ret = builder.fptoui(arg, llvm_ir.IntType(expr.size)) + self.update_cache(expr, ret) + return ret + if len(expr.args) > 1: if op == "*": @@ -718,6 +792,10 @@ class LLVMFunction(): callback = builder.urem elif op == "/": callback = builder.udiv + elif op == "fadd": + callback = builder.fadd + elif op == "fdiv": + callback = builder.fdiv else: raise NotImplementedError('Unknown op: %s' % op) diff --git a/miasm2/os_dep/linux_stdlib.py b/miasm2/os_dep/linux_stdlib.py index 542af25f..b05b2cd9 100644 --- a/miasm2/os_dep/linux_stdlib.py +++ b/miasm2/os_dep/linux_stdlib.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- from sys import stdout diff --git a/miasm2/os_dep/win_api_x86_32_seh.py b/miasm2/os_dep/win_api_x86_32_seh.py index 3b2fdfaa..9d37c9b1 100644 --- a/miasm2/os_dep/win_api_x86_32_seh.py +++ b/miasm2/os_dep/win_api_x86_32_seh.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #-*- coding:utf-8 -*- # diff --git a/test/arch/aarch64/unit/asm_test.py b/test/arch/aarch64/unit/asm_test.py index a9e6cfc6..cfb2a81c 100644 --- a/test/arch/aarch64/unit/asm_test.py +++ b/test/arch/aarch64/unit/asm_test.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python import sys import os @@ -10,13 +9,6 @@ from miasm2.core import asmbloc from elfesteem.strpatchwork import StrPatchwork from miasm2.analysis.machine import Machine from miasm2.jitter.csts import * -from pdb import pm - - -filename = os.environ.get('PYTHONSTARTUP') -if filename and os.path.isfile(filename): - execfile(filename) - reg_and_id = dict(mn_aarch64.regs.all_regs_ids_byname) diff --git a/test/arch/aarch64/unit/mn_ubfm.py b/test/arch/aarch64/unit/mn_ubfm.py index 6f1857fb..25d1cde7 100644..100755 --- a/test/arch/aarch64/unit/mn_ubfm.py +++ b/test/arch/aarch64/unit/mn_ubfm.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test diff --git a/test/arch/arm/sem.py b/test/arch/arm/sem.py index 922642d3..3695fd29 100644..100755 --- a/test/arch/arm/sem.py +++ b/test/arch/arm/sem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/arch/mips32/unit/asm_test.py b/test/arch/mips32/unit/asm_test.py index 7272c15e..35d87d85 100644 --- a/test/arch/mips32/unit/asm_test.py +++ b/test/arch/mips32/unit/asm_test.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python import sys import os @@ -10,17 +9,10 @@ from miasm2.core import asmbloc from elfesteem.strpatchwork import StrPatchwork from miasm2.analysis.machine import Machine from miasm2.jitter.csts import * -from pdb import pm - - -filename = os.environ.get('PYTHONSTARTUP') -if filename and os.path.isfile(filename): - execfile(filename) reg_and_id = dict(mn_mips32.regs.all_regs_ids_byname) - class Asm_Test(object): def __init__(self, jitter): diff --git a/test/arch/mips32/unit/mn_bcc.py b/test/arch/mips32/unit/mn_bcc.py index 4818c171..e9b28e77 100644..100755 --- a/test/arch/mips32/unit/mn_bcc.py +++ b/test/arch/mips32/unit/mn_bcc.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test diff --git a/test/arch/msp430/sem.py b/test/arch/msp430/sem.py index 4d39d357..433055e0 100644..100755 --- a/test/arch/msp430/sem.py +++ b/test/arch/msp430/sem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/arch/x86/sem.py b/test/arch/x86/sem.py index 93d2ff83..d2198847 100644..100755 --- a/test/arch/x86/sem.py +++ b/test/arch/x86/sem.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- # Loosely based on ARM's sem.py diff --git a/test/arch/x86/unit/asm_test.py b/test/arch/x86/unit/asm_test.py index 4e1d03b8..bf97dbe4 100644 --- a/test/arch/x86/unit/asm_test.py +++ b/test/arch/x86/unit/asm_test.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python import sys import os @@ -10,13 +9,6 @@ from miasm2.core import asmbloc from elfesteem.strpatchwork import StrPatchwork from miasm2.analysis.machine import Machine from miasm2.jitter.csts import * -from pdb import pm - - -filename = os.environ.get('PYTHONSTARTUP') -if filename and os.path.isfile(filename): - execfile(filename) - reg_and_id = dict(mn_x86.regs.all_regs_ids_byname) diff --git a/test/arch/x86/unit/mn_daa.py b/test/arch/x86/unit/mn_daa.py index d07bf849..21d609e4 100644..100755 --- a/test/arch/x86/unit/mn_daa.py +++ b/test/arch/x86/unit/mn_daa.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_das.py b/test/arch/x86/unit/mn_das.py index 2d8102d9..08df1a7a 100644..100755 --- a/test/arch/x86/unit/mn_das.py +++ b/test/arch/x86/unit/mn_das.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_float.py b/test/arch/x86/unit/mn_float.py index d704de73..f31a25e3 100644..100755 --- a/test/arch/x86/unit/mn_float.py +++ b/test/arch/x86/unit/mn_float.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_int.py b/test/arch/x86/unit/mn_int.py index 9d964220..09792371 100644..100755 --- a/test/arch/x86/unit/mn_int.py +++ b/test/arch/x86/unit/mn_int.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from miasm2.jitter.csts import EXCEPT_INT_XX diff --git a/test/arch/x86/unit/mn_pcmpeq.py b/test/arch/x86/unit/mn_pcmpeq.py index 5d0a59c6..22760db4 100644..100755 --- a/test/arch/x86/unit/mn_pcmpeq.py +++ b/test/arch/x86/unit/mn_pcmpeq.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pextr.py b/test/arch/x86/unit/mn_pextr.py index 696f077b..c4548d67 100644..100755 --- a/test/arch/x86/unit/mn_pextr.py +++ b/test/arch/x86/unit/mn_pextr.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pinsr.py b/test/arch/x86/unit/mn_pinsr.py index 74120e5c..4beaba13 100644..100755 --- a/test/arch/x86/unit/mn_pinsr.py +++ b/test/arch/x86/unit/mn_pinsr.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pmaxu.py b/test/arch/x86/unit/mn_pmaxu.py index 527b966f..752b9132 100644..100755 --- a/test/arch/x86/unit/mn_pmaxu.py +++ b/test/arch/x86/unit/mn_pmaxu.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pminu.py b/test/arch/x86/unit/mn_pminu.py index d667df4e..3be95a9a 100644..100755 --- a/test/arch/x86/unit/mn_pminu.py +++ b/test/arch/x86/unit/mn_pminu.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pmovmskb.py b/test/arch/x86/unit/mn_pmovmskb.py index b7e7b897..a3eb65f5 100644..100755 --- a/test/arch/x86/unit/mn_pmovmskb.py +++ b/test/arch/x86/unit/mn_pmovmskb.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pshufb.py b/test/arch/x86/unit/mn_pshufb.py index 5f4c7370..d10c18e3 100644..100755 --- a/test/arch/x86/unit/mn_pshufb.py +++ b/test/arch/x86/unit/mn_pshufb.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_psrl_psll.py b/test/arch/x86/unit/mn_psrl_psll.py index 44126b96..a5428dab 100644..100755 --- a/test/arch/x86/unit/mn_psrl_psll.py +++ b/test/arch/x86/unit/mn_psrl_psll.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_punpck.py b/test/arch/x86/unit/mn_punpck.py index f6a4772e..b519b4c3 100644..100755 --- a/test/arch/x86/unit/mn_punpck.py +++ b/test/arch/x86/unit/mn_punpck.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_pushpop.py b/test/arch/x86/unit/mn_pushpop.py index 1a3f5517..ffcc3fa5 100644..100755 --- a/test/arch/x86/unit/mn_pushpop.py +++ b/test/arch/x86/unit/mn_pushpop.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_16, Asm_Test_32 diff --git a/test/arch/x86/unit/mn_seh.py b/test/arch/x86/unit/mn_seh.py index d6fc56ca..dd3fd4ef 100644..100755 --- a/test/arch/x86/unit/mn_seh.py +++ b/test/arch/x86/unit/mn_seh.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from miasm2.os_dep.win_api_x86_32_seh import fake_seh_handler, build_teb, \ diff --git a/test/arch/x86/unit/mn_stack.py b/test/arch/x86/unit/mn_stack.py index 3bce3979..f9cfc60a 100644..100755 --- a/test/arch/x86/unit/mn_stack.py +++ b/test/arch/x86/unit/mn_stack.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 + import sys from asm_test import Asm_Test_32 diff --git a/test/arch/x86/unit/mn_strings.py b/test/arch/x86/unit/mn_strings.py index 5e7269f7..3cb70e2a 100644..100755 --- a/test/arch/x86/unit/mn_strings.py +++ b/test/arch/x86/unit/mn_strings.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 import sys from asm_test import Asm_Test_32 diff --git a/test/core/interval.py b/test/core/interval.py index 4572ac50..ab18e567 100644..100755 --- a/test/core/interval.py +++ b/test/core/interval.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- from miasm2.core.interval import * diff --git a/test/core/parse_asm.py b/test/core/parse_asm.py index a488d075..e91c8c8c 100644..100755 --- a/test/core/parse_asm.py +++ b/test/core/parse_asm.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/core/test_types.py b/test/core/test_types.py index f6e5cb13..ab1d47c4 100644..100755 --- a/test/core/test_types.py +++ b/test/core/test_types.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 # miasm2.core.types tests diff --git a/test/core/utils.py b/test/core/utils.py index f7de6565..b506f904 100644..100755 --- a/test/core/utils.py +++ b/test/core/utils.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 + #-*- coding:utf-8 -*- import unittest diff --git a/test/expression/expression_helper.py b/test/expression/expression_helper.py index a3a8fba4..6c800020 100644..100755 --- a/test/expression/expression_helper.py +++ b/test/expression/expression_helper.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/expression/stp.py b/test/expression/stp.py index fe09e865..b911a2a4 100644..100755 --- a/test/expression/stp.py +++ b/test/expression/stp.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/ir/analysis.py b/test/ir/analysis.py index 913d9c56..913d9c56 100755..100644 --- a/test/ir/analysis.py +++ b/test/ir/analysis.py diff --git a/test/ir/ir2C.py b/test/ir/ir2C.py index 36683904..395703ed 100644..100755 --- a/test/ir/ir2C.py +++ b/test/ir/ir2C.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/ir/symbexec.py b/test/ir/symbexec.py index 24b02341..2e776f74 100644..100755 --- a/test/ir/symbexec.py +++ b/test/ir/symbexec.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/jitter/jit_options.py b/test/jitter/jit_options.py new file mode 100644 index 00000000..cc955c64 --- /dev/null +++ b/test/jitter/jit_options.py @@ -0,0 +1,97 @@ +import os +import sys +from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE +from miasm2.analysis.machine import Machine +from pdb import pm + +# Shellcode + +# main: +# MOV EAX, 0x1 +# loop_main: +# CMP EAX, 0x10 +# JZ loop_end +# loop_inc: +# INC EAX +# JMP loop_main +# loop_end: +# RET +data = "b80100000083f810740340ebf8c3".decode("hex") +run_addr = 0x40000000 + +def code_sentinelle(jitter): + jitter.run = False + jitter.pc = 0 + return True + +def init_jitter(): + global data, run_addr + # Create jitter + myjit = Machine("x86_32").jitter(sys.argv[1]) + + myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data) + + # Init jitter + myjit.init_stack() + myjit.jit.log_regs = True + myjit.jit.log_mn = True + myjit.push_uint32_t(0x1337beef) + + myjit.add_breakpoint(0x1337beef, code_sentinelle) + return myjit + +# Test 'max_exec_per_call' +print "[+] First run, to jit blocks" +myjit = init_jitter() +myjit.init_run(run_addr) +myjit.continue_run() + +assert myjit.run is False +assert myjit.cpu.EAX == 0x10 + +## Let's specify a max_exec_per_call +## 5: main, loop_main, loop_inc, loop_main, loop_inc +myjit.jit.options["max_exec_per_call"] = 5 + +first_call = True +def cb(jitter): + global first_call + if first_call: + # Avoid breaking on the first pass (before any execution) + first_call = False + return True + return False + +## Second run +print "[+] Second run" +myjit.push_uint32_t(0x1337beef) +myjit.cpu.EAX = 0 +myjit.init_run(run_addr) +myjit.exec_cb = cb +myjit.continue_run() + +assert myjit.run is True +# Use a '<=' because it's a 'max_...' +assert myjit.cpu.EAX <= 3 + +# Test 'jit_maxline' +print "[+] Run instr one by one" +myjit = init_jitter() +myjit.jit.options["jit_maxline"] = 1 +myjit.jit.options["max_exec_per_call"] = 1 + +counter = 0 +def cb(jitter): + global counter + counter += 1 + return True + +myjit.init_run(run_addr) +myjit.exec_cb = cb +myjit.continue_run() + +assert myjit.run is False +assert myjit.cpu.EAX == 0x10 +## dry(1) + main(1) + (loop_main(2) + loop_inc(2))*(0x10 - 1) + loop_main(2) + +## loop_end(1) = 65 +assert counter == 65 diff --git a/test/jitter/jitload.py b/test/jitter/jitload.py index 283298db..544e9d18 100644 --- a/test/jitter/jitload.py +++ b/test/jitter/jitload.py @@ -1,3 +1,4 @@ +import sys from pdb import pm from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE @@ -9,7 +10,7 @@ from miasm2.expression.expression import ExprId, ExprInt32, ExprInt64, ExprAff, data = "8d49048d5b0180f90174058d5bffeb038d5b0189d8c3".decode("hex") # Init jitter -myjit = Machine("x86_32").jitter() +myjit = Machine("x86_32").jitter(sys.argv[1]) myjit.init_stack() run_addr = 0x40000000 diff --git a/test/jitter/vm_mngr.py b/test/jitter/vm_mngr.py index b2b7336b..87bc6f8f 100644 --- a/test/jitter/vm_mngr.py +++ b/test/jitter/vm_mngr.py @@ -1,7 +1,8 @@ +import sys from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE from miasm2.analysis.machine import Machine -myjit = Machine("x86_32").jitter() +myjit = Machine("x86_32").jitter(sys.argv[1]) base_addr = 0x13371337 page_size = 0x1000 diff --git a/test/os_dep/win_api_x86_32.py b/test/os_dep/win_api_x86_32.py index bb13138f..2e22ccea 100644..100755 --- a/test/os_dep/win_api_x86_32.py +++ b/test/os_dep/win_api_x86_32.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#! /usr/bin/env python2 #-*- coding:utf-8 -*- import unittest diff --git a/test/test_all.py b/test/test_all.py index 7cc8f6eb..59624832 100644..100755 --- a/test/test_all.py +++ b/test/test_all.py @@ -1,3 +1,5 @@ +#! /usr/bin/env python2 + import argparse import time import os @@ -55,7 +57,7 @@ class ArchUnitTest(RegressionTest): # script -> blacklisted jitter blacklist = { - "x86/unit/mn_float.py": ["python", "llvm"], + "x86/unit/mn_float.py": ["python"], } for script in ["x86/sem.py", "x86/unit/mn_strings.py", @@ -323,8 +325,11 @@ for i, test_args in enumerate(test_args): ## Jitter for script in ["jitload.py", "vm_mngr.py", + "jit_options.py", ]: - testset += RegressionTest([script], base_dir="jitter", tags=[TAGS["tcc"]]) + for engine in ArchUnitTest.jitter_engines: + testset += RegressionTest([script, engine], base_dir="jitter", + tags=[TAGS.get(engine,None)]) # Examples |