about summary refs log tree commit diff stats
path: root/miasm2/jitter/jitcore_gcc.py
blob: 5fd54c3d8dfb8eda98645a49371dc10496dee354 (plain) (blame)
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#-*- coding:utf-8 -*-

import os
import tempfile
import ctypes
import _ctypes
import platform
import sysconfig
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 = 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(
                        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 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(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