0%

Introduction to JIT

這邊是參考How to JIT - an introductionHello, JIT World: The Joy of Simple JITs 的感想。

Sample Code

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

// Allocates RWX memory of given size and returns a pointer to it. On failure,
// prints out the error and returns NULL.
void* alloc_executable_memory(size_t size) {
void* ptr = mmap(0, size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == (void*)-1) {
perror("mmap");
return NULL;
}
return ptr;
}

void emit_code_into_memory(unsigned char* m) {
unsigned char code[] = {
0x48, 0x89, 0xf8, // mov %rdi, %rax
0x48, 0x83, 0xc0, 0x04, // add $4, %rax
0xc3 // ret
};
memcpy(m, code, sizeof(code));
}

const size_t SIZE = 1024;
typedef long (*JittedFunc)(long);

// Allocates RWX memory directly.
void run_from_rwx() {
void* m = alloc_executable_memory(SIZE);
emit_code_into_memory(m);

JittedFunc func = m;
int result = func(2);
printf("result = %d\n", result);
}

我們知道,為了安全性,Process被執行的時候,只有Code section有執行的權限,在Data section或是Heap的資料無法執行。
因此上面的alloc_executable_memory要求OS分配一塊記憶體,不僅可讀寫,而且可執行。這是所有JIT不可或缺得部份。
Security的部份,可以參照上面兩偏的方法,一開始先分派城可讀寫,把Machine code複製進去之後,關閉寫入,更改成可執行。

雖然上面的程式展示了JIT如何運行,不過最大的問題是,誰看得懂emit_code_into_memory那群鬼Code是幹甚麼的,Assemlby Code都很難讀了,更何況一堆數字。
因此人門想了一堆方法,用以簡化這個過程。

Use AsmJIT for simplify work

直接看程式碼吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <asmjit/asmjit.h>
using namespace asmjit;
int main(int argc, char* argv[]) {
JitRuntime runtime;
X86Compiler c(&runtime);
c.addFunc(kFuncConvHost, FuncBuilder1<int, int>());
X86GpVar a(c, kVarTypeInt32, "a");
c.setArg(0, a);
c.add(a, 4);
c.ret(a);
c.endFunc();
void* funcPtr = c.make();
typedef int (*FuncType)(int);
FuncType func = asmjit_cast<FuncType>(funcPtr);
int x = func(2);
printf("result = %d\n", x);
runtime.release((void*)func);
return 0;
}

如果之前有看過LLVM API的話,發現他門實在相當類似。好得方法就會互相參考。
比起文章一開始的程式碼,這方式的可讀性強多了。