SRL Archive - C/C++ Help and Tutorials

Brainfuck JIT Compiler


R0b0t1

2009-09-16 07:57:35

How do you highlight for different languages? I can't remember.


Anyway, this program reads in a string that represents a brainfuck program, it then compiles it into the RAM, and jumps to it like a function, running it. Theoretically you could have a thread run it, and then use a different thread to give it input.

Only works on 64bit Linux, but I'm trying to fix that (main problem is that I use the x64 calling convention to preload registers with stuff, on x86 that doesn't happen).


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/user.h>

#define BIT64
#define bfcall(arr, tape, inarr, outarr) \
((int (*)(char *, char *, char *))func)(tape, inarr, outarr)

typedef int (*BFFUNC) (char*, char*, char*);
char *compile(char *f, int flen);

int main()
{
char f[] = ",.[,.]";
//0x31, 0xC0, 0xC3 //xor %eax, %eax; ret;
//0x89, 0xF8, 0xC3 //mov %edi, %eax; ret;
//0xFF, 0xF7, 0x8F, 0xC0, 0xC3 //push %edi; pop %eax; ret;
//0xB8, 0x10, 0x00, 0x00, 0x00, 0xC3 //mov $16, %eax; ret;
char *func, tape[128], inarr[128], outarr[128];
int ret = 0;

char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
memset(tape, 0, sizeof(tape));
memcpy(inarr, input, sizeof(input));
memset(outarr, 0, sizeof(outarr));

func = compile(f, 2048);
//edi esi edx
ret = ((BFFUNC)func)(tape, inarr, outarr);//bfcall(func, tape, inarr, outarr);
printf("%d\n%s\n", ret, outarr);

free(func);
return 0;
}

char *compile(char *f, int flen)
{
int i = 0, bcnt = 0, jmp = 0; char *func, *pp = f, *bstack[128];

if(posix_memalign((void **)&func, PAGE_SIZE, flen)) return NULL;
if(mprotect(func, 5, PROT_EXEC | PROT_WRITE)) { free(func); return NULL; }

for(; *pp && i < flen; pp++)
{
switch(*pp)
{
case '+': func[i++] = 0xFE; func[i++] = 0x07; break; // inc (%edi)
case '-': func[i++] = 0xFE; func[i++] = 0x0F; break; // dec (%edi)
case '>':
#ifdef BIT64
func[i++] = 0x48; // REX.W byte for 64bit addresses.
#endif
func[i++] = 0xFF; func[i++] = 0xC7; break; // inc %edi
case ',': func[i++] = 0xA4; // movsb
case '<': // fallthrough to dec %edi.
#ifdef BIT64
func[i++] = 0x48;
#endif
func[i++] = 0xFF; func[i++] = 0xCF; break; // dec %edi
case '.':
func[i++] = 0x8A; func[i++] = 0x07; //
func[i++] = 0x88; func[i++] = 0x02; //
#ifdef BIT64
func[i++] = 0x48;
#endif
func[i++] = 0xFF; func[i++] = 0xC2; // inc %edx
break;
case '[': bstack[bcnt++] = &func[i];
func[i++] = 0x80; func[i++] = 0x3F; func[i++] = 0x00;
func[i++] = 0x0F; func[i++] = 0x84; i += 4;
break;
case ']': func[i++] = 0x80; func[i++] = 0x3F; func[i++] = 0x00;
func[i++] = 0x0F; func[i++] = 0x85;
jmp = bstack[--bcnt] - &func[i + 4];
memcpy(&func[i], &jmp, 4); i += 4;
jmp = -(jmp + 9);
memcpy(bstack[bcnt] + 5, &jmp, 4);
break;
}
}
func[i++] = 0x31; func[i++] = 0xC0; // clear eax
func[i++] = 0x8A; func[i++] = 0x07; // move current cell to al
func[i] = 0xC3;

return func;
}

ian.

2009-09-16 08:00:10

 

Dunno about the other stuff though.. Sorry :(

Yakman

2009-09-16 15:15:55

awesome!

boberman

2009-09-19 23:23:55

If you want something in the register, isn't that easily fixed with a mov command? Yes, you don't get preloaded registers, but a mov isn't all THAT expensive. (unless you are talking about getting stuff from the stack, then you should use pushes and pops, but that's not too bad either).

Laur€ns

2009-09-20 15:09:42

watthefuck