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
|
||||
;; consume proper mnemonic assembly, rather than hex input.
|
||||
|
||||
struc OFile
|
||||
.fd resd 1
|
||||
.offset resd 1
|
||||
endstruc
|
||||
|
||||
section .bss
|
||||
global buf
|
||||
ifile resb 0x20
|
||||
ofile resb 0x20
|
||||
buf resb 0x100
|
||||
labels resq 1
|
||||
label_refs resq 1
|
||||
heap resq 1
|
||||
section .data
|
||||
_start_lbl db "_start", 0
|
||||
|
||||
ENC_RM equ 1
|
||||
ENC_MR equ 2
|
||||
ENC_MI equ 3
|
||||
ENC_OI equ 4
|
||||
ENC_M equ 5
|
||||
ENC_O equ 6
|
||||
ENC_I equ 7
|
||||
ENC_MC equ 8 ; memory with cl
|
||||
ENC_M1 equ 9 ; memory with 1
|
||||
section .data
|
||||
ENC_R_BITS equ 1
|
||||
ENC_M_BITS equ 2
|
||||
ENC_I_BITS equ 4
|
||||
ENC_CL_BITS equ 8
|
||||
ENC_ONE_BITS equ 16
|
||||
ENC_O_SIGNAL_BITS equ 32
|
||||
|
||||
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_16 equ 2
|
||||
|
|
@ -83,8 +101,19 @@ mnemonics_table:
|
|||
MN_CALL db "call", 0
|
||||
MN_XCHG db "xchg", 0
|
||||
MN_SYSCALL db "syscall", 0
|
||||
MNEMONICS_SIZE equ $ - mnemonics_table
|
||||
|
||||
;; 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:
|
||||
;; 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, 2, 2, ENC_OI, 0B8h, 00, 66h
|
||||
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, 2, 2, ENC_MI, 0C7h, 00, 66h
|
||||
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, 8, ENC_OI, 0B8h, 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
|
||||
|
|
@ -642,6 +671,84 @@ format_u32:
|
|||
add rsp, 16
|
||||
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:
|
||||
call format_u8
|
||||
sub rsp, 8
|
||||
|
|
@ -763,14 +870,53 @@ print_op_table:
|
|||
;; rdi: *u8
|
||||
strlen:
|
||||
xor rax, rax
|
||||
.strlen_loop:
|
||||
.loop:
|
||||
cmp byte [rdi + rax], 0
|
||||
je .strlen_done
|
||||
je .done
|
||||
inc rax
|
||||
jmp .strlen_loop
|
||||
.strlen_done:
|
||||
jmp .loop
|
||||
.done:
|
||||
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)
|
||||
;; rhs: (rdx, rcx)
|
||||
;; al
|
||||
|
|
@ -899,9 +1045,12 @@ exit:
|
|||
%define KIND_NONE 0
|
||||
%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
|
||||
%define KIND_IMM 4
|
||||
%define KIND_LABEL_FLAG 8
|
||||
; 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
|
||||
.type resd 1
|
||||
|
|
@ -918,6 +1067,11 @@ struc operand_imm
|
|||
.imm resq 1
|
||||
endstruc
|
||||
|
||||
struc register
|
||||
.size resd 1
|
||||
.num resd 1
|
||||
endstruc
|
||||
|
||||
abcd_reg_indices: db 0, 3, 1, 2
|
||||
rX_suffixes: db 'b', 'w', 'd'
|
||||
|
||||
|
|
@ -1169,7 +1323,8 @@ try_parse_mem:
|
|||
|
||||
mov dword [r12 + operand.type], KIND_MEM
|
||||
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 dword [r12 + operand.scale], 0
|
||||
mov dword [r12 + operand.disp], 0
|
||||
|
|
@ -1414,6 +1569,7 @@ try_parse_operand:
|
|||
.label:
|
||||
inc rbx ; skip the leading `'`
|
||||
mov dword [r12 + operand.type], KIND_IMM_LABEL
|
||||
mov dword [r12 + operand.size], 2 ; 32-bit
|
||||
mov rdi, rbx
|
||||
call try_parse_label
|
||||
test eax, eax
|
||||
|
|
@ -1458,7 +1614,531 @@ try_parse_label:
|
|||
pop rbx
|
||||
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
|
||||
|
||||
|
|
@ -1473,7 +2153,11 @@ struc LabelTable
|
|||
.labels resq 1
|
||||
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
|
||||
global fasthash
|
||||
fasthash:
|
||||
|
|
@ -1490,6 +2174,36 @@ fasthash:
|
|||
.done:
|
||||
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_lbl_ref:
|
||||
mov rcx, [rel label_refs]
|
||||
|
|
@ -1508,6 +2222,7 @@ push_lbl_common:
|
|||
|
||||
call fasthash
|
||||
mov r12d, eax ; store the hash in r12d
|
||||
.hash_in_r12d:
|
||||
|
||||
mov eax, dword [rbx + LabelTable.size]
|
||||
cmp eax, dword [rbx + LabelTable.cap]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@ unsafe extern "C" {
|
|||
#[link_name = "try_parse_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;
|
||||
|
||||
#[link_name = "buf"]
|
||||
|
|
@ -26,9 +33,9 @@ enum OperandKind {
|
|||
None = 0,
|
||||
Reg = 1,
|
||||
Mem = 2,
|
||||
Imm = 3,
|
||||
MemLabel = 4,
|
||||
ImmLabel = 5,
|
||||
Imm = 4,
|
||||
MemLabel = 2 | 8,
|
||||
ImmLabel = 4 | 8,
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
|
|
@ -56,7 +63,7 @@ impl Operand {
|
|||
fn label(name: &[u8]) -> Self {
|
||||
let mut this = Self {
|
||||
kind: OperandKind::ImmLabel,
|
||||
size: 0,
|
||||
size: 2,
|
||||
reg: Register::invalid(),
|
||||
index: Register::invalid(),
|
||||
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 {
|
||||
fn new(num: u32, size: u32) -> Self {
|
||||
Self {
|
||||
|
|
@ -110,6 +137,10 @@ impl Register {
|
|||
fn invalid() -> Self {
|
||||
Self { num: !0, size: !0 }
|
||||
}
|
||||
|
||||
fn rbp() -> Self {
|
||||
Self { num: 5, size: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn parse_mem() {
|
||||
let mut op = MaybeUninit::<Operand>::uninit();
|
||||
|
|
@ -169,7 +238,7 @@ mod tests {
|
|||
&Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 1,
|
||||
reg: Register::invalid(),
|
||||
reg: Register::rbp(),
|
||||
index: Register::new(8, 8),
|
||||
scale: 8,
|
||||
disp: 5,
|
||||
|
|
@ -219,7 +288,7 @@ mod tests {
|
|||
Operand {
|
||||
kind: OperandKind::Mem,
|
||||
size: 1,
|
||||
reg: Register::invalid(),
|
||||
reg: Register::rbp(),
|
||||
index: Register::new(8, 8),
|
||||
scale: 8,
|
||||
disp: 5,
|
||||
|
|
|
|||
Loading…
Reference in a new issue