about summary refs log tree commit diff stats
path: root/miasm2/jitter/jitcore_tcc.py
blob: 1bb25a3a2399e2ecfb96519c2d1059427d25ee59 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import os
from distutils.sysconfig import get_python_inc
from subprocess import Popen, PIPE
from hashlib import md5
import tempfile

from miasm2.jitter import jitcore, Jittcc


def jit_tcc_compil(func_name, func_code):
    global Jittcc
    c = Jittcc.tcc_compil(func_name, func_code)
    return c


def gen_core(arch, attrib):
    lib_dir = os.path.dirname(os.path.realpath(__file__))

    txt = ""
    txt += '#include "%s/queue.h"\n' % lib_dir
    txt += '#include "%s/vm_mngr.h"\n' % lib_dir
    txt += '#include "%s/vm_mngr_py.h"\n' % lib_dir
    txt += '#include "%s/JitCore.h"\n' % lib_dir
    txt += '#include "%s/arch/JitCore_%s.h"\n' % (lib_dir, arch.name)

    txt += r'''
#define RAISE(errtype, msg) {PyObject* p; p = PyErr_Format( errtype, msg ); return p;}
'''
    return txt


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 = """
 #ifdef __x86_64__
 #ifndef __LP64__
 /*
  for ubuntu ?!? XXX TODO
  /!\ force 64 bit system using 64 bits libc
  change this to __ILP32__ to do so.
 */
 #define __LP64__
 #endif
 #endif
 """ + "#include <Python.h>\n" + c_source

    return c_source


class myresolver:

    def __init__(self, offset):
        self.offset = offset

    def ret(self):
        return "return PyLong_FromUnsignedLongLong(0x%X);" % self.offset

from miasm2.core.utils import keydefaultdict


class resolver:

    def __init__(self):
        self.resolvers = keydefaultdict(myresolver)

    def get_resolver(self, offset):
        return self.resolvers[offset]


class JitCore_Tcc(jitcore.JitCore):

    "JiT management, using LibTCC as backend"

    def __init__(self, ir_arch, bs=None):
        self.jitted_block_delete_cb = self.deleteCB
        super(JitCore_Tcc, self).__init__(ir_arch, bs)
        self.resolver = resolver()
        self.exec_wrapper = Jittcc.tcc_exec_bloc
        self.tcc_states = {}
        self.ir_arch = ir_arch

        self.tempdir = os.path.join(tempfile.gettempdir(), "miasm_gcc_cache")
        try:
            os.mkdir(self.tempdir, 0755)
        except OSError:
            pass

    def deleteCB(self, offset):
        "Free the TCCState corresponding to @offset"
        if offset in self.tcc_states:
            Jittcc.tcc_end(self.tcc_states[offset])
            del self.tcc_states[offset]

    def load(self):
        # os.path.join(os.path.dirname(os.path.realpath(__file__)), "jitter")
        lib_dir = os.path.dirname(os.path.realpath(__file__))
        libs = []
        libs.append(os.path.join(lib_dir, 'VmMngr.so'))
        libs.append(
            os.path.join(lib_dir, 'arch/JitCore_%s.so' % (self.ir_arch.arch.name)))
        libs = ';'.join(libs)
        jittcc_path = Jittcc.__file__
        include_dir = os.path.dirname(jittcc_path)
        include_dir += ";" + os.path.join(include_dir, "arch")
        # print include_dir

        # XXX HACK
        # As debian/ubuntu have moved some include files using arch directory,
        # TCC doesn't know them, so we get the info from GCC
        # For example /usr/include/x86_64-linux-gnu which contains limits.h
        p = Popen(["cc", "-Wp,-v", "-E", "-"],
                  stdout=PIPE, stderr=PIPE, stdin=PIPE)
        p.stdin.close()
        include_files = p.stderr.read().split('\n')
        include_files = [x[1:]
                         for x in include_files if x.startswith(' /usr/include')]
        include_files += [include_dir, get_python_inc()]
        include_files = ";".join(include_files)
        Jittcc.tcc_set_emul_lib_path(include_files, libs)

    def init_codegen(self, codegen):
        """
        Get the code generator @codegen
        @codegen: an CGen instance
        """
        self.codegen = codegen

    def __del__(self):
        for tcc_state in self.tcc_states.values():
            Jittcc.tcc_end(tcc_state)

    def label2fname(self, label):
        """
        Generate function name from @label
        @label: asm_label instance
        """
        return "block_%s" % label.name

    def compil_code(self, block, func_code):
        """
        Compil the C code of @func_code from @block
        @block: original asm_block
        @func_code: C code of the block
        """
        label = block.label
        self.jitcount += 1
        tcc_state, mcode = jit_tcc_compil(self.label2fname(label), func_code)
        self.lbl2jitbloc[label.offset] = mcode
        self.tcc_states[label.offset] = tcc_state

    def gen_c_code(self, label, block):
        """
        Return the C code corresponding to the @irblocks
        @label: asm_label of the block to jit
        @irblocks: list of irblocks
        """
        f_name = self.label2fname(label)
        f_declaration = 'int %s(block_id * BlockDst, JitCpu* jitcpu)' % f_name
        out = self.codegen.gen_c(block, log_mn=self.log_mn, log_regs=self.log_regs)
        out = [f_declaration + '{'] + out + ['}\n']
        c_code = out

        return gen_C_source(self.ir_arch, c_code)

    def add_bloc(self, block):
        """Add a bloc to JiT and JiT it.
        @block: block to jit
        """
        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()
        fname_out = os.path.join(self.tempdir, "%s.c" % block_hash)
        if os.access(fname_out, os.R_OK):
            func_code = open(fname_out).read()
        else:
            func_code = self.gen_c_code(block.label, block)

            # Create unique C file
            fdesc, fname_tmp = tempfile.mkstemp(suffix=".c")
            os.write(fdesc, func_code)
            os.close(fdesc)
            os.rename(fname_tmp, fname_out)

        self.compil_code(block, func_code)