parse operands
This commit is contained in:
parent
f87ab3afa4
commit
c4f9eb0e55
|
|
@ -5,7 +5,9 @@
|
|||
section .bss
|
||||
global buf
|
||||
buf resb 0x100
|
||||
labels resb 0x1000
|
||||
labels resq 1
|
||||
label_refs resq 1
|
||||
heap resq 1
|
||||
section .data
|
||||
_start_lbl db "_start", 0
|
||||
|
||||
|
|
@ -374,6 +376,230 @@ op_table_end:
|
|||
|
||||
section .text
|
||||
|
||||
panic_abort:
|
||||
mov rdi, 1
|
||||
mov rax, 60
|
||||
syscall
|
||||
|
||||
;; Allocator
|
||||
|
||||
;; allocates $rdi bytes worth of pages via mmap
|
||||
alloc_pages:
|
||||
mov rax, 9 ; syscall: mmap
|
||||
mov rsi, rdi ; length: rdi
|
||||
xor rdi, rdi ; addr: NULL
|
||||
mov rdx, 3 ; prot: PROT_READ | PROT_WRITE
|
||||
mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
|
||||
mov r8, -1 ; fd: -1
|
||||
xor r9, r9 ; offset: 0
|
||||
syscall
|
||||
cmp rax, -1
|
||||
jae panic_abort
|
||||
ret
|
||||
|
||||
dealloc_pages:
|
||||
mov rax, 11 ; syscall: munmap
|
||||
mov rdi, rsi ; addr: rsi
|
||||
mov rsi, rdx ; length: rdx
|
||||
syscall
|
||||
cmp rax, -1
|
||||
jae panic_abort
|
||||
ret
|
||||
|
||||
;; reallocates memory at $rdi[..$rsi] to a new location of size $rdx.
|
||||
realloc_pages:
|
||||
sub rsp, 24
|
||||
mov qword [rsp], rsi
|
||||
mov qword [rsp + 8], rdi
|
||||
mov rdi, rdx
|
||||
call alloc_pages
|
||||
mov rsi, rdi
|
||||
mov rdi, qword [rsp + 8]
|
||||
mov rdx, qword [rsp]
|
||||
mov qword [rsp + 16], rax
|
||||
call memcpy
|
||||
mov rdi, qword [rsp + 8]
|
||||
mov rsi, qword [rsp]
|
||||
call dealloc_pages
|
||||
mov rax, qword [rsp + 16]
|
||||
add rsp, 24
|
||||
ret
|
||||
|
||||
make_slab:
|
||||
push rdi
|
||||
mov rdi, 0x4000
|
||||
call alloc_pages
|
||||
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
|
||||
pop rdi
|
||||
mov qword [rdi], 0 ; tail_end = 0
|
||||
mov qword [rdi + 8], 0 ; free = NULL
|
||||
mov qword [rdi + 16], rax ; first_block = allocated
|
||||
mov qword [rdi + 24], rax ; last_block = allocated
|
||||
mov rax, rdi
|
||||
ret
|
||||
|
||||
init_heap:
|
||||
push r14
|
||||
xor r14, r14
|
||||
mov rax, qword [rel heap]
|
||||
test rax, rax
|
||||
jnz .done
|
||||
mov rdi, 0x1000
|
||||
call alloc_pages
|
||||
mov qword [rel heap], rax
|
||||
.loop:
|
||||
cmp r14, 9
|
||||
jge .done
|
||||
mov rdi, r14
|
||||
shl rdi, 5 ; idx * 32
|
||||
mov rax, qword [rel heap]
|
||||
lea rdi, [rax + rdi] ; &heap.slabs[idx]
|
||||
call make_slab
|
||||
inc r14
|
||||
jmp .loop
|
||||
.done:
|
||||
pop r14
|
||||
ret
|
||||
|
||||
;; finds the correct slab for an allocation with size $rdi and align $rsi
|
||||
slab_bucket:
|
||||
xor rax, rax
|
||||
dec rdi ; if size is a power of two, dec so we can later inc
|
||||
dec rsi ; ^^
|
||||
or rdi, rsi ; we only care about the log2, so just gather all the bits
|
||||
bsr rsi, rdi ; log2((size-1) | (align-1))
|
||||
sub rsi, 2 ; +1 for the dec, -3 to collapse the first 3 slabs into one
|
||||
cmovae rax, rsi ; saturating sub
|
||||
ret
|
||||
|
||||
slab_alloc:
|
||||
push rbx
|
||||
mov rax, qword [rdi + 8] ; free
|
||||
test rax, rax
|
||||
jz .no_free
|
||||
mov rdx, qword [rax] ; next free chunk
|
||||
mov qword [rdi + 8], rdx ; free = next
|
||||
pop rbx
|
||||
ret
|
||||
.no_free:
|
||||
add esi, 3 ; undo the -3 from slab_bucket to get the actual log2(size)
|
||||
and esi, 63 ; clamp for safety
|
||||
mov edx, 16376 ; 0x4000 - 8
|
||||
mov ecx, esi
|
||||
shr rdx, cl ; 0x4000 - 8 >> log2(size)
|
||||
mov rax, qword [rdi] ; tail_end
|
||||
mov rbx, qword [rdi + 24] ; last_block
|
||||
cmp rax, rdx
|
||||
jb .alloc_from_block
|
||||
push rsi
|
||||
push rax
|
||||
push rdi
|
||||
mov rdi, 0x4000 ; allocate a new block
|
||||
call alloc_pages
|
||||
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
|
||||
pop rdi
|
||||
mov rsi, qword [rdi + 24] ; last_block
|
||||
mov qword [rsi + 0x4000 - 8], rax
|
||||
mov qword [rdi + 24], rax ; last_block = new block
|
||||
mov rbx, rax
|
||||
pop rax
|
||||
pop rsi
|
||||
.alloc_from_block:
|
||||
inc qword [rdi] ; tail_end++
|
||||
mov ecx, esi
|
||||
shl rax, cl
|
||||
add rax, rbx
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
;; allocate a chunk of memory of size $rdi and alignment $rsi
|
||||
heap_alloc:
|
||||
mov rax, qword [rel heap]
|
||||
cmp rax, 0
|
||||
je .init
|
||||
.is_init:
|
||||
push rdi
|
||||
call slab_bucket
|
||||
cmp rax, 9
|
||||
jge .mmap
|
||||
mov rsi, rax
|
||||
shl rax, 5 ; idx * 32
|
||||
mov rdi, qword [rel heap]
|
||||
lea rdi, [rdi + rax] ; &heap.slabs[idx]
|
||||
call slab_alloc
|
||||
pop rdi
|
||||
ret
|
||||
.init:
|
||||
push rdi
|
||||
push rsi
|
||||
call init_heap
|
||||
pop rsi
|
||||
pop rdi
|
||||
jmp .is_init
|
||||
.mmap:
|
||||
pop rdi
|
||||
call alloc_pages
|
||||
ret
|
||||
|
||||
;; deallocates a chunk of memory at $rdi of size $rsi and alignment $rdx
|
||||
heap_dealloc:
|
||||
push rsi
|
||||
push rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
call slab_bucket
|
||||
cmp rax, 9
|
||||
jge .mmap
|
||||
mov rsi, rax
|
||||
shl rax, 5 ; idx * 32
|
||||
mov rdi, qword [rel heap]
|
||||
lea rdi, [rdi + rax] ; &heap.slabs[idx]
|
||||
mov rax, qword [rdi + 8] ; free
|
||||
pop rsi
|
||||
mov qword [rsi], rax
|
||||
mov qword [rdi + 8], rsi
|
||||
pop rsi
|
||||
ret
|
||||
.mmap:
|
||||
pop rdi
|
||||
pop rsi
|
||||
call dealloc_pages
|
||||
ret
|
||||
|
||||
;; reallocates a chunk of memory at $rdi of size $rsi and alignment $rdx to a new location of size $rcx
|
||||
heap_realloc:
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
|
||||
mov rbx, rdi
|
||||
mov r12, rsi
|
||||
mov r13, rdx
|
||||
mov r14, rcx
|
||||
|
||||
mov rdi, r14
|
||||
mov rsi, r13
|
||||
call heap_alloc
|
||||
mov r14, rax
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, rax
|
||||
mov rdx, r12
|
||||
call memcpy
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, r12
|
||||
mov rdx, r13
|
||||
call heap_dealloc
|
||||
|
||||
mov rax, r14
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
;; format dil as hex u8 into $ax
|
||||
format_u8:
|
||||
xor ecx, ecx
|
||||
|
|
@ -613,6 +839,34 @@ is_digit:
|
|||
setb al
|
||||
ret
|
||||
|
||||
is_alphanumeric:
|
||||
lea eax, [rdi - 48]
|
||||
cmp eax, 10
|
||||
setb cl
|
||||
and dil, 0xdf
|
||||
add dil, -65
|
||||
cmp dil, 26
|
||||
setb al
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
is_id_start:
|
||||
call is_alpha
|
||||
cmp dil, 95
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
is_id_cont:
|
||||
call is_alphanumeric
|
||||
cmp dil, 45
|
||||
sete cl
|
||||
or al, cl
|
||||
cmp dil, 95
|
||||
sete cl
|
||||
or al, cl
|
||||
ret
|
||||
|
||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
||||
to_digit:
|
||||
lea eax, [rsi - 2]
|
||||
|
|
@ -646,6 +900,8 @@ exit:
|
|||
%define KIND_REG 1
|
||||
%define KIND_MEM 2
|
||||
%define KIND_IMM 3
|
||||
%define KIND_MEM_LABEL 4 ; a memory operand whose displacement is a label
|
||||
%define KIND_IMM_LABEL 5 ; an immediate value that is a label
|
||||
|
||||
struc operand
|
||||
.type resd 1
|
||||
|
|
@ -679,7 +935,7 @@ try_parse_reg:
|
|||
mov rbx, rdi
|
||||
mov dil, byte [rbx]
|
||||
call is_alpha
|
||||
jnb .done
|
||||
jnb .not_reg
|
||||
cmp dil, 'r'
|
||||
jne .not_64_or_rX
|
||||
|
||||
|
|
@ -707,10 +963,11 @@ try_parse_reg:
|
|||
mov rsi, 3
|
||||
mov dl, byte [rbx]
|
||||
call strfind
|
||||
mov r13d, 3
|
||||
cmp rax, 0
|
||||
jl .done
|
||||
mov r13d, eax
|
||||
mov edi, 3
|
||||
cmovl r13d, edi
|
||||
inc rbx
|
||||
jmp .done
|
||||
|
||||
.not_rX:
|
||||
|
|
@ -720,6 +977,11 @@ try_parse_reg:
|
|||
mov dx, word [rbx]
|
||||
add rbx, 2
|
||||
|
||||
xor eax, eax
|
||||
cmp r13d, 0
|
||||
sete al
|
||||
add rbx, rax ; spl, bpl, sil, dil have a 3rd character we need to account for
|
||||
|
||||
mov r12d, 4 ; rsp
|
||||
cmp dx, 'sp'
|
||||
je .done
|
||||
|
|
@ -740,12 +1002,20 @@ try_parse_reg:
|
|||
cmp dx, 'ip'
|
||||
je .done
|
||||
|
||||
sub rbx, rax ; its not spl, bpl, sil, dil, so undo the inc we did earlier
|
||||
|
||||
lea rax, [rel abcd_reg_indices]
|
||||
sub dl, 'a'
|
||||
cmp dl, 3
|
||||
ja .not_reg
|
||||
movzx edx, dl
|
||||
movzx r12d, byte [rax + rdx] ; rax, rcx, rdx, rbx
|
||||
|
||||
.done:
|
||||
mov dil, byte [rbx]
|
||||
call is_alphanumeric
|
||||
jnz .not_reg ; if the next char is alphanumeric, then this is not a valid reg
|
||||
|
||||
mov rax, rbx
|
||||
pop rdi
|
||||
sub rax, rdi ; number of bytes consumed
|
||||
|
|
@ -760,6 +1030,15 @@ try_parse_reg:
|
|||
pop rbx
|
||||
ret
|
||||
|
||||
.not_reg:
|
||||
xor rax, rax
|
||||
xor rdx, rdx
|
||||
pop rdi
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
.not_64_or_rX:
|
||||
cmp dil, 'e'
|
||||
jne .not_32
|
||||
|
|
@ -791,10 +1070,9 @@ try_parse_num:
|
|||
xor eax, eax
|
||||
mov rbx, rdi
|
||||
|
||||
; eat leading - and flip sign
|
||||
; eat leading -
|
||||
cmp byte [rbx], '-'
|
||||
sete al
|
||||
sub r13, rax
|
||||
lea rbx, [rbx + rax]
|
||||
|
||||
; eat any leading +
|
||||
|
|
@ -852,6 +1130,11 @@ try_parse_num:
|
|||
sub rax, rdi ; number of bytes consumed
|
||||
mov rdx, r13 ; acc
|
||||
|
||||
cmp byte [rdi], '-'
|
||||
jne .positive
|
||||
neg rdx
|
||||
.positive:
|
||||
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
|
|
@ -931,9 +1214,14 @@ try_parse_mem:
|
|||
jne .not_mem
|
||||
inc rbx
|
||||
|
||||
cmp byte [rbx], `'`
|
||||
je .disp_lbl
|
||||
|
||||
call skip_whitespace_rbx
|
||||
mov rdi, rbx
|
||||
call try_parse_reg
|
||||
test eax, eax
|
||||
jz .disp
|
||||
add rbx, rax
|
||||
|
||||
call skip_whitespace_rbx
|
||||
|
|
@ -958,6 +1246,7 @@ try_parse_mem:
|
|||
|
||||
; TODO: allow for label as displacement
|
||||
.disp:
|
||||
mov dil, byte [rbx]
|
||||
sub dil, `,`
|
||||
neg dil ; sign
|
||||
mov al, dil
|
||||
|
|
@ -969,6 +1258,12 @@ try_parse_mem:
|
|||
|
||||
inc rbx
|
||||
call skip_whitespace_rbx
|
||||
|
||||
.disp_skip_sign:
|
||||
cmp byte [rbx], `'`
|
||||
je .disp_lbl
|
||||
|
||||
|
||||
mov rdi, rbx
|
||||
call try_parse_num
|
||||
test eax, eax
|
||||
|
|
@ -983,19 +1278,39 @@ try_parse_mem:
|
|||
je .done
|
||||
jmp .not_mem
|
||||
|
||||
.disp_lbl:
|
||||
inc rbx ; skip the leading `'`
|
||||
mov dword [r12 + operand.type], KIND_MEM_LABEL
|
||||
mov rdi, rbx
|
||||
call try_parse_label
|
||||
test eax, eax
|
||||
jz .not_mem
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, rax
|
||||
add rbx, rax
|
||||
call fasthash
|
||||
mov dword [r12 + operand.disp], eax
|
||||
|
||||
call skip_whitespace_rbx
|
||||
cmp byte [rbx], ']'
|
||||
je .done
|
||||
jmp .not_mem
|
||||
|
||||
.base:
|
||||
mov qword [r12 + operand.reg], rdx
|
||||
cmp byte [rbx], ']'
|
||||
je .done
|
||||
cmp byte [rbx], '+'
|
||||
jne .not_mem
|
||||
jne .disp
|
||||
|
||||
inc rbx
|
||||
call skip_whitespace_rbx
|
||||
|
||||
mov rdi, rbx
|
||||
call try_parse_reg
|
||||
test eax, eax
|
||||
jz .disp
|
||||
jz .disp_skip_sign
|
||||
add rbx, rax
|
||||
mov qword [r12 + operand.index], rdx
|
||||
|
||||
|
|
@ -1026,6 +1341,232 @@ try_parse_mem:
|
|||
ret
|
||||
|
||||
|
||||
global try_parse_operand
|
||||
try_parse_operand:
|
||||
push rbx ; buf
|
||||
push r12 ; operand ptr
|
||||
|
||||
mov rbx, rdi
|
||||
mov r12, rsi ; output operand ptr
|
||||
|
||||
mov dword [r12 + operand.type], KIND_NONE
|
||||
mov dword [r12 + operand.size], 0
|
||||
mov qword [r12 + operand.reg], -1
|
||||
mov qword [r12 + operand.index], -1
|
||||
mov dword [r12 + operand.scale], 0
|
||||
mov dword [r12 + operand.disp], 0
|
||||
|
||||
mov al, byte [rbx]
|
||||
cmp al, '['
|
||||
je .mem
|
||||
cmp al, `'`
|
||||
je .label
|
||||
|
||||
mov dword [r12 + operand.type], KIND_IMM
|
||||
call try_parse_num
|
||||
test eax, eax
|
||||
jz .not_imm
|
||||
|
||||
mov qword [r12 + operand_imm.imm], rdx
|
||||
test rdx, rdx
|
||||
jz .done
|
||||
|
||||
mov rcx, rdx
|
||||
neg rdx
|
||||
cmovs rdx, rcx ; abs(imm)
|
||||
|
||||
bsr rcx, rdx ; # of bits required to represent the immediate
|
||||
mov edx, ecx
|
||||
|
||||
cmp edx, 1
|
||||
adc edx, -1 ; saturating sub
|
||||
|
||||
bsr edx, edx ; log2 of the (# of bits - 1) + 1
|
||||
inc edx
|
||||
|
||||
cmp ecx, 2
|
||||
mov ecx, 1
|
||||
cmovae ecx, edx ; checked ilog2
|
||||
|
||||
mov dword [r12 + operand.size], ecx
|
||||
jmp .done
|
||||
|
||||
.not_imm:
|
||||
mov dword [r12 + operand.type], KIND_REG
|
||||
mov rdi, rbx
|
||||
call try_parse_reg
|
||||
test eax, eax
|
||||
jz .not_reg
|
||||
|
||||
mov qword [r12 + operand.reg], rdx
|
||||
mov dword [r12 + operand.size], edx ; low dword of rdx is the size
|
||||
jmp .done
|
||||
|
||||
.not_reg:
|
||||
.mem:
|
||||
mov rdi, rbx
|
||||
mov rsi, r12
|
||||
call try_parse_mem
|
||||
test eax, eax
|
||||
jz .dont_know
|
||||
jmp .done
|
||||
|
||||
.label:
|
||||
inc rbx ; skip the leading `'`
|
||||
mov dword [r12 + operand.type], KIND_IMM_LABEL
|
||||
mov rdi, rbx
|
||||
call try_parse_label
|
||||
test eax, eax
|
||||
jz .dont_know
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, rax
|
||||
mov rbx, rax
|
||||
call fasthash
|
||||
mov dword [r12 + operand_imm.imm], eax
|
||||
mov rax, rbx ; number of bytes consumed
|
||||
jmp .done
|
||||
|
||||
.dont_know:
|
||||
xor rax, rax
|
||||
.done:
|
||||
mov rdx, r12 ; output operand ptr
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
|
||||
try_parse_label:
|
||||
push rbx ; buf
|
||||
push r12 ; label length
|
||||
|
||||
mov rbx, rdi
|
||||
xor r12d, r12d
|
||||
mov dil, byte [rbx]
|
||||
call is_id_start
|
||||
jz .done
|
||||
inc r12d
|
||||
.loop:
|
||||
mov dil, byte [rbx + r12]
|
||||
call is_id_cont
|
||||
jz .done
|
||||
inc r12d
|
||||
jmp .loop
|
||||
.done:
|
||||
mov eax, r12d
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
|
||||
|
||||
;; Labels
|
||||
|
||||
struc Label
|
||||
.name_hash resd 1
|
||||
.offset resd 1
|
||||
endstruc
|
||||
|
||||
struc LabelTable
|
||||
.size resd 1
|
||||
.cap resd 1
|
||||
.labels resq 1
|
||||
endstruc
|
||||
|
||||
|
||||
;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax
|
||||
global fasthash
|
||||
fasthash:
|
||||
mov eax, 0x811C9DC5 ; FNV-1a 32-bit offset basis
|
||||
.loop:
|
||||
test rsi, rsi
|
||||
jz .done
|
||||
movzx ecx, byte [rdi]
|
||||
xor eax, ecx
|
||||
imul eax, 0x01000193 ; FNV prime
|
||||
inc rdi
|
||||
dec rsi
|
||||
jmp .loop
|
||||
.done:
|
||||
ret
|
||||
|
||||
;; push label $rdi with length $rsi and offset $rdx into the label ref table
|
||||
push_lbl_ref:
|
||||
mov rcx, [rel label_refs]
|
||||
jmp push_lbl_common
|
||||
|
||||
;; push label $rdi with length $rsi and offset $edx into the label table
|
||||
push_lbl:
|
||||
mov rcx, [rel labels]
|
||||
push_lbl_common:
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
|
||||
mov rbx, rcx
|
||||
mov r13d, edx ; label offset
|
||||
|
||||
call fasthash
|
||||
mov r12d, eax ; store the hash in r12d
|
||||
|
||||
mov eax, dword [rbx + LabelTable.size]
|
||||
cmp eax, dword [rbx + LabelTable.cap]
|
||||
jl .has_space
|
||||
|
||||
mov rdi, qword [rbx + LabelTable.labels]
|
||||
mov esi, dword [rbx + LabelTable.cap]
|
||||
mov edx, 8
|
||||
mov ecx, esi
|
||||
shl ecx, 1
|
||||
call heap_realloc
|
||||
mov qword [rbx + LabelTable.labels], rax
|
||||
shl dword [rbx + LabelTable.cap], 1
|
||||
|
||||
.has_space:
|
||||
mov rax, qword [rbx + LabelTable.labels]
|
||||
mov edi, dword [rbx + LabelTable.size]
|
||||
lea rax, [rax + rdi * 8]
|
||||
mov dword [rax + Label.name_hash], r12d
|
||||
mov dword [rax + Label.offset], r13d
|
||||
inc dword [rbx + LabelTable.size]
|
||||
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
find_lbl:
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
|
||||
mov rbx, [rel labels]
|
||||
|
||||
call fasthash
|
||||
mov r12d, eax ; store the hash in r12d
|
||||
|
||||
xor r13d, r13d
|
||||
.loop:
|
||||
cmp r13d, dword [rbx + LabelTable.size]
|
||||
jge .not_found
|
||||
|
||||
mov rax, qword [rbx + LabelTable.labels]
|
||||
lea rax, [rax + r13 * 8]
|
||||
cmp dword [rax + Label.name_hash], r12d
|
||||
je .found
|
||||
inc r13d
|
||||
jmp .loop
|
||||
.found:
|
||||
mov eax, dword [rax + Label.offset]
|
||||
jmp .done
|
||||
.not_found:
|
||||
xor rax, rax
|
||||
.done:
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
ret
|
||||
|
||||
global _entry_print_table
|
||||
_entry_print_table:
|
||||
call print_op_table
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ unsafe extern "C" {
|
|||
|
||||
unsafe fn try_parse_reg(_: *const u8) -> (usize, Register);
|
||||
unsafe fn try_parse_mem(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
||||
#[link_name = "try_parse_operand"]
|
||||
unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
||||
|
||||
unsafe fn fasthash(_: *const u8, _: usize) -> u32;
|
||||
|
||||
#[link_name = "buf"]
|
||||
static mut BUF: [u8; 0x100];
|
||||
|
|
@ -16,10 +20,21 @@ struct Register {
|
|||
num: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum OperandKind {
|
||||
None = 0,
|
||||
Reg = 1,
|
||||
Mem = 2,
|
||||
Imm = 3,
|
||||
MemLabel = 4,
|
||||
ImmLabel = 5,
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Operand {
|
||||
kind: u32,
|
||||
kind: OperandKind,
|
||||
size: u32,
|
||||
reg: Register,
|
||||
index: Register,
|
||||
|
|
@ -27,6 +42,64 @@ struct Operand {
|
|||
disp: i32,
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
|
||||
let mut op = core::mem::MaybeUninit::<Operand>::uninit();
|
||||
let (n, ptr) = unsafe { try_parse_operand_impl(bytes.as_ptr(), op.as_mut_ptr()) };
|
||||
if n == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((n, unsafe { op.assume_init() }))
|
||||
}
|
||||
}
|
||||
|
||||
fn label(name: &[u8]) -> Self {
|
||||
let mut this = Self {
|
||||
kind: OperandKind::ImmLabel,
|
||||
size: 0,
|
||||
reg: Register::invalid(),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: 0,
|
||||
};
|
||||
unsafe {
|
||||
let hash = fasthash(name.as_ptr(), name.len());
|
||||
(&raw mut this).byte_add(8).cast::<u32>().write(hash as u32);
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
fn imm(value: i64) -> Self {
|
||||
let size = if value.abs() == 0 {
|
||||
value as u32
|
||||
} else {
|
||||
unsafe {
|
||||
(value.abs() as u64)
|
||||
.checked_ilog2()
|
||||
.unwrap_unchecked()
|
||||
.saturating_sub(1)
|
||||
.checked_ilog2()
|
||||
.unwrap_or(0)
|
||||
+ 1
|
||||
}
|
||||
};
|
||||
|
||||
let mut this = Self {
|
||||
kind: OperandKind::Imm,
|
||||
size,
|
||||
reg: Register::invalid(),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: 0,
|
||||
};
|
||||
unsafe {
|
||||
(&raw mut this).byte_add(8).cast::<i64>().write(value);
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl Register {
|
||||
fn new(num: u32, size: u32) -> Self {
|
||||
Self {
|
||||
|
|
@ -54,22 +127,22 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_mem() {
|
||||
let mut op = MaybeUninit::<Operand>::uninit();
|
||||
// let (n, _) =
|
||||
// unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) };
|
||||
// assert_eq!(
|
||||
// (n, unsafe { op.assume_init_ref() }),
|
||||
// (
|
||||
// 28,
|
||||
// &Operand {
|
||||
// kind: 2,
|
||||
// size: 3,
|
||||
// reg: Register::new(0, 8),
|
||||
// index: Register::new(3, 8),
|
||||
// scale: 2,
|
||||
// disp: 0x10,
|
||||
// }
|
||||
// )
|
||||
// );
|
||||
let (n, _) =
|
||||
unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) };
|
||||
assert_eq!(
|
||||
(n, unsafe { op.assume_init_ref() }),
|
||||
(
|
||||
28,
|
||||
&Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 3,
|
||||
reg: Register::new(0, 8),
|
||||
index: Register::new(3, 8),
|
||||
scale: 2,
|
||||
disp: 0x10,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
let (n, _) =
|
||||
unsafe { try_parse_mem(b"byte [r12 + r13 * 4 - 0x20]\0".as_ptr(), op.as_mut_ptr()) };
|
||||
|
|
@ -78,7 +151,7 @@ mod tests {
|
|||
(
|
||||
27,
|
||||
&Operand {
|
||||
kind: 2,
|
||||
kind: OperandKind::Mem,
|
||||
size: 0,
|
||||
reg: Register::new(12, 8),
|
||||
index: Register::new(13, 8),
|
||||
|
|
@ -94,7 +167,7 @@ mod tests {
|
|||
(
|
||||
17,
|
||||
&Operand {
|
||||
kind: 2,
|
||||
kind: OperandKind::Mem,
|
||||
size: 1,
|
||||
reg: Register::invalid(),
|
||||
index: Register::new(8, 8),
|
||||
|
|
@ -106,81 +179,159 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn parse_operand() {
|
||||
let cases = [
|
||||
(
|
||||
&b"rax\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Reg,
|
||||
size: 3,
|
||||
reg: Register::new(0, 8),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
&b"qword [rax + rbx * 2 + 0x10]\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 3,
|
||||
reg: Register::new(0, 8),
|
||||
index: Register::new(3, 8),
|
||||
scale: 2,
|
||||
disp: 0x10,
|
||||
},
|
||||
),
|
||||
(
|
||||
&b"byte [r12 + r13 * 4 - 0x20]\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 0,
|
||||
reg: Register::new(12, 8),
|
||||
index: Register::new(13, 8),
|
||||
scale: 4,
|
||||
disp: -0x20,
|
||||
},
|
||||
),
|
||||
(
|
||||
&b"word [r8 * 8 + 5]\0"[..],
|
||||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 1,
|
||||
reg: Register::invalid(),
|
||||
index: Register::new(8, 8),
|
||||
scale: 8,
|
||||
disp: 5,
|
||||
},
|
||||
),
|
||||
(&b"0x12345678\0"[..], Operand::imm(0x12345678)),
|
||||
(&b"-0x12345678\0"[..], Operand::imm(-0x12345678)),
|
||||
(&b"0\0"[..], Operand::imm(0)),
|
||||
(&b"-1\0"[..], Operand::imm(-1)),
|
||||
(&b"1\0"[..], Operand::imm(1)),
|
||||
(&b"255\0"[..], Operand::imm(255)),
|
||||
(&b"-255\0"[..], Operand::imm(-255)),
|
||||
(&b"'wazzaah\0"[..], Operand::label(b"wazzaah")),
|
||||
(
|
||||
&b"qword [rsp + 'hii]"[..],
|
||||
Operand {
|
||||
kind: OperandKind::MemLabel,
|
||||
size: 3,
|
||||
reg: Register::new(4, 8),
|
||||
index: Register::invalid(),
|
||||
scale: 0,
|
||||
disp: unsafe { fasthash(b"hii".as_ptr(), 3) as i32 },
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
for (text, expected) in cases {
|
||||
let (n, result) = Operand::parse(text).expect("Failed to parse operand");
|
||||
assert_eq!(
|
||||
result,
|
||||
expected,
|
||||
"Failed to parse '{}'",
|
||||
std::str::from_utf8(text).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_reg() {
|
||||
let cases = [
|
||||
(&b"rax"[..], (0, 8)),
|
||||
(&b"rcx"[..], (1, 8)),
|
||||
(&b"rdx"[..], (2, 8)),
|
||||
(&b"rbx"[..], (3, 8)),
|
||||
(&b"rsp"[..], (4, 8)),
|
||||
(&b"rbp"[..], (5, 8)),
|
||||
(&b"rsi"[..], (6, 8)),
|
||||
(&b"rdi"[..], (7, 8)),
|
||||
(&b"r8"[..], (8, 8)),
|
||||
(&b"r9"[..], (9, 8)),
|
||||
(&b"r10"[..], (10, 8)),
|
||||
(&b"r11"[..], (11, 8)),
|
||||
(&b"r12"[..], (12, 8)),
|
||||
(&b"r13"[..], (13, 8)),
|
||||
(&b"r14"[..], (14, 8)),
|
||||
(&b"r15"[..], (15, 8)),
|
||||
(&b"rip"[..], (!0, 8)),
|
||||
(&b"eax"[..], (0, 4)),
|
||||
(&b"ecx"[..], (1, 4)),
|
||||
(&b"edx"[..], (2, 4)),
|
||||
(&b"ebx"[..], (3, 4)),
|
||||
(&b"esp"[..], (4, 4)),
|
||||
(&b"ebp"[..], (5, 4)),
|
||||
(&b"esi"[..], (6, 4)),
|
||||
(&b"edi"[..], (7, 4)),
|
||||
(&b"r8d"[..], (8, 4)),
|
||||
(&b"r9d"[..], (9, 4)),
|
||||
(&b"r10d"[..], (10, 4)),
|
||||
(&b"r11d"[..], (11, 4)),
|
||||
(&b"r12d"[..], (12, 4)),
|
||||
(&b"r13d"[..], (13, 4)),
|
||||
(&b"r14d"[..], (14, 4)),
|
||||
(&b"r15d"[..], (15, 4)),
|
||||
(&b"r8w"[..], (8, 2)),
|
||||
(&b"r9w"[..], (9, 2)),
|
||||
(&b"r10w"[..], (10, 2)),
|
||||
(&b"r11w"[..], (11, 2)),
|
||||
(&b"r12w"[..], (12, 2)),
|
||||
(&b"r13w"[..], (13, 2)),
|
||||
(&b"r14w"[..], (14, 2)),
|
||||
(&b"r15w"[..], (15, 2)),
|
||||
(&b"r8b"[..], (8, 1)),
|
||||
(&b"r9b"[..], (9, 1)),
|
||||
(&b"r10b"[..], (10, 1)),
|
||||
(&b"r11b"[..], (11, 1)),
|
||||
(&b"r12b"[..], (12, 1)),
|
||||
(&b"r13b"[..], (13, 1)),
|
||||
(&b"r14b"[..], (14, 1)),
|
||||
(&b"r15b"[..], (15, 1)),
|
||||
(&b"ax"[..], (0, 2)),
|
||||
(&b"cx"[..], (1, 2)),
|
||||
(&b"dx"[..], (2, 2)),
|
||||
(&b"bx"[..], (3, 2)),
|
||||
(&b"sp"[..], (4, 2)),
|
||||
(&b"bp"[..], (5, 2)),
|
||||
(&b"si"[..], (6, 2)),
|
||||
(&b"di"[..], (7, 2)),
|
||||
(&b"al"[..], (0, 1)),
|
||||
(&b"cl"[..], (1, 1)),
|
||||
(&b"dl"[..], (2, 1)),
|
||||
(&b"bl"[..], (3, 1)),
|
||||
(&b"spl"[..], (4, 1)),
|
||||
(&b"bpl"[..], (5, 1)),
|
||||
(&b"sil"[..], (6, 1)),
|
||||
(&b"dil"[..], (7, 1)),
|
||||
(&b"rax\0"[..], (0, 8)),
|
||||
(&b"rcx\0"[..], (1, 8)),
|
||||
(&b"rdx\0"[..], (2, 8)),
|
||||
(&b"rbx\0"[..], (3, 8)),
|
||||
(&b"rsp\0"[..], (4, 8)),
|
||||
(&b"rbp\0"[..], (5, 8)),
|
||||
(&b"rsi\0"[..], (6, 8)),
|
||||
(&b"rdi\0"[..], (7, 8)),
|
||||
(&b"r8\0"[..], (8, 8)),
|
||||
(&b"r9\0"[..], (9, 8)),
|
||||
(&b"r10\0"[..], (10, 8)),
|
||||
(&b"r11\0"[..], (11, 8)),
|
||||
(&b"r12\0"[..], (12, 8)),
|
||||
(&b"r13\0"[..], (13, 8)),
|
||||
(&b"r14\0"[..], (14, 8)),
|
||||
(&b"r15\0"[..], (15, 8)),
|
||||
(&b"rip\0"[..], (!0, 8)),
|
||||
(&b"eax\0"[..], (0, 4)),
|
||||
(&b"ecx\0"[..], (1, 4)),
|
||||
(&b"edx\0"[..], (2, 4)),
|
||||
(&b"ebx\0"[..], (3, 4)),
|
||||
(&b"esp\0"[..], (4, 4)),
|
||||
(&b"ebp\0"[..], (5, 4)),
|
||||
(&b"esi\0"[..], (6, 4)),
|
||||
(&b"edi\0"[..], (7, 4)),
|
||||
(&b"r8d\0"[..], (8, 4)),
|
||||
(&b"r9d\0"[..], (9, 4)),
|
||||
(&b"r10d\0"[..], (10, 4)),
|
||||
(&b"r11d\0"[..], (11, 4)),
|
||||
(&b"r12d\0"[..], (12, 4)),
|
||||
(&b"r13d\0"[..], (13, 4)),
|
||||
(&b"r14d\0"[..], (14, 4)),
|
||||
(&b"r15d\0"[..], (15, 4)),
|
||||
(&b"r8w\0"[..], (8, 2)),
|
||||
(&b"r9w\0"[..], (9, 2)),
|
||||
(&b"r10w\0"[..], (10, 2)),
|
||||
(&b"r11w\0"[..], (11, 2)),
|
||||
(&b"r12w\0"[..], (12, 2)),
|
||||
(&b"r13w\0"[..], (13, 2)),
|
||||
(&b"r14w\0"[..], (14, 2)),
|
||||
(&b"r15w\0"[..], (15, 2)),
|
||||
(&b"r8b\0"[..], (8, 1)),
|
||||
(&b"r9b\0"[..], (9, 1)),
|
||||
(&b"r10b\0"[..], (10, 1)),
|
||||
(&b"r11b\0"[..], (11, 1)),
|
||||
(&b"r12b\0"[..], (12, 1)),
|
||||
(&b"r13b\0"[..], (13, 1)),
|
||||
(&b"r14b\0"[..], (14, 1)),
|
||||
(&b"r15b\0"[..], (15, 1)),
|
||||
(&b"ax\0"[..], (0, 2)),
|
||||
(&b"cx\0"[..], (1, 2)),
|
||||
(&b"dx\0"[..], (2, 2)),
|
||||
(&b"bx\0"[..], (3, 2)),
|
||||
(&b"sp\0"[..], (4, 2)),
|
||||
(&b"bp\0"[..], (5, 2)),
|
||||
(&b"si\0"[..], (6, 2)),
|
||||
(&b"di\0"[..], (7, 2)),
|
||||
(&b"al\0"[..], (0, 1)),
|
||||
(&b"cl\0"[..], (1, 1)),
|
||||
(&b"dl\0"[..], (2, 1)),
|
||||
(&b"bl\0"[..], (3, 1)),
|
||||
(&b"spl\0"[..], (4, 1)),
|
||||
(&b"bpl\0"[..], (5, 1)),
|
||||
(&b"sil\0"[..], (6, 1)),
|
||||
(&b"dil\0"[..], (7, 1)),
|
||||
];
|
||||
|
||||
for (text, (reg, size)) in cases {
|
||||
let (n, result) = unsafe { try_parse_reg(text.as_ptr()) };
|
||||
assert_eq!(
|
||||
result,
|
||||
Register::new(reg, size),
|
||||
(n, result),
|
||||
(text.len() - 1, Register::new(reg, size)),
|
||||
"Failed to parse '{}' n={n}",
|
||||
std::str::from_utf8(text).unwrap()
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue