1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#-*- coding:utf-8 -*-
import os
import tempfile
import ctypes
import _ctypes
import platform
from subprocess import check_call
from distutils.sysconfig import get_python_inc
from miasm2.jitter import Jitgcc
from miasm2.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, ir_arch, bin_stream):
super(JitCore_Gcc, self).__init__(ir_arch, 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.ir_arch.loc_db.get_location_offset(label)
self.offset_to_jitted_func[offset] = addr
self.states[offset] = lib
def add_block(self, block):
"""Add a bloc to JiT and JiT it.
@block: block to jit
"""
block_hash = self.hash_block(block)
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)
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(get_python_inc(), "..", "libs", "python27.lib"))
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, 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(ir_arch, func_code):
c_source = ""
c_source += "\n".join(func_code)
c_source = gen_core(ir_arch.arch, ir_arch.attrib) + c_source
c_source = "#define PARITY_IMPORT\n#include <Python.h>\n" + c_source
return c_source
|