diff options
Diffstat (limited to 'yjit_asm.c')
-rw-r--r-- | yjit_asm.c | 1786 |
1 files changed, 0 insertions, 1786 deletions
diff --git a/yjit_asm.c b/yjit_asm.c deleted file mode 100644 index 0d074d5e4d..0000000000 --- a/yjit_asm.c +++ /dev/null @@ -1,1786 +0,0 @@ -// This file is a fragment of the yjit.o compilation unit. See yjit.c. -// -// Note that the definition for some of these functions don't specify -// static inline, but their declaration in yjit_asm.h do. The resulting -// linkage is the same as if they both specify. The relevant sections in -// N1256 is 6.2.2p4, 6.2.2p5, and 6.7.4p5. -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <stdint.h> -#include <assert.h> -#include <errno.h> - -// For mmapp(), sysconf() -#ifndef _WIN32 -#include <unistd.h> -#include <sys/mman.h> -#endif - -#include "yjit_asm.h" - -// Compute the number of bits needed to encode a signed value -uint32_t sig_imm_size(int64_t imm) -{ - // Compute the smallest size this immediate fits in - if (imm >= INT8_MIN && imm <= INT8_MAX) - return 8; - if (imm >= INT16_MIN && imm <= INT16_MAX) - return 16; - if (imm >= INT32_MIN && imm <= INT32_MAX) - return 32; - - return 64; -} - -// Compute the number of bits needed to encode an unsigned value -uint32_t unsig_imm_size(uint64_t imm) -{ - // Compute the smallest size this immediate fits in - if (imm <= UINT8_MAX) - return 8; - else if (imm <= UINT16_MAX) - return 16; - else if (imm <= UINT32_MAX) - return 32; - - return 64; -} - -x86opnd_t mem_opnd(uint32_t num_bits, x86opnd_t base_reg, int32_t disp) -{ - bool is_iprel = base_reg.as.reg.reg_type == REG_IP; - - x86opnd_t opnd = { - OPND_MEM, - num_bits, - .as.mem = { base_reg.as.reg.reg_no, 0, 0, false, is_iprel, disp } - }; - - return opnd; -} - -x86opnd_t mem_opnd_sib(uint32_t num_bits, x86opnd_t base_reg, x86opnd_t index_reg, int32_t scale, int32_t disp) -{ - uint8_t scale_exp; - switch (scale) { - case 8: - scale_exp = 3; - break; - case 4: - scale_exp = 2; - break; - case 2: - scale_exp = 1; - break; - case 1: - scale_exp = 0; - break; - default: - rb_bug("yjit: scale not one of 1,2,4,8"); - break; - } - - bool is_iprel = base_reg.as.reg.reg_type == REG_IP; - - x86opnd_t opnd = { - OPND_MEM, - num_bits, - .as.mem = { - .base_reg_no = base_reg.as.reg.reg_no, - .idx_reg_no = index_reg.as.reg.reg_no, - .has_idx = 1, - .scale_exp = scale_exp, - .is_iprel = is_iprel, - .disp = disp - } - }; - - return opnd; -} - -static x86opnd_t resize_opnd(x86opnd_t opnd, uint32_t num_bits) -{ - assert (num_bits % 8 == 0); - x86opnd_t sub = opnd; - sub.num_bits = num_bits; - return sub; -} - -x86opnd_t imm_opnd(int64_t imm) -{ - x86opnd_t opnd = { - OPND_IMM, - sig_imm_size(imm), - .as.imm = imm - }; - - return opnd; -} - -x86opnd_t const_ptr_opnd(const void *ptr) -{ - x86opnd_t opnd = { - OPND_IMM, - 64, - .as.unsig_imm = (uint64_t)ptr - }; - - return opnd; -} - -// Align the current write position to a multiple of bytes -static uint8_t *align_ptr(uint8_t *ptr, uint32_t multiple) -{ - // Compute the pointer modulo the given alignment boundary - uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple; - - // If the pointer is already aligned, stop - if (rem == 0) - return ptr; - - // Pad the pointer by the necessary amount to align it - uint32_t pad = multiple - rem; - - return ptr + pad; -} - -// Allocate a block of executable memory -uint8_t *alloc_exec_mem(uint32_t mem_size) -{ -#ifndef _WIN32 - uint8_t *mem_block; - - // On Linux - #if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE) - // Align the requested address to page size - uint32_t page_size = (uint32_t)sysconf(_SC_PAGESIZE); - uint8_t *req_addr = align_ptr((uint8_t*)&alloc_exec_mem, page_size); - - do { - // Try to map a chunk of memory as executable - mem_block = (uint8_t*)mmap( - (void*)req_addr, - mem_size, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, - -1, - 0 - ); - - // If we succeeded, stop - if (mem_block != MAP_FAILED) { - break; - } - - // +4MB - req_addr += 4 * 1024 * 1024; - } while (req_addr < (uint8_t*)&alloc_exec_mem + INT32_MAX); - - // On MacOS and other platforms - #else - // Try to map a chunk of memory as executable - mem_block = (uint8_t*)mmap( - (void*)alloc_exec_mem, - mem_size, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0 - ); - #endif - - // Fallback - if (mem_block == MAP_FAILED) { - // Try again without the address hint (e.g., valgrind) - mem_block = (uint8_t*)mmap( - NULL, - mem_size, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0 - ); - } - - // Check that the memory mapping was successful - if (mem_block == MAP_FAILED) { - perror("mmap call failed"); - exit(-1); - } - - // Fill the executable memory with INT3 (0xCC) so that - // executing uninitialized memory will fault - memset(mem_block, 0xCC, mem_size); - - return mem_block; -#else - // Windows not supported for now - return NULL; -#endif -} - -// Head of the list of free code pages -static code_page_t *freelist = NULL; - -// Allocate a single code page from a pool of free pages -code_page_t *alloc_code_page(void) -{ - // If the free list is empty - if (!freelist) { - // Allocate many pages at once - uint8_t *code_chunk = alloc_exec_mem(PAGES_PER_ALLOC * CODE_PAGE_SIZE); - - // Do this in reverse order so we allocate our pages in order - for (int i = PAGES_PER_ALLOC - 1; i >= 0; --i) { - code_page_t *code_page = malloc(sizeof(code_page_t)); - code_page->mem_block = code_chunk + i * CODE_PAGE_SIZE; - assert ((intptr_t)code_page->mem_block % CODE_PAGE_SIZE == 0); - code_page->page_size = CODE_PAGE_SIZE; - code_page->_next = freelist; - freelist = code_page; - } - } - - code_page_t *free_page = freelist; - freelist = freelist->_next; - - return free_page; -} - -// Put a code page back into the allocation pool -void free_code_page(code_page_t *code_page) -{ - code_page->_next = freelist; - freelist = code_page; -} - -// Initialize a code block object -void cb_init(codeblock_t *cb, uint8_t *mem_block, uint32_t mem_size) -{ - assert (mem_block); - cb->mem_block = mem_block; - cb->mem_size = mem_size; - cb->write_pos = 0; - cb->num_labels = 0; - cb->num_refs = 0; -} - -// Align the current write position to a multiple of bytes -void cb_align_pos(codeblock_t *cb, uint32_t multiple) -{ - // Compute the pointer modulo the given alignment boundary - uint8_t *ptr = &cb->mem_block[cb->write_pos]; - uint8_t *aligned_ptr = align_ptr(ptr, multiple); - - // Pad the pointer by the necessary amount to align it - ptrdiff_t pad = aligned_ptr - ptr; - cb->write_pos += (int32_t)pad; -} - -// Set the current write position -void cb_set_pos(codeblock_t *cb, uint32_t pos) -{ - assert (pos < cb->mem_size); - cb->write_pos = pos; -} - -// Set the current write position from a pointer -void cb_set_write_ptr(codeblock_t *cb, uint8_t *code_ptr) -{ - intptr_t pos = code_ptr - cb->mem_block; - assert (pos < cb->mem_size); - cb->write_pos = (uint32_t)pos; -} - -// Get a direct pointer into the executable memory block -uint8_t *cb_get_ptr(codeblock_t *cb, uint32_t index) -{ - assert (index < cb->mem_size); - return &cb->mem_block[index]; -} - -// Get a direct pointer to the current write position -uint8_t *cb_get_write_ptr(codeblock_t *cb) -{ - return cb_get_ptr(cb, cb->write_pos); -} - -// Write a byte at the current position -void cb_write_byte(codeblock_t *cb, uint8_t byte) -{ - assert (cb->mem_block); - assert (cb->write_pos + 1 <= cb->mem_size); - cb->mem_block[cb->write_pos++] = byte; -} - -// Write multiple bytes starting from the current position -void cb_write_bytes(codeblock_t *cb, uint32_t num_bytes, ...) -{ - va_list va; - va_start(va, num_bytes); - - for (uint32_t i = 0; i < num_bytes; ++i) - { - uint8_t byte = va_arg(va, int); - cb_write_byte(cb, byte); - } - - va_end(va); -} - -// Write a signed integer over a given number of bits at the current position -void cb_write_int(codeblock_t *cb, uint64_t val, uint32_t num_bits) -{ - assert (num_bits > 0); - assert (num_bits % 8 == 0); - - // Switch on the number of bits - switch (num_bits) { - case 8: - cb_write_byte(cb, (uint8_t)val); - break; - - case 16: - cb_write_bytes( - cb, - 2, - (uint8_t)((val >> 0) & 0xFF), - (uint8_t)((val >> 8) & 0xFF) - ); - break; - - case 32: - cb_write_bytes( - cb, - 4, - (uint8_t)((val >> 0) & 0xFF), - (uint8_t)((val >> 8) & 0xFF), - (uint8_t)((val >> 16) & 0xFF), - (uint8_t)((val >> 24) & 0xFF) - ); - break; - - default: - { - // Compute the size in bytes - uint32_t num_bytes = num_bits / 8; - - // Write out the bytes - for (uint32_t i = 0; i < num_bytes; ++i) - { - uint8_t byte_val = (uint8_t)(val & 0xFF); - cb_write_byte(cb, byte_val); - val >>= 8; - } - } - } -} - -// Allocate a new label with a given name -uint32_t cb_new_label(codeblock_t *cb, const char *name) -{ - //if (hasASM) - // writeString(to!string(label) ~ ":"); - - assert (cb->num_labels < MAX_LABELS); - - // Allocate the new label - uint32_t label_idx = cb->num_labels++; - - // This label doesn't have an address yet - cb->label_addrs[label_idx] = 0; - cb->label_names[label_idx] = name; - - return label_idx; -} - -// Write a label at the current address -void cb_write_label(codeblock_t *cb, uint32_t label_idx) -{ - assert (label_idx < MAX_LABELS); - cb->label_addrs[label_idx] = cb->write_pos; -} - -// Add a label reference at the current write position -void cb_label_ref(codeblock_t *cb, uint32_t label_idx) -{ - assert (label_idx < MAX_LABELS); - assert (cb->num_refs < MAX_LABEL_REFS); - - // Keep track of the reference - cb->label_refs[cb->num_refs] = (labelref_t){ cb->write_pos, label_idx }; - cb->num_refs++; -} - -// Link internal label references -void cb_link_labels(codeblock_t *cb) -{ - uint32_t orig_pos = cb->write_pos; - - // For each label reference - for (uint32_t i = 0; i < cb->num_refs; ++i) - { - uint32_t ref_pos = cb->label_refs[i].pos; - uint32_t label_idx = cb->label_refs[i].label_idx; - assert (ref_pos < cb->mem_size); - assert (label_idx < MAX_LABELS); - - uint32_t label_addr = cb->label_addrs[label_idx]; - assert (label_addr < cb->mem_size); - - // Compute the offset from the reference's end to the label - int64_t offset = (int64_t)label_addr - (int64_t)(ref_pos + 4); - - cb_set_pos(cb, ref_pos); - cb_write_int(cb, offset, 32); - } - - cb->write_pos = orig_pos; - - // Clear the label positions and references - cb->num_labels = 0; - cb->num_refs = 0; -} - -// Check if an operand needs a REX byte to be encoded -static bool rex_needed(x86opnd_t opnd) -{ - if (opnd.type == OPND_NONE || opnd.type == OPND_IMM) - { - return false; - } - - if (opnd.type == OPND_REG) - { - return ( - opnd.as.reg.reg_no > 7 || - (opnd.num_bits == 8 && opnd.as.reg.reg_no >= 4 && opnd.as.reg.reg_no <= 7) - ); - } - - if (opnd.type == OPND_MEM) - { - return (opnd.as.mem.base_reg_no > 7) || (opnd.as.mem.has_idx && opnd.as.mem.idx_reg_no > 7); - } - - rb_bug("unreachable"); -} - -// Check if an SIB byte is needed to encode this operand -static bool sib_needed(x86opnd_t opnd) -{ - if (opnd.type != OPND_MEM) - return false; - - return ( - opnd.as.mem.has_idx || - opnd.as.mem.base_reg_no == RSP.as.reg.reg_no || - opnd.as.mem.base_reg_no == R12.as.reg.reg_no - ); -} - -// Compute the size of the displacement field needed for a memory operand -static uint32_t disp_size(x86opnd_t opnd) -{ - assert (opnd.type == OPND_MEM); - - // If using RIP as the base, use disp32 - if (opnd.as.mem.is_iprel) - { - return 32; - } - - // Compute the required displacement size - if (opnd.as.mem.disp != 0) - { - uint32_t num_bits = sig_imm_size(opnd.as.mem.disp); - assert (num_bits <= 32 && "displacement does not fit in 32 bits"); - - // x86 can only encode 8-bit and 32-bit displacements - if (num_bits == 16) - num_bits = 32;; - - return num_bits; - } - - // If EBP or RBP or R13 is used as the base, displacement must be encoded - if (opnd.as.mem.base_reg_no == RBP.as.reg.reg_no || - opnd.as.mem.base_reg_no == R13.as.reg.reg_no) - { - return 8; - } - - return 0; -} - -// Write the REX byte -static void cb_write_rex( - codeblock_t *cb, - bool w_flag, - uint8_t reg_no, - uint8_t idx_reg_no, - uint8_t rm_reg_no -) -{ - // 0 1 0 0 w r x b - // w - 64-bit operand size flag - // r - MODRM.reg extension - // x - SIB.index extension - // b - MODRM.rm or SIB.base extension - uint8_t w = w_flag? 1:0; - uint8_t r = (reg_no & 8)? 1:0; - uint8_t x = (idx_reg_no & 8)? 1:0; - uint8_t b = (rm_reg_no & 8)? 1:0; - - // Encode and write the REX byte - uint8_t rexByte = 0x40 + (w << 3) + (r << 2) + (x << 1) + (b); - cb_write_byte(cb, rexByte); -} - -// Write an opcode byte with an embedded register operand -static void cb_write_opcode(codeblock_t *cb, uint8_t opcode, x86opnd_t reg) -{ - // Write the reg field into the opcode byte - uint8_t op_byte = opcode | (reg.as.reg.reg_no & 7); - cb_write_byte(cb, op_byte); -} - -// Encode an RM instruction -static void cb_write_rm( - codeblock_t *cb, - bool szPref, - bool rexW, - x86opnd_t r_opnd, - x86opnd_t rm_opnd, - uint8_t opExt, - uint32_t op_len, - ...) -{ - assert (op_len > 0 && op_len <= 3); - assert (r_opnd.type == OPND_REG || r_opnd.type == OPND_NONE); - - // Flag to indicate the REX prefix is needed - bool need_rex = rexW || rex_needed(r_opnd) || rex_needed(rm_opnd); - - // Flag to indicate SIB byte is needed - bool need_sib = sib_needed(r_opnd) || sib_needed(rm_opnd); - - // Add the operand-size prefix, if needed - if (szPref == true) - cb_write_byte(cb, 0x66); - - // Add the REX prefix, if needed - if (need_rex) - { - // 0 1 0 0 w r x b - // w - 64-bit operand size flag - // r - MODRM.reg extension - // x - SIB.index extension - // b - MODRM.rm or SIB.base extension - - uint8_t w = rexW? 1:0; - - uint8_t r; - if (r_opnd.type != OPND_NONE) - r = (r_opnd.as.reg.reg_no & 8)? 1:0; - else - r = 0; - - uint8_t x; - if (need_sib && rm_opnd.as.mem.has_idx) - x = (rm_opnd.as.mem.idx_reg_no & 8)? 1:0; - else - x = 0; - - uint8_t b; - if (rm_opnd.type == OPND_REG) - b = (rm_opnd.as.reg.reg_no & 8)? 1:0; - else if (rm_opnd.type == OPND_MEM) - b = (rm_opnd.as.mem.base_reg_no & 8)? 1:0; - else - b = 0; - - // Encode and write the REX byte - uint8_t rex_byte = 0x40 + (w << 3) + (r << 2) + (x << 1) + (b); - cb_write_byte(cb, rex_byte); - } - - // Write the opcode bytes to the code block - va_list va; - va_start(va, op_len); - for (uint32_t i = 0; i < op_len; ++i) - { - uint8_t byte = va_arg(va, int); - cb_write_byte(cb, byte); - } - va_end(va); - - // MODRM.mod (2 bits) - // MODRM.reg (3 bits) - // MODRM.rm (3 bits) - - assert ( - !(opExt != 0xFF && r_opnd.type != OPND_NONE) && - "opcode extension and register operand present" - ); - - // Encode the mod field - uint8_t mod; - if (rm_opnd.type == OPND_REG) - { - mod = 3; - } - else - { - uint32_t dsize = disp_size(rm_opnd); - if (dsize == 0 || rm_opnd.as.mem.is_iprel) - mod = 0; - else if (dsize == 8) - mod = 1; - else if (dsize == 32) - mod = 2; - else - rb_bug("unreachable"); - } - - // Encode the reg field - uint8_t reg; - if (opExt != 0xFF) - reg = opExt; - else if (r_opnd.type == OPND_REG) - reg = r_opnd.as.reg.reg_no & 7; - else - reg = 0; - - // Encode the rm field - uint8_t rm; - if (rm_opnd.type == OPND_REG) - { - rm = rm_opnd.as.reg.reg_no & 7; - } - else - { - if (need_sib) - rm = 4; - else - rm = rm_opnd.as.mem.base_reg_no & 7; - } - - // Encode and write the ModR/M byte - uint8_t rm_byte = (mod << 6) + (reg << 3) + (rm); - cb_write_byte(cb, rm_byte); - - // Add the SIB byte, if needed - if (need_sib) - { - // SIB.scale (2 bits) - // SIB.index (3 bits) - // SIB.base (3 bits) - - assert (rm_opnd.type == OPND_MEM); - - // Encode the scale value - uint8_t scale = rm_opnd.as.mem.scale_exp; - - // Encode the index value - uint8_t index; - if (!rm_opnd.as.mem.has_idx) - index = 4; - else - index = rm_opnd.as.mem.idx_reg_no & 7; - - // Encode the base register - uint8_t base = rm_opnd.as.mem.base_reg_no & 7; - - // Encode and write the SIB byte - uint8_t sib_byte = (scale << 6) + (index << 3) + (base); - cb_write_byte(cb, sib_byte); - } - - // Add the displacement - if (rm_opnd.type == OPND_MEM) - { - uint32_t dsize = disp_size(rm_opnd); - if (dsize > 0) - cb_write_int(cb, rm_opnd.as.mem.disp, dsize); - } -} - -// Encode a mul-like single-operand RM instruction -static void write_rm_unary( - codeblock_t *cb, - const char *mnem, - uint8_t opMemReg8, - uint8_t opMemRegPref, - uint8_t opExt, - x86opnd_t opnd) -{ - // Write a disassembly string - //cb.writeASM(mnem, opnd); - - // Check the size of opnd0 - uint32_t opndSize; - if (opnd.type == OPND_REG || opnd.type == OPND_MEM) - opndSize = opnd.num_bits; - else - rb_bug("yjit: invalid operand"); - - assert (opndSize == 8 || opndSize == 16 || opndSize == 32 || opndSize == 64); - bool szPref = opndSize == 16; - bool rexW = opndSize == 64; - - if (opndSize == 8) - cb_write_rm(cb, false, false, NO_OPND, opnd, opExt, 1, opMemReg8); - else - cb_write_rm(cb, szPref, rexW, NO_OPND, opnd, opExt, 1, opMemRegPref); -} - -// Encode an add-like RM instruction with multiple possible encodings -static void cb_write_rm_multi( - codeblock_t *cb, - const char *mnem, - uint8_t opMemReg8, - uint8_t opMemRegPref, - uint8_t opRegMem8, - uint8_t opRegMemPref, - uint8_t opMemImm8, - uint8_t opMemImmSml, - uint8_t opMemImmLrg, - uint8_t opExtImm, - x86opnd_t opnd0, - x86opnd_t opnd1) -{ - assert (opnd0.type == OPND_REG || opnd0.type == OPND_MEM); - - /* - // Write disassembly string - if (!opnd1.isNone) - cb.writeASM(mnem, opnd0, opnd1); - else - cb.writeASM(mnem, opnd0); - */ - - // Check the size of opnd0 - uint32_t opndSize = opnd0.num_bits; - - // Check the size of opnd1 - if (opnd1.type == OPND_REG || opnd1.type == OPND_MEM) - { - assert (opnd1.num_bits == opndSize && "operand size mismatch"); - } - else if (opnd1.type == OPND_IMM) - { - assert (opnd1.num_bits <= opndSize); - } - - assert (opndSize == 8 || opndSize == 16 || opndSize == 32 || opndSize == 64); - bool szPref = opndSize == 16; - bool rexW = opndSize == 64; - - // R/M + Reg - if ((opnd0.type == OPND_MEM && opnd1.type == OPND_REG) || - (opnd0.type == OPND_REG && opnd1.type == OPND_REG)) - { - // R/M is opnd0 - if (opndSize == 8) - cb_write_rm(cb, false, false, opnd1, opnd0, 0xFF, 1, opMemReg8); - else - cb_write_rm(cb, szPref, rexW, opnd1, opnd0, 0xFF, 1, opMemRegPref); - } - - // Reg + R/M - else if (opnd0.type == OPND_REG && opnd1.type == OPND_MEM) - { - // R/M is opnd1 - if (opndSize == 8) - cb_write_rm(cb, false, false, opnd0, opnd1, 0xFF, 1, opRegMem8); - else - cb_write_rm(cb, szPref, rexW, opnd0, opnd1, 0xFF, 1, opRegMemPref); - } - - // R/M + Imm - else if (opnd1.type == OPND_IMM) - { - // 8-bit immediate - if (opnd1.num_bits <= 8) - { - if (opndSize == 8) - cb_write_rm(cb, false, false, NO_OPND, opnd0, opExtImm, 1, opMemImm8); - else - cb_write_rm(cb, szPref, rexW, NO_OPND, opnd0, opExtImm, 1, opMemImmSml); - - cb_write_int(cb, opnd1.as.imm, 8); - } - - // 32-bit immediate - else if (opnd1.num_bits <= 32) - { - assert (opnd1.num_bits <= opndSize && "immediate too large for dst"); - cb_write_rm(cb, szPref, rexW, NO_OPND, opnd0, opExtImm, 1, opMemImmLrg); - cb_write_int(cb, opnd1.as.imm, (opndSize > 32)? 32:opndSize); - } - - // Immediate too large - else - { - assert (false && "immediate value too large"); - } - } - - // Invalid operands - else - { - assert (false && "invalid operand combination"); - } -} - -// Encode a single-operand shift instruction -static void cb_write_shift( - codeblock_t *cb, - const char *mnem, - uint8_t opMemOnePref, - uint8_t opMemClPref, - uint8_t opMemImmPref, - uint8_t opExt, - x86opnd_t opnd0, - x86opnd_t opnd1) -{ - // Write a disassembly string - //cb.writeASM(mnem, opnd0, opnd1); - - // Check the size of opnd0 - uint32_t opndSize; - if (opnd0.type == OPND_REG || opnd0.type == OPND_MEM) - opndSize = opnd0.num_bits; - else - rb_bug("yjit: shift: invalid first operand"); - - assert (opndSize == 16 || opndSize == 32 || opndSize == 64); - bool szPref = opndSize == 16; - bool rexW = opndSize == 64; - - if (opnd1.type == OPND_IMM) - { - if (opnd1.as.imm == 1) - { - cb_write_rm(cb, szPref, rexW, NO_OPND, opnd0, opExt, 1, opMemOnePref); - } - else - { - assert (opnd1.num_bits <= 8); - cb_write_rm(cb, szPref, rexW, NO_OPND, opnd0, opExt, 1, opMemImmPref); - cb_write_byte(cb, (uint8_t)opnd1.as.imm); - } - } - /* - else if (opnd1.isReg && opnd1.reg == CL) - { - cb.writeRMInstr!('l', opExt, opMemClPref)(szPref, rexW, opnd0, X86Opnd.NONE); - } - */ - else - { - assert (false); - } -} - -// Encode a relative jump to a label (direct or conditional) -// Note: this always encodes a 32-bit offset -static void cb_write_jcc(codeblock_t *cb, const char *mnem, uint8_t op0, uint8_t op1, uint32_t label_idx) -{ - //cb.writeASM(mnem, label); - - // Write the opcode - if (op0 != 0xFF) - cb_write_byte(cb, op0); - cb_write_byte(cb, op1); - - // Add a reference to the label - cb_label_ref(cb, label_idx); - - // Relative 32-bit offset to be patched - cb_write_int(cb, 0, 32); -} - -// Encode a relative jump to a pointer at a 32-bit offset (direct or conditional) -static void cb_write_jcc_ptr(codeblock_t *cb, const char *mnem, uint8_t op0, uint8_t op1, uint8_t *dst_ptr) -{ - //cb.writeASM(mnem, label); - - // Write the opcode - if (op0 != 0xFF) - cb_write_byte(cb, op0); - cb_write_byte(cb, op1); - - // Pointer to the end of this jump instruction - uint8_t *end_ptr = &cb->mem_block[cb->write_pos] + 4; - - // Compute the jump offset - int64_t rel64 = (int64_t)(dst_ptr - end_ptr); - assert (rel64 >= INT32_MIN && rel64 <= INT32_MAX); - - // Write the relative 32-bit jump offset - cb_write_int(cb, (int32_t)rel64, 32); -} - -// Encode a conditional move instruction -static void cb_write_cmov(codeblock_t *cb, const char *mnem, uint8_t opcode1, x86opnd_t dst, x86opnd_t src) -{ - //cb.writeASM(mnem, dst, src); - - assert (dst.type == OPND_REG); - assert (src.type == OPND_REG || src.type == OPND_MEM); - assert (dst.num_bits >= 16 && "invalid dst reg size in cmov"); - - bool szPref = dst.num_bits == 16; - bool rexW = dst.num_bits == 64; - - cb_write_rm(cb, szPref, rexW, dst, src, 0xFF, 2, 0x0F, opcode1); -} - -// add - Integer addition -void add(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_rm_multi( - cb, - "add", - 0x00, // opMemReg8 - 0x01, // opMemRegPref - 0x02, // opRegMem8 - 0x03, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x00, // opExtImm - opnd0, - opnd1 - ); -} - -/// and - Bitwise AND -void and(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_rm_multi( - cb, - "and", - 0x20, // opMemReg8 - 0x21, // opMemRegPref - 0x22, // opRegMem8 - 0x23, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x04, // opExtImm - opnd0, - opnd1 - ); -} - -// call - Call to a pointer with a 32-bit displacement offset -static void call_rel32(codeblock_t *cb, int32_t rel32) -{ - //cb.writeASM("call", rel32); - - // Write the opcode - cb_write_byte(cb, 0xE8); - - // Write the relative 32-bit jump offset - cb_write_int(cb, (int32_t)rel32, 32); -} - -// call - Call a pointer, encode with a 32-bit offset if possible -void call_ptr(codeblock_t *cb, x86opnd_t scratch_reg, uint8_t *dst_ptr) -{ - assert (scratch_reg.type == OPND_REG); - - // Pointer to the end of this call instruction - uint8_t *end_ptr = &cb->mem_block[cb->write_pos] + 5; - - // Compute the jump offset - int64_t rel64 = (int64_t)(dst_ptr - end_ptr); - - // If the offset fits in 32-bit - if (rel64 >= INT32_MIN && rel64 <= INT32_MAX) - { - call_rel32(cb, (int32_t)rel64); - return; - } - - // Move the pointer into the scratch register and call - mov(cb, scratch_reg, const_ptr_opnd(dst_ptr)); - call(cb, scratch_reg); -} - -/// call - Call to label with 32-bit offset -void call_label(codeblock_t *cb, uint32_t label_idx) -{ - //cb.writeASM("call", label); - - // Write the opcode - cb_write_byte(cb, 0xE8); - - // Add a reference to the label - cb_label_ref(cb, label_idx); - - // Relative 32-bit offset to be patched - cb_write_int(cb, 0, 32); -} - -/// call - Indirect call with an R/M operand -void call(codeblock_t *cb, x86opnd_t opnd) -{ - //cb.writeASM("call", opnd); - cb_write_rm(cb, false, false, NO_OPND, opnd, 2, 1, 0xFF); -} - -/// cmovcc - Conditional move -void cmova(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmova", 0x47, dst, src); } -void cmovae(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovae", 0x43, dst, src); } -void cmovb(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovb", 0x42, dst, src); } -void cmovbe(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovbe", 0x46, dst, src); } -void cmovc(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovc", 0x42, dst, src); } -void cmove(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmove", 0x44, dst, src); } -void cmovg(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovg", 0x4F, dst, src); } -void cmovge(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovge", 0x4D, dst, src); } -void cmovl(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovl", 0x4C, dst, src); } -void cmovle(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovle", 0x4E, dst, src); } -void cmovna(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovna", 0x46, dst, src); } -void cmovnae(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnae", 0x42, dst, src); } -void cmovnb(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnb", 0x43, dst, src); } -void cmovnbe(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnbe", 0x47, dst, src); } -void cmovnc(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnc", 0x43, dst, src); } -void cmovne(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovne", 0x45, dst, src); } -void cmovng(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovng", 0x4E, dst, src); } -void cmovnge(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnge", 0x4C, dst, src); } -void cmovnl(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnl" , 0x4D, dst, src); } -void cmovnle(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnle", 0x4F, dst, src); } -void cmovno(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovno", 0x41, dst, src); } -void cmovnp(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnp", 0x4B, dst, src); } -void cmovns(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovns", 0x49, dst, src); } -void cmovnz(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovnz", 0x45, dst, src); } -void cmovo(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovo", 0x40, dst, src); } -void cmovp(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovp", 0x4A, dst, src); } -void cmovpe(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovpe", 0x4A, dst, src); } -void cmovpo(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovpo", 0x4B, dst, src); } -void cmovs(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovs", 0x48, dst, src); } -void cmovz(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) { cb_write_cmov(cb, "cmovz", 0x44, dst, src); } - -/// cmp - Compare and set flags -void cmp(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_rm_multi( - cb, - "cmp", - 0x38, // opMemReg8 - 0x39, // opMemRegPref - 0x3A, // opRegMem8 - 0x3B, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x07, // opExtImm - opnd0, - opnd1 - ); -} - -/// cdq - Convert doubleword to quadword -void cdq(codeblock_t *cb) -{ - //cb.writeASM("cdq"); - cb_write_byte(cb, 0x99); -} - -/// cqo - Convert quadword to octaword -void cqo(codeblock_t *cb) -{ - //cb.writeASM("cqo"); - cb_write_bytes(cb, 2, 0x48, 0x99); -} - -/// Interrupt 3 - trap to debugger -void int3(codeblock_t *cb) -{ - //cb.writeASM("INT 3"); - cb_write_byte(cb, 0xCC); -} - -/* -// div - Unsigned integer division -alias div = writeRMUnary!( - "div", - 0xF6, // opMemReg8 - 0xF7, // opMemRegPref - 0x06 // opExt -); -*/ - -/* -/// divsd - Divide scalar double -alias divsd = writeXMM64!( - "divsd", - 0xF2, // prefix - 0x0F, // opRegMem0 - 0x5E // opRegMem1 -); -*/ - -/* -// idiv - Signed integer division -alias idiv = writeRMUnary!( - "idiv", - 0xF6, // opMemReg8 - 0xF7, // opMemRegPref - 0x07 // opExt -); -*/ - -/* -/// imul - Signed integer multiplication with two operands -void imul(CodeBlock cb, X86Opnd opnd0, X86Opnd opnd1) -{ - cb.writeASM("imul", opnd0, opnd1); - - assert (opnd0.isReg, "invalid first operand"); - auto opndSize = opnd0.reg.size; - - // Check the size of opnd1 - if (opnd1.isReg) - assert (opnd1.reg.size is opndSize, "operand size mismatch"); - else if (opnd1.isMem) - assert (opnd1.mem.size is opndSize, "operand size mismatch"); - - assert (opndSize is 16 || opndSize is 32 || opndSize is 64); - auto szPref = opndSize is 16; - auto rexW = opndSize is 64; - - cb.writeRMInstr!('r', 0xFF, 0x0F, 0xAF)(szPref, rexW, opnd0, opnd1); -} -*/ - -/* -/// imul - Signed integer multiplication with three operands (one immediate) -void imul(CodeBlock cb, X86Opnd opnd0, X86Opnd opnd1, X86Opnd opnd2) -{ - cb.writeASM("imul", opnd0, opnd1, opnd2); - - assert (opnd0.isReg, "invalid first operand"); - auto opndSize = opnd0.reg.size; - - // Check the size of opnd1 - if (opnd1.isReg) - assert (opnd1.reg.size is opndSize, "operand size mismatch"); - else if (opnd1.isMem) - assert (opnd1.mem.size is opndSize, "operand size mismatch"); - - assert (opndSize is 16 || opndSize is 32 || opndSize is 64); - auto szPref = opndSize is 16; - auto rexW = opndSize is 64; - - assert (opnd2.isImm, "invalid third operand"); - auto imm = opnd2.imm; - - // 8-bit immediate - if (imm.immSize <= 8) - { - cb.writeRMInstr!('r', 0xFF, 0x6B)(szPref, rexW, opnd0, opnd1); - cb.writeInt(imm.imm, 8); - } - - // 32-bit immediate - else if (imm.immSize <= 32) - { - assert (imm.immSize <= opndSize, "immediate too large for dst"); - cb.writeRMInstr!('r', 0xFF, 0x69)(szPref, rexW, opnd0, opnd1); - cb.writeInt(imm.imm, min(opndSize, 32)); - } - - // Immediate too large - else - { - assert (false, "immediate value too large"); - } -} -*/ - -/// jcc - relative jumps to a label -void ja_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "ja" , 0x0F, 0x87, label_idx); } -void jae_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jae" , 0x0F, 0x83, label_idx); } -void jb_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jb" , 0x0F, 0x82, label_idx); } -void jbe_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jbe" , 0x0F, 0x86, label_idx); } -void jc_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jc" , 0x0F, 0x82, label_idx); } -void je_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "je" , 0x0F, 0x84, label_idx); } -void jg_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jg" , 0x0F, 0x8F, label_idx); } -void jge_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jge" , 0x0F, 0x8D, label_idx); } -void jl_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jl" , 0x0F, 0x8C, label_idx); } -void jle_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jle" , 0x0F, 0x8E, label_idx); } -void jna_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jna" , 0x0F, 0x86, label_idx); } -void jnae_label(codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnae", 0x0F, 0x82, label_idx); } -void jnb_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnb" , 0x0F, 0x83, label_idx); } -void jnbe_label(codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnbe", 0x0F, 0x87, label_idx); } -void jnc_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnc" , 0x0F, 0x83, label_idx); } -void jne_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jne" , 0x0F, 0x85, label_idx); } -void jng_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jng" , 0x0F, 0x8E, label_idx); } -void jnge_label(codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnge", 0x0F, 0x8C, label_idx); } -void jnl_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnl" , 0x0F, 0x8D, label_idx); } -void jnle_label(codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnle", 0x0F, 0x8F, label_idx); } -void jno_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jno" , 0x0F, 0x81, label_idx); } -void jnp_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnp" , 0x0F, 0x8b, label_idx); } -void jns_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jns" , 0x0F, 0x89, label_idx); } -void jnz_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jnz" , 0x0F, 0x85, label_idx); } -void jo_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jo" , 0x0F, 0x80, label_idx); } -void jp_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jp" , 0x0F, 0x8A, label_idx); } -void jpe_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jpe" , 0x0F, 0x8A, label_idx); } -void jpo_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jpo" , 0x0F, 0x8B, label_idx); } -void js_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "js" , 0x0F, 0x88, label_idx); } -void jz_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jz" , 0x0F, 0x84, label_idx); } -void jmp_label (codeblock_t *cb, uint32_t label_idx) { cb_write_jcc(cb, "jmp" , 0xFF, 0xE9, label_idx); } - -/// jcc - relative jumps to a pointer (32-bit offset) -void ja_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "ja" , 0x0F, 0x87, ptr); } -void jae_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jae" , 0x0F, 0x83, ptr); } -void jb_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jb" , 0x0F, 0x82, ptr); } -void jbe_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jbe" , 0x0F, 0x86, ptr); } -void jc_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jc" , 0x0F, 0x82, ptr); } -void je_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "je" , 0x0F, 0x84, ptr); } -void jg_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jg" , 0x0F, 0x8F, ptr); } -void jge_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jge" , 0x0F, 0x8D, ptr); } -void jl_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jl" , 0x0F, 0x8C, ptr); } -void jle_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jle" , 0x0F, 0x8E, ptr); } -void jna_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jna" , 0x0F, 0x86, ptr); } -void jnae_ptr(codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnae", 0x0F, 0x82, ptr); } -void jnb_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnb" , 0x0F, 0x83, ptr); } -void jnbe_ptr(codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnbe", 0x0F, 0x87, ptr); } -void jnc_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnc" , 0x0F, 0x83, ptr); } -void jne_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jne" , 0x0F, 0x85, ptr); } -void jng_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jng" , 0x0F, 0x8E, ptr); } -void jnge_ptr(codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnge", 0x0F, 0x8C, ptr); } -void jnl_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnl" , 0x0F, 0x8D, ptr); } -void jnle_ptr(codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnle", 0x0F, 0x8F, ptr); } -void jno_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jno" , 0x0F, 0x81, ptr); } -void jnp_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnp" , 0x0F, 0x8b, ptr); } -void jns_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jns" , 0x0F, 0x89, ptr); } -void jnz_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jnz" , 0x0F, 0x85, ptr); } -void jo_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jo" , 0x0F, 0x80, ptr); } -void jp_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jp" , 0x0F, 0x8A, ptr); } -void jpe_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jpe" , 0x0F, 0x8A, ptr); } -void jpo_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jpo" , 0x0F, 0x8B, ptr); } -void js_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "js" , 0x0F, 0x88, ptr); } -void jz_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jz" , 0x0F, 0x84, ptr); } -void jmp_ptr (codeblock_t *cb, uint8_t *ptr) { cb_write_jcc_ptr(cb, "jmp" , 0xFF, 0xE9, ptr); } - -/// jmp - Indirect jump near to an R/M operand -void jmp_rm(codeblock_t *cb, x86opnd_t opnd) -{ - //cb.writeASM("jmp", opnd); - cb_write_rm(cb, false, false, NO_OPND, opnd, 4, 1, 0xFF); -} - -// jmp - Jump with relative 32-bit offset -void jmp32(codeblock_t *cb, int32_t offset) -{ - //cb.writeASM("jmp", ((offset > 0)? "+":"-") ~ to!string(offset)); - cb_write_byte(cb, 0xE9); - cb_write_int(cb, offset, 32); -} - -/// lea - Load Effective Address -void lea(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) -{ - //cb.writeASM("lea", dst, src); - assert (dst.num_bits == 64); - cb_write_rm(cb, false, true, dst, src, 0xFF, 1, 0x8D); -} - -/// mov - Data move operation -void mov(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) -{ - // R/M + Imm - if (src.type == OPND_IMM) - { - //cb.writeASM("mov", dst, src); - - // R + Imm - if (dst.type == OPND_REG) - { - assert ( - src.num_bits <= dst.num_bits || - unsig_imm_size(src.as.imm) <= dst.num_bits - ); - - if (dst.num_bits == 16) - cb_write_byte(cb, 0x66); - if (rex_needed(dst) || dst.num_bits == 64) - cb_write_rex(cb, dst.num_bits == 64, 0, 0, dst.as.reg.reg_no); - - cb_write_opcode(cb, (dst.num_bits == 8)? 0xB0:0xB8, dst); - - cb_write_int(cb, src.as.imm, dst.num_bits); - } - - // M + Imm - else if (dst.type == OPND_MEM) - { - assert (src.num_bits <= dst.num_bits); - - if (dst.num_bits == 8) - cb_write_rm(cb, false, false, NO_OPND, dst, 0xFF, 1, 0xC6); - else - cb_write_rm(cb, dst.num_bits == 16, dst.num_bits == 64, NO_OPND, dst, 0, 1, 0xC7); - - cb_write_int(cb, src.as.imm, (dst.num_bits > 32)? 32:dst.num_bits); - } - - else - { - assert (false); - } - } - else - { - cb_write_rm_multi( - cb, - "mov", - 0x88, // opMemReg8 - 0x89, // opMemRegPref - 0x8A, // opRegMem8 - 0x8B, // opRegMemPref - 0xC6, // opMemImm8 - 0xFF, // opMemImmSml (not available) - 0xFF, // opMemImmLrg - 0xFF, // opExtImm - dst, - src - ); - } -} - -/// movsx - Move with sign extension (signed integers) -void movsx(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) -{ - assert (dst.type == OPND_REG); - assert (src.type == OPND_REG || src.type == OPND_MEM); - assert (src.num_bits < dst.num_bits); - - //cb.writeASM("movsx", dst, src); - - if (src.num_bits == 8) - { - cb_write_rm(cb, dst.num_bits == 16, dst.num_bits == 64, dst, src, 0xFF, 2, 0x0F, 0xBE); - } - else if (src.num_bits == 16) - { - cb_write_rm(cb, dst.num_bits == 16, dst.num_bits == 64, dst, src, 0xFF, 2, 0x0F, 0xBF); - } - else if (src.num_bits == 32) - { - cb_write_rm(cb, false, true, dst, src, 0xFF, 1, 0x63); - } - else - { - assert (false); - } -} - -/* -/// movzx - Move with zero extension (unsigned values) -void movzx(codeblock_t *cb, x86opnd_t dst, x86opnd_t src) -{ - cb.writeASM("movzx", dst, src); - - uint32_t dstSize; - if (dst.isReg) - dstSize = dst.reg.size; - else - assert (false, "movzx dst must be a register"); - - uint32_t srcSize; - if (src.isReg) - srcSize = src.reg.size; - else if (src.isMem) - srcSize = src.mem.size; - else - assert (false); - - assert ( - srcSize < dstSize, - "movzx: srcSize >= dstSize" - ); - - if (srcSize is 8) - { - cb.writeRMInstr!('r', 0xFF, 0x0F, 0xB6)(dstSize is 16, dstSize is 64, dst, src); - } - else if (srcSize is 16) - { - cb.writeRMInstr!('r', 0xFF, 0x0F, 0xB7)(dstSize is 16, dstSize is 64, dst, src); - } - else - { - assert (false, "invalid src operand size for movxz"); - } -} -*/ - -// neg - Integer negation (multiplication by -1) -void neg(codeblock_t *cb, x86opnd_t opnd) -{ - write_rm_unary( - cb, - "neg", - 0xF6, // opMemReg8 - 0xF7, // opMemRegPref - 0x03, // opExt - opnd - ); -} - -// nop - Noop, one or multiple bytes long -void nop(codeblock_t *cb, uint32_t length) -{ - switch (length) { - case 0: - break; - - case 1: - //cb.writeASM("nop1"); - cb_write_byte(cb, 0x90); - break; - - case 2: - //cb.writeASM("nop2"); - cb_write_bytes(cb, 2, 0x66,0x90); - break; - - case 3: - //cb.writeASM("nop3"); - cb_write_bytes(cb, 3, 0x0F,0x1F,0x00); - break; - - case 4: - //cb.writeASM("nop4"); - cb_write_bytes(cb, 4, 0x0F,0x1F,0x40,0x00); - break; - - case 5: - //cb.writeASM("nop5"); - cb_write_bytes(cb, 5, 0x0F,0x1F,0x44,0x00,0x00); - break; - - case 6: - //cb.writeASM("nop6"); - cb_write_bytes(cb, 6, 0x66,0x0F,0x1F,0x44,0x00,0x00); - break; - - case 7: - //cb.writeASM("nop7"); - cb_write_bytes(cb, 7, 0x0F,0x1F,0x80,0x00,0x00,0x00,0x00); - break; - - case 8: - //cb.writeASM("nop8"); - cb_write_bytes(cb, 8, 0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00); - break; - - case 9: - //cb.writeASM("nop9"); - cb_write_bytes(cb, 9, 0x66,0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00); - break; - - default: - { - uint32_t written = 0; - while (written + 9 <= length) - { - nop(cb, 9); - written += 9; - } - nop(cb, length - written); - } - break; - } -} - -// not - Bitwise NOT -void not(codeblock_t *cb, x86opnd_t opnd) -{ - write_rm_unary( - cb, - "not", - 0xF6, // opMemReg8 - 0xF7, // opMemRegPref - 0x02, // opExt - opnd - ); -} - -/// or - Bitwise OR -void or(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_rm_multi( - cb, - "or", - 0x08, // opMemReg8 - 0x09, // opMemRegPref - 0x0A, // opRegMem8 - 0x0B, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x01, // opExtImm - opnd0, - opnd1 - ); -} - -/// pop - Pop a register off the stack -void pop(codeblock_t *cb, x86opnd_t opnd) -{ - assert (opnd.num_bits == 64); - - //cb.writeASM("pop", opnd); - - if (opnd.type == OPND_REG) { - if (rex_needed(opnd)) - cb_write_rex(cb, false, 0, 0, opnd.as.reg.reg_no); - cb_write_opcode(cb, 0x58, opnd); - } - else if (opnd.type == OPND_MEM) { - cb_write_rm(cb, false, false, NO_OPND, opnd, 0, 1, 0x8F); - } - else { - assert(false && "unexpected operand type"); - } -} - -/// popfq - Pop the flags register (64-bit) -void popfq(codeblock_t *cb) -{ - //cb.writeASM("popfq"); - - // REX.W + 0x9D - cb_write_bytes(cb, 2, 0x48, 0x9D); -} - -/// push - Push an operand on the stack -void push(codeblock_t *cb, x86opnd_t opnd) -{ - assert (opnd.num_bits == 64); - - //cb.writeASM("push", opnd); - - if (opnd.type == OPND_REG) { - if (rex_needed(opnd)) - cb_write_rex(cb, false, 0, 0, opnd.as.reg.reg_no); - cb_write_opcode(cb, 0x50, opnd); - } - else if (opnd.type == OPND_MEM) { - cb_write_rm(cb, false, false, NO_OPND, opnd, 6, 1, 0xFF); - } - else { - assert(false && "unexpected operand type"); - } -} - -/// pushfq - Push the flags register (64-bit) -void pushfq(codeblock_t *cb) -{ - //cb.writeASM("pushfq"); - cb_write_byte(cb, 0x9C); -} - -/// ret - Return from call, popping only the return address -void ret(codeblock_t *cb) -{ - //cb.writeASM("ret"); - cb_write_byte(cb, 0xC3); -} - -// sal - Shift arithmetic left -void sal(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_shift( - cb, - "sal", - 0xD1, // opMemOnePref, - 0xD3, // opMemClPref, - 0xC1, // opMemImmPref, - 0x04, - opnd0, - opnd1 - ); -} - -/// sar - Shift arithmetic right (signed) -void sar(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_shift( - cb, - "sar", - 0xD1, // opMemOnePref, - 0xD3, // opMemClPref, - 0xC1, // opMemImmPref, - 0x07, - opnd0, - opnd1 - ); -} -// shl - Shift logical left -void shl(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_shift( - cb, - "shl", - 0xD1, // opMemOnePref, - 0xD3, // opMemClPref, - 0xC1, // opMemImmPref, - 0x04, - opnd0, - opnd1 - ); -} - -/// shr - Shift logical right (unsigned) -void shr(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_shift( - cb, - "shr", - 0xD1, // opMemOnePref, - 0xD3, // opMemClPref, - 0xC1, // opMemImmPref, - 0x05, - opnd0, - opnd1 - ); -} - -/// sub - Integer subtraction -void sub(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_rm_multi( - cb, - "sub", - 0x28, // opMemReg8 - 0x29, // opMemRegPref - 0x2A, // opRegMem8 - 0x2B, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x05, // opExtImm - opnd0, - opnd1 - ); -} - -/// test - Logical Compare -void test(codeblock_t *cb, x86opnd_t rm_opnd, x86opnd_t test_opnd) -{ - assert (rm_opnd.type == OPND_REG || rm_opnd.type == OPND_MEM); - assert (test_opnd.type == OPND_REG || test_opnd.type == OPND_IMM); - - // If the second operand is an immediate - if (test_opnd.type == OPND_IMM) - { - x86opnd_t imm_opnd = test_opnd; - - if (imm_opnd.as.imm >= 0) - { - assert (unsig_imm_size(imm_opnd.as.unsig_imm) <= 32); - assert (unsig_imm_size(imm_opnd.as.unsig_imm) <= rm_opnd.num_bits); - - // Use the smallest operand size possible - rm_opnd = resize_opnd(rm_opnd, unsig_imm_size(imm_opnd.as.unsig_imm)); - - if (rm_opnd.num_bits == 8) - { - cb_write_rm(cb, false, false, NO_OPND, rm_opnd, 0x00, 1, 0xF6); - cb_write_int(cb, imm_opnd.as.imm, rm_opnd.num_bits); - } - else - { - cb_write_rm(cb, rm_opnd.num_bits == 16, false, NO_OPND, rm_opnd, 0x00, 1, 0xF7); - cb_write_int(cb, imm_opnd.as.imm, rm_opnd.num_bits); - } - } - else - { - // This mode only applies to 64-bit R/M operands with 32-bit signed immediates - assert (imm_opnd.as.imm < 0); - assert (sig_imm_size(imm_opnd.as.imm) <= 32); - assert (rm_opnd.num_bits == 64); - cb_write_rm(cb, false, true, NO_OPND, rm_opnd, 0x00, 1, 0xF7); - cb_write_int(cb, imm_opnd.as.imm, 32); - } - } - else - { - assert (test_opnd.num_bits == rm_opnd.num_bits); - - if (rm_opnd.num_bits == 8) - { - cb_write_rm(cb, false, false, test_opnd, rm_opnd, 0xFF, 1, 0x84); - } - else - { - cb_write_rm(cb, rm_opnd.num_bits == 16, rm_opnd.num_bits == 64, test_opnd, rm_opnd, 0xFF, 1, 0x85); - } - } -} - -/// Undefined opcode -void ud2(codeblock_t *cb) -{ - cb_write_bytes(cb, 2, 0x0F, 0x0B); -} - -/// xchg - Exchange Register/Memory with Register -void xchg(codeblock_t *cb, x86opnd_t rm_opnd, x86opnd_t r_opnd) -{ - assert (rm_opnd.num_bits == 64); - assert (r_opnd.num_bits == 64); - assert (rm_opnd.type == OPND_REG); - assert (r_opnd.type == OPND_REG); - - // If we're exchanging with RAX - if (rm_opnd.type == OPND_REG && rm_opnd.as.reg.reg_no == RAX.as.reg.reg_no) - { - // Write the REX byte - cb_write_rex(cb, rm_opnd.num_bits == 64, 0, 0, r_opnd.as.reg.reg_no); - - // Write the opcode and register number - cb_write_byte(cb, 0x90 + (r_opnd.as.reg.reg_no & 7)); - } - else - { - cb_write_rm(cb, rm_opnd.num_bits == 16, rm_opnd.num_bits == 64, r_opnd, rm_opnd, 0xFF, 1, 0x87); - } -} - -/// xor - Exclusive bitwise OR -void xor(codeblock_t *cb, x86opnd_t opnd0, x86opnd_t opnd1) -{ - cb_write_rm_multi( - cb, - "xor", - 0x30, // opMemReg8 - 0x31, // opMemRegPref - 0x32, // opRegMem8 - 0x33, // opRegMemPref - 0x80, // opMemImm8 - 0x83, // opMemImmSml - 0x81, // opMemImmLrg - 0x06, // opExtImm - opnd0, - opnd1 - ); -} - -// LOCK - lock prefix for atomic shared memory operations -void cb_write_lock_prefix(codeblock_t *cb) -{ - cb_write_byte(cb, 0xF0); -} |