1 #include "assembler-intrinsics-i386.hpp"
2 #include "serialization.hpp"
5 namespace assembler_intrinsics
7 const I386::reg
I386::reg_none(255);
8 const I386::reg
I386::reg_ax(0);
9 const I386::reg
I386::reg_bx(3);
10 const I386::reg
I386::reg_cx(1);
11 const I386::reg
I386::reg_dx(2);
12 const I386::reg
I386::reg_bp(5);
13 const I386::reg
I386::reg_sp(4);
14 const I386::reg
I386::reg_si(6);
15 const I386::reg
I386::reg_di(7);
16 const I386::reg
I386::reg_r8(8);
17 const I386::reg
I386::reg_r9(9);
18 const I386::reg
I386::reg_r10(10);
19 const I386::reg
I386::reg_r11(11);
20 const I386::reg
I386::reg_r12(12);
21 const I386::reg
I386::reg_r13(13);
22 const I386::reg
I386::reg_r14(14);
23 const I386::reg
I386::reg_r15(15);
24 const I386::reg
I386::reg_rip(254);
26 const I386::reg
i386_r0(0);
27 const I386::reg
i386_r1(1);
28 const I386::reg
i386_r2(2);
29 const I386::reg
i386_r3(3);
30 const I386::reg
i386_r4(4);
31 const I386::reg
i386_r5(5);
32 const I386::reg
i386_r6(6);
33 const I386::reg
i386_r7(7);
35 I386::reg::reg(uint8_t _val
)
40 I386::ref
I386::reg::operator[](int32_t off
) const
43 return ref::reg_off(*this, off
);
45 return I386::ref::rip_off(off
);
47 throw std::runtime_error("Bad register offset-base");
50 I386::sib_scale_intermediate
I386::reg::operator*(uint8_t scale
) const
52 return sib_scale_intermediate(*this, scale
);
55 I386::ref
I386::reg::operator[](const reg r
) const
57 return ref::sib(*this, r
, 1);
60 I386::ref
I386::reg::operator[](sib_scale_intermediate r
) const
62 return ref::sib(*this, r
.index
, r
.scale
);
65 I386::ref
I386::reg::operator[](sib_scale_off_intermediate r
) const
67 return ref::sib(*this, r
.index
, r
.scale
, r
.offset
);
70 I386::sib_scale_off_intermediate
I386::reg::operator+(int32_t off
) const
72 return sib_scale_off_intermediate(*this, 1, off
);
75 bool I386::reg::hbit() { return (val
& 8); }
76 uint8_t I386::reg::lbits() { return val
& 7; }
77 uint8_t I386::reg::num() { return val
& 15; }
78 bool I386::reg::valid(bool amd64
) { return (val
< (amd64
? 16 : 8)); }
79 bool I386::reg::is_none() { return (val
== 255); }
81 I386::sib_scale_off_intermediate::sib_scale_off_intermediate(reg _idx
, uint8_t _scale
, int32_t _offset
)
82 : index(_idx
), scale(_scale
), offset(_offset
)
86 I386::sib_scale_intermediate::sib_scale_intermediate(reg _idx
, uint8_t _scale
)
87 : index(_idx
), scale(_scale
)
91 I386::sib_scale_off_intermediate
I386::sib_scale_intermediate::operator+(int32_t off
) const
93 return sib_scale_off_intermediate(index
, scale
, off
);
97 void check_register(I386::reg r
, bool amd64
)
100 throw std::runtime_error("Illegal register");
103 void I386::label(assembler::label
& l
)
108 I386::low::low(bool _need_amd64
, uint8_t _rex_prefix
, std::vector
<uint8_t> _ref
)
109 : needs_amd64(_need_amd64
), rex(_rex_prefix
), mref(_ref
)
111 if(rex
) needs_amd64
= true;
113 bool I386::low::need_amd64() { return needs_amd64
; }
114 bool I386::low::has_rex() { return (rex
!= 0); }
115 uint8_t I386::low::rex_prefix() { return rex
; }
116 std::vector
<uint8_t> I386::low::bytes() { return mref
; }
118 void I386::low::emit_rex(assembler::assembler
& a
)
120 if(has_rex()) a(rex
);
123 void I386::low::emit_bytes(assembler::assembler
& a
)
125 for(auto i
: mref
) a(i
);
132 I386::ref::ref(reg r
)
135 throw std::runtime_error("Illegal register");
136 needs_amd64
= r
.hbit();
137 rex
= r
.hbit() ? 1 : 0;
138 mref
.push_back(0xC0 + r
.lbits());
141 I386::ref::ref(sib_scale_intermediate r
)
143 *this = sib(reg_none
, r
.index
, r
.scale
);
146 I386::ref::ref(sib_scale_off_intermediate r
)
148 *this = sib(reg_none
, r
.index
, r
.scale
, r
.offset
);
151 I386::ref
I386::ref::reg_off(reg r
, int32_t off
)
154 throw std::runtime_error("Illegal register");
155 bool need_off
= (off
!= 0);
156 bool need_loff
= (off
< -128 || off
> 127);
158 x
.needs_amd64
= r
.hbit();
159 x
.rex
= r
.hbit() ? 1 : 0;
161 need_off
= true; //EBP and R13 always need offset.
162 uint8_t rtype
= need_loff
? 2 : (need_off
? 1 : 0);
164 //SIB is required for these.
165 x
.mref
.push_back(0x04 + rtype
* 0x40);
166 x
.mref
.push_back(0x24);
168 x
.mref
.push_back(r
.lbits() + rtype
* 0x40);
170 size_t bytes
= x
.mref
.size();
172 x
.mref
.resize(bytes
+ 4);
173 serialization::s32l(&x
.mref
[bytes
], off
);
174 } else if(need_off
) {
175 x
.mref
.resize(bytes
+ 1);
176 serialization::s8l(&x
.mref
[bytes
], off
);
181 I386::ref
I386::ref::rip_off(int32_t off
)
184 x
.needs_amd64
= true;
187 serialization::s32l(&x
.mref
[1], off
);
192 I386::ref
I386::ref::sib(reg base
, reg index
, uint8_t scale
, int32_t off
)
195 if(!base
.is_none() && !base
.valid(true))
196 throw std::runtime_error("Illegal base in SIB");
197 if((!index
.is_none() && !index
.valid(true)) || index
.num() == 4)
198 throw std::runtime_error("Illegal index in SIB");
201 case 1: ss
= 0; break;
202 case 2: ss
= 1; break;
203 case 4: ss
= 2; break;
204 case 8: ss
= 3; break;
205 default: throw std::runtime_error("Illegal scale in SIB");
208 bool need_off
= (off
!= 0);
209 bool need_loff
= (off
< -128 || off
> 127);
211 //Base is the offset.
212 x
.mref
.push_back(0x04); //SIB coming, no offset
213 x
.mref
.push_back(ss
* 64 + index
.lbits() + 5); //Base is "EBP".
217 need_off
= true; //This always needs offset.
218 uint8_t rtype
= need_loff
? 2 : (need_off
? 1 : 0);
219 x
.mref
.push_back(0x04 + rtype
* 0x40); //SIB coming.
220 x
.mref
.push_back(ss
* 64 + index
.lbits() * 8 + base
.lbits());
223 if(!base
.is_none()) x
.rex
|= (base
.hbit() ? 1 : 0);
224 if(!index
.is_none()) x
.rex
|= (index
.hbit() ? 2 : 0);
225 x
.needs_amd64
= (x
.rex
!= 0);
226 size_t bytes
= x
.mref
.size();
228 x
.mref
.resize(bytes
+ 4);
229 serialization::s32l(&x
.mref
[bytes
], off
);
230 } else if(need_off
) {
231 x
.mref
.resize(bytes
+ 1);
232 serialization::s8l(&x
.mref
[bytes
], off
);
237 I386::low
I386::ref::operator()(reg r
, bool set_size_flag
, bool amd64
)
239 check_register(r
, amd64
);
241 c
[0] |= r
.lbits() * 8;
243 if(r
.hbit()) _rex
+=4; //Set R
244 if(set_size_flag
&& amd64
) _rex
+=8; //Set S if needed.
245 if(_rex
) _rex
+=0x40; //Ret rex prefix bits.
246 if((_rex
|| needs_amd64
) && !amd64
)
247 throw std::runtime_error("Illegal memory reference for i386");
248 return low(needs_amd64
, _rex
, c
);
251 void I386::ref::emit(assembler::assembler
& a
, bool set_size_flag
, bool amd64
, reg r
, uint8_t op1
)
253 auto xref
= (*this)(r
, set_size_flag
, amd64
);
259 void I386::ref::emit(assembler::assembler
& a
, bool set_size_flag
, bool amd64
, reg r
, uint8_t op1
,
262 auto xref
= (*this)(r
, set_size_flag
, amd64
);
268 I386::I386(assembler::assembler
& _a
, bool _amd64
)
269 : a(_a
), amd64(_amd64
)
273 bool I386::is_amd64()
285 uint8_t I386::wordsize()
287 return amd64
? 8 : 4;
291 void I386::push_reg(reg r
)
293 check_register(r
, amd64
);
294 if(amd64
&& r
.hbit()) a(0x41);
298 void I386::pop_reg(reg r
)
300 check_register(r
, amd64
);
301 if(amd64
&& r
.hbit()) a(0x41);
304 //MOV NWORD <reg>,<regmem>
305 void I386::mov_reg_regmem(reg r
, I386::ref mem
)
307 mem
.emit(a
, true, amd64
, r
, 0x8B);
309 //XOR NWORD <reg>,<regmem>
310 void I386::xor_reg_regmem(reg r
, I386::ref mem
)
312 mem
.emit(a
, true, amd64
, r
, 0x33);
316 void I386::lea_reg_mem(reg r
, I386::ref mem
)
318 mem
.emit(a
, true, amd64
, r
, 0x8D);
321 //AND NWORD <regmem>, imm
322 void I386::and_regmem_imm(I386::ref mem
, int32_t imm
)
324 mem
.emit(a
, true, amd64
, i386_r4
, 0x81);
325 a(assembler::vle
<int32_t>(imm
));
328 //SHL NWORD <regmem>, imm
329 void I386::shl_regmem_imm(I386::ref mem
, uint8_t imm
)
331 mem
.emit(a
, true, amd64
, i386_r4
, 0xC1);
335 //SHL BYTE <regmem>, imm
336 void I386::shl_regmem_imm8(ref mem
, uint8_t imm
)
338 mem
.emit(a
, true, amd64
, i386_r4
, 0xC0);
342 //ADD NWORD <regmem>, imm
343 void I386::add_regmem_imm(I386::ref mem
, int32_t imm
)
345 mem
.emit(a
, true, amd64
, i386_r0
, 0x81);
346 a(assembler::vle
<int32_t>(imm
));
349 //ADD NWORD <reg>,<regmem>
350 void I386::add_reg_regmem(reg r
, I386::ref mem
)
352 mem
.emit(a
, true, amd64
, r
, 0x03);
355 //MOV BYTE <regmem>, imm
356 void I386::mov_regmem_imm8(I386::ref mem
, uint8_t imm
)
358 mem
.emit(a
, false, amd64
, i386_r0
, 0xC6);
362 //MOV DWORD <regmem>, imm
363 void I386::mov_regmem_imm32(I386::ref mem
, uint32_t imm
)
365 mem
.emit(a
, false, amd64
, i386_r0
, 0xC7);
366 a(assembler::vle
<uint32_t>(imm
));
369 //MOV NWORD <reg>, imm.
370 void I386::mov_reg_imm(reg r
, size_t imm
)
372 check_register(r
, amd64
);
373 if(amd64
) a(0x48 | (r
.hbit() ? 1 : 0));
375 a(assembler::vle
<size_t>(imm
));
378 //CMP NWORD <regmem>, imm
379 void I386::cmp_regmem_imm(I386::ref mem
, int32_t imm
)
381 mem
.emit(a
, true, amd64
, i386_r7
, 0x81);
382 a(assembler::vle
<int32_t>(imm
));
385 //MOV WORD <regmem>, <reg>
386 void I386::mov_regmem_reg16(I386::ref mem
, reg r
)
389 mem
.emit(a
, false, amd64
, r
, 0x89);
392 //MOV BYTE <regmem>, <reg>
393 void I386::mov_regmem_reg8(ref mem
, reg r
)
395 mem
.emit(a
, false, amd64
, r
, 0x88);
398 //MOV WORD <reg>, <regmem>
399 void I386::mov_reg_regmem16(reg r
, I386::ref mem
)
402 mem
.emit(a
, false, amd64
, r
, 0x8B);
405 //MOV BYTE <reg>, <regmem>
406 void I386::mov_reg_regmem8(reg r
, ref mem
)
408 mem
.emit(a
, false, amd64
, r
, 0x8A);
411 //OR BYTE <regmem>, imm
412 void I386::or_regmem_imm8(I386::ref mem
, uint8_t imm
)
414 mem
.emit(a
, false, amd64
, i386_r1
, 0x80);
418 //AND BYTE <regmem>, imm
419 void I386::and_regmem_imm8(I386::ref mem
, uint8_t imm
)
421 mem
.emit(a
, false, amd64
, i386_r4
, 0x80);
426 void I386::inc_regmem(I386::ref mem
)
428 mem
.emit(a
, true, amd64
, i386_r0
, 0xFF);
431 //TEST BYTE <regmem>, imm
432 void I386::test_regmem_imm8(I386::ref mem
, uint8_t imm
)
434 mem
.emit(a
, false, amd64
, i386_r0
, 0xF6);
438 //CMP BYTE <regmem>, imm
439 void I386::cmp_regmem_imm8(I386::ref mem
, uint8_t imm
)
441 mem
.emit(a
, false, amd64
, i386_r7
, 0x80);
445 //MOV NWORD <reg>, <addr>.
446 void I386::mov_reg_addr(reg r
, assembler::label
& l
)
448 if(amd64
) a(0x48 + (r
.hbit() ? 1 : 0));
454 void I386::neg_regmem8(ref mem
)
456 mem
.emit(a
, false, amd64
, i386_r3
, 0xF6);
459 //ADD BYTE <regmem>, imm
460 void I386::add_regmem_imm8(ref mem
, uint8_t imm
)
462 mem
.emit(a
, false, amd64
, i386_r0
, 0x80);
466 //OR BYTE <regmem>, <reg>
467 void I386::or_regmem_reg8(ref mem
, reg r
)
469 mem
.emit(a
, false, amd64
, r
, 0x08);
473 void I386::call_addr(assembler::label
& l
)
476 call_regmem(reg_rip
[2]);
477 a(0xEB, 0x08); //JMP +8
480 a(0xE8, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
485 void I386::call_regmem(I386::ref mem
)
487 mem
.emit(a
, false, amd64
, i386_r2
, 0xFF);
491 void I386::jmp_regmem(I386::ref mem
)
493 mem
.emit(a
, false, amd64
, i386_r4
, 0xFF);
497 void I386::setnz_regmem(I386::ref mem
)
499 mem
.emit(a
, false, amd64
, i386_r0
, 0x0F, 0x95);
503 void I386::jnz_short(assembler::label
& l
)
505 a(0x75, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
509 void I386::jz_short(assembler::label
& l
)
511 a(0x74, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
515 void I386::jmp_short(assembler::label
& l
)
517 a(0xEB, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
521 void I386::jmp_long(assembler::label
& l
)
523 a(0xE9, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
527 void I386::jz_long(assembler::label
& l
)
529 a(0x0F, 0x84, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
533 void I386::jae_short(assembler::label
& l
)
535 a(0x73, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
539 void I386::jae_long(assembler::label
& l
)
541 a(0x0F, 0x83, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
549 void I386::address(assembler::label
& l
)
551 a(assembler::relocation_tag((amd64
? assembler::i386_reloc_abs64
: assembler::i386_reloc_abs32
), l
),
552 assembler::pad_tag(wordsize()));