1 // This file is a fragment of the yjit.o compilation unit. See yjit.c.
3 // Note that the definition for some of these functions don't specify
4 // static inline, but their declaration in yjit_asm.h do. The resulting
5 // linkage is the same as if they both specify. The relevant sections in
6 // N1256 is 6.2.2p4, 6.2.2p5, and 6.7.4p5.
15 // For mmapp(), sysconf()
23 // Compute the number of bits needed to encode a signed value
24 uint32_t sig_imm_size(int64_t imm
)
26 // Compute the smallest size this immediate fits in
27 if (imm
>= INT8_MIN
&& imm
<= INT8_MAX
)
29 if (imm
>= INT16_MIN
&& imm
<= INT16_MAX
)
31 if (imm
>= INT32_MIN
&& imm
<= INT32_MAX
)
37 // Compute the number of bits needed to encode an unsigned value
38 uint32_t unsig_imm_size(uint64_t imm
)
40 // Compute the smallest size this immediate fits in
43 else if (imm
<= UINT16_MAX
)
45 else if (imm
<= UINT32_MAX
)
51 x86opnd_t
mem_opnd(uint32_t num_bits
, x86opnd_t base_reg
, int32_t disp
)
53 bool is_iprel
= base_reg
.as
.reg
.reg_type
== REG_IP
;
58 .as
.mem
= { base_reg
.as
.reg
.reg_no
, 0, 0, false, is_iprel
, disp
}
64 x86opnd_t
mem_opnd_sib(uint32_t num_bits
, x86opnd_t base_reg
, x86opnd_t index_reg
, int32_t scale
, int32_t disp
)
81 rb_bug("yjit: scale not one of 1,2,4,8");
85 bool is_iprel
= base_reg
.as
.reg
.reg_type
== REG_IP
;
91 .base_reg_no
= base_reg
.as
.reg
.reg_no
,
92 .idx_reg_no
= index_reg
.as
.reg
.reg_no
,
94 .scale_exp
= scale_exp
,
103 static x86opnd_t
resize_opnd(x86opnd_t opnd
, uint32_t num_bits
)
105 assert (num_bits
% 8 == 0);
106 x86opnd_t sub
= opnd
;
107 sub
.num_bits
= num_bits
;
111 x86opnd_t
imm_opnd(int64_t imm
)
122 x86opnd_t
const_ptr_opnd(const void *ptr
)
127 .as
.unsig_imm
= (uint64_t)ptr
133 // Align the current write position to a multiple of bytes
134 static uint8_t *align_ptr(uint8_t *ptr
, uint32_t multiple
)
136 // Compute the pointer modulo the given alignment boundary
137 uint32_t rem
= ((uint32_t)(uintptr_t)ptr
) % multiple
;
139 // If the pointer is already aligned, stop
143 // Pad the pointer by the necessary amount to align it
144 uint32_t pad
= multiple
- rem
;
149 // Allocate a block of executable memory
150 static uint8_t *alloc_exec_mem(uint32_t mem_size
)
156 #if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
157 // Align the requested address to page size
158 uint32_t page_size
= (uint32_t)sysconf(_SC_PAGESIZE
);
159 uint8_t *req_addr
= align_ptr((uint8_t*)&alloc_exec_mem
, page_size
);
162 // Try to map a chunk of memory as executable
163 mem_block
= (uint8_t*)mmap(
166 PROT_READ
| PROT_EXEC
,
167 MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED_NOREPLACE
,
172 // If we succeeded, stop
173 if (mem_block
!= MAP_FAILED
) {
178 req_addr
+= 4 * 1024 * 1024;
179 } while (req_addr
< (uint8_t*)&alloc_exec_mem
+ INT32_MAX
);
181 // On MacOS and other platforms
183 // Try to map a chunk of memory as executable
184 mem_block
= (uint8_t*)mmap(
185 (void*)alloc_exec_mem
,
187 PROT_READ
| PROT_EXEC
,
188 MAP_PRIVATE
| MAP_ANONYMOUS
,
195 if (mem_block
== MAP_FAILED
) {
196 // Try again without the address hint (e.g., valgrind)
197 mem_block
= (uint8_t*)mmap(
200 PROT_READ
| PROT_EXEC
,
201 MAP_PRIVATE
| MAP_ANONYMOUS
,
207 // Check that the memory mapping was successful
208 if (mem_block
== MAP_FAILED
) {
209 perror("mmap call failed");
214 codeblock_t
*cb
= &block
;
216 cb_init(cb
, mem_block
, mem_size
);
218 // Fill the executable memory with PUSH DS (0x1E) so that
219 // executing uninitialized memory will fault with #UD in
221 cb_mark_all_writeable(cb
);
222 memset(mem_block
, 0x1E, mem_size
);
223 cb_mark_all_executable(cb
);
227 // Windows not supported for now
232 // Initialize a code block object
233 void cb_init(codeblock_t
*cb
, uint8_t *mem_block
, uint32_t mem_size
)
236 cb
->mem_block_
= mem_block
;
237 cb
->mem_size
= mem_size
;
241 cb
->current_aligned_write_pos
= ALIGNED_WRITE_POSITION_NONE
;
244 // Set the current write position
245 void cb_set_pos(codeblock_t
*cb
, uint32_t pos
)
247 // Assert here since while assembler functions do bounds checking, there is
248 // nothing stopping users from taking out an out-of-bounds pointer and
249 // doing bad accesses with it.
250 assert (pos
< cb
->mem_size
);
254 // Align the current write position to a multiple of bytes
255 void cb_align_pos(codeblock_t
*cb
, uint32_t multiple
)
257 // Compute the pointer modulo the given alignment boundary
258 uint8_t *ptr
= cb_get_write_ptr(cb
);
259 uint8_t *aligned_ptr
= align_ptr(ptr
, multiple
);
260 const uint32_t write_pos
= cb
->write_pos
;
262 // Pad the pointer by the necessary amount to align it
263 ptrdiff_t pad
= aligned_ptr
- ptr
;
264 cb_set_pos(cb
, write_pos
+ (int32_t)pad
);
267 // Set the current write position from a pointer
268 void cb_set_write_ptr(codeblock_t
*cb
, uint8_t *code_ptr
)
270 intptr_t pos
= code_ptr
- cb
->mem_block_
;
271 assert (pos
< cb
->mem_size
);
272 cb_set_pos(cb
, (uint32_t)pos
);
275 // Get a direct pointer into the executable memory block
276 uint8_t *cb_get_ptr(const codeblock_t
*cb
, uint32_t index
)
278 if (index
< cb
->mem_size
) {
279 return &cb
->mem_block_
[index
];
286 // Get a direct pointer to the current write position
287 uint8_t *cb_get_write_ptr(const codeblock_t
*cb
)
289 return cb_get_ptr(cb
, cb
->write_pos
);
292 // Write a byte at the current position
293 void cb_write_byte(codeblock_t
*cb
, uint8_t byte
)
295 assert (cb
->mem_block_
);
296 if (cb
->write_pos
< cb
->mem_size
) {
297 cb_mark_position_writeable(cb
, cb
->write_pos
);
298 cb
->mem_block_
[cb
->write_pos
] = byte
;
302 cb
->dropped_bytes
= true;
306 // Write multiple bytes starting from the current position
307 void cb_write_bytes(codeblock_t
*cb
, uint32_t num_bytes
, ...)
310 va_start(va
, num_bytes
);
312 for (uint32_t i
= 0; i
< num_bytes
; ++i
)
314 uint8_t byte
= va_arg(va
, int);
315 cb_write_byte(cb
, byte
);
321 // Write a signed integer over a given number of bits at the current position
322 void cb_write_int(codeblock_t
*cb
, uint64_t val
, uint32_t num_bits
)
324 assert (num_bits
> 0);
325 assert (num_bits
% 8 == 0);
327 // Switch on the number of bits
330 cb_write_byte(cb
, (uint8_t)val
);
337 (uint8_t)((val
>> 0) & 0xFF),
338 (uint8_t)((val
>> 8) & 0xFF)
346 (uint8_t)((val
>> 0) & 0xFF),
347 (uint8_t)((val
>> 8) & 0xFF),
348 (uint8_t)((val
>> 16) & 0xFF),
349 (uint8_t)((val
>> 24) & 0xFF)
355 // Compute the size in bytes
356 uint32_t num_bytes
= num_bits
/ 8;
358 // Write out the bytes
359 for (uint32_t i
= 0; i
< num_bytes
; ++i
)
361 uint8_t byte_val
= (uint8_t)(val
& 0xFF);
362 cb_write_byte(cb
, byte_val
);
369 // Allocate a new label with a given name
370 uint32_t cb_new_label(codeblock_t
*cb
, const char *name
)
373 // writeString(to!string(label) ~ ":");
375 assert (cb
->num_labels
< MAX_LABELS
);
377 // Allocate the new label
378 uint32_t label_idx
= cb
->num_labels
++;
380 // This label doesn't have an address yet
381 cb
->label_addrs
[label_idx
] = 0;
382 cb
->label_names
[label_idx
] = name
;
387 // Write a label at the current address
388 void cb_write_label(codeblock_t
*cb
, uint32_t label_idx
)
390 assert (label_idx
< MAX_LABELS
);
391 cb
->label_addrs
[label_idx
] = cb
->write_pos
;
394 // Add a label reference at the current write position
395 void cb_label_ref(codeblock_t
*cb
, uint32_t label_idx
)
397 assert (label_idx
< MAX_LABELS
);
398 assert (cb
->num_refs
< MAX_LABEL_REFS
);
400 // Keep track of the reference
401 cb
->label_refs
[cb
->num_refs
] = (labelref_t
){ cb
->write_pos
, label_idx
};
405 // Link internal label references
406 void cb_link_labels(codeblock_t
*cb
)
408 uint32_t orig_pos
= cb
->write_pos
;
410 // For each label reference
411 for (uint32_t i
= 0; i
< cb
->num_refs
; ++i
)
413 uint32_t ref_pos
= cb
->label_refs
[i
].pos
;
414 uint32_t label_idx
= cb
->label_refs
[i
].label_idx
;
415 assert (ref_pos
< cb
->mem_size
);
416 assert (label_idx
< MAX_LABELS
);
418 uint32_t label_addr
= cb
->label_addrs
[label_idx
];
419 assert (label_addr
< cb
->mem_size
);
421 // Compute the offset from the reference's end to the label
422 int64_t offset
= (int64_t)label_addr
- (int64_t)(ref_pos
+ 4);
424 cb_set_pos(cb
, ref_pos
);
425 cb_write_int(cb
, offset
, 32);
428 cb
->write_pos
= orig_pos
;
430 // Clear the label positions and references
435 // Check if an operand needs a REX byte to be encoded
436 static bool rex_needed(x86opnd_t opnd
)
438 if (opnd
.type
== OPND_NONE
|| opnd
.type
== OPND_IMM
)
443 if (opnd
.type
== OPND_REG
)
446 opnd
.as
.reg
.reg_no
> 7 ||
447 (opnd
.num_bits
== 8 && opnd
.as
.reg
.reg_no
>= 4 && opnd
.as
.reg
.reg_no
<= 7)
451 if (opnd
.type
== OPND_MEM
)
453 return (opnd
.as
.mem
.base_reg_no
> 7) || (opnd
.as
.mem
.has_idx
&& opnd
.as
.mem
.idx_reg_no
> 7);
456 rb_bug("unreachable");
459 // Check if an SIB byte is needed to encode this operand
460 static bool sib_needed(x86opnd_t opnd
)
462 if (opnd
.type
!= OPND_MEM
)
466 opnd
.as
.mem
.has_idx
||
467 opnd
.as
.mem
.base_reg_no
== RSP
.as
.reg
.reg_no
||
468 opnd
.as
.mem
.base_reg_no
== R12
.as
.reg
.reg_no
472 // Compute the size of the displacement field needed for a memory operand
473 static uint32_t disp_size(x86opnd_t opnd
)
475 assert (opnd
.type
== OPND_MEM
);
477 // If using RIP as the base, use disp32
478 if (opnd
.as
.mem
.is_iprel
)
483 // Compute the required displacement size
484 if (opnd
.as
.mem
.disp
!= 0)
486 uint32_t num_bits
= sig_imm_size(opnd
.as
.mem
.disp
);
487 assert (num_bits
<= 32 && "displacement does not fit in 32 bits");
489 // x86 can only encode 8-bit and 32-bit displacements
496 // If EBP or RBP or R13 is used as the base, displacement must be encoded
497 if (opnd
.as
.mem
.base_reg_no
== RBP
.as
.reg
.reg_no
||
498 opnd
.as
.mem
.base_reg_no
== R13
.as
.reg
.reg_no
)
506 // Write the REX byte
507 static void cb_write_rex(
516 // w - 64-bit operand size flag
517 // r - MODRM.reg extension
518 // x - SIB.index extension
519 // b - MODRM.rm or SIB.base extension
520 uint8_t w
= w_flag
? 1:0;
521 uint8_t r
= (reg_no
& 8)? 1:0;
522 uint8_t x
= (idx_reg_no
& 8)? 1:0;
523 uint8_t b
= (rm_reg_no
& 8)? 1:0;
525 // Encode and write the REX byte
526 uint8_t rexByte
= 0x40 + (w
<< 3) + (r
<< 2) + (x
<< 1) + (b
);
527 cb_write_byte(cb
, rexByte
);
530 // Write an opcode byte with an embedded register operand
531 static void cb_write_opcode(codeblock_t
*cb
, uint8_t opcode
, x86opnd_t reg
)
533 // Write the reg field into the opcode byte
534 uint8_t op_byte
= opcode
| (reg
.as
.reg
.reg_no
& 7);
535 cb_write_byte(cb
, op_byte
);
538 // Encode an RM instruction
539 static void cb_write_rm(
549 assert (op_len
> 0 && op_len
<= 3);
550 assert (r_opnd
.type
== OPND_REG
|| r_opnd
.type
== OPND_NONE
);
552 // Flag to indicate the REX prefix is needed
553 bool need_rex
= rexW
|| rex_needed(r_opnd
) || rex_needed(rm_opnd
);
555 // Flag to indicate SIB byte is needed
556 bool need_sib
= sib_needed(r_opnd
) || sib_needed(rm_opnd
);
558 // Add the operand-size prefix, if needed
560 cb_write_byte(cb
, 0x66);
562 // Add the REX prefix, if needed
566 // w - 64-bit operand size flag
567 // r - MODRM.reg extension
568 // x - SIB.index extension
569 // b - MODRM.rm or SIB.base extension
571 uint8_t w
= rexW
? 1:0;
574 if (r_opnd
.type
!= OPND_NONE
)
575 r
= (r_opnd
.as
.reg
.reg_no
& 8)? 1:0;
580 if (need_sib
&& rm_opnd
.as
.mem
.has_idx
)
581 x
= (rm_opnd
.as
.mem
.idx_reg_no
& 8)? 1:0;
586 if (rm_opnd
.type
== OPND_REG
)
587 b
= (rm_opnd
.as
.reg
.reg_no
& 8)? 1:0;
588 else if (rm_opnd
.type
== OPND_MEM
)
589 b
= (rm_opnd
.as
.mem
.base_reg_no
& 8)? 1:0;
593 // Encode and write the REX byte
594 uint8_t rex_byte
= 0x40 + (w
<< 3) + (r
<< 2) + (x
<< 1) + (b
);
595 cb_write_byte(cb
, rex_byte
);
598 // Write the opcode bytes to the code block
600 va_start(va
, op_len
);
601 for (uint32_t i
= 0; i
< op_len
; ++i
)
603 uint8_t byte
= va_arg(va
, int);
604 cb_write_byte(cb
, byte
);
608 // MODRM.mod (2 bits)
609 // MODRM.reg (3 bits)
613 !(opExt
!= 0xFF && r_opnd
.type
!= OPND_NONE
) &&
614 "opcode extension and register operand present"
617 // Encode the mod field
619 if (rm_opnd
.type
== OPND_REG
)
625 uint32_t dsize
= disp_size(rm_opnd
);
626 if (dsize
== 0 || rm_opnd
.as
.mem
.is_iprel
)
630 else if (dsize
== 32)
633 rb_bug("unreachable");
636 // Encode the reg field
640 else if (r_opnd
.type
== OPND_REG
)
641 reg
= r_opnd
.as
.reg
.reg_no
& 7;
645 // Encode the rm field
647 if (rm_opnd
.type
== OPND_REG
)
649 rm
= rm_opnd
.as
.reg
.reg_no
& 7;
656 rm
= rm_opnd
.as
.mem
.base_reg_no
& 7;
659 // Encode and write the ModR/M byte
660 uint8_t rm_byte
= (mod
<< 6) + (reg
<< 3) + (rm
);
661 cb_write_byte(cb
, rm_byte
);
663 // Add the SIB byte, if needed
666 // SIB.scale (2 bits)
667 // SIB.index (3 bits)
670 assert (rm_opnd
.type
== OPND_MEM
);
672 // Encode the scale value
673 uint8_t scale
= rm_opnd
.as
.mem
.scale_exp
;
675 // Encode the index value
677 if (!rm_opnd
.as
.mem
.has_idx
)
680 index
= rm_opnd
.as
.mem
.idx_reg_no
& 7;
682 // Encode the base register
683 uint8_t base
= rm_opnd
.as
.mem
.base_reg_no
& 7;
685 // Encode and write the SIB byte
686 uint8_t sib_byte
= (scale
<< 6) + (index
<< 3) + (base
);
687 cb_write_byte(cb
, sib_byte
);
690 // Add the displacement
691 if (rm_opnd
.type
== OPND_MEM
)
693 uint32_t dsize
= disp_size(rm_opnd
);
695 cb_write_int(cb
, rm_opnd
.as
.mem
.disp
, dsize
);
699 // Encode a mul-like single-operand RM instruction
700 static void write_rm_unary(
704 uint8_t opMemRegPref
,
708 // Write a disassembly string
709 //cb.writeASM(mnem, opnd);
711 // Check the size of opnd0
713 if (opnd
.type
== OPND_REG
|| opnd
.type
== OPND_MEM
)
714 opndSize
= opnd
.num_bits
;
716 rb_bug("yjit: invalid operand");
718 assert (opndSize
== 8 || opndSize
== 16 || opndSize
== 32 || opndSize
== 64);
719 bool szPref
= opndSize
== 16;
720 bool rexW
= opndSize
== 64;
723 cb_write_rm(cb
, false, false, NO_OPND
, opnd
, opExt
, 1, opMemReg8
);
725 cb_write_rm(cb
, szPref
, rexW
, NO_OPND
, opnd
, opExt
, 1, opMemRegPref
);
728 // Encode an add-like RM instruction with multiple possible encodings
729 static void cb_write_rm_multi(
733 uint8_t opMemRegPref
,
735 uint8_t opRegMemPref
,
743 assert (opnd0
.type
== OPND_REG
|| opnd0
.type
== OPND_MEM
);
746 // Write disassembly string
748 cb.writeASM(mnem, opnd0, opnd1);
750 cb.writeASM(mnem, opnd0);
753 // Check the size of opnd0
754 uint32_t opndSize
= opnd0
.num_bits
;
756 // Check the size of opnd1
757 if (opnd1
.type
== OPND_REG
|| opnd1
.type
== OPND_MEM
)
759 assert (opnd1
.num_bits
== opndSize
&& "operand size mismatch");
761 else if (opnd1
.type
== OPND_IMM
)
763 assert (opnd1
.num_bits
<= opndSize
);
766 assert (opndSize
== 8 || opndSize
== 16 || opndSize
== 32 || opndSize
== 64);
767 bool szPref
= opndSize
== 16;
768 bool rexW
= opndSize
== 64;
771 if ((opnd0
.type
== OPND_MEM
&& opnd1
.type
== OPND_REG
) ||
772 (opnd0
.type
== OPND_REG
&& opnd1
.type
== OPND_REG
))
776 cb_write_rm(cb
, false, false, opnd1
, opnd0
, 0xFF, 1, opMemReg8
);
778 cb_write_rm(cb
, szPref
, rexW
, opnd1
, opnd0
, 0xFF, 1, opMemRegPref
);
782 else if (opnd0
.type
== OPND_REG
&& opnd1
.type
== OPND_MEM
)
786 cb_write_rm(cb
, false, false, opnd0
, opnd1
, 0xFF, 1, opRegMem8
);
788 cb_write_rm(cb
, szPref
, rexW
, opnd0
, opnd1
, 0xFF, 1, opRegMemPref
);
792 else if (opnd1
.type
== OPND_IMM
)
795 if (opnd1
.num_bits
<= 8)
798 cb_write_rm(cb
, false, false, NO_OPND
, opnd0
, opExtImm
, 1, opMemImm8
);
800 cb_write_rm(cb
, szPref
, rexW
, NO_OPND
, opnd0
, opExtImm
, 1, opMemImmSml
);
802 cb_write_int(cb
, opnd1
.as
.imm
, 8);
806 else if (opnd1
.num_bits
<= 32)
808 assert (opnd1
.num_bits
<= opndSize
&& "immediate too large for dst");
809 cb_write_rm(cb
, szPref
, rexW
, NO_OPND
, opnd0
, opExtImm
, 1, opMemImmLrg
);
810 cb_write_int(cb
, opnd1
.as
.imm
, (opndSize
> 32)? 32:opndSize
);
813 // Immediate too large
816 assert (false && "immediate value too large");
823 assert (false && "invalid operand combination");
827 // Encode a single-operand shift instruction
828 static void cb_write_shift(
831 uint8_t opMemOnePref
,
833 uint8_t opMemImmPref
,
838 // Write a disassembly string
839 //cb.writeASM(mnem, opnd0, opnd1);
841 // Check the size of opnd0
843 if (opnd0
.type
== OPND_REG
|| opnd0
.type
== OPND_MEM
)
844 opndSize
= opnd0
.num_bits
;
846 rb_bug("yjit: shift: invalid first operand");
848 assert (opndSize
== 16 || opndSize
== 32 || opndSize
== 64);
849 bool szPref
= opndSize
== 16;
850 bool rexW
= opndSize
== 64;
852 if (opnd1
.type
== OPND_IMM
)
854 if (opnd1
.as
.imm
== 1)
856 cb_write_rm(cb
, szPref
, rexW
, NO_OPND
, opnd0
, opExt
, 1, opMemOnePref
);
860 assert (opnd1
.num_bits
<= 8);
861 cb_write_rm(cb
, szPref
, rexW
, NO_OPND
, opnd0
, opExt
, 1, opMemImmPref
);
862 cb_write_byte(cb
, (uint8_t)opnd1
.as
.imm
);
866 else if (opnd1.isReg && opnd1.reg == CL)
868 cb.writeRMInstr!('l', opExt, opMemClPref)(szPref, rexW, opnd0, X86Opnd.NONE);
877 // Encode a relative jump to a label (direct or conditional)
878 // Note: this always encodes a 32-bit offset
879 static void cb_write_jcc(codeblock_t
*cb
, const char *mnem
, uint8_t op0
, uint8_t op1
, uint32_t label_idx
)
881 //cb.writeASM(mnem, label);
885 cb_write_byte(cb
, op0
);
886 cb_write_byte(cb
, op1
);
888 // Add a reference to the label
889 cb_label_ref(cb
, label_idx
);
891 // Relative 32-bit offset to be patched
892 cb_write_int(cb
, 0, 32);
895 // Encode a relative jump to a pointer at a 32-bit offset (direct or conditional)
896 static void cb_write_jcc_ptr(codeblock_t
*cb
, const char *mnem
, uint8_t op0
, uint8_t op1
, uint8_t *dst_ptr
)
898 //cb.writeASM(mnem, label);
902 cb_write_byte(cb
, op0
);
903 cb_write_byte(cb
, op1
);
905 // Pointer to the end of this jump instruction
906 uint8_t *end_ptr
= cb_get_ptr(cb
, cb
->write_pos
+ 4);
908 // Compute the jump offset
909 int64_t rel64
= (int64_t)(dst_ptr
- end_ptr
);
910 if (rel64
>= INT32_MIN
&& rel64
<= INT32_MAX
) {
911 // Write the relative 32-bit jump offset
912 cb_write_int(cb
, (int32_t)rel64
, 32);
915 // Offset doesn't fit in 4 bytes. Report error.
916 cb
->dropped_bytes
= true;
920 // Encode a conditional move instruction
921 static void cb_write_cmov(codeblock_t
*cb
, const char *mnem
, uint8_t opcode1
, x86opnd_t dst
, x86opnd_t src
)
923 //cb.writeASM(mnem, dst, src);
925 assert (dst
.type
== OPND_REG
);
926 assert (src
.type
== OPND_REG
|| src
.type
== OPND_MEM
);
927 assert (dst
.num_bits
>= 16 && "invalid dst reg size in cmov");
929 bool szPref
= dst
.num_bits
== 16;
930 bool rexW
= dst
.num_bits
== 64;
932 cb_write_rm(cb
, szPref
, rexW
, dst
, src
, 0xFF, 2, 0x0F, opcode1
);
935 // add - Integer addition
936 void add(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
942 0x01, // opMemRegPref
944 0x03, // opRegMemPref
954 /// and - Bitwise AND
955 void and(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
961 0x21, // opMemRegPref
963 0x23, // opRegMemPref
973 // call - Call to a pointer with a 32-bit displacement offset
974 static void call_rel32(codeblock_t
*cb
, int32_t rel32
)
976 //cb.writeASM("call", rel32);
979 cb_write_byte(cb
, 0xE8);
981 // Write the relative 32-bit jump offset
982 cb_write_int(cb
, (int32_t)rel32
, 32);
985 // call - Call a pointer, encode with a 32-bit offset if possible
986 void call_ptr(codeblock_t
*cb
, x86opnd_t scratch_reg
, uint8_t *dst_ptr
)
988 assert (scratch_reg
.type
== OPND_REG
);
990 // Pointer to the end of this call instruction
991 uint8_t *end_ptr
= cb_get_ptr(cb
, cb
->write_pos
+ 5);
993 // Compute the jump offset
994 int64_t rel64
= (int64_t)(dst_ptr
- end_ptr
);
996 // If the offset fits in 32-bit
997 if (rel64
>= INT32_MIN
&& rel64
<= INT32_MAX
) {
998 call_rel32(cb
, (int32_t)rel64
);
1002 // Move the pointer into the scratch register and call
1003 mov(cb
, scratch_reg
, const_ptr_opnd(dst_ptr
));
1004 call(cb
, scratch_reg
);
1007 /// call - Call to label with 32-bit offset
1008 void call_label(codeblock_t
*cb
, uint32_t label_idx
)
1010 //cb.writeASM("call", label);
1013 cb_write_byte(cb
, 0xE8);
1015 // Add a reference to the label
1016 cb_label_ref(cb
, label_idx
);
1018 // Relative 32-bit offset to be patched
1019 cb_write_int(cb
, 0, 32);
1022 /// call - Indirect call with an R/M operand
1023 void call(codeblock_t
*cb
, x86opnd_t opnd
)
1025 //cb.writeASM("call", opnd);
1026 cb_write_rm(cb
, false, false, NO_OPND
, opnd
, 2, 1, 0xFF);
1029 /// cmovcc - Conditional move
1030 void cmova(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmova", 0x47, dst
, src
); }
1031 void cmovae(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovae", 0x43, dst
, src
); }
1032 void cmovb(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovb", 0x42, dst
, src
); }
1033 void cmovbe(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovbe", 0x46, dst
, src
); }
1034 void cmovc(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovc", 0x42, dst
, src
); }
1035 void cmove(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmove", 0x44, dst
, src
); }
1036 void cmovg(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovg", 0x4F, dst
, src
); }
1037 void cmovge(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovge", 0x4D, dst
, src
); }
1038 void cmovl(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovl", 0x4C, dst
, src
); }
1039 void cmovle(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovle", 0x4E, dst
, src
); }
1040 void cmovna(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovna", 0x46, dst
, src
); }
1041 void cmovnae(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnae", 0x42, dst
, src
); }
1042 void cmovnb(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnb", 0x43, dst
, src
); }
1043 void cmovnbe(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnbe", 0x47, dst
, src
); }
1044 void cmovnc(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnc", 0x43, dst
, src
); }
1045 void cmovne(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovne", 0x45, dst
, src
); }
1046 void cmovng(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovng", 0x4E, dst
, src
); }
1047 void cmovnge(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnge", 0x4C, dst
, src
); }
1048 void cmovnl(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnl" , 0x4D, dst
, src
); }
1049 void cmovnle(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnle", 0x4F, dst
, src
); }
1050 void cmovno(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovno", 0x41, dst
, src
); }
1051 void cmovnp(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnp", 0x4B, dst
, src
); }
1052 void cmovns(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovns", 0x49, dst
, src
); }
1053 void cmovnz(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovnz", 0x45, dst
, src
); }
1054 void cmovo(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovo", 0x40, dst
, src
); }
1055 void cmovp(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovp", 0x4A, dst
, src
); }
1056 void cmovpe(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovpe", 0x4A, dst
, src
); }
1057 void cmovpo(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovpo", 0x4B, dst
, src
); }
1058 void cmovs(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovs", 0x48, dst
, src
); }
1059 void cmovz(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
) { cb_write_cmov(cb
, "cmovz", 0x44, dst
, src
); }
1061 /// cmp - Compare and set flags
1062 void cmp(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1068 0x39, // opMemRegPref
1070 0x3B, // opRegMemPref
1072 0x83, // opMemImmSml
1073 0x81, // opMemImmLrg
1080 /// cdq - Convert doubleword to quadword
1081 void cdq(codeblock_t
*cb
)
1083 //cb.writeASM("cdq");
1084 cb_write_byte(cb
, 0x99);
1087 /// cqo - Convert quadword to octaword
1088 void cqo(codeblock_t
*cb
)
1090 //cb.writeASM("cqo");
1091 cb_write_bytes(cb
, 2, 0x48, 0x99);
1094 /// Interrupt 3 - trap to debugger
1095 void int3(codeblock_t
*cb
)
1097 //cb.writeASM("INT 3");
1098 cb_write_byte(cb
, 0xCC);
1102 // div - Unsigned integer division
1103 alias div = writeRMUnary!(
1106 0xF7, // opMemRegPref
1112 /// divsd - Divide scalar double
1113 alias divsd = writeXMM64!(
1122 // idiv - Signed integer division
1123 alias idiv = writeRMUnary!(
1126 0xF7, // opMemRegPref
1132 /// imul - Signed integer multiplication with two operands
1133 void imul(CodeBlock cb, X86Opnd opnd0, X86Opnd opnd1)
1135 cb.writeASM("imul", opnd0, opnd1);
1137 assert (opnd0.isReg, "invalid first operand");
1138 auto opndSize = opnd0.reg.size;
1140 // Check the size of opnd1
1142 assert (opnd1.reg.size is opndSize, "operand size mismatch");
1143 else if (opnd1.isMem)
1144 assert (opnd1.mem.size is opndSize, "operand size mismatch");
1146 assert (opndSize is 16 || opndSize is 32 || opndSize is 64);
1147 auto szPref = opndSize is 16;
1148 auto rexW = opndSize is 64;
1150 cb.writeRMInstr!('r', 0xFF, 0x0F, 0xAF)(szPref, rexW, opnd0, opnd1);
1155 /// imul - Signed integer multiplication with three operands (one immediate)
1156 void imul(CodeBlock cb, X86Opnd opnd0, X86Opnd opnd1, X86Opnd opnd2)
1158 cb.writeASM("imul", opnd0, opnd1, opnd2);
1160 assert (opnd0.isReg, "invalid first operand");
1161 auto opndSize = opnd0.reg.size;
1163 // Check the size of opnd1
1165 assert (opnd1.reg.size is opndSize, "operand size mismatch");
1166 else if (opnd1.isMem)
1167 assert (opnd1.mem.size is opndSize, "operand size mismatch");
1169 assert (opndSize is 16 || opndSize is 32 || opndSize is 64);
1170 auto szPref = opndSize is 16;
1171 auto rexW = opndSize is 64;
1173 assert (opnd2.isImm, "invalid third operand");
1174 auto imm = opnd2.imm;
1177 if (imm.immSize <= 8)
1179 cb.writeRMInstr!('r', 0xFF, 0x6B)(szPref, rexW, opnd0, opnd1);
1180 cb.writeInt(imm.imm, 8);
1184 else if (imm.immSize <= 32)
1186 assert (imm.immSize <= opndSize, "immediate too large for dst");
1187 cb.writeRMInstr!('r', 0xFF, 0x69)(szPref, rexW, opnd0, opnd1);
1188 cb.writeInt(imm.imm, min(opndSize, 32));
1191 // Immediate too large
1194 assert (false, "immediate value too large");
1199 /// jcc - relative jumps to a label
1200 void ja_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "ja" , 0x0F, 0x87, label_idx
); }
1201 void jae_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jae" , 0x0F, 0x83, label_idx
); }
1202 void jb_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jb" , 0x0F, 0x82, label_idx
); }
1203 void jbe_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jbe" , 0x0F, 0x86, label_idx
); }
1204 void jc_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jc" , 0x0F, 0x82, label_idx
); }
1205 void je_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "je" , 0x0F, 0x84, label_idx
); }
1206 void jg_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jg" , 0x0F, 0x8F, label_idx
); }
1207 void jge_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jge" , 0x0F, 0x8D, label_idx
); }
1208 void jl_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jl" , 0x0F, 0x8C, label_idx
); }
1209 void jle_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jle" , 0x0F, 0x8E, label_idx
); }
1210 void jna_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jna" , 0x0F, 0x86, label_idx
); }
1211 void jnae_label(codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnae", 0x0F, 0x82, label_idx
); }
1212 void jnb_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnb" , 0x0F, 0x83, label_idx
); }
1213 void jnbe_label(codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnbe", 0x0F, 0x87, label_idx
); }
1214 void jnc_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnc" , 0x0F, 0x83, label_idx
); }
1215 void jne_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jne" , 0x0F, 0x85, label_idx
); }
1216 void jng_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jng" , 0x0F, 0x8E, label_idx
); }
1217 void jnge_label(codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnge", 0x0F, 0x8C, label_idx
); }
1218 void jnl_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnl" , 0x0F, 0x8D, label_idx
); }
1219 void jnle_label(codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnle", 0x0F, 0x8F, label_idx
); }
1220 void jno_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jno" , 0x0F, 0x81, label_idx
); }
1221 void jnp_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnp" , 0x0F, 0x8b, label_idx
); }
1222 void jns_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jns" , 0x0F, 0x89, label_idx
); }
1223 void jnz_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jnz" , 0x0F, 0x85, label_idx
); }
1224 void jo_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jo" , 0x0F, 0x80, label_idx
); }
1225 void jp_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jp" , 0x0F, 0x8A, label_idx
); }
1226 void jpe_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jpe" , 0x0F, 0x8A, label_idx
); }
1227 void jpo_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jpo" , 0x0F, 0x8B, label_idx
); }
1228 void js_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "js" , 0x0F, 0x88, label_idx
); }
1229 void jz_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jz" , 0x0F, 0x84, label_idx
); }
1230 void jmp_label (codeblock_t
*cb
, uint32_t label_idx
) { cb_write_jcc(cb
, "jmp" , 0xFF, 0xE9, label_idx
); }
1232 /// jcc - relative jumps to a pointer (32-bit offset)
1233 void ja_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "ja" , 0x0F, 0x87, ptr
); }
1234 void jae_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jae" , 0x0F, 0x83, ptr
); }
1235 void jb_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jb" , 0x0F, 0x82, ptr
); }
1236 void jbe_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jbe" , 0x0F, 0x86, ptr
); }
1237 void jc_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jc" , 0x0F, 0x82, ptr
); }
1238 void je_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "je" , 0x0F, 0x84, ptr
); }
1239 void jg_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jg" , 0x0F, 0x8F, ptr
); }
1240 void jge_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jge" , 0x0F, 0x8D, ptr
); }
1241 void jl_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jl" , 0x0F, 0x8C, ptr
); }
1242 void jle_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jle" , 0x0F, 0x8E, ptr
); }
1243 void jna_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jna" , 0x0F, 0x86, ptr
); }
1244 void jnae_ptr(codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnae", 0x0F, 0x82, ptr
); }
1245 void jnb_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnb" , 0x0F, 0x83, ptr
); }
1246 void jnbe_ptr(codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnbe", 0x0F, 0x87, ptr
); }
1247 void jnc_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnc" , 0x0F, 0x83, ptr
); }
1248 void jne_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jne" , 0x0F, 0x85, ptr
); }
1249 void jng_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jng" , 0x0F, 0x8E, ptr
); }
1250 void jnge_ptr(codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnge", 0x0F, 0x8C, ptr
); }
1251 void jnl_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnl" , 0x0F, 0x8D, ptr
); }
1252 void jnle_ptr(codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnle", 0x0F, 0x8F, ptr
); }
1253 void jno_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jno" , 0x0F, 0x81, ptr
); }
1254 void jnp_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnp" , 0x0F, 0x8b, ptr
); }
1255 void jns_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jns" , 0x0F, 0x89, ptr
); }
1256 void jnz_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jnz" , 0x0F, 0x85, ptr
); }
1257 void jo_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jo" , 0x0F, 0x80, ptr
); }
1258 void jp_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jp" , 0x0F, 0x8A, ptr
); }
1259 void jpe_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jpe" , 0x0F, 0x8A, ptr
); }
1260 void jpo_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jpo" , 0x0F, 0x8B, ptr
); }
1261 void js_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "js" , 0x0F, 0x88, ptr
); }
1262 void jz_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jz" , 0x0F, 0x84, ptr
); }
1263 void jmp_ptr (codeblock_t
*cb
, uint8_t *ptr
) { cb_write_jcc_ptr(cb
, "jmp" , 0xFF, 0xE9, ptr
); }
1265 /// jmp - Indirect jump near to an R/M operand
1266 void jmp_rm(codeblock_t
*cb
, x86opnd_t opnd
)
1268 //cb.writeASM("jmp", opnd);
1269 cb_write_rm(cb
, false, false, NO_OPND
, opnd
, 4, 1, 0xFF);
1272 // jmp - Jump with relative 32-bit offset
1273 void jmp32(codeblock_t
*cb
, int32_t offset
)
1275 //cb.writeASM("jmp", ((offset > 0)? "+":"-") ~ to!string(offset));
1276 cb_write_byte(cb
, 0xE9);
1277 cb_write_int(cb
, offset
, 32);
1280 /// lea - Load Effective Address
1281 void lea(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
)
1283 //cb.writeASM("lea", dst, src);
1284 assert (dst
.num_bits
== 64);
1285 cb_write_rm(cb
, false, true, dst
, src
, 0xFF, 1, 0x8D);
1288 // Does this number fit in 32 bits and stays the same if you zero extend it to 64 bit?
1289 // If the sign bit is clear, sign extension and zero extension yield the same
1292 zero_extendable_32bit(uint64_t number
)
1294 return number
<= UINT32_MAX
&& (number
& (1ull << 31ull)) == 0;
1297 /// mov - Data move operation
1298 void mov(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
)
1301 if (src
.type
== OPND_IMM
)
1303 //cb.writeASM("mov", dst, src);
1306 if (dst
.type
== OPND_REG
)
1309 src
.num_bits
<= dst
.num_bits
||
1310 unsig_imm_size(src
.as
.imm
) <= dst
.num_bits
1313 // In case the source immediate could be zero extended to be 64
1314 // bit, we can use the 32-bit operands version of the instruction.
1315 // For example, we can turn mov(rax, 0x34) into the equivalent
1317 if (dst
.num_bits
== 64 && zero_extendable_32bit(src
.as
.unsig_imm
)) {
1318 if (rex_needed(dst
))
1319 cb_write_rex(cb
, false, 0, 0, dst
.as
.reg
.reg_no
);
1320 cb_write_opcode(cb
, 0xB8, dst
);
1321 cb_write_int(cb
, src
.as
.imm
, 32);
1324 if (dst
.num_bits
== 16)
1325 cb_write_byte(cb
, 0x66);
1327 if (rex_needed(dst
) || dst
.num_bits
== 64)
1328 cb_write_rex(cb
, dst
.num_bits
== 64, 0, 0, dst
.as
.reg
.reg_no
);
1330 cb_write_opcode(cb
, (dst
.num_bits
== 8)? 0xB0:0xB8, dst
);
1332 cb_write_int(cb
, src
.as
.imm
, dst
.num_bits
);
1337 else if (dst
.type
== OPND_MEM
)
1339 assert (src
.num_bits
<= dst
.num_bits
);
1341 if (dst
.num_bits
== 8)
1342 cb_write_rm(cb
, false, false, NO_OPND
, dst
, 0xFF, 1, 0xC6);
1344 cb_write_rm(cb
, dst
.num_bits
== 16, dst
.num_bits
== 64, NO_OPND
, dst
, 0, 1, 0xC7);
1346 const uint32_t output_num_bits
= (dst
.num_bits
> 32u) ? 32u : dst
.num_bits
;
1347 // assert that we can write whole immediate without loss of information
1348 assert (sig_imm_size(src
.as
.imm
) <= output_num_bits
);
1349 cb_write_int(cb
, src
.as
.imm
, output_num_bits
);
1363 0x89, // opMemRegPref
1365 0x8B, // opRegMemPref
1367 0xFF, // opMemImmSml (not available)
1368 0xFF, // opMemImmLrg
1376 /// movsx - Move with sign extension (signed integers)
1377 void movsx(codeblock_t
*cb
, x86opnd_t dst
, x86opnd_t src
)
1379 assert (dst
.type
== OPND_REG
);
1380 assert (src
.type
== OPND_REG
|| src
.type
== OPND_MEM
);
1381 assert (src
.num_bits
< dst
.num_bits
);
1383 //cb.writeASM("movsx", dst, src);
1385 if (src
.num_bits
== 8)
1387 cb_write_rm(cb
, dst
.num_bits
== 16, dst
.num_bits
== 64, dst
, src
, 0xFF, 2, 0x0F, 0xBE);
1389 else if (src
.num_bits
== 16)
1391 cb_write_rm(cb
, dst
.num_bits
== 16, dst
.num_bits
== 64, dst
, src
, 0xFF, 2, 0x0F, 0xBF);
1393 else if (src
.num_bits
== 32)
1395 cb_write_rm(cb
, false, true, dst
, src
, 0xFF, 1, 0x63);
1404 /// movzx - Move with zero extension (unsigned values)
1405 void movzx(codeblock_t *cb, x86opnd_t dst, x86opnd_t src)
1407 cb.writeASM("movzx", dst, src);
1411 dstSize = dst.reg.size;
1413 assert (false, "movzx dst must be a register");
1417 srcSize = src.reg.size;
1419 srcSize = src.mem.size;
1425 "movzx: srcSize >= dstSize"
1430 cb.writeRMInstr!('r', 0xFF, 0x0F, 0xB6)(dstSize is 16, dstSize is 64, dst, src);
1432 else if (srcSize is 16)
1434 cb.writeRMInstr!('r', 0xFF, 0x0F, 0xB7)(dstSize is 16, dstSize is 64, dst, src);
1438 assert (false, "invalid src operand size for movxz");
1443 // neg - Integer negation (multiplication by -1)
1444 void neg(codeblock_t
*cb
, x86opnd_t opnd
)
1450 0xF7, // opMemRegPref
1456 // nop - Noop, one or multiple bytes long
1457 void nop(codeblock_t
*cb
, uint32_t length
)
1464 //cb.writeASM("nop1");
1465 cb_write_byte(cb
, 0x90);
1469 //cb.writeASM("nop2");
1470 cb_write_bytes(cb
, 2, 0x66,0x90);
1474 //cb.writeASM("nop3");
1475 cb_write_bytes(cb
, 3, 0x0F,0x1F,0x00);
1479 //cb.writeASM("nop4");
1480 cb_write_bytes(cb
, 4, 0x0F,0x1F,0x40,0x00);
1484 //cb.writeASM("nop5");
1485 cb_write_bytes(cb
, 5, 0x0F,0x1F,0x44,0x00,0x00);
1489 //cb.writeASM("nop6");
1490 cb_write_bytes(cb
, 6, 0x66,0x0F,0x1F,0x44,0x00,0x00);
1494 //cb.writeASM("nop7");
1495 cb_write_bytes(cb
, 7, 0x0F,0x1F,0x80,0x00,0x00,0x00,0x00);
1499 //cb.writeASM("nop8");
1500 cb_write_bytes(cb
, 8, 0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00);
1504 //cb.writeASM("nop9");
1505 cb_write_bytes(cb
, 9, 0x66,0x0F,0x1F,0x84,0x00,0x00,0x00,0x00,0x00);
1510 uint32_t written
= 0;
1511 while (written
+ 9 <= length
)
1516 nop(cb
, length
- written
);
1522 // not - Bitwise NOT
1523 void not(codeblock_t
*cb
, x86opnd_t opnd
)
1529 0xF7, // opMemRegPref
1536 void or(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1542 0x09, // opMemRegPref
1544 0x0B, // opRegMemPref
1546 0x83, // opMemImmSml
1547 0x81, // opMemImmLrg
1554 /// pop - Pop a register off the stack
1555 void pop(codeblock_t
*cb
, x86opnd_t opnd
)
1557 assert (opnd
.num_bits
== 64);
1559 //cb.writeASM("pop", opnd);
1561 if (opnd
.type
== OPND_REG
) {
1562 if (rex_needed(opnd
))
1563 cb_write_rex(cb
, false, 0, 0, opnd
.as
.reg
.reg_no
);
1564 cb_write_opcode(cb
, 0x58, opnd
);
1566 else if (opnd
.type
== OPND_MEM
) {
1567 cb_write_rm(cb
, false, false, NO_OPND
, opnd
, 0, 1, 0x8F);
1570 assert(false && "unexpected operand type");
1574 /// popfq - Pop the flags register (64-bit)
1575 void popfq(codeblock_t
*cb
)
1577 //cb.writeASM("popfq");
1580 cb_write_bytes(cb
, 2, 0x48, 0x9D);
1583 /// push - Push an operand on the stack
1584 void push(codeblock_t
*cb
, x86opnd_t opnd
)
1586 assert (opnd
.num_bits
== 64);
1588 //cb.writeASM("push", opnd);
1590 if (opnd
.type
== OPND_REG
) {
1591 if (rex_needed(opnd
))
1592 cb_write_rex(cb
, false, 0, 0, opnd
.as
.reg
.reg_no
);
1593 cb_write_opcode(cb
, 0x50, opnd
);
1595 else if (opnd
.type
== OPND_MEM
) {
1596 cb_write_rm(cb
, false, false, NO_OPND
, opnd
, 6, 1, 0xFF);
1599 assert(false && "unexpected operand type");
1603 /// pushfq - Push the flags register (64-bit)
1604 void pushfq(codeblock_t
*cb
)
1606 //cb.writeASM("pushfq");
1607 cb_write_byte(cb
, 0x9C);
1610 /// ret - Return from call, popping only the return address
1611 void ret(codeblock_t
*cb
)
1613 //cb.writeASM("ret");
1614 cb_write_byte(cb
, 0xC3);
1617 // sal - Shift arithmetic left
1618 void sal(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1623 0xD1, // opMemOnePref,
1624 0xD3, // opMemClPref,
1625 0xC1, // opMemImmPref,
1632 /// sar - Shift arithmetic right (signed)
1633 void sar(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1638 0xD1, // opMemOnePref,
1639 0xD3, // opMemClPref,
1640 0xC1, // opMemImmPref,
1646 // shl - Shift logical left
1647 void shl(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1652 0xD1, // opMemOnePref,
1653 0xD3, // opMemClPref,
1654 0xC1, // opMemImmPref,
1661 /// shr - Shift logical right (unsigned)
1662 void shr(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1667 0xD1, // opMemOnePref,
1668 0xD3, // opMemClPref,
1669 0xC1, // opMemImmPref,
1676 /// sub - Integer subtraction
1677 void sub(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1683 0x29, // opMemRegPref
1685 0x2B, // opRegMemPref
1687 0x83, // opMemImmSml
1688 0x81, // opMemImmLrg
1695 /// test - Logical Compare
1696 void test(codeblock_t
*cb
, x86opnd_t rm_opnd
, x86opnd_t test_opnd
)
1698 assert (rm_opnd
.type
== OPND_REG
|| rm_opnd
.type
== OPND_MEM
);
1699 assert (test_opnd
.type
== OPND_REG
|| test_opnd
.type
== OPND_IMM
);
1701 // If the second operand is an immediate
1702 if (test_opnd
.type
== OPND_IMM
)
1704 x86opnd_t imm_opnd
= test_opnd
;
1706 if (imm_opnd
.as
.imm
>= 0)
1708 assert (unsig_imm_size(imm_opnd
.as
.unsig_imm
) <= 32);
1709 assert (unsig_imm_size(imm_opnd
.as
.unsig_imm
) <= rm_opnd
.num_bits
);
1711 // Use the smallest operand size possible
1712 rm_opnd
= resize_opnd(rm_opnd
, unsig_imm_size(imm_opnd
.as
.unsig_imm
));
1714 if (rm_opnd
.num_bits
== 8)
1716 cb_write_rm(cb
, false, false, NO_OPND
, rm_opnd
, 0x00, 1, 0xF6);
1717 cb_write_int(cb
, imm_opnd
.as
.imm
, rm_opnd
.num_bits
);
1721 cb_write_rm(cb
, rm_opnd
.num_bits
== 16, false, NO_OPND
, rm_opnd
, 0x00, 1, 0xF7);
1722 cb_write_int(cb
, imm_opnd
.as
.imm
, rm_opnd
.num_bits
);
1727 // This mode only applies to 64-bit R/M operands with 32-bit signed immediates
1728 assert (imm_opnd
.as
.imm
< 0);
1729 assert (sig_imm_size(imm_opnd
.as
.imm
) <= 32);
1730 assert (rm_opnd
.num_bits
== 64);
1731 cb_write_rm(cb
, false, true, NO_OPND
, rm_opnd
, 0x00, 1, 0xF7);
1732 cb_write_int(cb
, imm_opnd
.as
.imm
, 32);
1737 assert (test_opnd
.num_bits
== rm_opnd
.num_bits
);
1739 if (rm_opnd
.num_bits
== 8)
1741 cb_write_rm(cb
, false, false, test_opnd
, rm_opnd
, 0xFF, 1, 0x84);
1745 cb_write_rm(cb
, rm_opnd
.num_bits
== 16, rm_opnd
.num_bits
== 64, test_opnd
, rm_opnd
, 0xFF, 1, 0x85);
1750 /// Undefined opcode
1751 void ud2(codeblock_t
*cb
)
1753 cb_write_bytes(cb
, 2, 0x0F, 0x0B);
1756 /// xchg - Exchange Register/Memory with Register
1757 void xchg(codeblock_t
*cb
, x86opnd_t rm_opnd
, x86opnd_t r_opnd
)
1759 assert (rm_opnd
.num_bits
== 64);
1760 assert (r_opnd
.num_bits
== 64);
1761 assert (rm_opnd
.type
== OPND_REG
);
1762 assert (r_opnd
.type
== OPND_REG
);
1764 // If we're exchanging with RAX
1765 if (rm_opnd
.type
== OPND_REG
&& rm_opnd
.as
.reg
.reg_no
== RAX
.as
.reg
.reg_no
)
1767 // Write the REX byte
1768 cb_write_rex(cb
, rm_opnd
.num_bits
== 64, 0, 0, r_opnd
.as
.reg
.reg_no
);
1770 // Write the opcode and register number
1771 cb_write_byte(cb
, 0x90 + (r_opnd
.as
.reg
.reg_no
& 7));
1775 cb_write_rm(cb
, rm_opnd
.num_bits
== 16, rm_opnd
.num_bits
== 64, r_opnd
, rm_opnd
, 0xFF, 1, 0x87);
1779 /// xor - Exclusive bitwise OR
1780 void xor(codeblock_t
*cb
, x86opnd_t opnd0
, x86opnd_t opnd1
)
1786 0x31, // opMemRegPref
1788 0x33, // opRegMemPref
1790 0x83, // opMemImmSml
1791 0x81, // opMemImmLrg
1798 // LOCK - lock prefix for atomic shared memory operations
1799 void cb_write_lock_prefix(codeblock_t
*cb
)
1801 cb_write_byte(cb
, 0xF0);
1804 void cb_mark_all_writeable(codeblock_t
* cb
)
1806 if (mprotect(cb
->mem_block_
, cb
->mem_size
, PROT_READ
| PROT_WRITE
)) {
1807 fprintf(stderr
, "Couldn't make JIT page (%p) writeable, errno: %s", (void *)cb
->mem_block_
, strerror(errno
));
1812 void cb_mark_position_writeable(codeblock_t
* cb
, uint32_t write_pos
)
1814 uint32_t pagesize
= (uint32_t)sysconf(_SC_PAGESIZE
);
1815 uint32_t aligned_position
= (write_pos
/ pagesize
) * pagesize
;
1817 if (cb
->current_aligned_write_pos
!= aligned_position
) {
1818 cb
->current_aligned_write_pos
= aligned_position
;
1819 void *const page_addr
= cb_get_ptr(cb
, aligned_position
);
1820 if (mprotect(page_addr
, pagesize
, PROT_READ
| PROT_WRITE
)) {
1821 fprintf(stderr
, "Couldn't make JIT page (%p) writeable, errno: %s", page_addr
, strerror(errno
));
1827 void cb_mark_all_executable(codeblock_t
* cb
)
1829 cb
->current_aligned_write_pos
= ALIGNED_WRITE_POSITION_NONE
;
1830 if (mprotect(cb
->mem_block_
, cb
->mem_size
, PROT_READ
| PROT_EXEC
)) {
1831 fprintf(stderr
, "Couldn't make JIT page (%p) executable, errno: %s", (void *)cb
->mem_block_
, strerror(errno
));