Skip to content

Instantly share code, notes, and snippets.

@jdmichaud
Created November 26, 2025 18:07
Show Gist options
  • Select an option

  • Save jdmichaud/481698c3da0691cec3ef7a9bccfe3231 to your computer and use it in GitHub Desktop.

Select an option

Save jdmichaud/481698c3da0691cec3ef7a9bccfe3231 to your computer and use it in GitHub Desktop.
jit
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
int main() {
/*
This is raw x86-64 machine code for a TINY *leaf function*:
lea eax, [rdi + 1] ; compute (arg + 1)
ret ; return
Why this works without stack manipulation:
- According to the Linux x86-64 System V ABI:
• The first integer argument is passed in RDI
• The return value must be placed in EAX
• Leaf functions do NOT need a stack frame
as long as they:
- don’t touch the stack
- don’t use local variables
- don’t call other functions
- don’t modify callee-saved registers
(RBX, RBP, R12–R15)
• The *caller* is responsible for RSP alignment
before calling the function
Because our function:
- uses only RDI → EAX
- does not use the stack
- does not touch callee-saved registers
- returns immediately
…it is a completely valid function without:
push rbp
mov rbp, rsp
sub rsp, XXX
(i.e. the usual prologue/epilogue)
This is why JIT engines often generate tiny stackless stubs.
*/
unsigned char code[] = {
0x8D, 0x47, 0x01, // lea eax, [rdi + 1]
0xC3 // ret
};
/*
JIT STEP 1: Allocate executable memory
We ask Linux for a memory region that is:
- writable (so we can place instructions)
- executable (so the CPU can run them)
In real JITs, this is usually done in two steps
(W^X security): first WRITE, then mprotect() to EXEC.
For simplicity, we request both at once.
*/
void* mem = mmap(NULL, sizeof(code),
PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
/*
JIT STEP 2: Copy our generated machine code into that memory.
After this, the buffer contains valid executable instructions.
*/
memcpy(mem, code, sizeof(code));
/*
JIT STEP 3: Treat the memory region as a function.
We cast the pointer to a function pointer type whose
signature matches the code we wrote:
int func(int)
The ABI ensures:
- Argument arrives in RDI
- Return value in EAX
- Caller has aligned the stack
*/
int (*func)(int) = mem;
/*
JIT STEP 4: Call it like a normal function.
The CPU jumps into our generated code and executes it.
*/
printf("%d\n", func(41)); // prints 42
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment