Skip to content

Instantly share code, notes, and snippets.

@rrampage
Last active July 9, 2025 01:26
Show Gist options
  • Save rrampage/8d1d2d7ec73ff164e589ae91815fbfdb to your computer and use it in GitHub Desktop.
Save rrampage/8d1d2d7ec73ff164e589ae91815fbfdb to your computer and use it in GitHub Desktop.
JIT calculator
// as -o /tmp/calc.o calc.S && ld -s -o /tmp/calc /tmp/calc.o && strip --strip-section-headers /tmp/calc
.equ SYS_MEMFD_CREATE, 279
.equ SYS_FTRUNCATE, 46
.equ SYS_MMAP, 222
.equ SYS_READ, 63
.equ SYS_WRITE, 64
.equ SYS_EXIT, 93
.equ BUF_SIZE, 64
.equ PAGE_SIZE, 4096
.equ PROT_READ, 1
.equ PROT_WRITE, 2
.equ PROT_EXEC, 4
.equ MAP_SHARED, 1
.equ STDIN, 0
.equ STDOUT, 1
.section .rodata
jit_buf: .ascii "jit_buf"
.bss
buffer: .space BUF_SIZE
output_buffer: .space 16
.text
.global _start
//brk_val: .word 0xd4200000
// mov_x0_0: .word 0xd2800000
// mov_x1_2: .word 0xd2800041
mul_x0_2: .word 0x9b017c00 // ascii 42
add_x0_1: .word 0x91000400 // ascii 43
// NOP
nop_0: .word 0xd503201f // ignore
sub_x0_1: .word 0xd1000400 // ascii 45
// NOP
nop_1: .word 0xd503201f // ignore
sdiv_x0_2: .word 0x9ac10c00 // ascii 47
//mov_x8_93: .word 0xd2800ba8
//svc_0: .word 0xd4000001
ret_val: .word 0xd65f03c0
_start:
// memfd_create
adr x0, jit_buf
mov x8, #SYS_MEMFD_CREATE
svc #0
cmp x0, #0
blt exit
mov x19, x0 // save fd
mov x1, #PAGE_SIZE
mov x8, #SYS_FTRUNCATE
svc #0
cmp x0, #0
blt exit
// syscall6(SYS_mmap, NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
mov x0, #0
// mov x1, #PAGE_SIZE
mov x2, #(PROT_READ | PROT_WRITE)
mov x3, #MAP_SHARED
mov x4, x19
mov x5, #0
mov x8, #SYS_MMAP
svc #0
cmp x0, #0
blt exit
mov x20, x0 // rw mapping
mov x0, #0
// mov x1, #PAGE_SIZE
mov x2, #(PROT_READ | PROT_EXEC)
mov x3, #MAP_SHARED
// mov x4, x19
// mov x5, #0
// mov x8, #SYS_MMAP
svc #0
cmp x0, #0
blt exit
mov x21, x0 // rx mapping
// read
mov x8, #SYS_READ
mov x0, #STDIN
adr x1, buffer
mov x2, #BUF_SIZE
svc #0
cmp x0, #0
blt exit
mov x22, x0 // nread
// jit buffer write
mov x0, #0
mov x1, #0
adr x4, buffer
// Read the rest of string and write instructions
parse_input:
cmp x1, x22
b.ge done
ldrb w3, [x4, x1]
add x1, x1, #1
subs w6, w3, #'*' // sub 42 and jump to correct instruction
b.mi parse_input
cmp w6, 6
b.ge parse_input
adr x5, mul_x0_2
lsl x6, x6, 2
ldr x2, [x5, x6]
str x2, [x20, x0]
add x0, x0, #4
b parse_input
done:
ldr w2, ret_val
str w2, [x20, x0]
mov x0, #0
mov x1, #2
blr x21
// Print output to stdout
mov x10, x0
mov x6, #10 // Constant 10 for div
mov x2, #14 // i=14
adr x1, output_buffer
mov w3, #'\n'
strb w3, [x1, 15]
clz x5, x0
cmp x5, #0
b.gt int_loop
mov x7, #1 // sign
sub x10, xzr, x10 // negated number
int_loop:
sdiv x11, x10, x6 // div by 10
msub x3, x11, x6, x10 // modulo 10
add x3, x3, #'0'
strb w3, [x1, x2]
mov x10, x11
sub x2, x2, #1
cmp x10, #0
b.gt int_loop
cmp x7, #1
b.ne rest
mov w3, #'-'
strb w3, [x1, x2]
sub x2, x2, #1
rest:
mov x8, #SYS_WRITE
mov x0, #STDOUT
add x1, x1, x2
sub x2, xzr, x2
add x2, x2, #16
svc #0
exit:
mov w8, 93
svc #0
/**
* Compiled with:
gcc -s -O3 -ffreestanding -nostdlib -nostartfiles -fno-stack-protector -Wl,--entry=_start -Wl,--gc-sections -Wl,-z,now -nostdinc -static -o cjit jit_calc.c
*/
typedef unsigned long long size_t;
typedef unsigned char u8;
typedef unsigned int u32;
#define NULL 0
#define BUF_SIZE 64
#define SYS_memfd_create 279
#define SYS_ftruncate 46
#define SYS_mmap 222
#define SYS_read 63
#define SYS_exit 93
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4
#define MAP_SHARED 1
#define syscall1(num, arg1) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r"(_arg1) \
: "r"(_arg1), \
"r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define syscall3(num, arg1, arg2, arg3) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
register long _arg3 __asm__ ("x2") = (long)(arg3); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r" (_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
#define syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
register long _num __asm__ ("x8") = (num); \
register long _arg1 __asm__ ("x0") = (long)(arg1); \
register long _arg2 __asm__ ("x1") = (long)(arg2); \
register long _arg3 __asm__ ("x2") = (long)(arg3); \
register long _arg4 __asm__ ("x3") = (long)(arg4); \
register long _arg5 __asm__ ("x4") = (long)(arg5); \
register long _arg6 __asm__ ("x5") = (long)(arg6); \
\
__asm__ volatile ( \
"svc #0\n" \
: "=r" (_arg1) \
: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
"r"(_arg6), "r"(_num) \
: "memory", "cc" \
); \
_arg1; \
})
/*
Sample strace output:
memfd_create("jit_buf", 0) = 3
ftruncate(3, 4096) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0xe56cd91e6000
mmap(NULL, 4096, PROT_READ|PROT_EXEC, MAP_SHARED, 3, 0) = 0xe56cd91e5000
exit(42)
*/
#define MOV_X0_0 0xd2800000
#define MOV_X1_2 0xd2800041
#define ADD_X0_X0_1 0x91000400
#define SUB_X0_X0_1 0xd1000400
#define MUL_X0_X0_X1 0x9b017c00
#define SDIV_X0_X0_X1 0x9ac10c00
#define MOV_X8_93 0xd2800ba8
#define SVC_0 0xd4000001
int jit(u32* code, const char* input, int n) {
/*
ans: x0
mov x0, #0
+: add x0, x0, 1
-: sub x0, x0, 1
*: mul x0, x0, 2
/: div x0, x0, 2
78: d2800000 mov x0, #0x0 // #0
7c: d2800041 mov x1, #0x2 // #2
80: 91000400 add x0, x0, #0x1
84: d1000400 sub x0, x0, #0x1
88: 9b017c00 mul x0, x0, x1
8c: 9ac10c00 sdiv x0, x0, x1
90: d2800ba8 mov x8, #0x5d // #93
94: d4000001 svc #0x0
*/
int i = 0;
code[i++] = MOV_X0_0;
code[i++] = MOV_X1_2;
for (int j = 0; j < n; j++) {
char c = input[j];
switch (c)
{
case '+':
code[i++] = ADD_X0_X0_1;
break;
case '-':
code[i++] = SUB_X0_X0_1;
break;
case '*':
code[i++] = MUL_X0_X0_X1;
break;
case '/':
code[i++] = SDIV_X0_X0_X1;
break;
default:
break;
}
}
// Outputs value of x0 as exit code
code[i++] = MOV_X8_93;
code[i++] = SVC_0;
return i;
}
void main() {
char input[BUF_SIZE];
size_t pagesize = 4096;
int fd = syscall1(SYS_memfd_create, "jit_buf");
syscall3(SYS_ftruncate, fd, pagesize, 0);
// Create RW and RX mappings
u8 *rw = (void*)syscall6(SYS_mmap, NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
void *rx = (void*)syscall6(SYS_mmap, NULL, pagesize, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
int nread = syscall3(SYS_read, 0, input, BUF_SIZE);
if (nread <= 0) { syscall1(SYS_exit, nread);}
u32 *code = (u32 *)rw;
int code_size = jit(code, input, nread);
((void(*)())rx)();
}
__attribute__((section(".text.startup"))) void _start(void) {
#if defined(__aarch64__)
asm volatile (
"mov fp, #0\n\t"
"mov lr, #0\n\t"
"mov x0, sp\n\t"
"and sp, x0, #-16\n\t"
"bl %0\n\t"
:
: [main] "X"(&main)
:"x11", "memory"
);
#else
#error "Unsupported architecture"
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment