#-*- coding:utf-8 -*- import sys import os import tempfile import ctypes import _ctypes import platform import sysconfig from subprocess import check_call from miasm.jitter import Jitgcc from miasm.jitter.jitcore_cc_base import JitCore_Cc_Base, gen_core is_win = platform.system() == "Windows" class JitCore_Gcc(JitCore_Cc_Base): "JiT management, using a C compiler as backend" def __init__(self, lifter, bin_stream): super(JitCore_Gcc, self).__init__(lifter, bin_stream) self.exec_wrapper = Jitgcc.gcc_exec_block def deleteCB(self, offset): """Free the state associated to @offset and delete it @offset: gcc state offset """ flib = None if is_win: flib = _ctypes.FreeLibrary else: flib = _ctypes.dlclose flib(self.states[offset]._handle) del self.states[offset] def load_code(self, label, fname_so): lib = ctypes.cdll.LoadLibrary(fname_so) func = getattr(lib, self.FUNCNAME) addr = ctypes.cast(func, ctypes.c_void_p).value offset = self.lifter.loc_db.get_location_offset(label) self.offset_to_jitted_func[offset] = addr self.states[offset] = lib def add_block(self, block): """Add a block to JiT and JiT it. @block: block to jit """ block_hash = self.hash_block(block) ext = sysconfig.get_config_var('EXT_SUFFIX') if ext is None: ext = ".so" if not is_win else ".pyd" fname_out = os.path.join(self.tempdir, "%s%s" % (block_hash, ext)) if not os.access(fname_out, os.R_OK | os.X_OK): func_code = self.gen_c_code(block) # Create unique C file fdesc, fname_in = tempfile.mkstemp(suffix=".c") os.write(fdesc, func_code.encode()) os.close(fdesc) # Create unique SO file fdesc, fname_tmp = tempfile.mkstemp(suffix=ext) os.close(fdesc) inc_dir = ["-I%s" % inc for inc in self.include_files] libs = ["%s" % lib for lib in self.libs] if is_win: libs.append( os.path.join( sysconfig.get_paths()['include'], "..", "libs", "python%d%d.lib" % (sys.version_info.major, sys.version_info.minor) ) ) cl = [ "cl", "/nologo", "/W3", "/MP", "/Od", "/DNDEBUG", "/D_WINDOWS", "/Gm-", "/EHsc", "/RTC1", "/MD", "/GS", fname_in ] + inc_dir + libs cl += ["/link", "/DLL", "/OUT:" + fname_tmp] out_dir, _ = os.path.split(fname_tmp) check_call(cl, cwd = out_dir) basename_out, _ = os.path.splitext(fname_tmp) basename_in, _ = os.path.splitext(os.path.basename(fname_in)) for ext in ('.obj', '.exp', '.lib'): artifact_out_path = os.path.join( out_dir, basename_out + ext ) if os.path.isfile(artifact_out_path): os.remove(artifact_out_path) artifact_in_path = os.path.join( out_dir, basename_in + ext ) if os.path.isfile(artifact_in_path): os.remove(artifact_in_path) else: args = [ "cc", "-O3", "-shared", "-fPIC", fname_in, "-o", fname_tmp ] + inc_dir + libs check_call(args) # Move temporary file to final file try: os.rename(fname_tmp, fname_out) except WindowsError as e: # On Windows, os.rename works slightly differently than on # Linux; quoting the documentation: # "On Unix, if dst exists and is a file, it will be replaced # silently if the user has permission. The operation may fail # on some Unix flavors if src and dst are on different # filesystems. If successful, the renaming will be an atomic # operation (this is a POSIX requirement). On Windows, if dst # already exists, OSError will be raised even if it is a file; # there may be no way to implement an atomic rename when dst # names an existing file." # [Error 183] Cannot create a file when that file already exists if e.winerror != 183: raise os.remove(fname_tmp) os.remove(fname_in) self.load_code(block.loc_key, fname_out) @staticmethod def gen_C_source(lifter, func_code): c_source = "" c_source += "\n".join(func_code) c_source = gen_core(lifter.arch, lifter.attrib) + c_source c_source = "#define PARITY_IMPORT\n#include \n" + c_source return c_source