about summary refs log tree commit diff stats
path: root/src/miasm/jitter/jitcore_llvm.py
blob: 4f1871b2af9edcb7fe3e2a7c281ae2526be26c05 (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
from __future__ import print_function
import os
import glob
import importlib
import tempfile
import sysconfig

from miasm.jitter.llvmconvert import *
import miasm.jitter.jitcore as jitcore
from miasm.jitter import Jitllvm
import platform

import llvmlite
llvmlite.binding.load_library_permanently(Jitllvm.__file__)

is_win = platform.system() == "Windows"

class JitCore_LLVM(jitcore.JitCore):
    "JiT management, using LLVM as backend"

    # Architecture dependent libraries
    arch_dependent_libs = {
        "x86": "JitCore_x86",
        "arm": "JitCore_arm",
        "msp430": "JitCore_msp430",
        "mips32": "JitCore_mips32",
        "aarch64": "JitCore_aarch64",
        "ppc32": "JitCore_ppc32",
    }

    def __init__(self, lifter, bin_stream):
        super(JitCore_LLVM, self).__init__(lifter, bin_stream)

        self.options.update(
            {
                "safe_mode": True,   # Verify each function
                "optimise": True,     # Optimise functions
                "log_func": False,    # Print LLVM functions
                "log_assembly": False,  # Print assembly executed
            }
        )

        self.exec_wrapper = Jitllvm.llvm_exec_block
        self.lifter = lifter

        # Cache temporary dir
        self.tempdir = os.path.join(tempfile.gettempdir(), "miasm_cache")
        try:
            os.mkdir(self.tempdir, 0o755)
        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
        libs_to_load = []

        # Get architecture dependent Jitcore library (if any)
        lib_dir = os.path.dirname(os.path.realpath(__file__))
        ext = sysconfig.get_config_var('EXT_SUFFIX')
        if ext is None:
            ext = ".so" if not is_win else ".pyd"
        if is_win:
            # sysconfig.get_config_var('EXT_SUFFIX') is .pyd on Windows and need to be forced to .lib
            # Additionally windows built libraries may have a name like VmMngr.cp38-win_amd64.lib
            ext_files = glob.glob(os.path.join(lib_dir, "VmMngr.*pyd"))
            if len(ext_files) == 1:
                ext = os.path.basename(ext_files[0]).replace("VmMngr", "")
        lib_dir = os.path.join(lib_dir, 'arch')
        try:
            jit_lib = os.path.join(
                lib_dir, self.arch_dependent_libs[self.lifter.arch.name] + ext
            )
            libs_to_load.append(jit_lib)
        except KeyError:
            pass

        # Create a context
        self.context = LLVMContext_JIT(libs_to_load, self.lifter)

        # Set the optimisation level
        self.context.optimise_level()

        # Save the current architecture parameters
        self.arch = self.lifter.arch

        # Get the correspondence between registers and vmcpu struct
        mod_name = "miasm.jitter.arch.JitCore_%s" % (self.lifter.arch.name)
        mod = importlib.import_module(mod_name)
        self.context.set_vmcpu(mod.get_gpreg_offset_all())

        # Enable caching
        self.context.enable_cache()

    def add_block(self, block):
        """Add a block to JiT and JiT it.
        @block: the block to add
        """

        block_hash = self.hash_block(block)
        fname_out = os.path.join(self.tempdir, "%s.bc" % block_hash)

        if not os.access(fname_out, os.R_OK):
            # Build a function in the context
            func = LLVMFunction(self.context, self.FUNCNAME)

            # Set log level
            func.log_regs = self.log_regs
            func.log_mn = self.log_mn

            # Import asm block
            func.from_asmblock(block)

            # Verify
            if self.options["safe_mode"] is True:
                func.verify()

            # 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())

            # 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, self.FUNCNAME)

        # Store a pointer on the function jitted code
        loc_key = block.loc_key
        offset = self.lifter.loc_db.get_location_offset(loc_key)
        self.offset_to_jitted_func[offset] = ptr