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
)
87 I386::sib_scale_intermediate::sib_scale_intermediate(reg _idx
, uint8_t _scale
)
88 : index(_idx
), scale(_scale
)
92 I386::sib_scale_off_intermediate
I386::sib_scale_intermediate::operator+(int32_t off
) const
94 return sib_scale_off_intermediate(index
, scale
, off
);
98 void check_register(I386::reg r
, bool amd64
)
101 throw std::runtime_error("Illegal register");
104 void I386::label(assembler::label
& l
)
109 I386::low::low(bool _need_amd64
, uint8_t _rex_prefix
, std::vector
<uint8_t> _ref
)
110 : needs_amd64(_need_amd64
), rex(_rex_prefix
), mref(_ref
)
112 if(rex
) needs_amd64
= true;
114 bool I386::low::need_amd64() { return needs_amd64
; }
115 bool I386::low::has_rex() { return (rex
!= 0); }
116 uint8_t I386::low::rex_prefix() { return rex
; }
117 std::vector
<uint8_t> I386::low::bytes() { return mref
; }
119 void I386::low::emit_rex(assembler::assembler
& a
)
121 if(has_rex()) a(rex
);
124 void I386::low::emit_bytes(assembler::assembler
& a
)
126 for(auto i
: mref
) a(i
);
133 I386::ref::ref(reg r
)
136 throw std::runtime_error("Illegal register");
137 needs_amd64
= r
.hbit();
138 rex
= r
.hbit() ? 1 : 0;
139 mref
.push_back(0xC0 + r
.lbits());
142 I386::ref::ref(sib_scale_intermediate r
)
144 *this = sib(reg_none
, r
.index
, r
.scale
);
147 I386::ref::ref(sib_scale_off_intermediate r
)
149 *this = sib(reg_none
, r
.index
, r
.scale
, r
.offset
);
152 I386::ref
I386::ref::reg_off(reg r
, int32_t off
)
155 throw std::runtime_error("Illegal register");
156 bool need_off
= (off
!= 0);
157 bool need_loff
= (off
< -128 || off
> 127);
159 x
.needs_amd64
= r
.hbit();
160 x
.rex
= r
.hbit() ? 1 : 0;
162 need_off
= true; //EBP and R13 always need offset.
163 uint8_t rtype
= need_loff
? 2 : (need_off
? 1 : 0);
165 //SIB is required for these.
166 x
.mref
.push_back(0x04 + rtype
* 0x40);
167 x
.mref
.push_back(0x24);
169 x
.mref
.push_back(r
.lbits() + rtype
* 0x40);
171 size_t bytes
= x
.mref
.size();
173 x
.mref
.resize(bytes
+ 4);
174 serialization::s32l(&x
.mref
[bytes
], off
);
175 } else if(need_off
) {
176 x
.mref
.resize(bytes
+ 1);
177 serialization::s8l(&x
.mref
[bytes
], off
);
182 I386::ref
I386::ref::rip_off(int32_t off
)
185 x
.needs_amd64
= true;
188 serialization::s32l(&x
.mref
[1], off
);
193 I386::ref
I386::ref::sib(reg base
, reg index
, uint8_t scale
, int32_t off
)
196 if(!base
.is_none() && !base
.valid(true))
197 throw std::runtime_error("Illegal base in SIB");
198 if((!index
.is_none() && !index
.valid(true)) || index
.num() == 4)
199 throw std::runtime_error("Illegal index in SIB");
202 case 1: ss
= 0; break;
203 case 2: ss
= 1; break;
204 case 4: ss
= 2; break;
205 case 8: ss
= 3; break;
206 default: throw std::runtime_error("Illegal scale in SIB");
209 bool need_off
= (off
!= 0);
210 bool need_loff
= (off
< -128 || off
> 127);
212 //Base is the offset.
213 x
.mref
.push_back(0x04); //SIB coming, no offset
214 x
.mref
.push_back(ss
* 64 + index
.lbits() + 5); //Base is "EBP".
218 need_off
= true; //This always needs offset.
219 uint8_t rtype
= need_loff
? 2 : (need_off
? 1 : 0);
220 x
.mref
.push_back(0x04 + rtype
* 0x40); //SIB coming.
221 x
.mref
.push_back(ss
* 64 + index
.lbits() * 8 + base
.lbits());
224 if(!base
.is_none()) x
.rex
|= (base
.hbit() ? 1 : 0);
225 if(!index
.is_none()) x
.rex
|= (index
.hbit() ? 2 : 0);
226 x
.needs_amd64
= (x
.rex
!= 0);
227 size_t bytes
= x
.mref
.size();
229 x
.mref
.resize(bytes
+ 4);
230 serialization::s32l(&x
.mref
[bytes
], off
);
231 } else if(need_off
) {
232 x
.mref
.resize(bytes
+ 1);
233 serialization::s8l(&x
.mref
[bytes
], off
);
238 I386::low
I386::ref::operator()(reg r
, bool set_size_flag
, bool amd64
)
240 check_register(r
, amd64
);
242 c
[0] |= r
.lbits() * 8;
244 if(r
.hbit()) _rex
+=4; //Set R
245 if(set_size_flag
&& amd64
) _rex
+=8; //Set S if needed.
246 if(_rex
) _rex
+=0x40; //Ret rex prefix bits.
247 if((_rex
|| needs_amd64
) && !amd64
)
248 throw std::runtime_error("Illegal memory reference for i386");
249 return low(needs_amd64
, _rex
, c
);
252 void I386::ref::emit(assembler::assembler
& a
, bool set_size_flag
, bool amd64
, reg r
, uint8_t op1
)
254 auto xref
= (*this)(r
, set_size_flag
, amd64
);
260 void I386::ref::emit(assembler::assembler
& a
, bool set_size_flag
, bool amd64
, reg r
, uint8_t op1
,
263 auto xref
= (*this)(r
, set_size_flag
, amd64
);
269 I386::I386(assembler::assembler
& _a
, bool _amd64
)
270 : a(_a
), amd64(_amd64
)
274 bool I386::is_amd64()
286 uint8_t I386::wordsize()
288 return amd64
? 8 : 4;
292 void I386::push_reg(reg r
)
294 check_register(r
, amd64
);
295 if(amd64
&& r
.hbit()) a(0x41);
299 void I386::pop_reg(reg r
)
301 check_register(r
, amd64
);
302 if(amd64
&& r
.hbit()) a(0x41);
305 //MOV NWORD <reg>,<regmem>
306 void I386::mov_reg_regmem(reg r
, I386::ref mem
)
308 mem
.emit(a
, true, amd64
, r
, 0x8B);
310 //XOR NWORD <reg>,<regmem>
311 void I386::xor_reg_regmem(reg r
, I386::ref mem
)
313 mem
.emit(a
, true, amd64
, r
, 0x33);
317 void I386::lea_reg_mem(reg r
, I386::ref mem
)
319 mem
.emit(a
, true, amd64
, r
, 0x8D);
322 //AND NWORD <regmem>, imm
323 void I386::and_regmem_imm(I386::ref mem
, int32_t imm
)
325 mem
.emit(a
, true, amd64
, i386_r4
, 0x81);
326 a(assembler::vle
<int32_t>(imm
));
329 //SHL NWORD <regmem>, imm
330 void I386::shl_regmem_imm(I386::ref mem
, uint8_t imm
)
332 mem
.emit(a
, true, amd64
, i386_r4
, 0xC1);
336 //SHL BYTE <regmem>, imm
337 void I386::shl_regmem_imm8(ref mem
, uint8_t imm
)
339 mem
.emit(a
, true, amd64
, i386_r4
, 0xC0);
343 //ADD NWORD <regmem>, imm
344 void I386::add_regmem_imm(I386::ref mem
, int32_t imm
)
346 mem
.emit(a
, true, amd64
, i386_r0
, 0x81);
347 a(assembler::vle
<int32_t>(imm
));
350 //ADD NWORD <reg>,<regmem>
351 void I386::add_reg_regmem(reg r
, I386::ref mem
)
353 mem
.emit(a
, true, amd64
, r
, 0x03);
356 //MOV BYTE <regmem>, imm
357 void I386::mov_regmem_imm8(I386::ref mem
, uint8_t imm
)
359 mem
.emit(a
, false, amd64
, i386_r0
, 0xC6);
363 //MOV DWORD <regmem>, imm
364 void I386::mov_regmem_imm32(I386::ref mem
, uint32_t imm
)
366 mem
.emit(a
, false, amd64
, i386_r0
, 0xC7);
367 a(assembler::vle
<uint32_t>(imm
));
370 //MOV NWORD <reg>, imm.
371 void I386::mov_reg_imm(reg r
, size_t imm
)
373 check_register(r
, amd64
);
374 if(amd64
) a(0x48 | (r
.hbit() ? 1 : 0));
376 a(assembler::vle
<size_t>(imm
));
379 //CMP NWORD <regmem>, imm
380 void I386::cmp_regmem_imm(I386::ref mem
, int32_t imm
)
382 mem
.emit(a
, true, amd64
, i386_r7
, 0x81);
383 a(assembler::vle
<int32_t>(imm
));
386 //MOV WORD <regmem>, <reg>
387 void I386::mov_regmem_reg16(I386::ref mem
, reg r
)
390 mem
.emit(a
, false, amd64
, r
, 0x89);
393 //MOV BYTE <regmem>, <reg>
394 void I386::mov_regmem_reg8(ref mem
, reg r
)
396 mem
.emit(a
, false, amd64
, r
, 0x88);
399 //MOV WORD <reg>, <regmem>
400 void I386::mov_reg_regmem16(reg r
, I386::ref mem
)
403 mem
.emit(a
, false, amd64
, r
, 0x8B);
406 //MOV BYTE <reg>, <regmem>
407 void I386::mov_reg_regmem8(reg r
, ref mem
)
409 mem
.emit(a
, false, amd64
, r
, 0x8A);
412 //OR BYTE <regmem>, imm
413 void I386::or_regmem_imm8(I386::ref mem
, uint8_t imm
)
415 mem
.emit(a
, false, amd64
, i386_r1
, 0x80);
419 //AND BYTE <regmem>, imm
420 void I386::and_regmem_imm8(I386::ref mem
, uint8_t imm
)
422 mem
.emit(a
, false, amd64
, i386_r4
, 0x80);
427 void I386::inc_regmem(I386::ref mem
)
429 mem
.emit(a
, true, amd64
, i386_r0
, 0xFF);
432 //TEST BYTE <regmem>, imm
433 void I386::test_regmem_imm8(I386::ref mem
, uint8_t imm
)
435 mem
.emit(a
, false, amd64
, i386_r0
, 0xF6);
439 //CMP BYTE <regmem>, imm
440 void I386::cmp_regmem_imm8(I386::ref mem
, uint8_t imm
)
442 mem
.emit(a
, false, amd64
, i386_r7
, 0x80);
446 //MOV NWORD <reg>, <addr>.
447 void I386::mov_reg_addr(reg r
, assembler::label
& l
)
449 if(amd64
) a(0x48 + (r
.hbit() ? 1 : 0));
455 void I386::neg_regmem8(ref mem
)
457 mem
.emit(a
, false, amd64
, i386_r3
, 0xF6);
460 //ADD BYTE <regmem>, imm
461 void I386::add_regmem_imm8(ref mem
, uint8_t imm
)
463 mem
.emit(a
, false, amd64
, i386_r0
, 0x80);
467 //OR BYTE <regmem>, <reg>
468 void I386::or_regmem_reg8(ref mem
, reg r
)
470 mem
.emit(a
, false, amd64
, r
, 0x08);
474 void I386::call_addr(assembler::label
& l
)
477 call_regmem(reg_rip
[2]);
478 a(0xEB, 0x08); //JMP +8
481 a(0xE8, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
486 void I386::call_regmem(I386::ref mem
)
488 mem
.emit(a
, false, amd64
, i386_r2
, 0xFF);
492 void I386::jmp_regmem(I386::ref mem
)
494 mem
.emit(a
, false, amd64
, i386_r4
, 0xFF);
498 void I386::setnz_regmem(I386::ref mem
)
500 mem
.emit(a
, false, amd64
, i386_r0
, 0x0F, 0x95);
504 void I386::jnz_short(assembler::label
& l
)
506 a(0x75, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
510 void I386::jz_short(assembler::label
& l
)
512 a(0x74, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
516 void I386::jmp_short(assembler::label
& l
)
518 a(0xEB, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
522 void I386::jmp_long(assembler::label
& l
)
524 a(0xE9, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
528 void I386::jz_long(assembler::label
& l
)
530 a(0x0F, 0x84, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
534 void I386::jae_short(assembler::label
& l
)
536 a(0x73, assembler::relocation_tag(assembler::i386_reloc_rel8
, l
), 0x00);
540 void I386::jae_long(assembler::label
& l
)
542 a(0x0F, 0x83, assembler::relocation_tag(assembler::i386_reloc_rel32
, l
), assembler::pad_tag(4));
550 void I386::address(assembler::label
& l
)
552 a(assembler::relocation_tag((amd64
? assembler::i386_reloc_abs64
: assembler::i386_reloc_abs32
), l
),
553 assembler::pad_tag(wordsize()));