1use crate::{
2 error::{EbpfError, UserDefinedError},
3 jit::{emit, emit_variable_length, JitCompiler, OperandSize},
4};
5
6pub const RAX: u8 = 0;
7pub const RCX: u8 = 1;
8pub const RDX: u8 = 2;
9pub const RBX: u8 = 3;
10pub const RSP: u8 = 4;
11pub const RBP: u8 = 5;
12pub const RSI: u8 = 6;
13pub const RDI: u8 = 7;
14pub const R8: u8 = 8;
15pub const R9: u8 = 9;
16pub const R10: u8 = 10;
17pub const R11: u8 = 11;
18pub const R12: u8 = 12;
19pub const R13: u8 = 13;
20pub const R14: u8 = 14;
21pub const R15: u8 = 15;
22
23pub const ARGUMENT_REGISTERS: [u8; 6] = [RDI, RSI, RDX, RCX, R8, R9];
26pub const CALLER_SAVED_REGISTERS: [u8; 9] = [RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11];
27pub const CALLEE_SAVED_REGISTERS: [u8; 6] = [RBP, RBX, R12, R13, R14, R15];
28
29struct X86Rex {
30 w: bool,
31 r: bool,
32 x: bool,
33 b: bool,
34}
35
36struct X86ModRm {
37 mode: u8,
38 r: u8,
39 m: u8,
40}
41
42struct X86Sib {
43 scale: u8,
44 index: u8,
45 base: u8,
46}
47
48#[derive(PartialEq, Eq, Copy, Clone)]
49pub enum X86IndirectAccess {
50 Offset(i32),
52 OffsetIndexShift(i32, u8, u8),
54}
55
56#[allow(dead_code)]
57#[derive(PartialEq, Eq, Copy, Clone)]
58pub enum FenceType {
59 Load = 5,
61 All = 6,
63 Store = 7,
65}
66
67#[derive(PartialEq, Eq, Copy, Clone)]
68pub struct X86Instruction {
69 pub size: OperandSize,
70 pub opcode_escape_sequence: u8,
71 pub opcode: u8,
72 pub modrm: bool,
73 pub indirect: Option<X86IndirectAccess>,
74 pub first_operand: u8,
75 pub second_operand: u8,
76 pub immediate_size: OperandSize,
77 pub immediate: i64,
78}
79
80impl Default for X86Instruction {
81 fn default() -> Self {
82 Self {
83 size: OperandSize::S64,
84 opcode_escape_sequence: 0,
85 opcode: 0,
86 modrm: true,
87 indirect: None,
88 first_operand: 0,
89 second_operand: 0,
90 immediate_size: OperandSize::S0,
91 immediate: 0,
92 }
93 }
94}
95
96impl X86Instruction {
97 pub fn emit<E: UserDefinedError>(&self, jit: &mut JitCompiler) -> Result<(), EbpfError<E>> {
98 let mut rex = X86Rex {
99 w: self.size == OperandSize::S64,
100 r: self.first_operand & 0b1000 != 0,
101 x: false,
102 b: self.second_operand & 0b1000 != 0,
103 };
104 let mut modrm = X86ModRm {
105 mode: 0,
106 r: self.first_operand & 0b111,
107 m: self.second_operand & 0b111,
108 };
109 let mut sib = X86Sib {
110 scale: 0,
111 index: 0,
112 base: 0,
113 };
114 let mut displacement_size = OperandSize::S0;
115 let mut displacement = 0;
116 if self.modrm {
117 match self.indirect {
118 Some(X86IndirectAccess::Offset(offset)) => {
119 displacement = offset;
120 debug_assert_ne!(self.second_operand & 0b111, RSP); if (displacement >= -128 && displacement <= 127)
122 || (displacement == 0 && self.second_operand & 0b111 == RBP)
123 {
124 displacement_size = OperandSize::S8;
125 modrm.mode = 1;
126 } else {
127 displacement_size = OperandSize::S32;
128 modrm.mode = 2;
129 }
130 }
131 Some(X86IndirectAccess::OffsetIndexShift(offset, index, shift)) => {
132 displacement = offset;
133 displacement_size = OperandSize::S32;
134 modrm.mode = 2;
135 modrm.m = RSP;
136 rex.x = index & 0b1000 != 0;
137 sib.scale = shift & 0b11;
138 sib.index = index & 0b111;
139 sib.base = self.second_operand & 0b111;
140 }
141 None => {
142 modrm.mode = 3;
143 }
144 }
145 }
146 jit.emit_random_noop()?;
147 if self.size == OperandSize::S16 {
148 emit::<u8, E>(jit, 0x66)?;
149 }
150 let rex =
151 ((rex.w as u8) << 3) | ((rex.r as u8) << 2) | ((rex.x as u8) << 1) | (rex.b as u8);
152 if rex != 0 {
153 emit::<u8, E>(jit, 0x40 | rex)?;
154 }
155 match self.opcode_escape_sequence {
156 1 => emit::<u8, E>(jit, 0x0f)?,
157 2 => emit::<u16, E>(jit, 0x0f38)?,
158 3 => emit::<u16, E>(jit, 0x0f3a)?,
159 _ => {}
160 }
161 emit::<u8, E>(jit, self.opcode)?;
162 if self.modrm {
163 emit::<u8, E>(jit, (modrm.mode << 6) | (modrm.r << 3) | modrm.m)?;
164 let sib = (sib.scale << 6) | (sib.index << 3) | sib.base;
165 if sib != 0 {
166 emit::<u8, E>(jit, sib)?;
167 }
168 emit_variable_length(jit, displacement_size, displacement as u64)?;
169 }
170 emit_variable_length(jit, self.immediate_size, self.immediate as u64)
171 }
172
173 pub fn mov(size: OperandSize, source: u8, destination: u8) -> Self {
175 Self {
176 size,
177 opcode: 0x89,
178 first_operand: source,
179 second_operand: destination,
180 ..Self::default()
181 }
182 }
183
184 pub fn xchg(size: OperandSize, source: u8, destination: u8) -> Self {
186 Self {
187 size,
188 opcode: 0x87,
189 first_operand: source,
190 second_operand: destination,
191 ..Self::default()
192 }
193 }
194
195 pub fn bswap(size: OperandSize, destination: u8) -> Self {
197 match size {
198 OperandSize::S16 => Self {
199 size,
200 opcode: 0xc1,
201 second_operand: destination,
202 immediate_size: OperandSize::S8,
203 immediate: 8,
204 ..Self::default()
205 },
206 OperandSize::S32 | OperandSize::S64 => Self {
207 size,
208 opcode_escape_sequence: 1,
209 opcode: 0xc8 | (destination & 0b111),
210 modrm: false,
211 second_operand: destination,
212 ..Self::default()
213 },
214 _ => unimplemented!(),
215 }
216 }
217
218 pub fn sign_extend_i32_to_i64(source: u8, destination: u8) -> Self {
220 Self {
221 opcode: 0x63,
222 first_operand: source,
223 second_operand: destination,
224 ..Self::default()
225 }
226 }
227
228 pub fn test(
230 size: OperandSize,
231 source: u8,
232 destination: u8,
233 indirect: Option<X86IndirectAccess>,
234 ) -> Self {
235 Self {
236 size,
237 opcode: 0x85,
238 first_operand: source,
239 second_operand: destination,
240 indirect,
241 ..Self::default()
242 }
243 }
244
245 pub fn test_immediate(
247 size: OperandSize,
248 destination: u8,
249 immediate: i64,
250 indirect: Option<X86IndirectAccess>,
251 ) -> Self {
252 Self {
253 size,
254 opcode: 0xf7,
255 first_operand: RAX,
256 second_operand: destination,
257 immediate_size: OperandSize::S32,
258 immediate,
259 indirect,
260 ..Self::default()
261 }
262 }
263
264 pub fn cmp(
266 size: OperandSize,
267 source: u8,
268 destination: u8,
269 indirect: Option<X86IndirectAccess>,
270 ) -> Self {
271 Self {
272 size,
273 opcode: 0x39,
274 first_operand: source,
275 second_operand: destination,
276 indirect,
277 ..Self::default()
278 }
279 }
280
281 pub fn cmp_immediate(
283 size: OperandSize,
284 destination: u8,
285 immediate: i64,
286 indirect: Option<X86IndirectAccess>,
287 ) -> Self {
288 Self {
289 size,
290 opcode: 0x81,
291 first_operand: RDI,
292 second_operand: destination,
293 immediate_size: OperandSize::S32,
294 immediate,
295 indirect,
296 ..Self::default()
297 }
298 }
299
300 pub fn lea(
302 size: OperandSize,
303 source: u8,
304 destination: u8,
305 indirect: Option<X86IndirectAccess>,
306 ) -> Self {
307 Self {
308 size,
309 opcode: 0x8d,
310 first_operand: destination,
311 second_operand: source,
312 indirect,
313 ..Self::default()
314 }
315 }
316
317 pub fn load(
319 size: OperandSize,
320 source: u8,
321 destination: u8,
322 indirect: X86IndirectAccess,
323 ) -> Self {
324 Self {
325 size: if size == OperandSize::S64 {
326 OperandSize::S64
327 } else {
328 OperandSize::S32
329 },
330 opcode_escape_sequence: if size == OperandSize::S8 || size == OperandSize::S16 {
331 1
332 } else {
333 0
334 },
335 opcode: match size {
336 OperandSize::S8 => 0xb6,
337 OperandSize::S16 => 0xb7,
338 _ => 0x8b,
339 },
340 first_operand: destination,
341 second_operand: source,
342 indirect: Some(indirect),
343 ..Self::default()
344 }
345 }
346
347 pub fn store(
349 size: OperandSize,
350 source: u8,
351 destination: u8,
352 indirect: X86IndirectAccess,
353 ) -> Self {
354 Self {
355 size,
356 opcode: match size {
357 OperandSize::S8 => 0x88,
358 _ => 0x89,
359 },
360 first_operand: source,
361 second_operand: destination,
362 indirect: Some(indirect),
363 ..Self::default()
364 }
365 }
366
367 pub fn load_immediate(size: OperandSize, destination: u8, immediate: i64) -> Self {
369 let immediate_size =
370 if immediate >= std::i32::MIN as i64 && immediate <= std::i32::MAX as i64 {
371 OperandSize::S32
372 } else {
373 OperandSize::S64
374 };
375 match immediate_size {
376 OperandSize::S32 => Self {
377 size,
378 opcode: 0xc7,
379 second_operand: destination,
380 immediate_size: OperandSize::S32,
381 immediate,
382 ..Self::default()
383 },
384 OperandSize::S64 => Self {
385 size,
386 opcode: 0xb8 | (destination & 0b111),
387 modrm: false,
388 second_operand: destination,
389 immediate_size: OperandSize::S64,
390 immediate,
391 ..Self::default()
392 },
393 _ => unimplemented!(),
394 }
395 }
396
397 pub fn store_immediate(
399 size: OperandSize,
400 destination: u8,
401 indirect: X86IndirectAccess,
402 immediate: i64,
403 ) -> Self {
404 Self {
405 size,
406 opcode: match size {
407 OperandSize::S8 => 0xc6,
408 _ => 0xc7,
409 },
410 second_operand: destination,
411 indirect: Some(indirect),
412 immediate_size: if size == OperandSize::S64 {
413 OperandSize::S32
414 } else {
415 size
416 },
417 immediate,
418 ..Self::default()
419 }
420 }
421
422 pub fn push(source: u8) -> Self {
424 Self {
425 size: OperandSize::S32,
426 opcode: 0x50 | (source & 0b111),
427 modrm: false,
428 second_operand: source,
429 ..Self::default()
430 }
431 }
432
433 pub fn pop(destination: u8) -> Self {
435 Self {
436 size: OperandSize::S32,
437 opcode: 0x58 | (destination & 0b111),
438 modrm: false,
439 second_operand: destination,
440 ..Self::default()
441 }
442 }
443
444 pub fn call_reg(
446 size: OperandSize,
447 destination: u8,
448 indirect: Option<X86IndirectAccess>,
449 ) -> Self {
450 Self {
451 size,
452 opcode: 0xff,
453 first_operand: 2,
454 second_operand: destination,
455 indirect,
456 ..Self::default()
457 }
458 }
459
460 pub fn return_near() -> Self {
462 Self {
463 size: OperandSize::S32,
464 opcode: 0xc3,
465 modrm: false,
466 ..Self::default()
467 }
468 }
469
470 #[allow(dead_code)]
472 pub fn noop() -> Self {
473 Self {
474 size: OperandSize::S32,
475 opcode: 0x90,
476 modrm: false,
477 ..Self::default()
478 }
479 }
480
481 #[allow(dead_code)]
483 pub fn interrupt(immediate: u8) -> Self {
484 if immediate == 3 {
485 Self {
486 size: OperandSize::S32,
487 opcode: 0xcc,
488 modrm: false,
489 ..Self::default()
490 }
491 } else {
492 Self {
493 size: OperandSize::S32,
494 opcode: 0xcd,
495 modrm: false,
496 immediate_size: OperandSize::S8,
497 immediate: immediate as i64,
498 ..Self::default()
499 }
500 }
501 }
502
503 #[allow(dead_code)]
505 pub fn cycle_count() -> Self {
506 Self {
507 size: OperandSize::S32,
508 opcode_escape_sequence: 1,
509 opcode: 0x31,
510 modrm: false,
511 ..Self::default()
512 }
513 }
514
515 #[allow(dead_code)]
517 pub fn fence(fence_type: FenceType) -> Self {
518 Self {
519 size: OperandSize::S32,
520 opcode_escape_sequence: 1,
521 opcode: 0xae,
522 first_operand: fence_type as u8,
523 ..Self::default()
524 }
525 }
526}