Created
October 28, 2016 18:41
-
-
Save elfsternberg/415e66ea6daec4f0bca6db5c1c140942 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdlib> | |
#include <err.h> | |
#include <iostream> | |
#include <cstdint> | |
#include <sys/mman.h> | |
#include <sysexits.h> | |
#include <unistd.h> | |
/* Compiles with clang 3.9. | |
clang++ -std=c++11 -o babyjit babyjit.cc | |
Compiles with GCC 4.8.5 | |
g++ -std=c++11 -o babyjit babyjit.cc | |
Example derived from: | |
https://cmcenroe.me/2016/10/13/babys-first-jit.html | |
*/ | |
typedef int32_t (*fptr)(int32_t); | |
int main(int argc, char **argv) { | |
if (argc < 2) { | |
return EX_USAGE; | |
} | |
int32_t term = static_cast<int32_t>(strtol(argv[1], NULL, 10)); | |
/* getpagesize() is deprecated */ | |
int page = sysconf(_SC_PAGESIZE); | |
/* This cast is necessary in modern C++ | |
MAP_ANON is deprecated; the manual says use MAP_ANONYMOUS instead. | |
*/ | |
uint8_t* code = reinterpret_cast<uint8_t*>(mmap(NULL, page, | |
PROT_READ | PROT_WRITE, | |
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); | |
if (code == MAP_FAILED) { | |
err(EX_OSERR, "mmap"); | |
} | |
/* | |
The prog array is a representation of the tiny assembly | |
language function below; that function was compiled with NASM | |
as-is, and the binary dumped with the command | |
xxd -g1 <binary> | cut -d':' -f2 | sed 's/ /, 0x/g; s/^, //; s/, 0x,.*$//' | |
and then pasted into the initializer for prog[] below. | |
bits 64 | |
mov rax, strict dword 0 | |
add rax, rdi | |
ret | |
This represents a naive tile of a functor of one argument that adds | |
that argument to an value "enclosed" at run-time. | |
*/ | |
int prog[] = {0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x48, 0x01, 0xf8, 0xc3}; | |
int progsize = sizeof(prog) / sizeof(prog[0]); | |
for(int i = 0; i < progsize; i++) { | |
code[i] = prog[i]; | |
} | |
/* The "JIT" part: insert a number from the command line argument | |
into the pre-compiled "tile" above. The bytes of our term have | |
to be placed into the DWORD in their little-endian order, byte | |
by byte. Fun! | |
*/ | |
code[3] = (uint8_t) term; | |
code[4] = (uint8_t) (term >> 8); | |
code[5] = (uint8_t) (term >> 16); | |
code[6] = (uint8_t) (term >> 24); | |
/* Made the code "code" rather than "data" */ | |
if (mprotect(code, page, PROT_READ | PROT_EXEC)) { | |
err(EX_OSERR, "mprotect"); | |
} | |
/* Make the JIT-compiled function accessible from C++ */ | |
fptr fn = (fptr)code; | |
/* Run the function on a couple of values, just for show. */ | |
std::cout << fn(1) << ", " << fn(2) << ", " << fn(3) << std::endl; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment