#include #include #include #include #include #include #include #define DBGTIME(tag,block) \ do { \ clock_t start; \ if(params.verbose){ \ start=clock(); \ fprintf(stderr,"%s",(tag)); \ } \ {block} \ if(params.verbose){ \ fprintf(stderr," (%lfs)\n",(double)(clock()-start)/CLOCKS_PER_SEC); \ } \ } while(0) int uniqid(void){ static int i=0; return i++; } void usage(const char *argv0){ fprintf(stderr, "Usage: %s [-h] [-m ] \n" "Reads a Brainfuck program from and saves the compiled executable\n" "in .\n" "Uses nasm for assembling and gcc for linking.\n" " -h Show help\n" " -m Specify amount of tape cells (bytes) for the BF code\n" " -v Show more info about the compile process\n", argv0); } __attribute__((noreturn)) void usage1(const char *argv0){ usage(argv0); exit(1); } typedef struct Params{ int memsize; const char *srcfname,*dstfname; bool verbose; } Params; Params params={30000,NULL,NULL,false}; void parseargs(int argc,char **argv){ if(argc<2)usage1(argv[0]); for(int i=1;i=0); if(pid==0){ /*dup2(in,0); dup2(out,1);*/ execl("/bin/sh","/bin/sh","-c",cmd,NULL); __builtin_unreachable(); } else { int statloc; DBGTIME(cmd,{ while(true){ waitpid(pid,&statloc,0); if(WIFEXITED(statloc))break; } }); return WEXITSTATUS(statloc); } } FILE* gettempfilew(char name[20]){ memcpy(name,"/tmp/tmp.XXXXXXXXXX",20); int fd=mkstemp(name); assert(fd>=0); FILE *f=fdopen(fd,"w"); assert(f); return f; } int compilechain(const char *asmfname,const char *dstfname){ char ofname[20]; FILE *f=gettempfilew(ofname); if(params.verbose)fprintf(stderr,"o -> %s\n",ofname); assert(strlen(ofname)==19); fclose(f); char buf[25+19+1+strlen(asmfname)+1]; sprintf(buf,"nasm -f macho64 -w+all -o %s %s",ofname,asmfname); int ret=runcmd(buf); if(ret)return ret; char buf2[4+19+4+strlen(dstfname)+1]; sprintf(buf2,"gcc %s -o %s",ofname,dstfname); ret=runcmd(buf2); unlink(ofname); if(params.verbose)fprintf(stderr,"unlink(%s) (ofile)\n",ofname); return ret; } void writeprologue(FILE *asmf){ fprintf(asmf, "global _main\n" "\n" "extern _malloc\n" "extern _free\n" "extern _printf\n" "extern _putchar\n" "extern _write\n" "\n" "default rel\n" "\n" "section .text\n" "\n" "_main:\n" "\tpush rbp ; prologue\n" "\tmov rbp, rsp\n" "\tadd rsp, 8\n" "\tpush rbx\n" "\tmov edi, %d\n" "\tcall _malloc\n" "\tcmp rax, 0\n" "\tjz nomem\n" "\tmov rbx, rax ;bufp\n" "\tmov [buf], rax ;buf\n" "\n", params.memsize); } void writeprogram(FILE *srcf,FILE *asmf){ int addc=0,shiftc=0; //int lastshiftc=0; struct stackitem {int id; struct stackitem *next;} *loopstack=NULL; bool eof=false; while(!eof){ char c=fgetc(srcf); if(feof(srcf))eof=1; if(!eof&&strchr("+-><[].,",c)==NULL)continue; if(addc&&c!='+'&&c!='-'){ addc=(addc%256)+256%256; if(addc==1)fprintf(asmf,"\tinc byte [rbx] ; 1 +\n"); else if(addc==-1)fprintf(asmf,"\tdec byte [rbx] ; 1 -\n"); else fprintf(asmf,"\tadd byte [rbx], %d ; %d %c\n",addc,addc<0?-addc:addc,addc<0?'-':'+'); addc=0; } if(shiftc&&c!='>'&&c!='<'){ shiftc=(shiftc%256)+256%256; /*if(lastshiftc>0&&shiftc<0){ int id=uniqid(); fprintf(asmf, "\tcmp [maxp], rbx\n" "\tjge skip%d\n" "\tmov [maxp], rbx\n" "skip%d:\n", id,id); } lastshiftc=shiftc;*/ if(shiftc==1)fprintf(asmf,"\tinc rbx ; 1 >\n"); else if(shiftc==-1)fprintf(asmf,"\tdec rbx ; 1 <\n"); else fprintf(asmf,"\tadd rbx, %d ; %d %c\n",shiftc,shiftc<0?-shiftc:shiftc,shiftc<0?'<':'>'); shiftc=0; } switch(c){ case '+': addc++; break; case '-': addc--; break; case '>': shiftc++; break; case '<': shiftc--; break; case '[':{ struct stackitem *ls=malloc(sizeof(struct stackitem)); assert(ls); ls->id=uniqid(); ls->next=loopstack; loopstack=ls; fprintf(asmf, "loop%d: ; [\n" "\tcmp byte [rbx], 0\n" "\tjz loop%d_end\n" "loop%d_body:\n", ls->id,ls->id,ls->id); break; } case ']':{ assert(loopstack); fprintf(asmf, "\tcmp byte [rbx], 0 ; ]\n" "\tjnz loop%d_body\n" "loop%d_end:\n", loopstack->id,loopstack->id); struct stackitem *ls=loopstack->next; free(loopstack); loopstack=ls; break; } case '.': fprintf(asmf, "\tmov edi, 0 ; .\n" "\tmov dil, byte [rbx]\n" "\tcall _putchar\n"); break; case ',': fprintf(asmf, "\tcall _getchar ; ,\n" "\tmov byte [rbx], al\n"); break; default: if(!eof)assert(false); } } assert(!loopstack); } void writeepilogue(FILE *asmf){ fprintf(asmf, "\n" "\tmov rdi, [buf] ; epilogue\n" "\tcall _free\n" "\n" /*"\tlea rdi, [formatstr] ;output last cell reached\n" "\tmov rsi, [maxp]\n" "\tsub rsi, [buf]\n" "\txor eax, eax\n" "\tcall _printf\n" "\n"*/ "\txor eax, eax\n" "\tjmp mainend\n" "\n" "nomem:\n" "\tmov edi, 2\n" "\tlea rsi, [nomemstr]\n" "\tmov rdx, nomemstr.len\n" "\tcall _write\n" "\tmov eax, 1\n" "\n" "mainend:\n" "\tpop rbx\n" "\tmov rsp, rbp\n" "\tpop rbp\n" "\tret\n" "\n" "\n" "section .data\n" "sectalign 8\n" "\n" "nomemstr: db \"Out of memory!\"\n" ".len: equ $-nomemstr\n" "\n" "formatstr: db \"Last cell reached: %%d\", 10, 0\n" "\n" "align 8, db 0\n" "maxp: dq 0\n" "\n" "\n" "section .bss\n" "sectalign 8\n" "\n" "buf: resq 1\n"); } int main(int argc,char **argv){ parseargs(argc,argv); FILE *srcf=fopen(params.srcfname,"r"); assert(srcf); char asmfname[20]; FILE *asmf=gettempfilew(asmfname); if(params.verbose)fprintf(stderr,"asm -> %s\n",asmfname); DBGTIME("writeprologue",writeprologue(asmf);); DBGTIME("writeprogram",writeprogram(srcf,asmf);); DBGTIME("writeepilogue",writeepilogue(asmf);); fclose(asmf); fclose(srcf); int ret=compilechain(asmfname,params.dstfname); unlink(asmfname); if(params.verbose)fprintf(stderr,"unlink(%s) (asmfname)\n",asmfname); return ret; }