parse, encode inst
This commit is contained in:
parent
c4f9eb0e55
commit
c48c4261da
|
|
@ -2,24 +2,42 @@
|
||||||
;; It's also the first stage in this bootstrapping experiment that will
|
;; It's also the first stage in this bootstrapping experiment that will
|
||||||
;; consume proper mnemonic assembly, rather than hex input.
|
;; consume proper mnemonic assembly, rather than hex input.
|
||||||
|
|
||||||
|
struc OFile
|
||||||
|
.fd resd 1
|
||||||
|
.offset resd 1
|
||||||
|
endstruc
|
||||||
|
|
||||||
section .bss
|
section .bss
|
||||||
global buf
|
global buf
|
||||||
|
ifile resb 0x20
|
||||||
|
ofile resb 0x20
|
||||||
buf resb 0x100
|
buf resb 0x100
|
||||||
labels resq 1
|
|
||||||
label_refs resq 1
|
|
||||||
heap resq 1
|
heap resq 1
|
||||||
section .data
|
|
||||||
_start_lbl db "_start", 0
|
|
||||||
|
|
||||||
ENC_RM equ 1
|
section .data
|
||||||
ENC_MR equ 2
|
ENC_R_BITS equ 1
|
||||||
ENC_MI equ 3
|
ENC_M_BITS equ 2
|
||||||
ENC_OI equ 4
|
ENC_I_BITS equ 4
|
||||||
ENC_M equ 5
|
ENC_CL_BITS equ 8
|
||||||
ENC_O equ 6
|
ENC_ONE_BITS equ 16
|
||||||
ENC_I equ 7
|
ENC_O_SIGNAL_BITS equ 32
|
||||||
ENC_MC equ 8 ; memory with cl
|
|
||||||
ENC_M1 equ 9 ; memory with 1
|
ENC_RM_BITS equ ENC_R_BITS | ENC_M_BITS
|
||||||
|
ENC_O_BITS equ ENC_R_BITS | ENC_O_SIGNAL_BITS
|
||||||
|
|
||||||
|
ENC_DISCARD_BITS equ ENC_CL_BITS | ENC_ONE_BITS
|
||||||
|
|
||||||
|
ENC_RM equ ENC_R_BITS | ENC_RM_BITS << 8
|
||||||
|
ENC_MR equ ENC_RM_BITS | ENC_R_BITS << 8
|
||||||
|
ENC_MI equ ENC_RM_BITS | ENC_I_BITS << 8
|
||||||
|
ENC_OI equ ENC_O_BITS | ENC_I_BITS << 8
|
||||||
|
ENC_M equ ENC_RM_BITS
|
||||||
|
ENC_O equ ENC_O_BITS
|
||||||
|
ENC_I equ ENC_I_BITS
|
||||||
|
; r/m with cl
|
||||||
|
ENC_MC equ ENC_RM_BITS | ENC_CL_BITS << 8
|
||||||
|
; r/m with 1
|
||||||
|
ENC_M1 equ ENC_RM_BITS | ENC_ONE_BITS << 8
|
||||||
|
|
||||||
OPSIZE_8 equ 1
|
OPSIZE_8 equ 1
|
||||||
OPSIZE_16 equ 2
|
OPSIZE_16 equ 2
|
||||||
|
|
@ -83,8 +101,19 @@ mnemonics_table:
|
||||||
MN_CALL db "call", 0
|
MN_CALL db "call", 0
|
||||||
MN_XCHG db "xchg", 0
|
MN_XCHG db "xchg", 0
|
||||||
MN_SYSCALL db "syscall", 0
|
MN_SYSCALL db "syscall", 0
|
||||||
|
MNEMONICS_SIZE equ $ - mnemonics_table
|
||||||
|
|
||||||
;; Opcodes support up to 2 operands
|
;; Opcodes support up to 2 operands
|
||||||
|
struc OpEntry
|
||||||
|
.mnemonic_offset resd 1
|
||||||
|
.num_operands resd 1
|
||||||
|
.op1_size resd 1
|
||||||
|
.op2_size resd 1
|
||||||
|
.encoding resd 1
|
||||||
|
.opcode resd 1
|
||||||
|
.reg_bits resd 1
|
||||||
|
.prefix resd 1
|
||||||
|
endstruc
|
||||||
|
|
||||||
op_table:
|
op_table:
|
||||||
;; MN_OFFS | NUM | SIZES | ENCOD | OPCODE | RM | PRE
|
;; MN_OFFS | NUM | SIZES | ENCOD | OPCODE | RM | PRE
|
||||||
|
|
@ -99,11 +128,11 @@ op_table:
|
||||||
dd mnt - MN_MOV, 2, 1, 1, ENC_OI, 0B0h, 00, 00h
|
dd mnt - MN_MOV, 2, 1, 1, ENC_OI, 0B0h, 00, 00h
|
||||||
dd mnt - MN_MOV, 2, 2, 2, ENC_OI, 0B8h, 00, 66h
|
dd mnt - MN_MOV, 2, 2, 2, ENC_OI, 0B8h, 00, 66h
|
||||||
dd mnt - MN_MOV, 2, 4, 4, ENC_OI, 0B8h, 00, 00h
|
dd mnt - MN_MOV, 2, 4, 4, ENC_OI, 0B8h, 00, 00h
|
||||||
dd mnt - MN_MOV, 2, 8, 8, ENC_OI, 0B8h, 00, 00h
|
|
||||||
dd mnt - MN_MOV, 2, 1, 1, ENC_MI, 0C6h, 00, 00h
|
dd mnt - MN_MOV, 2, 1, 1, ENC_MI, 0C6h, 00, 00h
|
||||||
dd mnt - MN_MOV, 2, 2, 2, ENC_MI, 0C7h, 00, 66h
|
dd mnt - MN_MOV, 2, 2, 2, ENC_MI, 0C7h, 00, 66h
|
||||||
dd mnt - MN_MOV, 2, 4, 4, ENC_MI, 0C7h, 00, 00h
|
dd mnt - MN_MOV, 2, 4, 4, ENC_MI, 0C7h, 00, 00h
|
||||||
dd mnt - MN_MOV, 2, 8, 4, ENC_MI, 0C7h, 00, 00h
|
dd mnt - MN_MOV, 2, 8, 4, ENC_MI, 0C7h, 00, 00h
|
||||||
|
dd mnt - MN_MOV, 2, 8, 8, ENC_OI, 0B8h, 00, 00h
|
||||||
|
|
||||||
dd mnt - MN_TEST, 2, 1, 1, ENC_MR, 84h, 00, 00h
|
dd mnt - MN_TEST, 2, 1, 1, ENC_MR, 84h, 00, 00h
|
||||||
dd mnt - MN_TEST, 2, 2, 2, ENC_MR, 85h, 00, 66h
|
dd mnt - MN_TEST, 2, 2, 2, ENC_MR, 85h, 00, 66h
|
||||||
|
|
@ -642,6 +671,84 @@ format_u32:
|
||||||
add rsp, 16
|
add rsp, 16
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
;; print the first $rsi bytes of the contents of $rdi as hex
|
||||||
|
print_u8s_le:
|
||||||
|
test esi, esi
|
||||||
|
jz .ret
|
||||||
|
push rbx
|
||||||
|
push r12
|
||||||
|
push r13
|
||||||
|
sub rsp, 16
|
||||||
|
|
||||||
|
xor r12d, r12d
|
||||||
|
mov rbx, rdi
|
||||||
|
mov r13d, esi
|
||||||
|
.loop:
|
||||||
|
cmp r12d, r13d
|
||||||
|
jge .done
|
||||||
|
movzx edi, bl
|
||||||
|
call format_u8
|
||||||
|
mov word [rsp + r12 * 2], ax
|
||||||
|
inc r12d
|
||||||
|
shr rbx, 8
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
lea rdi, [rsp]
|
||||||
|
mov esi, r13d
|
||||||
|
shl rsi, 1
|
||||||
|
call print_str
|
||||||
|
add rsp, 16
|
||||||
|
pop r13
|
||||||
|
pop r12
|
||||||
|
pop rbx
|
||||||
|
.ret:
|
||||||
|
ret
|
||||||
|
|
||||||
|
print_non_zero_u8s_le:
|
||||||
|
test rdi, rdi
|
||||||
|
jz .ret
|
||||||
|
push rbx
|
||||||
|
push r12
|
||||||
|
xor r12d, r12d
|
||||||
|
mov rbx, rdi
|
||||||
|
sub rsp, 16
|
||||||
|
.loop:
|
||||||
|
movzx edi, bl
|
||||||
|
test edi, edi
|
||||||
|
jz .done
|
||||||
|
call format_u8
|
||||||
|
mov word [rsp + r12 * 2], ax
|
||||||
|
inc r12d
|
||||||
|
shr rbx, 8
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
lea rdi, [rsp]
|
||||||
|
mov rsi, r12
|
||||||
|
shl rsi, 1
|
||||||
|
call print_str
|
||||||
|
add rsp, 16
|
||||||
|
pop r12
|
||||||
|
pop rbx
|
||||||
|
.ret:
|
||||||
|
ret
|
||||||
|
|
||||||
|
swap_bytes_and_shift_in_place:
|
||||||
|
bswap rdi
|
||||||
|
mov rcx, 64
|
||||||
|
bsf rcx, rdi
|
||||||
|
and cl, 56
|
||||||
|
shr rdi, cl
|
||||||
|
ret
|
||||||
|
|
||||||
|
print_non_zero_u8s_be:
|
||||||
|
test rdi, rdi
|
||||||
|
jz .done
|
||||||
|
call swap_bytes_and_shift_in_place
|
||||||
|
xor eax, eax
|
||||||
|
call print_non_zero_u8s_le
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
|
||||||
print_u8:
|
print_u8:
|
||||||
call format_u8
|
call format_u8
|
||||||
sub rsp, 8
|
sub rsp, 8
|
||||||
|
|
@ -763,14 +870,53 @@ print_op_table:
|
||||||
;; rdi: *u8
|
;; rdi: *u8
|
||||||
strlen:
|
strlen:
|
||||||
xor rax, rax
|
xor rax, rax
|
||||||
.strlen_loop:
|
.loop:
|
||||||
cmp byte [rdi + rax], 0
|
cmp byte [rdi + rax], 0
|
||||||
je .strlen_done
|
je .done
|
||||||
inc rax
|
inc rax
|
||||||
jmp .strlen_loop
|
jmp .loop
|
||||||
.strlen_done:
|
.done:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
;; find in the first $rsi bytes of $rdi (haystack) the first $rcx bytes of $rdx (needle).
|
||||||
|
;; returns the index in $rax, or -1 if not found.
|
||||||
|
strstr:
|
||||||
|
push rbx
|
||||||
|
push r12
|
||||||
|
push r13
|
||||||
|
mov r13, rdi ; save original haystack pointer
|
||||||
|
mov rax, rsi ; remaining haystack length
|
||||||
|
.loop_haystack:
|
||||||
|
cmp rax, rcx
|
||||||
|
jb .not_found ; not enough haystack left for needle
|
||||||
|
|
||||||
|
xor rbx, rbx ; needle index
|
||||||
|
.loop_needle:
|
||||||
|
cmp rbx, rcx
|
||||||
|
jz .found ; found the needle
|
||||||
|
|
||||||
|
mov r12b, byte [rdx + rbx] ; needle[needle_index]
|
||||||
|
cmp r12b, byte [rdi + rbx] ; haystack[haystack_index + needle_index]
|
||||||
|
jne .next_haystack ; mismatch, move to next haystack index
|
||||||
|
inc rbx
|
||||||
|
jmp .loop_needle
|
||||||
|
.next_haystack:
|
||||||
|
inc rdi ; move to next haystack index
|
||||||
|
dec rax ; decrease remaining haystack length
|
||||||
|
jmp .loop_haystack
|
||||||
|
.not_found:
|
||||||
|
mov rax, -1
|
||||||
|
jmp .done
|
||||||
|
.found:
|
||||||
|
sub rdi, r13 ; calculate the index of the found needle
|
||||||
|
mov rax, rdi
|
||||||
|
.done:
|
||||||
|
pop r13
|
||||||
|
pop r12
|
||||||
|
pop rbx
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
;; lhs: (rdi, rsi)
|
;; lhs: (rdi, rsi)
|
||||||
;; rhs: (rdx, rcx)
|
;; rhs: (rdx, rcx)
|
||||||
;; al
|
;; al
|
||||||
|
|
@ -899,9 +1045,12 @@ exit:
|
||||||
%define KIND_NONE 0
|
%define KIND_NONE 0
|
||||||
%define KIND_REG 1
|
%define KIND_REG 1
|
||||||
%define KIND_MEM 2
|
%define KIND_MEM 2
|
||||||
%define KIND_IMM 3
|
%define KIND_IMM 4
|
||||||
%define KIND_MEM_LABEL 4 ; a memory operand whose displacement is a label
|
%define KIND_LABEL_FLAG 8
|
||||||
%define KIND_IMM_LABEL 5 ; an immediate value that is a label
|
; a memory operand whose displacement is a label
|
||||||
|
%define KIND_MEM_LABEL KIND_LABEL_FLAG | KIND_MEM
|
||||||
|
; an immediate value that is a label
|
||||||
|
%define KIND_IMM_LABEL KIND_LABEL_FLAG | KIND_IMM
|
||||||
|
|
||||||
struc operand
|
struc operand
|
||||||
.type resd 1
|
.type resd 1
|
||||||
|
|
@ -918,6 +1067,11 @@ struc operand_imm
|
||||||
.imm resq 1
|
.imm resq 1
|
||||||
endstruc
|
endstruc
|
||||||
|
|
||||||
|
struc register
|
||||||
|
.size resd 1
|
||||||
|
.num resd 1
|
||||||
|
endstruc
|
||||||
|
|
||||||
abcd_reg_indices: db 0, 3, 1, 2
|
abcd_reg_indices: db 0, 3, 1, 2
|
||||||
rX_suffixes: db 'b', 'w', 'd'
|
rX_suffixes: db 'b', 'w', 'd'
|
||||||
|
|
||||||
|
|
@ -1169,7 +1323,8 @@ try_parse_mem:
|
||||||
|
|
||||||
mov dword [r12 + operand.type], KIND_MEM
|
mov dword [r12 + operand.type], KIND_MEM
|
||||||
mov dword [r12 + operand.size], 0
|
mov dword [r12 + operand.size], 0
|
||||||
mov qword [r12 + operand.reg], -1
|
mov dword [r12 + operand.reg + register.size], 0
|
||||||
|
mov dword [r12 + operand.reg + register.num], 5 ; default to EBP (no base)
|
||||||
mov qword [r12 + operand.index], -1
|
mov qword [r12 + operand.index], -1
|
||||||
mov dword [r12 + operand.scale], 0
|
mov dword [r12 + operand.scale], 0
|
||||||
mov dword [r12 + operand.disp], 0
|
mov dword [r12 + operand.disp], 0
|
||||||
|
|
@ -1414,6 +1569,7 @@ try_parse_operand:
|
||||||
.label:
|
.label:
|
||||||
inc rbx ; skip the leading `'`
|
inc rbx ; skip the leading `'`
|
||||||
mov dword [r12 + operand.type], KIND_IMM_LABEL
|
mov dword [r12 + operand.type], KIND_IMM_LABEL
|
||||||
|
mov dword [r12 + operand.size], 2 ; 32-bit
|
||||||
mov rdi, rbx
|
mov rdi, rbx
|
||||||
call try_parse_label
|
call try_parse_label
|
||||||
test eax, eax
|
test eax, eax
|
||||||
|
|
@ -1459,6 +1615,530 @@ try_parse_label:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
struc Instruction
|
||||||
|
.mnemonic_offs resd 1
|
||||||
|
.num_operands resd 1
|
||||||
|
.operand1 resb operand_size
|
||||||
|
.operand2 resb operand_size
|
||||||
|
endstruc
|
||||||
|
|
||||||
|
global try_parse_inst
|
||||||
|
try_parse_inst:
|
||||||
|
push rbx ; buf
|
||||||
|
push r13 ; inst ptr
|
||||||
|
push rdi
|
||||||
|
|
||||||
|
mov rbx, rdi
|
||||||
|
mov r13, rsi ; output inst ptr
|
||||||
|
|
||||||
|
mov dword [r13 + Instruction.num_operands], 0
|
||||||
|
mov dword [r13 + Instruction.mnemonic_offs], 0
|
||||||
|
|
||||||
|
call skip_whitespace_rbx
|
||||||
|
mov rdi, rbx
|
||||||
|
call try_parse_label ; parse mnemonic
|
||||||
|
test eax, eax
|
||||||
|
jz .not_inst
|
||||||
|
|
||||||
|
lea rdi, [rel mnemonics]
|
||||||
|
mov esi, MNEMONICS_SIZE
|
||||||
|
mov rdx, rbx
|
||||||
|
mov ecx, eax
|
||||||
|
add rbx, rax
|
||||||
|
call strstr
|
||||||
|
cmp rax, -1
|
||||||
|
je .not_inst
|
||||||
|
|
||||||
|
mov dword [r13 + Instruction.mnemonic_offs], eax
|
||||||
|
|
||||||
|
call skip_whitespace_rbx
|
||||||
|
mov rdi, rbx
|
||||||
|
lea rsi, [r13 + Instruction.operand1]
|
||||||
|
call try_parse_operand
|
||||||
|
test eax, eax
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov dword [r13 + Instruction.num_operands], 1
|
||||||
|
add rbx, rax
|
||||||
|
|
||||||
|
call skip_whitespace_rbx
|
||||||
|
cmp byte [rbx], `,`
|
||||||
|
jne .done
|
||||||
|
|
||||||
|
inc rbx
|
||||||
|
call skip_whitespace_rbx
|
||||||
|
|
||||||
|
mov rdi, rbx
|
||||||
|
lea rsi, [r13 + Instruction.operand2]
|
||||||
|
call try_parse_operand
|
||||||
|
test eax, eax
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov dword [r13 + Instruction.num_operands], 2
|
||||||
|
add rbx, rax
|
||||||
|
|
||||||
|
.done:
|
||||||
|
mov rax, rbx
|
||||||
|
pop rdi
|
||||||
|
sub rax, rdi ; number of bytes consumed
|
||||||
|
mov rdx, r13 ; output inst ptr
|
||||||
|
|
||||||
|
pop r13
|
||||||
|
pop rbx
|
||||||
|
ret
|
||||||
|
|
||||||
|
.not_inst:
|
||||||
|
xor rax, rax
|
||||||
|
mov rdx, r13 ; output inst ptr
|
||||||
|
|
||||||
|
pop rdi
|
||||||
|
pop r13
|
||||||
|
pop rbx
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; find the op_table entry returning the pointer to the entry in $rax, or 0 if not found.
|
||||||
|
;; inputs:
|
||||||
|
;; $edi: mnemonic offset
|
||||||
|
;; $esi: encoding
|
||||||
|
;; $edx: size mask
|
||||||
|
;; $ecx: number of operands
|
||||||
|
find_op_table_entry:
|
||||||
|
push rbx
|
||||||
|
lea rax, [rel op_table]
|
||||||
|
lea rbx, [rel op_table_end]
|
||||||
|
neg edi
|
||||||
|
.loop:
|
||||||
|
cmp rax, rbx
|
||||||
|
jge .not_found
|
||||||
|
|
||||||
|
cmp edi, dword [rax] ; mnemonic offset
|
||||||
|
jne .next
|
||||||
|
|
||||||
|
cmp ecx, dword [rax + 4] ; number of operands
|
||||||
|
jne .next
|
||||||
|
|
||||||
|
mov r8d, dword [rax + 12] ; op2 size
|
||||||
|
shl r8d, 8
|
||||||
|
or r8d, dword [rax + 8] ; op1 size
|
||||||
|
mov r9d, edx
|
||||||
|
and r9d, r8d
|
||||||
|
cmp r9d, r8d
|
||||||
|
jne .next
|
||||||
|
|
||||||
|
mov r8d, dword [rax + 16] ; encoding
|
||||||
|
mov r9d, esi
|
||||||
|
and r9d, 0xff00
|
||||||
|
and r9d, r8d
|
||||||
|
jz .next
|
||||||
|
mov r9d, esi
|
||||||
|
and r9d, 0xff
|
||||||
|
and r9d, r8d
|
||||||
|
jnz .found
|
||||||
|
|
||||||
|
.next:
|
||||||
|
add rax, 32
|
||||||
|
jmp .loop
|
||||||
|
.not_found:
|
||||||
|
xor eax, eax
|
||||||
|
.found:
|
||||||
|
pop rbx
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
;; encode the instruction at $rdi into ofile
|
||||||
|
global encode_inst
|
||||||
|
encode_inst:
|
||||||
|
push rbx
|
||||||
|
push r12
|
||||||
|
push r13
|
||||||
|
|
||||||
|
xor ebx, ebx ; bytes written
|
||||||
|
mov r12, rdi ; inst ptr
|
||||||
|
|
||||||
|
; calculate the encoding
|
||||||
|
xor esi, esi
|
||||||
|
xor eax, eax
|
||||||
|
; we have to check if the 2nd operand, if it exists, is CL or 1
|
||||||
|
cmp dword [r12 + Instruction.num_operands], 2
|
||||||
|
jb .op1
|
||||||
|
|
||||||
|
; calculate size mask of op2.
|
||||||
|
; for reg and mem operands, this is 1 << .size.
|
||||||
|
; for imm operands, this is !(!0 << .size)
|
||||||
|
mov ecx, dword [r12 + Instruction.operand2 + operand.size]
|
||||||
|
mov eax, 1
|
||||||
|
shl eax, cl
|
||||||
|
|
||||||
|
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM
|
||||||
|
jne .size_done_op2
|
||||||
|
xor eax, eax
|
||||||
|
lea ecx, [ecx - 1]
|
||||||
|
sub eax, 1
|
||||||
|
shl eax, cl
|
||||||
|
|
||||||
|
.size_done_op2:
|
||||||
|
shl eax, 8
|
||||||
|
|
||||||
|
mov esi, dword [r12 + Instruction.operand2 + operand.type]
|
||||||
|
shl esi, 8
|
||||||
|
|
||||||
|
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_REG
|
||||||
|
jne .check_imm_one
|
||||||
|
cmp qword [r12 + Instruction.operand2 + operand.reg], 1
|
||||||
|
jne .op1
|
||||||
|
or esi, ENC_CL_BITS << 8
|
||||||
|
|
||||||
|
.check_imm_one:
|
||||||
|
cmp dword [r12 + Instruction.operand2 + operand.type], KIND_IMM
|
||||||
|
jne .op1
|
||||||
|
cmp qword [r12 + Instruction.operand2 + operand_imm.imm], 1
|
||||||
|
jne .op1
|
||||||
|
or esi, ENC_ONE_BITS << 8
|
||||||
|
|
||||||
|
.op1:
|
||||||
|
or esi, dword [r12 + Instruction.operand1 + operand.type]
|
||||||
|
|
||||||
|
; calculate size mask of op1.
|
||||||
|
; for reg and mem operands, this is 1 << .size.
|
||||||
|
; for imm operands, this is !(!0 << .size)
|
||||||
|
mov ecx, dword [r12 + Instruction.operand1 + operand.size]
|
||||||
|
mov edx, 1
|
||||||
|
shl edx, cl
|
||||||
|
cmp dword [r12 + Instruction.operand1 + operand.type], KIND_IMM
|
||||||
|
jne .size_done
|
||||||
|
xor edx, edx
|
||||||
|
lea ecx, [ecx - 1]
|
||||||
|
sub edx, 1
|
||||||
|
shl edx, cl
|
||||||
|
.size_done:
|
||||||
|
xor edx, eax
|
||||||
|
|
||||||
|
mov ecx, dword [r12 + Instruction.num_operands]
|
||||||
|
mov edi, dword [r12 + Instruction.mnemonic_offs]
|
||||||
|
call find_op_table_entry
|
||||||
|
test rax, rax
|
||||||
|
jz .done
|
||||||
|
mov r13, rax ; pointer to the op_table entry
|
||||||
|
|
||||||
|
; write the prefix, if any
|
||||||
|
cmp dword [r13 + 24], 0
|
||||||
|
je .rex
|
||||||
|
|
||||||
|
movzx edi, byte [r13 + 24]
|
||||||
|
mov esi, 1
|
||||||
|
call write_bytes
|
||||||
|
|
||||||
|
.rex:
|
||||||
|
; shuffle operands into reg-mem order
|
||||||
|
lea rdi, [r12 + Instruction.operand1]
|
||||||
|
lea rsi, [r12 + Instruction.operand2]
|
||||||
|
|
||||||
|
; discard rsi if it is not used in the encoding
|
||||||
|
test dword [r13 + OpEntry.encoding], ENC_DISCARD_BITS << 8
|
||||||
|
jz .no_discard
|
||||||
|
xor rsi, rsi
|
||||||
|
.no_discard:
|
||||||
|
|
||||||
|
test dword [r13 + OpEntry.encoding], ENC_M_BITS
|
||||||
|
jz .no_swap
|
||||||
|
xchg rdi, rsi
|
||||||
|
.no_swap:
|
||||||
|
|
||||||
|
sub rsp, 16
|
||||||
|
mov qword [rsp], rdi ; reg operand
|
||||||
|
mov qword [rsp + 8], rsi ; mem operand
|
||||||
|
|
||||||
|
; compute the REX byte, if any
|
||||||
|
call compute_rex
|
||||||
|
|
||||||
|
mov edi, eax
|
||||||
|
call write_non_zero_bytes
|
||||||
|
|
||||||
|
; write the opcode and any ENC_O bits
|
||||||
|
xor esi, esi
|
||||||
|
test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS
|
||||||
|
jz .no_o_bits
|
||||||
|
mov rsi, qword [rsp]
|
||||||
|
mov esi, dword [rsi + operand.reg + register.num]
|
||||||
|
and esi, 0x7
|
||||||
|
|
||||||
|
.no_o_bits:
|
||||||
|
mov edi, dword [r13 + OpEntry.opcode]
|
||||||
|
call write_opcode_with_bits
|
||||||
|
|
||||||
|
test dword [r13 + OpEntry.encoding], ENC_O_SIGNAL_BITS | ENC_O_SIGNAL_BITS << 8
|
||||||
|
jnz .imm
|
||||||
|
|
||||||
|
; compute the ModRM and SIB bytes, if any
|
||||||
|
mov rdi, qword [rsp]
|
||||||
|
mov rsi, qword [rsp + 8]
|
||||||
|
mov edx, dword [r13 + OpEntry.reg_bits]
|
||||||
|
call compute_modrm_sib
|
||||||
|
push rax
|
||||||
|
|
||||||
|
shl edx, 8 ; shift SIB
|
||||||
|
or eax, edx ; combine modRM and SIB
|
||||||
|
mov edi, eax
|
||||||
|
call write_non_zero_bytes
|
||||||
|
|
||||||
|
pop rax
|
||||||
|
shl eax, 6
|
||||||
|
dec eax
|
||||||
|
cmp eax, 2
|
||||||
|
jge .imm
|
||||||
|
mov rdi, qword [rsp + 8]
|
||||||
|
mov edi, dword [rdi + operand.disp]
|
||||||
|
call write_disp
|
||||||
|
|
||||||
|
mov rdi, qword [rsp + 8]
|
||||||
|
test dword [rdi + operand.type], KIND_LABEL_FLAG
|
||||||
|
jz .imm
|
||||||
|
|
||||||
|
mov edi, dword [rdi + operand.disp]
|
||||||
|
lea rsi, [rbx - 4]
|
||||||
|
call push_lbl_ref_by_hash
|
||||||
|
|
||||||
|
.imm:
|
||||||
|
mov rdi, qword [rsp]
|
||||||
|
mov rsi, qword [rsp + 8]
|
||||||
|
add rsp, 16
|
||||||
|
; write immediate, if any
|
||||||
|
cmp dword [rsi + operand.type], KIND_IMM
|
||||||
|
je .imm1
|
||||||
|
mov rsi, rdi
|
||||||
|
cmp dword [rsi + operand.type], KIND_IMM
|
||||||
|
jne .done
|
||||||
|
|
||||||
|
.imm1:
|
||||||
|
push rsi
|
||||||
|
test dword [rsi + operand.type], KIND_LABEL_FLAG
|
||||||
|
jz .imm2
|
||||||
|
|
||||||
|
mov edi, dword [rsi + operand.disp]
|
||||||
|
mov rsi, rbx
|
||||||
|
call push_lbl_ref_by_hash
|
||||||
|
|
||||||
|
.imm2:
|
||||||
|
pop rsi
|
||||||
|
mov rdi, qword [rsi + operand_imm.imm]
|
||||||
|
mov esi, dword [r13 + OpEntry.op2_size]
|
||||||
|
call write_imm
|
||||||
|
|
||||||
|
.done:
|
||||||
|
mov rax, rbx ; bytes written
|
||||||
|
pop r13
|
||||||
|
pop r12
|
||||||
|
pop rbx
|
||||||
|
ret
|
||||||
|
|
||||||
|
write_imm:
|
||||||
|
jmp write_bytes
|
||||||
|
|
||||||
|
write_disp:
|
||||||
|
cmp edi, 0xff
|
||||||
|
seta sil
|
||||||
|
shl sil, 1
|
||||||
|
inc sil
|
||||||
|
movzx rsi, sil
|
||||||
|
jmp write_bytes
|
||||||
|
|
||||||
|
write_bytes:
|
||||||
|
call print_u8s_le
|
||||||
|
add rbx, rax
|
||||||
|
ret
|
||||||
|
|
||||||
|
write_opcode_with_bits:
|
||||||
|
call swap_bytes_and_shift_in_place
|
||||||
|
or rdi, rsi
|
||||||
|
jmp write_non_zero_bytes
|
||||||
|
|
||||||
|
;; writes the contents of the $rdi register until the first zero byte.
|
||||||
|
write_non_zero_bytes:
|
||||||
|
call print_non_zero_u8s_le
|
||||||
|
add rbx, rax
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; compute the modRM and SIB bytes for the operands $rdi and $rsi, in reg-mem order, with the $rdx reg-bits
|
||||||
|
compute_modrm_sib:
|
||||||
|
xor eax, eax ; modrm
|
||||||
|
mov eax, edx
|
||||||
|
shl eax, 3 ; reg-bits
|
||||||
|
|
||||||
|
xor edx, edx ; sib
|
||||||
|
cmp dword [rdi + operand.type], KIND_REG
|
||||||
|
jne .check_mem
|
||||||
|
|
||||||
|
mov eax, dword [rdi + operand.reg + register.num]
|
||||||
|
and eax, 7
|
||||||
|
shl eax, 3 ; reg-bits
|
||||||
|
|
||||||
|
.check_mem:
|
||||||
|
test rsi, rsi
|
||||||
|
jz .done_mem
|
||||||
|
mov ecx, dword [rsi + operand.reg + register.num]
|
||||||
|
and ecx, 7
|
||||||
|
or eax, ecx ; r/m bits
|
||||||
|
|
||||||
|
or eax, 0xC0 ; mod = 11b
|
||||||
|
cmp dword [rsi + operand.type], KIND_REG
|
||||||
|
je .done_mem
|
||||||
|
and eax, 0x37 ; clear mod bits, mod = 00b
|
||||||
|
|
||||||
|
cmp dword [rsi + operand.type], KIND_MEM
|
||||||
|
jne .done_mem
|
||||||
|
|
||||||
|
cmp dword [rsi + operand.disp], 0
|
||||||
|
jne .sib ; mod = 01b or 10b
|
||||||
|
; if mod == 0 && r/m & 111 == 101 (rbp or r13), then we need a displacement, even if its 0
|
||||||
|
mov ecx, eax
|
||||||
|
and ecx, 7
|
||||||
|
cmp ecx, 5
|
||||||
|
jne .done_mem
|
||||||
|
|
||||||
|
.sib:
|
||||||
|
cmp dword [rsi + operand.disp], 0xff
|
||||||
|
seta cl
|
||||||
|
mov edx, 1
|
||||||
|
shl edx, cl
|
||||||
|
shl edx, 6 ; mod = 01b or 10b
|
||||||
|
or eax, edx
|
||||||
|
|
||||||
|
xor edx, edx ; sib
|
||||||
|
; copy r/m bits to SIB base bits
|
||||||
|
mov edx, eax
|
||||||
|
shr edx, 3
|
||||||
|
and edx, 7
|
||||||
|
|
||||||
|
and eax, 0b11000111 ; clear the r/m bits
|
||||||
|
or eax, 0b00100000 ; set the r/m bits to 100b (SIB follows)
|
||||||
|
|
||||||
|
mov edx, 0b00100000 ; index = 100b (no index)
|
||||||
|
|
||||||
|
mov ecx, dword [rsi + operand.scale]
|
||||||
|
and ecx, 3
|
||||||
|
shl ecx, 6 ; scale bits
|
||||||
|
or edx, ecx
|
||||||
|
|
||||||
|
mov ecx, dword [rsi + operand.index + register.num]
|
||||||
|
cmp ecx, -1
|
||||||
|
jne .done_mem
|
||||||
|
|
||||||
|
and ecx, 7
|
||||||
|
shl ecx, 3 ; index bits
|
||||||
|
or edx, ecx
|
||||||
|
|
||||||
|
.done_mem:
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; compute the rex bits for the operands $rdi and $rsi, in reg-mem order
|
||||||
|
compute_rex:
|
||||||
|
xor eax, eax
|
||||||
|
test rdi, rdi
|
||||||
|
jz .check_mem
|
||||||
|
cmp dword [rdi + operand.type], KIND_REG
|
||||||
|
jne .check_mem
|
||||||
|
|
||||||
|
test dword [rdi + operand.reg + register.num], 8
|
||||||
|
setne dl
|
||||||
|
shl dl, 2 ; REX.R
|
||||||
|
or al, dl
|
||||||
|
|
||||||
|
cmp dword [rdi + operand.size], 3
|
||||||
|
setnb dl
|
||||||
|
shl dl, 3 ; REX.W
|
||||||
|
or al, dl
|
||||||
|
|
||||||
|
.check_mem:
|
||||||
|
test rsi, rsi
|
||||||
|
jz .done
|
||||||
|
test dword [rsi + operand.type], KIND_REG | KIND_MEM
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
test dword [rsi + operand.reg + register.num], 8
|
||||||
|
setne dl
|
||||||
|
or al, dl ; REX.B
|
||||||
|
|
||||||
|
cmp dword [rsi + operand.size], 3
|
||||||
|
setnb dl
|
||||||
|
shl dl, 3 ; REX.W
|
||||||
|
or al, dl
|
||||||
|
|
||||||
|
test dword [rsi + operand.type], KIND_MEM
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov ecx, dword [rsi + operand.index + register.num]
|
||||||
|
cmp ecx, -1
|
||||||
|
je .done
|
||||||
|
|
||||||
|
test ecx, 8
|
||||||
|
setne dl
|
||||||
|
shl dl, 1 ; REX.X
|
||||||
|
or al, dl
|
||||||
|
|
||||||
|
.done:
|
||||||
|
test al, al
|
||||||
|
setne dl
|
||||||
|
shl dl, 6 ; REX 4Xh
|
||||||
|
or al, dl
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; parse buffer $rdi, bytes written in $rsi, return updated bytes written.
|
||||||
|
parse_line:
|
||||||
|
push rbx ; buf
|
||||||
|
push r12 ; line length
|
||||||
|
push rdi
|
||||||
|
|
||||||
|
mov rbx, rdi
|
||||||
|
mov r12, rsi ; bytes written
|
||||||
|
call skip_whitespace_rbx
|
||||||
|
|
||||||
|
cmp byte [rbx], '@'
|
||||||
|
je .label
|
||||||
|
|
||||||
|
mov rdi, rbx
|
||||||
|
sub rsp, 72
|
||||||
|
mov rsi, rsp
|
||||||
|
call try_parse_inst
|
||||||
|
test eax, eax
|
||||||
|
jz .done_inst
|
||||||
|
|
||||||
|
lea rdi, [rsp]
|
||||||
|
call encode_inst
|
||||||
|
add r12, rax
|
||||||
|
|
||||||
|
.done_inst:
|
||||||
|
add rsp, 72
|
||||||
|
jmp .done
|
||||||
|
|
||||||
|
.label:
|
||||||
|
inc rbx ; skip the leading '@'
|
||||||
|
mov rdi, rbx
|
||||||
|
call try_parse_label
|
||||||
|
test eax, eax
|
||||||
|
jz .done
|
||||||
|
|
||||||
|
mov rdi, rbx
|
||||||
|
mov rsi, rax
|
||||||
|
add rbx, rax
|
||||||
|
mov rdx, r12 ; label offset
|
||||||
|
call push_lbl
|
||||||
|
|
||||||
|
.done:
|
||||||
|
mov rax, r12
|
||||||
|
pop rdi
|
||||||
|
pop r12
|
||||||
|
pop rbx
|
||||||
|
ret
|
||||||
|
|
||||||
;; Labels
|
;; Labels
|
||||||
|
|
||||||
|
|
@ -1473,7 +2153,11 @@ struc LabelTable
|
||||||
.labels resq 1
|
.labels resq 1
|
||||||
endstruc
|
endstruc
|
||||||
|
|
||||||
|
section .data
|
||||||
|
labels dq 0
|
||||||
|
label_refs dq 0
|
||||||
|
|
||||||
|
section .text
|
||||||
;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax
|
;; calculates the hash of a byte sequence in $rdi of length $rsi, returning it in $eax
|
||||||
global fasthash
|
global fasthash
|
||||||
fasthash:
|
fasthash:
|
||||||
|
|
@ -1490,6 +2174,36 @@ fasthash:
|
||||||
.done:
|
.done:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
init_label_tables:
|
||||||
|
mov edi, 0x100
|
||||||
|
mov esi, 8
|
||||||
|
call heap_alloc
|
||||||
|
lea rdi, [rel labels]
|
||||||
|
mov dword [rdi + LabelTable.size], 0
|
||||||
|
mov dword [rdi + LabelTable.cap], 0x100 / 8
|
||||||
|
mov qword [rdi + LabelTable.labels], rax
|
||||||
|
|
||||||
|
mov edi, 0x100
|
||||||
|
mov esi, 8
|
||||||
|
call heap_alloc
|
||||||
|
lea rdi, [rel label_refs]
|
||||||
|
mov dword [rdi + LabelTable.size], 0
|
||||||
|
mov dword [rdi + LabelTable.cap], 0x100 / 8
|
||||||
|
mov qword [rdi + LabelTable.labels], rax
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; push label hash $edi with offset $esi into the label ref table.
|
||||||
|
push_lbl_ref_by_hash:
|
||||||
|
push rbx
|
||||||
|
push r12
|
||||||
|
push r13
|
||||||
|
|
||||||
|
mov rbx, [rel label_refs]
|
||||||
|
mov r13d, esi ; label offset
|
||||||
|
mov r12d, edi ; label hash
|
||||||
|
jmp push_lbl_common.hash_in_r12d
|
||||||
|
|
||||||
;; push label $rdi with length $rsi and offset $rdx into the label ref table
|
;; push label $rdi with length $rsi and offset $rdx into the label ref table
|
||||||
push_lbl_ref:
|
push_lbl_ref:
|
||||||
mov rcx, [rel label_refs]
|
mov rcx, [rel label_refs]
|
||||||
|
|
@ -1508,6 +2222,7 @@ push_lbl_common:
|
||||||
|
|
||||||
call fasthash
|
call fasthash
|
||||||
mov r12d, eax ; store the hash in r12d
|
mov r12d, eax ; store the hash in r12d
|
||||||
|
.hash_in_r12d:
|
||||||
|
|
||||||
mov eax, dword [rbx + LabelTable.size]
|
mov eax, dword [rbx + LabelTable.size]
|
||||||
cmp eax, dword [rbx + LabelTable.cap]
|
cmp eax, dword [rbx + LabelTable.cap]
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,13 @@ unsafe extern "C" {
|
||||||
#[link_name = "try_parse_operand"]
|
#[link_name = "try_parse_operand"]
|
||||||
unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
||||||
|
|
||||||
|
#[link_name = "try_parse_inst"]
|
||||||
|
unsafe fn try_parse_inst_impl(_: *const u8, _: *mut Instruction)
|
||||||
|
-> (usize, *const Instruction);
|
||||||
|
|
||||||
|
#[link_name = "encode_inst"]
|
||||||
|
unsafe fn encode_inst_impl(_: *mut Instruction) -> usize;
|
||||||
|
|
||||||
unsafe fn fasthash(_: *const u8, _: usize) -> u32;
|
unsafe fn fasthash(_: *const u8, _: usize) -> u32;
|
||||||
|
|
||||||
#[link_name = "buf"]
|
#[link_name = "buf"]
|
||||||
|
|
@ -26,9 +33,9 @@ enum OperandKind {
|
||||||
None = 0,
|
None = 0,
|
||||||
Reg = 1,
|
Reg = 1,
|
||||||
Mem = 2,
|
Mem = 2,
|
||||||
Imm = 3,
|
Imm = 4,
|
||||||
MemLabel = 4,
|
MemLabel = 2 | 8,
|
||||||
ImmLabel = 5,
|
ImmLabel = 4 | 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C, align(8))]
|
#[repr(C, align(8))]
|
||||||
|
|
@ -56,7 +63,7 @@ impl Operand {
|
||||||
fn label(name: &[u8]) -> Self {
|
fn label(name: &[u8]) -> Self {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
kind: OperandKind::ImmLabel,
|
kind: OperandKind::ImmLabel,
|
||||||
size: 0,
|
size: 2,
|
||||||
reg: Register::invalid(),
|
reg: Register::invalid(),
|
||||||
index: Register::invalid(),
|
index: Register::invalid(),
|
||||||
scale: 0,
|
scale: 0,
|
||||||
|
|
@ -100,6 +107,26 @@ impl Operand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct Instruction {
|
||||||
|
mnemonic_offset: i32,
|
||||||
|
num_operands: u32,
|
||||||
|
operands: [Operand; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
|
||||||
|
let mut inst = core::mem::MaybeUninit::<Instruction>::uninit();
|
||||||
|
let (n, ptr) = unsafe { try_parse_inst_impl(bytes.as_ptr(), inst.as_mut_ptr()) };
|
||||||
|
if n == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((n, unsafe { inst.assume_init() }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Register {
|
impl Register {
|
||||||
fn new(num: u32, size: u32) -> Self {
|
fn new(num: u32, size: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -110,6 +137,10 @@ impl Register {
|
||||||
fn invalid() -> Self {
|
fn invalid() -> Self {
|
||||||
Self { num: !0, size: !0 }
|
Self { num: !0, size: !0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rbp() -> Self {
|
||||||
|
Self { num: 5, size: 0 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -124,6 +155,44 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_inst() {
|
||||||
|
let res = Instruction::parse(b"mov byte [rax], 4\0");
|
||||||
|
// assert_eq!(
|
||||||
|
// res,
|
||||||
|
// Some((
|
||||||
|
// 12,
|
||||||
|
// Instruction {
|
||||||
|
// mnemonic_offset: 0,
|
||||||
|
// num_operands: 2,
|
||||||
|
// operands: [
|
||||||
|
// Operand {
|
||||||
|
// kind: OperandKind::Reg,
|
||||||
|
// size: 3,
|
||||||
|
// reg: Register::new(0, 8),
|
||||||
|
// index: Register::invalid(),
|
||||||
|
// scale: 0,
|
||||||
|
// disp: 0,
|
||||||
|
// },
|
||||||
|
// Operand {
|
||||||
|
// kind: OperandKind::Reg,
|
||||||
|
// size: 3,
|
||||||
|
// reg: Register::new(3, 8),
|
||||||
|
// index: Register::invalid(),
|
||||||
|
// scale: 0,
|
||||||
|
// disp: 0,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// }
|
||||||
|
// ))
|
||||||
|
// );
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut inst = res.unwrap().1;
|
||||||
|
encode_inst_impl((&raw mut inst).cast());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_mem() {
|
fn parse_mem() {
|
||||||
let mut op = MaybeUninit::<Operand>::uninit();
|
let mut op = MaybeUninit::<Operand>::uninit();
|
||||||
|
|
@ -169,7 +238,7 @@ mod tests {
|
||||||
&Operand {
|
&Operand {
|
||||||
kind: OperandKind::Mem,
|
kind: OperandKind::Mem,
|
||||||
size: 1,
|
size: 1,
|
||||||
reg: Register::invalid(),
|
reg: Register::rbp(),
|
||||||
index: Register::new(8, 8),
|
index: Register::new(8, 8),
|
||||||
scale: 8,
|
scale: 8,
|
||||||
disp: 5,
|
disp: 5,
|
||||||
|
|
@ -219,7 +288,7 @@ mod tests {
|
||||||
Operand {
|
Operand {
|
||||||
kind: OperandKind::Mem,
|
kind: OperandKind::Mem,
|
||||||
size: 1,
|
size: 1,
|
||||||
reg: Register::invalid(),
|
reg: Register::rbp(),
|
||||||
index: Register::new(8, 8),
|
index: Register::new(8, 8),
|
||||||
scale: 8,
|
scale: 8,
|
||||||
disp: 5,
|
disp: 5,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue