Compare commits
3 commits
a93885868c
...
f5bebc56b8
| Author | SHA1 | Date | |
|---|---|---|---|
| f5bebc56b8 | |||
| 6ab1331032 | |||
| a9ac2eeff9 |
7 changed files with 180 additions and 119 deletions
260
bfc.asm
260
bfc.asm
|
|
@ -1,4 +1,4 @@
|
|||
; bfc : a brainfuck compiler & interpreter
|
||||
; bfc : a brainfuck compiler
|
||||
; Copyright (C) 2018 Weber Yann
|
||||
;
|
||||
; This program is free software; you can redistribute it and/or modify
|
||||
|
|
@ -14,14 +14,13 @@
|
|||
; You should have received a copy of the GNU General Public License
|
||||
; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
;
|
||||
; A brainfuck compiler & interpreter :
|
||||
; A brainfuck compiler :
|
||||
; Build : nasm -felf64 bfc.asm && ld -s -melf_x86_64 bfc.o -o bfc
|
||||
;
|
||||
; ./bfc [-h] [-e [-o a.out]] FILE.bf
|
||||
; ./bfc [-h] [-o a.out] FILE.bf
|
||||
; Options :
|
||||
; -h print usage and exit
|
||||
; -e tell bfc to produce a elf file
|
||||
; -o with -e indicate the file to create
|
||||
; -o indicate the file to create default is a.out
|
||||
; FILE.bf the brainfuck source file to compile
|
||||
[bits 64]
|
||||
%use smartalign
|
||||
|
|
@ -34,22 +33,33 @@ ALIGNMODE k8
|
|||
%define MAP_INC_MASK 0x0FFF
|
||||
%define BFMEM_INIT_SZ MAP_INC_SIZE
|
||||
|
||||
%define ELF_CODE_OFFSET 0x00400080
|
||||
|
||||
section .data
|
||||
|
||||
bf_start_head:
|
||||
push 0x00400085
|
||||
;dw 0x6885004000
|
||||
bf_start_head_sz: equ $ - bf_start_head
|
||||
bf_start:
|
||||
jmp .start
|
||||
.mremap: ; rbx is resize size
|
||||
cmp rbx, MAP_INC_SIZE
|
||||
jg .remap_cont
|
||||
xor rbx, rbx
|
||||
.remap_cont:
|
||||
xor rbx, MAP_INC_MASK
|
||||
add rbx, MAP_INC_SIZE
|
||||
add r14, rbx
|
||||
.mremap:
|
||||
; Resize the mmap
|
||||
; rbx is resize min size
|
||||
; rdi is map ptr offset
|
||||
; r14 is map_len
|
||||
; r15 is map_addr
|
||||
; Returns :
|
||||
; r15 new map_addr
|
||||
; r14 new map_len
|
||||
; rdi map ptr offset
|
||||
; rsi map ptr
|
||||
push rdi
|
||||
|
||||
; set min resize
|
||||
xor rdx, rdx
|
||||
.loop_min_remap:
|
||||
add rdx, MAP_INC_SIZE
|
||||
cmp rdx, rbx
|
||||
jl .loop_min_remap
|
||||
add r14, rdx ; newlen
|
||||
|
||||
mov rax, 0x19 ; mremap
|
||||
mov rdi, r15 ; addr
|
||||
mov rsi, r14 ; oldlen
|
||||
|
|
@ -61,6 +71,10 @@ section .data
|
|||
cmp rax, 0
|
||||
jle .erremap
|
||||
mov r15, rax
|
||||
; restore rsi & rdi
|
||||
pop rdi
|
||||
mov rsi, r15
|
||||
add rsi, rdi
|
||||
ret
|
||||
.erremap:
|
||||
mov rax, 0x3c
|
||||
|
|
@ -68,23 +82,55 @@ section .data
|
|||
syscall
|
||||
align 8
|
||||
.lbl_incresize: equ $ - bf_start
|
||||
.incresize: ; rbx is resize size
|
||||
|
||||
.incresize:
|
||||
; Resize the map on ptr increment
|
||||
; rbx is increment count
|
||||
; rdi is map ptr offset
|
||||
; r14 is map_len
|
||||
; r15 is map_addr
|
||||
call .mremap
|
||||
ret
|
||||
align 8
|
||||
.lbl_decresize: equ $ - bf_start
|
||||
.decresize: ; rbx is decrement
|
||||
push rsi
|
||||
push r14
|
||||
|
||||
.decresize:
|
||||
; Resize the map on ptr decrement
|
||||
; rbx is decrement count
|
||||
; rdi is map ptr offset
|
||||
; r14 is map_len
|
||||
; r15 is map_addr
|
||||
; Note : data has to be shifted
|
||||
push r14 ; old map len
|
||||
call .incresize
|
||||
pop rcx ; old size
|
||||
; shift datas
|
||||
pop rcx ; old len
|
||||
mov rbx, r14 ; new_len
|
||||
push rsi
|
||||
push rdi
|
||||
mov rsi, r15
|
||||
mov rdi, rsi
|
||||
inc rdi
|
||||
.decresize_cpy:
|
||||
add rsi, rcx ; add old_len to map_ptr
|
||||
add rdi, rbx ; add new_len to map_ptr
|
||||
sub rbx, rcx ; new_len - old_len : resize
|
||||
std ; set DF to dec rsi & rdi
|
||||
.decresize_shift:
|
||||
movsb
|
||||
loop .decresize_cpy
|
||||
mov byte [r15], 0
|
||||
loop .decresize_shift
|
||||
; set first map bytes to 0
|
||||
mov rdi, r15
|
||||
add rdi, rbx
|
||||
dec rdi
|
||||
mov rcx, rbx
|
||||
.decresize_zeros:
|
||||
mov byte [rdi], 0x0
|
||||
dec rdi
|
||||
loop .decresize_zeros
|
||||
pop rdi
|
||||
pop rsi
|
||||
; update map_ptr & offset given the shift
|
||||
add rdi, rbx
|
||||
add rsi, rbx
|
||||
ret
|
||||
.errmap:
|
||||
mov rax, 0x3c
|
||||
|
|
@ -103,67 +149,74 @@ section .data
|
|||
syscall
|
||||
cmp rax, 0
|
||||
jle .errmap
|
||||
mov r15, rax
|
||||
|
||||
; Sets BF runtime :
|
||||
; r14 is map len
|
||||
mov r15, rax ; r15 is map addr
|
||||
mov rdi, (BFMEM_INIT_SZ / 2) ; rdi is ptr idx
|
||||
mov rsi, r15
|
||||
add rsi, (BFMEM_INIT_SZ / 2) ; BF ptr
|
||||
add rsi, rdi ; rsi is bf ptr
|
||||
align 8
|
||||
bf_start_sz: equ $ - bf_start
|
||||
|
||||
|
||||
; In piece of code call jump is achieved by adding
|
||||
; an offset to the JIT map base addr
|
||||
; this base address has to be on top of the stack
|
||||
; when executing this small piece of code
|
||||
;
|
||||
; the first instruction has to be a mov of a byte
|
||||
; in a register. This operation will be updated to
|
||||
; "pass" a parameter
|
||||
; In piece of code the first instruction has to be a mov of
|
||||
; a quadword in a register. This operation will be updated
|
||||
; to "pass" a parameter
|
||||
; rsi is map_ptr
|
||||
; rdi is map ptr offset
|
||||
; r14 is map_len
|
||||
; r15 is map_addr
|
||||
|
||||
bf_decptr:
|
||||
; dec map ptr
|
||||
; rbx is dec count
|
||||
mov rbx, strict qword 0x1
|
||||
push rbx
|
||||
cmp rsi, rbx
|
||||
cmp rdi, rbx
|
||||
jge .end
|
||||
mov rax, [rsp+8]
|
||||
add rax, bf_start.lbl_decresize
|
||||
mov rax, ELF_CODE_OFFSET + bf_start.lbl_decresize
|
||||
call rax
|
||||
;call (ELF_CODE_OFFSET + bf_start.lbl_decresize)
|
||||
.end:
|
||||
pop rbx
|
||||
sub rsi, rbx
|
||||
sub rdi, rbx
|
||||
bf_decptr_sz: equ $ - bf_decptr
|
||||
|
||||
bf_incptr:
|
||||
mov rbx, strict qword 0x1
|
||||
push rbx
|
||||
mov rax, rsi
|
||||
sub rax, r15
|
||||
cmp rax, r14
|
||||
jl .end
|
||||
mov rax, [rsp+8]
|
||||
add rax, bf_start.lbl_incresize
|
||||
add rbx, rdi
|
||||
cmp rdi, r14
|
||||
jge .end
|
||||
mov rbx, [rsp]
|
||||
mov rax, ELF_CODE_OFFSET + bf_start.lbl_incresize
|
||||
call rax
|
||||
;call (ELF_CODE_OFFSET + bf_start.lbl_incresize)
|
||||
.end:
|
||||
pop rbx
|
||||
add rsi, rbx
|
||||
add rdi, rbx
|
||||
bf_incptr_sz: equ $ - bf_incptr
|
||||
|
||||
bf_incval:
|
||||
mov rbx, strict qword 0x1
|
||||
xor rax, rax
|
||||
mov al,[rsi]
|
||||
add rax, rbx
|
||||
add al, bl
|
||||
mov [rsi], al
|
||||
bf_incval_sz: equ $ - bf_incval
|
||||
|
||||
bf_decval:
|
||||
mov rbx, strict qword 0x1
|
||||
xor rax, rax
|
||||
mov al, [rsi]
|
||||
sub rax, rbx
|
||||
sub al, bl
|
||||
mov [rsi], al
|
||||
bf_decval_sz: equ $ - bf_decval
|
||||
|
||||
bf_readval:
|
||||
mov rdx, strict qword 0x1
|
||||
push rdi
|
||||
push rsi
|
||||
xor rax, rax ; read
|
||||
xor rdi, rdi ; stdin
|
||||
|
|
@ -173,22 +226,28 @@ section .data
|
|||
mov byte [rsi], 0
|
||||
.end:
|
||||
pop rsi
|
||||
pop rdi
|
||||
bf_readval_sz: equ $ - bf_readval
|
||||
|
||||
bf_writeval:
|
||||
mov rdx, strict qword 0x1
|
||||
mov rcx, strict qword 0x1
|
||||
push rdi
|
||||
push rsi
|
||||
xor rax, rax ; write
|
||||
inc rax
|
||||
mov rdi, rax ; stdout
|
||||
syscall
|
||||
.loop_write:
|
||||
push rcx
|
||||
mov rax, 1 ; write
|
||||
mov rdi, rax ; stdout
|
||||
mov rsi, [rsp+8]
|
||||
mov rdx, 1 ; 1 chr
|
||||
syscall
|
||||
pop rcx
|
||||
loop .loop_write
|
||||
pop rsi
|
||||
pop rdi
|
||||
bf_writeval_sz: equ $ - bf_writeval
|
||||
|
||||
bf_loopstart:
|
||||
mov rbx, strict qword 0x1
|
||||
add rbx, [rsp]
|
||||
xor rdx, rdx
|
||||
mov dl, [rsi]
|
||||
cmp dl, 0
|
||||
jnz .end
|
||||
|
|
@ -198,8 +257,6 @@ section .data
|
|||
|
||||
bf_loopend:
|
||||
mov rbx, strict qword 0x1
|
||||
add rbx, [rsp]
|
||||
xor rdx, rdx
|
||||
mov dl, [rsi]
|
||||
cmp dl, 0
|
||||
jz .end
|
||||
|
|
@ -265,7 +322,7 @@ section .data
|
|||
chr_list : db ": ", 0xA, 0x0
|
||||
read_error: db "Error reading file "
|
||||
read_error_sz: equ $ - read_error
|
||||
usage_err: db "Usage : [-e [-o a.out]] FILE.BF"
|
||||
usage_err: db "Usage : [-o a.out] FILE.BF"
|
||||
usage_err_sz: equ $ - usage_err
|
||||
open_err: db "Error opening file", 0xa
|
||||
open_err_sz: equ $ - open_err
|
||||
|
|
@ -276,9 +333,9 @@ _start:
|
|||
; using heap to store arguments
|
||||
%define bf_source [r13]
|
||||
%define elf_file [r13+0x8]
|
||||
%define elf_out [r13+0x11]
|
||||
%define heap_size 0x12
|
||||
%define heap_size 0x10
|
||||
|
||||
;heap init
|
||||
mov rax, 0xc
|
||||
xor rdi, rdi
|
||||
syscall
|
||||
|
|
@ -288,12 +345,15 @@ _start:
|
|||
mov rax, 0xc
|
||||
syscall
|
||||
|
||||
mov rax, default_output
|
||||
mov elf_file, rax
|
||||
|
||||
;argument parsing
|
||||
mov rcx, [rsp] ; argc
|
||||
cmp rcx, 2
|
||||
jl .badarg
|
||||
je .init_1arg
|
||||
cmp rcx, 5
|
||||
;je .init_1arg
|
||||
cmp rcx, 4
|
||||
jg .badarg
|
||||
|
||||
mov rsi, rsp
|
||||
|
|
@ -312,8 +372,6 @@ _start:
|
|||
mov al, [rdi+1]
|
||||
cmp al, 0x68 ; '-h'
|
||||
je .badarg
|
||||
cmp al, 0x65 ; '-e'
|
||||
je .elfout_arg
|
||||
cmp al, 0x6f ; '-o'
|
||||
jne .badarg
|
||||
|
||||
|
|
@ -327,20 +385,6 @@ _start:
|
|||
loop .argloop
|
||||
jmp .init
|
||||
|
||||
.elfout_arg:
|
||||
mov al, 0x1
|
||||
mov elf_out, al
|
||||
mov rax, elf_file
|
||||
test rax, rax
|
||||
jz .default_out
|
||||
.elfout_arg_end:
|
||||
loop .argloop
|
||||
jmp .init
|
||||
.default_out:
|
||||
mov rax, default_output
|
||||
mov elf_file, rax
|
||||
jmp .elfout_arg_end
|
||||
|
||||
.filearg:
|
||||
mov rax, bf_source
|
||||
cmp rax, 0
|
||||
|
|
@ -348,11 +392,7 @@ _start:
|
|||
jnz .badarg ; file allready given
|
||||
mov bf_source, rdi
|
||||
loop .argloop
|
||||
jmp .init
|
||||
|
||||
.init_1arg:
|
||||
mov rax, [rsp+16]
|
||||
mov bf_source, rax
|
||||
jmp .init ; useless
|
||||
.init:
|
||||
; code map init
|
||||
; rsi map size
|
||||
|
|
@ -381,29 +421,8 @@ _start:
|
|||
mov rax, 0x3 ; close
|
||||
syscall
|
||||
|
||||
mov al, elf_out
|
||||
test al, al
|
||||
jnz .write_elf
|
||||
|
||||
|
||||
.code_jmp:
|
||||
; restore heap
|
||||
mov rax, 0xc
|
||||
mov rdi, r13
|
||||
syscall
|
||||
; set code map perm
|
||||
mov rax, 0xA ; mprotect
|
||||
mov rdi, r15
|
||||
mov rsi, r14
|
||||
mov rdx, 0x4 | 0x1 ; PROT_EXEC | PROT_READ
|
||||
syscall
|
||||
|
||||
push r15
|
||||
jmp r15 ; end... jumping in bf code map
|
||||
|
||||
.write_elf: ; writing elf file
|
||||
; writing elf file
|
||||
mov rax, [rsp] ; map len
|
||||
add rax, bf_start_head_sz
|
||||
mov [elf_section_text_sz], rax
|
||||
add rax, elf_head_sz ; elf head + map_ptr
|
||||
mov [elf_head + 0x60], rax
|
||||
|
|
@ -439,12 +458,6 @@ _start:
|
|||
mov rdx, elf_head_sz
|
||||
syscall
|
||||
|
||||
mov rax, 1
|
||||
mov rdi, [rsp]
|
||||
mov rsi, bf_start_head
|
||||
mov rdx, bf_start_head_sz
|
||||
syscall
|
||||
|
||||
mov rax, 1
|
||||
mov rdi, [rsp]
|
||||
mov rsi, r15 ; map_addr
|
||||
|
|
@ -769,15 +782,24 @@ compile_bf:
|
|||
push rdx
|
||||
call code_cpy
|
||||
mov map_ptr, rax ; rax is map_ptr
|
||||
pop rdx
|
||||
sub rax, rdx
|
||||
add rax, 2 ;arg addr in code map
|
||||
mov rdi, rax
|
||||
pop rdx ; bf_loopend_sz
|
||||
lea rdi, [rax + 2]
|
||||
sub rdi, rdx ; arg addr for loop_end : map_ptr - loopend_sz
|
||||
|
||||
pop rbx ; loop_start code offset
|
||||
mov [rax], rbx ; loop end jump to start
|
||||
|
||||
lea rax, [rbx+ELF_CODE_OFFSET] ; jmp to loop_start in loop_end
|
||||
mov [rdi], rax
|
||||
.br1:
|
||||
|
||||
mov rax, map_ptr
|
||||
sub rax, r15 ; loop end offset in map
|
||||
add rbx, r15 ; loop start addr
|
||||
mov [rbx+2], rax ; start jump to end
|
||||
sub rax, r15 ; map_ptr - map_addr : map_offset of loop_end nxt instr
|
||||
lea rdi, [rbx + r15 + 2] ; arg addr for loop_start in map
|
||||
lea rax, [rax + ELF_CODE_OFFSET]
|
||||
.br2:
|
||||
mov [rdi], rax
|
||||
.br3:
|
||||
jmp .nxtinstr
|
||||
|
||||
.callcpy:
|
||||
|
|
|
|||
33
runtests.sh
Executable file
33
runtests.sh
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
#!/bin/sh
|
||||
|
||||
bfc="./bfc"
|
||||
beef="beef"
|
||||
|
||||
make || exit 1
|
||||
|
||||
for bftest in tests/*.bf
|
||||
do
|
||||
for input in tests/*.in
|
||||
do
|
||||
beefres=$(tempfile)
|
||||
bfcelf=$(tempfile)
|
||||
bfcelfres=$(tempfile)
|
||||
$beef -i $input $bftest > $beefres
|
||||
$bfc -o $bfcelf $bftest 2>/dev/null
|
||||
chmod +x $bfcelf
|
||||
$bfcelf < $input > $bfcelfres
|
||||
|
||||
diff=$(diff $bfcelfres $beefres)
|
||||
if [ -n "$diff" ]
|
||||
then
|
||||
echo "========"
|
||||
echo "Error : $bftest on $input differs between bfc and beef"
|
||||
diff $bfcelfres $beefres
|
||||
hexdump -C $bfcres
|
||||
hexdump -C $beefres
|
||||
echo "========"
|
||||
fi
|
||||
echo "$bftest $input [OK]"
|
||||
rm $beefres $bfcres $bfcelf $bfcelfres
|
||||
done
|
||||
done
|
||||
1
tests/echo.bf
Normal file
1
tests/echo.bf
Normal file
|
|
@ -0,0 +1 @@
|
|||
+[,[.>]<]
|
||||
2
tests/hello.in
Normal file
2
tests/hello.in
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Hello world !
|
||||
|
||||
1
tests/helloworld.bf
Normal file
1
tests/helloworld.bf
Normal file
|
|
@ -0,0 +1 @@
|
|||
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
|
||||
1
tests/reverse.bf
Normal file
1
tests/reverse.bf
Normal file
|
|
@ -0,0 +1 @@
|
|||
+[<,]# >[>]<-<[<]#>[.>]
|
||||
1
tests/rot13.bf
Normal file
1
tests/rot13.bf
Normal file
|
|
@ -0,0 +1 @@
|
|||
>++++++++[>+++++++++++[<<+>>-]<-]<-.++++++++++++++++++++++++..+++++.>++++++++++++[>+++++++[<<->>-]<-]<.>++++++++[>+++++++++++[<<+>>-]<-]<-.--------..+++++.>++++++++++++[>+++++++[<<->>-]<-]<.+.-.++++++++++++++++++++++++++.++++++++++.>+++[>+++++++++++++++++++[<<->>-]<-]<-.
|
||||
Loading…
Add table
Add a link
Reference in a new issue