Skip to content

Instantly share code, notes, and snippets.

@elfsternberg
Created October 28, 2016 18:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elfsternberg/415e66ea6daec4f0bca6db5c1c140942 to your computer and use it in GitHub Desktop.
Save elfsternberg/415e66ea6daec4f0bca6db5c1c140942 to your computer and use it in GitHub Desktop.
#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