1#![allow(clippy::arithmetic_side_effects)]
15
16#[cfg(not(feature = "shuttle-test"))]
17use rand::{thread_rng, Rng};
18
19#[cfg(feature = "shuttle-test")]
20use shuttle::rand::{thread_rng, Rng};
21
22use rand::{
23 distributions::{Distribution, Uniform},
24 rngs::SmallRng,
25 SeedableRng,
26};
27use std::{fmt::Debug, mem, ptr};
28
29use crate::{
30 ebpf::{self, FIRST_SCRATCH_REG, FRAME_PTR_REG, INSN_SIZE, SCRATCH_REGS},
31 elf::Executable,
32 error::{EbpfError, ProgramResult},
33 memory_management::{
34 allocate_pages, free_pages, get_system_page_size, protect_pages, round_to_page_size,
35 },
36 memory_region::MemoryMapping,
37 program::BuiltinFunction,
38 vm::{get_runtime_environment_key, Config, ContextObject, EbpfVm, RuntimeEnvironmentSlot},
39 x86::{
40 FenceType, X86IndirectAccess, X86Instruction,
41 X86Register::{self, *},
42 ARGUMENT_REGISTERS, CALLEE_SAVED_REGISTERS, CALLER_SAVED_REGISTERS,
43 },
44};
45
46pub const MAX_EMPTY_PROGRAM_MACHINE_CODE_LENGTH: usize = 4096;
48pub const MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION: usize = 110;
50pub const MACHINE_CODE_PER_INSTRUCTION_METER_CHECKPOINT: usize = 24;
52pub const MAX_START_PADDING_LENGTH: usize = 256;
54
55pub struct JitProgram {
57 page_size: usize,
59 pc_section: &'static mut [u32],
61 text_section: &'static mut [u8],
63}
64
65impl JitProgram {
66 fn new(pc: usize, code_size: usize) -> Result<Self, EbpfError> {
67 let page_size = get_system_page_size();
68 let pc_loc_table_size = round_to_page_size(pc * std::mem::size_of::<u32>(), page_size);
69 let over_allocated_code_size = round_to_page_size(code_size, page_size);
70 unsafe {
71 let raw = allocate_pages(pc_loc_table_size + over_allocated_code_size)?;
72 Ok(Self {
73 page_size,
74 pc_section: std::slice::from_raw_parts_mut(raw.cast::<u32>(), pc),
75 text_section: std::slice::from_raw_parts_mut(
76 raw.add(pc_loc_table_size),
77 over_allocated_code_size,
78 ),
79 })
80 }
81 }
82
83 fn seal(&mut self, text_section_usage: usize) -> Result<(), EbpfError> {
84 if self.page_size == 0 {
85 return Ok(());
86 }
87 let raw = self.pc_section.as_ptr() as *mut u8;
88 let pc_loc_table_size =
89 round_to_page_size(std::mem::size_of_val(self.pc_section), self.page_size);
90 let over_allocated_code_size = round_to_page_size(self.text_section.len(), self.page_size);
91 let code_size = round_to_page_size(text_section_usage, self.page_size);
92 unsafe {
93 std::ptr::write_bytes(
95 raw.add(pc_loc_table_size).add(text_section_usage),
96 0xcc,
97 code_size - text_section_usage,
98 );
99 if over_allocated_code_size > code_size {
100 free_pages(
101 raw.add(pc_loc_table_size).add(code_size),
102 over_allocated_code_size - code_size,
103 )?;
104 }
105 self.text_section =
106 std::slice::from_raw_parts_mut(raw.add(pc_loc_table_size), text_section_usage);
107 protect_pages(
108 self.pc_section.as_mut_ptr().cast::<u8>(),
109 pc_loc_table_size,
110 false,
111 )?;
112 protect_pages(self.text_section.as_mut_ptr(), code_size, true)?;
113 }
114 Ok(())
115 }
116
117 pub(crate) fn invoke<C: ContextObject>(
118 &self,
119 _config: &Config,
120 vm: &mut EbpfVm<C>,
121 registers: [u64; 12],
122 ) {
123 unsafe {
124 let runtime_environment = std::ptr::addr_of_mut!(*vm)
125 .cast::<u64>()
126 .offset(get_runtime_environment_key() as isize);
127 let instruction_meter =
128 (vm.previous_instruction_meter as i64).wrapping_add(registers[11] as i64);
129 let entrypoint = &self.text_section
130 [self.pc_section[registers[11] as usize] as usize & (i32::MAX as u32 as usize)]
131 as *const u8;
132 macro_rules! stmt_expr_attribute_asm {
133 ($($prologue:literal,)+ cfg(not(feature = $feature:literal)), $guarded:tt, $($epilogue:tt)+) => {
134 #[cfg(feature = $feature)]
135 std::arch::asm!($($prologue,)+ $($epilogue)+);
136 #[cfg(not(feature = $feature))]
137 std::arch::asm!($($prologue,)+ $guarded, $($epilogue)+);
138 }
139 }
140 stmt_expr_attribute_asm!(
141 "push rbx",
143 "push rbp",
144 "mov [{host_stack_pointer}], rsp",
145 "add QWORD PTR [{host_stack_pointer}], -8",
146 cfg(not(feature = "jit-enable-host-stack-frames")),
148 "xor rbp, rbp",
149 "mov [rsp-8], rax",
150 "mov rax, [r11 + 0x00]",
151 "mov rsi, [r11 + 0x08]",
152 "mov rdx, [r11 + 0x10]",
153 "mov rcx, [r11 + 0x18]",
154 "mov r8, [r11 + 0x20]",
155 "mov r9, [r11 + 0x28]",
156 "mov rbx, [r11 + 0x30]",
157 "mov r12, [r11 + 0x38]",
158 "mov r13, [r11 + 0x40]",
159 "mov r14, [r11 + 0x48]",
160 "mov r15, [r11 + 0x50]",
161 "mov r11, [r11 + 0x58]",
162 "call [rsp-8]",
163 "pop rbp",
164 "pop rbx",
165 host_stack_pointer = in(reg) &mut vm.host_stack_pointer,
166 inlateout("rdi") runtime_environment => _,
167 inlateout("r10") instruction_meter => _,
168 inlateout("rax") entrypoint => _,
169 inlateout("r11") ®isters => _,
170 lateout("rsi") _, lateout("rdx") _, lateout("rcx") _, lateout("r8") _,
171 lateout("r9") _, lateout("r12") _, lateout("r13") _, lateout("r14") _, lateout("r15") _,
172 );
174 }
175 }
176
177 pub fn machine_code_length(&self) -> usize {
179 self.text_section.len()
180 }
181
182 pub fn mem_size(&self) -> usize {
184 let pc_loc_table_size =
185 round_to_page_size(std::mem::size_of_val(self.pc_section), self.page_size);
186 let code_size = round_to_page_size(self.text_section.len(), self.page_size);
187 pc_loc_table_size + code_size
188 }
189}
190
191impl Drop for JitProgram {
192 fn drop(&mut self) {
193 let pc_loc_table_size =
194 round_to_page_size(std::mem::size_of_val(self.pc_section), self.page_size);
195 let code_size = round_to_page_size(self.text_section.len(), self.page_size);
196 if pc_loc_table_size + code_size > 0 {
197 unsafe {
198 let _ = free_pages(
199 self.pc_section.as_ptr() as *mut u8,
200 pc_loc_table_size + code_size,
201 );
202 }
203 }
204 }
205}
206
207impl Debug for JitProgram {
208 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 fmt.write_fmt(format_args!("JitProgram {:?}", self as *const _))
210 }
211}
212
213impl PartialEq for JitProgram {
214 fn eq(&self, other: &Self) -> bool {
215 std::ptr::eq(self as *const _, other as *const _)
216 }
217}
218
219const ANCHOR_TRACE: usize = 0;
222const ANCHOR_THROW_EXCEEDED_MAX_INSTRUCTIONS: usize = 1;
223const ANCHOR_EPILOGUE: usize = 2;
224const ANCHOR_THROW_EXCEPTION_UNCHECKED: usize = 3;
225const ANCHOR_EXIT: usize = 4;
226const ANCHOR_THROW_EXCEPTION: usize = 5;
227const ANCHOR_CALL_DEPTH_EXCEEDED: usize = 6;
228const ANCHOR_CALL_OUTSIDE_TEXT_SEGMENT: usize = 7;
229const ANCHOR_DIV_BY_ZERO: usize = 8;
230const ANCHOR_DIV_OVERFLOW: usize = 9;
231const ANCHOR_CALL_REG_UNSUPPORTED_INSTRUCTION: usize = 10;
232const ANCHOR_CALL_UNSUPPORTED_INSTRUCTION: usize = 11;
233const ANCHOR_EXTERNAL_FUNCTION_CALL: usize = 12;
234const ANCHOR_INTERNAL_FUNCTION_CALL_PROLOGUE: usize = 13;
235const ANCHOR_INTERNAL_FUNCTION_CALL_REG: usize = 14;
236const ANCHOR_TRANSLATE_MEMORY_ADDRESS: usize = 21;
237const ANCHOR_COUNT: usize = 34; const REGISTER_MAP: [X86Register; 11] = [
240 CALLER_SAVED_REGISTERS[0], ARGUMENT_REGISTERS[1], ARGUMENT_REGISTERS[2], ARGUMENT_REGISTERS[3], ARGUMENT_REGISTERS[4], ARGUMENT_REGISTERS[5], CALLEE_SAVED_REGISTERS[1], CALLEE_SAVED_REGISTERS[2], CALLEE_SAVED_REGISTERS[3], CALLEE_SAVED_REGISTERS[4], CALLEE_SAVED_REGISTERS[5], ];
252
253const REGISTER_PTR_TO_VM: X86Register = ARGUMENT_REGISTERS[0];
255const REGISTER_INSTRUCTION_METER: X86Register = CALLER_SAVED_REGISTERS[7];
257const REGISTER_SCRATCH: X86Register = CALLER_SAVED_REGISTERS[8];
259
260#[derive(Copy, Clone, Debug)]
262pub enum OperandSize {
263 S0 = 0,
265 S8 = 8,
267 S16 = 16,
269 S32 = 32,
271 S64 = 64,
273}
274
275enum Value {
276 Register(X86Register),
277 RegisterIndirect(X86Register, i32, bool),
278 RegisterPlusConstant32(X86Register, i32, bool),
279 RegisterPlusConstant64(X86Register, i64, bool),
280 Constant64(i64, bool),
281}
282
283struct Argument {
284 index: usize,
285 value: Value,
286}
287
288#[derive(Debug)]
289struct Jump {
290 location: *const u8,
291 target_pc: usize,
292}
293
294pub struct JitCompiler<'a, C: ContextObject> {
345 result: JitProgram,
346 text_section_jumps: Vec<Jump>,
347 anchors: [*const u8; ANCHOR_COUNT],
348 offset_in_text_section: usize,
349 executable: &'a Executable<C>,
350 program: &'a [u8],
351 program_vm_addr: u64,
352 config: &'a Config,
353 pc: usize,
354 last_instruction_meter_validation_pc: usize,
355 next_noop_insertion: u32,
356 noop_range: Uniform<u32>,
357 runtime_environment_key: i32,
358 immediate_value_key: i64,
359 diversification_rng: SmallRng,
360 stopwatch_is_active: bool,
361}
362
363#[rustfmt::skip]
364impl<'a, C: ContextObject> JitCompiler<'a, C> {
365 pub fn new(executable: &'a Executable<C>) -> Result<Self, EbpfError> {
367 let config = executable.get_config();
368 let (program_vm_addr, program) = executable.get_text_bytes();
369
370 let mut pc = 0;
372 if !executable.get_sbpf_version().disable_lddw() {
373 while (pc + 1) * ebpf::INSN_SIZE <= program.len() {
374 let insn = ebpf::get_insn_unchecked(program, pc);
375 pc += match insn.opc {
376 ebpf::LD_DW_IMM => 2,
377 _ => 1,
378 };
379 }
380 } else {
381 pc = program.len() / ebpf::INSN_SIZE;
382 }
383
384 let mut code_length_estimate = MAX_EMPTY_PROGRAM_MACHINE_CODE_LENGTH + MAX_START_PADDING_LENGTH + MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION * pc;
385 if config.noop_instruction_rate != 0 {
386 code_length_estimate += code_length_estimate / config.noop_instruction_rate as usize;
387 }
388 if config.instruction_meter_checkpoint_distance != 0 {
389 code_length_estimate += pc / config.instruction_meter_checkpoint_distance * MACHINE_CODE_PER_INSTRUCTION_METER_CHECKPOINT;
390 }
391 debug_assert!(code_length_estimate < (i32::MAX as usize));
393
394 let runtime_environment_key = get_runtime_environment_key();
395 let mut diversification_rng = SmallRng::from_rng(thread_rng()).map_err(|_| EbpfError::JitNotCompiled)?;
396 let immediate_value_key = diversification_rng.gen::<i64>();
397
398 Ok(Self {
399 result: JitProgram::new(pc, code_length_estimate)?,
400 text_section_jumps: vec![],
401 anchors: [std::ptr::null(); ANCHOR_COUNT],
402 offset_in_text_section: 0,
403 executable,
404 program_vm_addr,
405 program,
406 config,
407 pc: 0,
408 last_instruction_meter_validation_pc: 0,
409 next_noop_insertion: if config.noop_instruction_rate == 0 { u32::MAX } else { diversification_rng.gen_range(0..config.noop_instruction_rate * 2) },
410 noop_range: Uniform::new_inclusive(0, config.noop_instruction_rate * 2),
411 runtime_environment_key,
412 immediate_value_key,
413 diversification_rng,
414 stopwatch_is_active: false,
415 })
416 }
417
418 pub fn compile(mut self) -> Result<JitProgram, EbpfError> {
420 if self.config.noop_instruction_rate != 0 {
422 for _ in 0..self.diversification_rng.gen_range(0..MAX_START_PADDING_LENGTH) {
423 self.emit::<u8>(0x90);
425 }
426 }
427
428 self.emit_subroutines();
429
430 let mut function_iter = self.executable.get_function_registry().keys().map(|insn_ptr| insn_ptr as usize).peekable();
431 while self.pc * ebpf::INSN_SIZE < self.program.len() {
432 if self.offset_in_text_section + MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION * 2 >= self.result.text_section.len() {
433 return Err(EbpfError::ExhaustedTextSegment(self.pc));
434 }
435 let mut insn = ebpf::get_insn_unchecked(self.program, self.pc);
436 self.result.pc_section[self.pc] = self.offset_in_text_section as u32;
437 if self.executable.get_sbpf_version().static_syscalls() {
438 if function_iter.peek() == Some(&self.pc) {
439 function_iter.next();
440 } else {
441 self.result.pc_section[self.pc] |= 1 << 31;
442 }
443 }
444
445 if self.last_instruction_meter_validation_pc + self.config.instruction_meter_checkpoint_distance <= self.pc {
447 self.emit_validate_instruction_count(Some(self.pc));
448 }
449
450 if self.config.enable_instruction_tracing {
451 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.pc as i64));
452 self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_TRACE, 5)));
453 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, 0));
454 }
455
456 let dst = REGISTER_MAP[insn.dst as usize];
457 let src = REGISTER_MAP[insn.src as usize];
458 let target_pc = (self.pc as isize + insn.off as isize + 1) as usize;
459
460 match insn.opc {
461 ebpf::LD_DW_IMM if !self.executable.get_sbpf_version().disable_lddw() => {
462 self.emit_validate_and_profile_instruction_count(Some(self.pc + 2));
463 self.pc += 1;
464 self.result.pc_section[self.pc] = unsafe { self.anchors[ANCHOR_CALL_UNSUPPORTED_INSTRUCTION].offset_from(self.result.text_section.as_ptr()) as u32 };
465 ebpf::augment_lddw_unchecked(self.program, &mut insn);
466 if self.should_sanitize_constant(insn.imm) {
467 self.emit_sanitized_load_immediate(dst, insn.imm);
468 } else {
469 self.emit_ins(X86Instruction::load_immediate(dst, insn.imm));
470 }
471 },
472
473 ebpf::LD_B_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
475 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 1, None);
476 },
477 ebpf::LD_H_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
478 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 2, None);
479 },
480 ebpf::LD_W_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
481 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 4, None);
482 },
483 ebpf::LD_DW_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
484 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 8, None);
485 },
486
487 ebpf::ST_B_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
489 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 1, Some(Value::Constant64(insn.imm, true)));
490 },
491 ebpf::ST_H_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
492 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 2, Some(Value::Constant64(insn.imm, true)));
493 },
494 ebpf::ST_W_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
495 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 4, Some(Value::Constant64(insn.imm, true)));
496 },
497 ebpf::ST_DW_IMM if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
498 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 8, Some(Value::Constant64(insn.imm, true)));
499 },
500
501 ebpf::ST_B_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
503 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 1, Some(Value::Register(src)));
504 },
505 ebpf::ST_H_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
506 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 2, Some(Value::Register(src)));
507 },
508 ebpf::ST_W_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
509 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 4, Some(Value::Register(src)));
510 },
511 ebpf::ST_DW_REG if !self.executable.get_sbpf_version().move_memory_instruction_classes() => {
512 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 8, Some(Value::Register(src)));
513 },
514
515 ebpf::ADD32_IMM => {
517 self.emit_sanitized_alu(OperandSize::S32, 0x01, 0, dst, insn.imm);
518 if !self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
519 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x63, dst, dst, None)); }
521 },
522 ebpf::ADD32_REG => {
523 self.emit_ins(X86Instruction::alu(OperandSize::S32, 0x01, src, dst, None));
524 if !self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
525 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x63, dst, dst, None)); }
527 },
528 ebpf::SUB32_IMM => {
529 if self.executable.get_sbpf_version().swap_sub_reg_imm_operands() {
530 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S32, 0xf7, 3, dst, 0, None));
531 if insn.imm != 0 {
532 self.emit_sanitized_alu(OperandSize::S32, 0x01, 0, dst, insn.imm);
533 }
534 } else {
535 self.emit_sanitized_alu(OperandSize::S32, 0x29, 5, dst, insn.imm);
536 }
537 if !self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
538 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x63, dst, dst, None)); }
540 },
541 ebpf::SUB32_REG => {
542 self.emit_ins(X86Instruction::alu(OperandSize::S32, 0x29, src, dst, None));
543 if !self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
544 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x63, dst, dst, None)); }
546 },
547 ebpf::MUL32_IMM | ebpf::DIV32_IMM | ebpf::MOD32_IMM if !self.executable.get_sbpf_version().enable_pqr() =>
548 self.emit_product_quotient_remainder(
549 OperandSize::S32,
550 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MOD,
551 (insn.opc & ebpf::BPF_ALU_OP_MASK) != ebpf::BPF_MUL,
552 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MUL,
553 dst, dst, Some(insn.imm),
554 ),
555 ebpf::LD_1B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
556 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 1, None);
557 },
558 ebpf::MUL32_REG | ebpf::DIV32_REG | ebpf::MOD32_REG if !self.executable.get_sbpf_version().enable_pqr() =>
559 self.emit_product_quotient_remainder(
560 OperandSize::S32,
561 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MOD,
562 (insn.opc & ebpf::BPF_ALU_OP_MASK) != ebpf::BPF_MUL,
563 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MUL,
564 src, dst, None,
565 ),
566 ebpf::LD_2B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
567 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 2, None);
568 },
569 ebpf::OR32_IMM => self.emit_sanitized_alu(OperandSize::S32, 0x09, 1, dst, insn.imm),
570 ebpf::OR32_REG => self.emit_ins(X86Instruction::alu(OperandSize::S32, 0x09, src, dst, None)),
571 ebpf::AND32_IMM => self.emit_sanitized_alu(OperandSize::S32, 0x21, 4, dst, insn.imm),
572 ebpf::AND32_REG => self.emit_ins(X86Instruction::alu(OperandSize::S32, 0x21, src, dst, None)),
573 ebpf::LSH32_IMM => self.emit_shift(OperandSize::S32, 4, REGISTER_SCRATCH, dst, Some(insn.imm)),
574 ebpf::LSH32_REG => self.emit_shift(OperandSize::S32, 4, src, dst, None),
575 ebpf::RSH32_IMM => self.emit_shift(OperandSize::S32, 5, REGISTER_SCRATCH, dst, Some(insn.imm)),
576 ebpf::RSH32_REG => self.emit_shift(OperandSize::S32, 5, src, dst, None),
577 ebpf::NEG32 if !self.executable.get_sbpf_version().disable_neg() => self.emit_ins(X86Instruction::alu_immediate(OperandSize::S32, 0xf7, 3, dst, 0, None)),
578 ebpf::LD_4B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
579 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 4, None);
580 },
581 ebpf::LD_8B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
582 self.emit_address_translation(Some(dst), Value::RegisterPlusConstant64(src, insn.off as i64, true), 8, None);
583 },
584 ebpf::XOR32_IMM => self.emit_sanitized_alu(OperandSize::S32, 0x31, 6, dst, insn.imm),
585 ebpf::XOR32_REG => self.emit_ins(X86Instruction::alu(OperandSize::S32, 0x31, src, dst, None)),
586 ebpf::MOV32_IMM => {
587 if self.should_sanitize_constant(insn.imm) {
588 self.emit_sanitized_load_immediate(dst, insn.imm as u32 as u64 as i64);
589 } else {
590 self.emit_ins(X86Instruction::load_immediate(dst, insn.imm as u32 as u64 as i64));
591 }
592 }
593 ebpf::MOV32_REG => {
594 if self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
595 self.emit_ins(X86Instruction::mov_with_sign_extension(OperandSize::S64, src, dst));
596 } else {
597 self.emit_ins(X86Instruction::mov(OperandSize::S32, src, dst));
598 }
599 }
600 ebpf::ARSH32_IMM => self.emit_shift(OperandSize::S32, 7, REGISTER_SCRATCH, dst, Some(insn.imm)),
601 ebpf::ARSH32_REG => self.emit_shift(OperandSize::S32, 7, src, dst, None),
602 ebpf::LE if !self.executable.get_sbpf_version().disable_le() => {
603 match insn.imm {
604 16 => {
605 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S32, 0x81, 4, dst, 0xffff, None)); }
607 32 => {
608 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S32, 0x81, 4, dst, -1, None)); }
610 64 => {}
611 _ => {
612 return Err(EbpfError::InvalidInstruction);
613 }
614 }
615 },
616 ebpf::BE => {
617 match insn.imm {
618 16 => {
619 self.emit_ins(X86Instruction::bswap(OperandSize::S16, dst));
620 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S32, 0x81, 4, dst, 0xffff, None)); }
622 32 => self.emit_ins(X86Instruction::bswap(OperandSize::S32, dst)),
623 64 => self.emit_ins(X86Instruction::bswap(OperandSize::S64, dst)),
624 _ => {
625 return Err(EbpfError::InvalidInstruction);
626 }
627 }
628 },
629
630 ebpf::ADD64_IMM => self.emit_sanitized_alu(OperandSize::S64, 0x01, 0, dst, insn.imm),
632 ebpf::ADD64_REG => self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, src, dst, None)),
633 ebpf::SUB64_IMM => {
634 if self.executable.get_sbpf_version().swap_sub_reg_imm_operands() {
635 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xf7, 3, dst, 0, None));
636 if insn.imm != 0 {
637 self.emit_sanitized_alu(OperandSize::S64, 0x01, 0, dst, insn.imm);
638 }
639 } else {
640 self.emit_sanitized_alu(OperandSize::S64, 0x29, 5, dst, insn.imm);
641 }
642 }
643 ebpf::SUB64_REG => self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x29, src, dst, None)),
644 ebpf::MUL64_IMM | ebpf::DIV64_IMM | ebpf::MOD64_IMM if !self.executable.get_sbpf_version().enable_pqr() =>
645 self.emit_product_quotient_remainder(
646 OperandSize::S64,
647 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MOD,
648 (insn.opc & ebpf::BPF_ALU_OP_MASK) != ebpf::BPF_MUL,
649 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MUL,
650 dst, dst, Some(insn.imm),
651 ),
652 ebpf::ST_1B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
653 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 1, Some(Value::Constant64(insn.imm, true)));
654 },
655 ebpf::ST_2B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
656 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 2, Some(Value::Constant64(insn.imm, true)));
657 },
658 ebpf::MUL64_REG | ebpf::DIV64_REG | ebpf::MOD64_REG if !self.executable.get_sbpf_version().enable_pqr() =>
659 self.emit_product_quotient_remainder(
660 OperandSize::S64,
661 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MOD,
662 (insn.opc & ebpf::BPF_ALU_OP_MASK) != ebpf::BPF_MUL,
663 (insn.opc & ebpf::BPF_ALU_OP_MASK) == ebpf::BPF_MUL,
664 src, dst, None,
665 ),
666 ebpf::ST_1B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
667 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 1, Some(Value::Register(src)));
668 },
669 ebpf::ST_2B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
670 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 2, Some(Value::Register(src)));
671 },
672 ebpf::OR64_IMM => self.emit_sanitized_alu(OperandSize::S64, 0x09, 1, dst, insn.imm),
673 ebpf::OR64_REG => self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x09, src, dst, None)),
674 ebpf::AND64_IMM => self.emit_sanitized_alu(OperandSize::S64, 0x21, 4, dst, insn.imm),
675 ebpf::AND64_REG => self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x21, src, dst, None)),
676 ebpf::LSH64_IMM => self.emit_shift(OperandSize::S64, 4, REGISTER_SCRATCH, dst, Some(insn.imm)),
677 ebpf::LSH64_REG => self.emit_shift(OperandSize::S64, 4, src, dst, None),
678 ebpf::RSH64_IMM => self.emit_shift(OperandSize::S64, 5, REGISTER_SCRATCH, dst, Some(insn.imm)),
679 ebpf::RSH64_REG => self.emit_shift(OperandSize::S64, 5, src, dst, None),
680 ebpf::ST_4B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
681 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 4, Some(Value::Constant64(insn.imm, true)));
682 },
683 ebpf::NEG64 if !self.executable.get_sbpf_version().disable_neg() => self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xf7, 3, dst, 0, None)),
684 ebpf::ST_4B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
685 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 4, Some(Value::Register(src)));
686 },
687 ebpf::ST_8B_IMM if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
688 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 8, Some(Value::Constant64(insn.imm, true)));
689 },
690 ebpf::ST_8B_REG if self.executable.get_sbpf_version().move_memory_instruction_classes() => {
691 self.emit_address_translation(None, Value::RegisterPlusConstant64(dst, insn.off as i64, true), 8, Some(Value::Register(src)));
692 },
693 ebpf::XOR64_IMM => self.emit_sanitized_alu(OperandSize::S64, 0x31, 6, dst, insn.imm),
694 ebpf::XOR64_REG => self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x31, src, dst, None)),
695 ebpf::MOV64_IMM => {
696 if self.should_sanitize_constant(insn.imm) {
697 self.emit_sanitized_load_immediate(dst, insn.imm);
698 } else {
699 self.emit_ins(X86Instruction::load_immediate(dst, insn.imm));
700 }
701 }
702 ebpf::MOV64_REG => self.emit_ins(X86Instruction::mov(OperandSize::S64, src, dst)),
703 ebpf::ARSH64_IMM => self.emit_shift(OperandSize::S64, 7, REGISTER_SCRATCH, dst, Some(insn.imm)),
704 ebpf::ARSH64_REG => self.emit_shift(OperandSize::S64, 7, src, dst, None),
705 ebpf::HOR64_IMM if self.executable.get_sbpf_version().disable_lddw() => {
706 self.emit_sanitized_alu(OperandSize::S64, 0x09, 1, dst, (insn.imm as u64).wrapping_shl(32) as i64);
707 }
708
709 ebpf::LMUL32_IMM | ebpf::LMUL64_IMM | ebpf::UHMUL64_IMM | ebpf::SHMUL64_IMM |
711 ebpf::UDIV32_IMM | ebpf::UDIV64_IMM | ebpf::UREM32_IMM | ebpf::UREM64_IMM |
712 ebpf::SDIV32_IMM | ebpf::SDIV64_IMM | ebpf::SREM32_IMM | ebpf::SREM64_IMM
713 if self.executable.get_sbpf_version().enable_pqr() => {
714 let signed = insn.opc & (1 << 7) != 0;
715 let mut imm = insn.imm;
716 if !signed {
717 imm &= u32::MAX as i64;
718 }
719 self.emit_product_quotient_remainder(
720 if insn.opc & (1 << 4) != 0 { OperandSize::S64 } else { OperandSize::S32 },
721 insn.opc & (1 << 5) != 0,
722 insn.opc & (1 << 6) != 0,
723 signed,
724 dst, dst, Some(imm),
725 )
726 }
727 ebpf::LMUL32_REG | ebpf::LMUL64_REG | ebpf::UHMUL64_REG | ebpf::SHMUL64_REG |
728 ebpf::UDIV32_REG | ebpf::UDIV64_REG | ebpf::UREM32_REG | ebpf::UREM64_REG |
729 ebpf::SDIV32_REG | ebpf::SDIV64_REG | ebpf::SREM32_REG | ebpf::SREM64_REG
730 if self.executable.get_sbpf_version().enable_pqr() =>
731 self.emit_product_quotient_remainder(
732 if insn.opc & (1 << 4) != 0 { OperandSize::S64 } else { OperandSize::S32 },
733 insn.opc & (1 << 5) != 0,
734 insn.opc & (1 << 6) != 0,
735 insn.opc & (1 << 7) != 0,
736 src, dst, None,
737 ),
738
739 ebpf::JA => {
741 self.emit_validate_and_profile_instruction_count(Some(target_pc));
742 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, target_pc as i64));
743 let jump_offset = self.relative_to_target_pc(target_pc, 5);
744 self.emit_ins(X86Instruction::jump_immediate(jump_offset));
745 },
746 ebpf::JEQ_IMM => self.emit_conditional_branch_imm(0x84, false, insn.imm, dst, target_pc),
747 ebpf::JEQ_REG => self.emit_conditional_branch_reg(0x84, false, src, dst, target_pc),
748 ebpf::JGT_IMM => self.emit_conditional_branch_imm(0x87, false, insn.imm, dst, target_pc),
749 ebpf::JGT_REG => self.emit_conditional_branch_reg(0x87, false, src, dst, target_pc),
750 ebpf::JGE_IMM => self.emit_conditional_branch_imm(0x83, false, insn.imm, dst, target_pc),
751 ebpf::JGE_REG => self.emit_conditional_branch_reg(0x83, false, src, dst, target_pc),
752 ebpf::JLT_IMM => self.emit_conditional_branch_imm(0x82, false, insn.imm, dst, target_pc),
753 ebpf::JLT_REG => self.emit_conditional_branch_reg(0x82, false, src, dst, target_pc),
754 ebpf::JLE_IMM => self.emit_conditional_branch_imm(0x86, false, insn.imm, dst, target_pc),
755 ebpf::JLE_REG => self.emit_conditional_branch_reg(0x86, false, src, dst, target_pc),
756 ebpf::JSET_IMM => self.emit_conditional_branch_imm(0x85, true, insn.imm, dst, target_pc),
757 ebpf::JSET_REG => self.emit_conditional_branch_reg(0x85, true, src, dst, target_pc),
758 ebpf::JNE_IMM => self.emit_conditional_branch_imm(0x85, false, insn.imm, dst, target_pc),
759 ebpf::JNE_REG => self.emit_conditional_branch_reg(0x85, false, src, dst, target_pc),
760 ebpf::JSGT_IMM => self.emit_conditional_branch_imm(0x8f, false, insn.imm, dst, target_pc),
761 ebpf::JSGT_REG => self.emit_conditional_branch_reg(0x8f, false, src, dst, target_pc),
762 ebpf::JSGE_IMM => self.emit_conditional_branch_imm(0x8d, false, insn.imm, dst, target_pc),
763 ebpf::JSGE_REG => self.emit_conditional_branch_reg(0x8d, false, src, dst, target_pc),
764 ebpf::JSLT_IMM => self.emit_conditional_branch_imm(0x8c, false, insn.imm, dst, target_pc),
765 ebpf::JSLT_REG => self.emit_conditional_branch_reg(0x8c, false, src, dst, target_pc),
766 ebpf::JSLE_IMM => self.emit_conditional_branch_imm(0x8e, false, insn.imm, dst, target_pc),
767 ebpf::JSLE_REG => self.emit_conditional_branch_reg(0x8e, false, src, dst, target_pc),
768 ebpf::CALL_IMM => {
769 if let (false, Some((_, function))) =
771 (self.executable.get_sbpf_version().static_syscalls(),
772 self.executable.get_loader().get_function_registry().lookup_by_key(insn.imm as u32)) {
773 self.emit_syscall_dispatch(function);
775 } else if let Some((_function_name, target_pc)) =
776 self.executable
777 .get_function_registry()
778 .lookup_by_key(
779 self
780 .executable
781 .get_sbpf_version()
782 .calculate_call_imm_target_pc(self.pc, insn.imm)
783 ) {
784 self.emit_internal_call(Value::Constant64(target_pc as i64, true));
786 } else {
787 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.pc as i64));
788 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_CALL_UNSUPPORTED_INSTRUCTION, 5)));
789 }
790 },
791 ebpf::SYSCALL if self.executable.get_sbpf_version().static_syscalls() => {
792 if let Some((_, function)) = self.executable.get_loader().get_function_registry().lookup_by_key(insn.imm as u32) {
793 self.emit_syscall_dispatch(function);
794 } else {
795 debug_assert!(false, "Invalid syscall should have been detected in the verifier.")
796 }
797 },
798 ebpf::CALL_REG => {
799 let target_pc = if self.executable.get_sbpf_version().callx_uses_src_reg() {
800 src
801 } else {
802 REGISTER_MAP[insn.imm as usize]
803 };
804 self.emit_internal_call(Value::Register(target_pc));
805 },
806 ebpf::RETURN
807 | ebpf::EXIT => {
808 if (insn.opc == ebpf::EXIT && self.executable.get_sbpf_version().static_syscalls())
809 || (insn.opc == ebpf::RETURN && !self.executable.get_sbpf_version().static_syscalls()) {
810 return Err(EbpfError::UnsupportedInstruction);
811 }
812 self.emit_validate_and_profile_instruction_count(Some(0));
813
814 let call_depth_access = X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::CallDepth));
815 self.emit_ins(X86Instruction::cmp_immediate(OperandSize::S32, REGISTER_PTR_TO_VM, 0, Some(call_depth_access)));
817 self.emit_ins(X86Instruction::conditional_jump_immediate(0x84, self.relative_to_anchor(ANCHOR_EXIT, 6)));
819
820 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 5, REGISTER_PTR_TO_VM, 1, Some(call_depth_access))); self.emit_ins(X86Instruction::return_near());
825 },
826
827 _ => return Err(EbpfError::UnsupportedInstruction),
828 }
829
830 self.pc += 1;
831 }
832
833 if self.offset_in_text_section + MAX_MACHINE_CODE_LENGTH_PER_INSTRUCTION * 2 >= self.result.text_section.len() {
835 return Err(EbpfError::ExhaustedTextSegment(self.pc));
836 }
837 self.emit_validate_and_profile_instruction_count(Some(self.pc + 1));
838 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.pc as i64)); self.emit_set_exception_kind(EbpfError::ExecutionOverrun);
840 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));
841
842 self.resolve_jumps();
843 self.result.seal(self.offset_in_text_section)?;
844 Ok(self.result)
845 }
846
847 fn should_sanitize_constant(&self, value: i64) -> bool {
848 if !self.config.sanitize_user_provided_values {
849 return false;
850 }
851
852 match value as u64 {
853 0xFFFF
854 | 0xFFFFFF
855 | 0xFFFFFFFF
856 | 0xFFFFFFFFFF
857 | 0xFFFFFFFFFFFF
858 | 0xFFFFFFFFFFFFFF
859 | 0xFFFFFFFFFFFFFFFF => false,
860 v if v <= 0xFF => false,
861 v if !v <= 0xFF => false,
862 _ => true
863 }
864 }
865
866 fn slot_in_vm(&self, slot: RuntimeEnvironmentSlot) -> i32 {
867 8 * (slot as i32 - self.runtime_environment_key)
868 }
869
870 pub(crate) fn emit<T>(&mut self, data: T) {
871 unsafe {
872 let ptr = self.result.text_section.as_ptr().add(self.offset_in_text_section);
873 #[allow(clippy::cast_ptr_alignment)]
874 ptr::write_unaligned(ptr as *mut T, data as T);
875 }
876 self.offset_in_text_section += mem::size_of::<T>();
877 }
878
879 pub(crate) fn emit_variable_length(&mut self, size: OperandSize, data: u64) {
880 match size {
881 OperandSize::S0 => {},
882 OperandSize::S8 => self.emit::<u8>(data as u8),
883 OperandSize::S16 => self.emit::<u16>(data as u16),
884 OperandSize::S32 => self.emit::<u32>(data as u32),
885 OperandSize::S64 => self.emit::<u64>(data),
886 }
887 }
888
889 #[inline(always)]
891 fn emit_ins(&mut self, instruction: X86Instruction) {
892 instruction.emit(self);
893 if self.next_noop_insertion == 0 {
894 self.next_noop_insertion = self.noop_range.sample(&mut self.diversification_rng);
895 self.emit::<u8>(0x90);
897 } else {
898 self.next_noop_insertion -= 1;
899 }
900 }
901
902 fn emit_sanitized_load_immediate(&mut self, destination: X86Register, value: i64) {
903 let lower_key = self.immediate_value_key as i32 as i64;
904 if value >= i32::MIN as i64 && value <= i32::MAX as i64 {
905 self.emit_ins(X86Instruction::load_immediate(destination, value.wrapping_sub(lower_key)));
906 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, destination, lower_key, None)); } else if value as u64 & u32::MAX as u64 == 0 {
908 self.emit_ins(X86Instruction::load_immediate(destination, value.rotate_right(32).wrapping_sub(lower_key)));
909 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, destination, lower_key, None)); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xc1, 4, destination, 32, None)); } else if destination != REGISTER_SCRATCH {
912 self.emit_ins(X86Instruction::load_immediate(destination, value.wrapping_sub(self.immediate_value_key)));
913 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.immediate_value_key));
914 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, REGISTER_SCRATCH, destination, None)); } else {
916 let upper_key = (self.immediate_value_key >> 32) as i32 as i64;
917 self.emit_ins(X86Instruction::load_immediate(destination, value.wrapping_sub(lower_key).rotate_right(32).wrapping_sub(upper_key)));
918 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, destination, upper_key, None)); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xc1, 1, destination, 32, None)); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, destination, lower_key, None)); }
922 }
923
924 fn emit_sanitized_alu(&mut self, size: OperandSize, opcode: u8, opcode_extension: u8, destination: X86Register, immediate: i64) {
925 if self.should_sanitize_constant(immediate) {
926 self.emit_sanitized_load_immediate(REGISTER_SCRATCH, immediate);
927 self.emit_ins(X86Instruction::alu(size, opcode, REGISTER_SCRATCH, destination, None));
928 } else if immediate >= i32::MIN as i64 && immediate <= i32::MAX as i64 {
929 self.emit_ins(X86Instruction::alu_immediate(size, 0x81, opcode_extension, destination, immediate, None));
930 } else {
931 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, immediate));
932 self.emit_ins(X86Instruction::alu(size, opcode, REGISTER_SCRATCH, destination, None));
933 }
934 }
935
936 #[allow(dead_code)]
937 fn emit_stopwatch(&mut self, begin: bool) {
938 self.stopwatch_is_active = true;
939 self.emit_ins(X86Instruction::push(RDX, None));
940 self.emit_ins(X86Instruction::push(RAX, None));
941 self.emit_ins(X86Instruction::fence(FenceType::Load)); self.emit_ins(X86Instruction::cycle_count()); self.emit_ins(X86Instruction::fence(FenceType::Load)); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xc1, 4, RDX, 32, None)); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x09, RDX, RAX, None)); if begin {
947 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x29, RAX, REGISTER_PTR_TO_VM, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::StopwatchNumerator))))); } else {
949 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, RAX, REGISTER_PTR_TO_VM, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::StopwatchNumerator))))); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, REGISTER_PTR_TO_VM, 1, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::StopwatchDenominator))))); }
952 self.emit_ins(X86Instruction::pop(RAX));
953 self.emit_ins(X86Instruction::pop(RDX));
954 }
955
956 fn emit_validate_instruction_count(&mut self, pc: Option<usize>) {
957 if !self.config.enable_instruction_meter {
958 return;
959 }
960 if let Some(pc) = pc {
962 self.last_instruction_meter_validation_pc = pc;
963 self.emit_sanitized_load_immediate(REGISTER_SCRATCH, pc as i64);
964 }
965 self.emit_ins(X86Instruction::cmp(OperandSize::S64, REGISTER_SCRATCH, REGISTER_INSTRUCTION_METER, None));
967 self.emit_ins(X86Instruction::conditional_jump_immediate(0x86, self.relative_to_anchor(ANCHOR_THROW_EXCEEDED_MAX_INSTRUCTIONS, 6)));
968 }
969
970 fn emit_profile_instruction_count(&mut self, target_pc: Option<usize>) {
971 if !self.config.enable_instruction_meter {
972 return;
973 }
974 match target_pc {
975 Some(target_pc) => {
976 self.emit_sanitized_alu(OperandSize::S64, 0x01, 0, REGISTER_INSTRUCTION_METER, target_pc as i64 - self.pc as i64 - 1); },
978 None => {
979 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, REGISTER_SCRATCH, REGISTER_INSTRUCTION_METER, None)); self.emit_sanitized_alu(OperandSize::S64, 0x81, 5, REGISTER_INSTRUCTION_METER, self.pc as i64 + 1); },
982 }
983 }
984
985 fn emit_undo_profile_instruction_count(&mut self, target_pc: usize) {
986 if self.config.enable_instruction_meter {
987 self.emit_sanitized_alu(OperandSize::S64, 0x01, 0, REGISTER_INSTRUCTION_METER, self.pc as i64 + 1 - target_pc as i64); }
989 }
990
991 fn emit_validate_and_profile_instruction_count(&mut self, target_pc: Option<usize>) {
992 self.emit_validate_instruction_count(Some(self.pc));
993 self.emit_profile_instruction_count(target_pc);
994 }
995
996 fn emit_rust_call(&mut self, target: Value, arguments: &[Argument], result_reg: Option<X86Register>) {
997 let mut saved_registers = CALLER_SAVED_REGISTERS.to_vec();
998 if let Some(reg) = result_reg {
999 if let Some(dst) = saved_registers.iter().position(|x| *x == reg) {
1000 saved_registers.remove(dst);
1001 }
1002 }
1003
1004 for reg in saved_registers.iter() {
1006 self.emit_ins(X86Instruction::push(*reg, None));
1007 }
1008
1009 let stack_arguments = arguments.len().saturating_sub(ARGUMENT_REGISTERS.len()) as i64;
1010 if stack_arguments % 2 != 0 {
1011 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 5, RSP, 8, None));
1014 }
1015
1016 for argument in arguments {
1018 let is_stack_argument = argument.index >= ARGUMENT_REGISTERS.len();
1019 let dst = if is_stack_argument {
1020 RSP } else {
1022 ARGUMENT_REGISTERS[argument.index]
1023 };
1024 match argument.value {
1025 Value::Register(reg) => {
1026 if is_stack_argument {
1027 self.emit_ins(X86Instruction::push(reg, None));
1028 } else if reg != dst {
1029 self.emit_ins(X86Instruction::mov(OperandSize::S64, reg, dst));
1030 }
1031 },
1032 Value::RegisterIndirect(reg, offset, user_provided) => {
1033 debug_assert!(!user_provided);
1034 if is_stack_argument {
1035 debug_assert!(reg != RSP);
1036 self.emit_ins(X86Instruction::push(reg, Some(X86IndirectAccess::Offset(offset))));
1037 } else if reg == RSP {
1038 self.emit_ins(X86Instruction::load(OperandSize::S64, RSP, dst, X86IndirectAccess::OffsetIndexShift(offset, RSP, 0)));
1039 } else {
1040 self.emit_ins(X86Instruction::load(OperandSize::S64, reg, dst, X86IndirectAccess::Offset(offset)));
1041 }
1042 },
1043 Value::RegisterPlusConstant32(reg, offset, user_provided) => {
1044 debug_assert!(!user_provided);
1045 if is_stack_argument {
1046 self.emit_ins(X86Instruction::push(reg, None));
1047 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP, offset as i64, Some(X86IndirectAccess::OffsetIndexShift(0, RSP, 0))));
1048 } else if reg == RSP {
1049 self.emit_ins(X86Instruction::lea(OperandSize::S64, RSP, dst, Some(X86IndirectAccess::OffsetIndexShift(offset, RSP, 0))));
1050 } else {
1051 self.emit_ins(X86Instruction::lea(OperandSize::S64, reg, dst, Some(X86IndirectAccess::Offset(offset))));
1052 }
1053 },
1054 Value::RegisterPlusConstant64(reg, offset, user_provided) => {
1055 debug_assert!(!user_provided);
1056 if is_stack_argument {
1057 self.emit_ins(X86Instruction::push(reg, None));
1058 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP, offset, Some(X86IndirectAccess::OffsetIndexShift(0, RSP, 0))));
1059 } else {
1060 self.emit_ins(X86Instruction::load_immediate(dst, offset));
1061 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, reg, dst, None));
1062 }
1063 },
1064 Value::Constant64(value, user_provided) => {
1065 debug_assert!(!user_provided && !is_stack_argument);
1066 self.emit_ins(X86Instruction::load_immediate(dst, value));
1067 },
1068 }
1069 }
1070
1071 match target {
1072 Value::Register(reg) => {
1073 self.emit_ins(X86Instruction::call_reg(reg, None));
1074 },
1075 Value::Constant64(value, user_provided) => {
1076 debug_assert!(!user_provided);
1077 self.emit_ins(X86Instruction::load_immediate(RAX, value));
1078 self.emit_ins(X86Instruction::call_reg(RAX, None));
1079 },
1080 _ => {
1081 #[cfg(debug_assertions)]
1082 unreachable!();
1083 }
1084 }
1085
1086 if let Some(reg) = result_reg {
1088 self.emit_ins(X86Instruction::mov(OperandSize::S64, RAX, reg));
1089 }
1090
1091 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP,
1093 if stack_arguments % 2 != 0 { stack_arguments + 1 } else { stack_arguments } * 8, None));
1094
1095 for reg in saved_registers.iter().rev() {
1096 self.emit_ins(X86Instruction::pop(*reg));
1097 }
1098 }
1099
1100 fn emit_internal_call(&mut self, dst: Value) {
1101 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.pc as i64));
1103 self.last_instruction_meter_validation_pc = self.pc;
1104 self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_INTERNAL_FUNCTION_CALL_PROLOGUE, 5)));
1105
1106 match dst {
1107 Value::Register(reg) => {
1108 self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_SCRATCH, RSP, X86IndirectAccess::OffsetIndexShift(-24, RSP, 0)));
1111 self.emit_ins(X86Instruction::mov(OperandSize::S64, reg, REGISTER_SCRATCH));
1113 self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_INTERNAL_FUNCTION_CALL_REG, 5)));
1114 },
1115 Value::Constant64(target_pc, user_provided) => {
1116 debug_assert!(user_provided);
1117 self.emit_profile_instruction_count(Some(target_pc as usize));
1118 if user_provided && self.should_sanitize_constant(target_pc) {
1119 self.emit_sanitized_load_immediate(REGISTER_SCRATCH, target_pc);
1120 } else {
1121 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, target_pc));
1122 }
1123 let jump_offset = self.relative_to_target_pc(target_pc as usize, 5);
1124 self.emit_ins(X86Instruction::call_immediate(jump_offset));
1125 },
1126 _ => {
1127 #[cfg(debug_assertions)]
1128 unreachable!();
1129 }
1130 }
1131
1132 self.emit_undo_profile_instruction_count(0);
1133
1134 self.emit_ins(X86Instruction::pop(REGISTER_MAP[FRAME_PTR_REG]));
1136 for reg in REGISTER_MAP.iter().skip(FIRST_SCRATCH_REG).take(SCRATCH_REGS).rev() {
1137 self.emit_ins(X86Instruction::pop(*reg));
1138 }
1139 }
1140
1141 fn emit_syscall_dispatch(&mut self, function: BuiltinFunction<C>) {
1142 self.emit_validate_and_profile_instruction_count(Some(0));
1143 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, function as usize as i64));
1144 self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL, 5)));
1145 self.emit_undo_profile_instruction_count(0);
1146 }
1147
1148 fn emit_address_translation(&mut self, dst: Option<X86Register>, vm_addr: Value, len: u64, value: Option<Value>) {
1149 debug_assert_ne!(dst.is_some(), value.is_some());
1150
1151 let stack_slot_of_value_to_store = X86IndirectAccess::OffsetIndexShift(-96, RSP, 0);
1152 match value {
1153 Some(Value::Register(reg)) => {
1154 self.emit_ins(X86Instruction::store(OperandSize::S64, reg, RSP, stack_slot_of_value_to_store));
1155 }
1156 Some(Value::Constant64(constant, user_provided)) => {
1157 debug_assert!(user_provided);
1158 let lower_key = self.immediate_value_key as i32 as i64;
1160 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, constant.wrapping_sub(lower_key)));
1161 self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_SCRATCH, RSP, stack_slot_of_value_to_store));
1162 }
1163 _ => {}
1164 }
1165
1166 match vm_addr {
1167 Value::RegisterPlusConstant64(reg, constant, user_provided) => {
1168 if user_provided && self.should_sanitize_constant(constant) {
1169 self.emit_sanitized_load_immediate(REGISTER_SCRATCH, constant);
1170 } else {
1171 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, constant));
1172 }
1173 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, reg, REGISTER_SCRATCH, None));
1174 },
1175 _ => {
1176 #[cfg(debug_assertions)]
1177 unreachable!();
1178 },
1179 }
1180
1181 if self.config.enable_address_translation {
1182 let anchor_base = match value {
1183 Some(Value::Register(_reg)) => 4,
1184 Some(Value::Constant64(_constant, _user_provided)) => 8,
1185 _ => 0,
1186 };
1187 let anchor = ANCHOR_TRANSLATE_MEMORY_ADDRESS + anchor_base + len.trailing_zeros() as usize;
1188 self.emit_ins(X86Instruction::push_immediate(OperandSize::S64, self.pc as i32));
1189 self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(anchor, 5)));
1190 if let Some(dst) = dst {
1191 self.emit_ins(X86Instruction::mov(OperandSize::S64, REGISTER_SCRATCH, dst));
1192 }
1193 } else if let Some(dst) = dst {
1194 match len {
1195 1 => self.emit_ins(X86Instruction::load(OperandSize::S8, REGISTER_SCRATCH, dst, X86IndirectAccess::Offset(0))),
1196 2 => self.emit_ins(X86Instruction::load(OperandSize::S16, REGISTER_SCRATCH, dst, X86IndirectAccess::Offset(0))),
1197 4 => self.emit_ins(X86Instruction::load(OperandSize::S32, REGISTER_SCRATCH, dst, X86IndirectAccess::Offset(0))),
1198 8 => self.emit_ins(X86Instruction::load(OperandSize::S64, REGISTER_SCRATCH, dst, X86IndirectAccess::Offset(0))),
1199 _ => unreachable!(),
1200 }
1201 } else {
1202 self.emit_ins(X86Instruction::xchg(OperandSize::S64, RSP, REGISTER_MAP[0], Some(stack_slot_of_value_to_store))); match len {
1204 1 => self.emit_ins(X86Instruction::store(OperandSize::S8, REGISTER_MAP[0], REGISTER_SCRATCH, X86IndirectAccess::Offset(0))),
1205 2 => self.emit_ins(X86Instruction::store(OperandSize::S16, REGISTER_MAP[0], REGISTER_SCRATCH, X86IndirectAccess::Offset(0))),
1206 4 => self.emit_ins(X86Instruction::store(OperandSize::S32, REGISTER_MAP[0], REGISTER_SCRATCH, X86IndirectAccess::Offset(0))),
1207 8 => self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_MAP[0], REGISTER_SCRATCH, X86IndirectAccess::Offset(0))),
1208 _ => unreachable!(),
1209 }
1210 self.emit_ins(X86Instruction::xchg(OperandSize::S64, RSP, REGISTER_MAP[0], Some(stack_slot_of_value_to_store))); }
1212 }
1213
1214 fn emit_conditional_branch_reg(&mut self, op: u8, bitwise: bool, first_operand: X86Register, second_operand: X86Register, target_pc: usize) {
1215 self.emit_validate_and_profile_instruction_count(Some(target_pc));
1216 if bitwise { self.emit_ins(X86Instruction::test(OperandSize::S64, first_operand, second_operand, None));
1218 } else { self.emit_ins(X86Instruction::cmp(OperandSize::S64, first_operand, second_operand, None));
1220 }
1221 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, target_pc as i64));
1222 let jump_offset = self.relative_to_target_pc(target_pc, 6);
1223 self.emit_ins(X86Instruction::conditional_jump_immediate(op, jump_offset));
1224 self.emit_undo_profile_instruction_count(target_pc);
1225 }
1226
1227 fn emit_conditional_branch_imm(&mut self, op: u8, bitwise: bool, immediate: i64, second_operand: X86Register, target_pc: usize) {
1228 self.emit_validate_and_profile_instruction_count(Some(target_pc));
1229 if self.should_sanitize_constant(immediate) {
1230 self.emit_sanitized_load_immediate(REGISTER_SCRATCH, immediate);
1231 if bitwise { self.emit_ins(X86Instruction::test(OperandSize::S64, REGISTER_SCRATCH, second_operand, None));
1233 } else { self.emit_ins(X86Instruction::cmp(OperandSize::S64, REGISTER_SCRATCH, second_operand, None));
1235 }
1236 } else if bitwise { self.emit_ins(X86Instruction::test_immediate(OperandSize::S64, second_operand, immediate, None));
1238 } else { self.emit_ins(X86Instruction::cmp_immediate(OperandSize::S64, second_operand, immediate, None));
1240 }
1241 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, target_pc as i64));
1242 let jump_offset = self.relative_to_target_pc(target_pc, 6);
1243 self.emit_ins(X86Instruction::conditional_jump_immediate(op, jump_offset));
1244 self.emit_undo_profile_instruction_count(target_pc);
1245 }
1246
1247 fn emit_shift(&mut self, size: OperandSize, opcode_extension: u8, source: X86Register, destination: X86Register, immediate: Option<i64>) {
1248 if let Some(immediate) = immediate {
1249 self.emit_ins(X86Instruction::alu_immediate(size, 0xc1, opcode_extension, destination, immediate, None));
1250 return;
1251 }
1252 if let OperandSize::S32 = size {
1253 self.emit_ins(X86Instruction::mov(OperandSize::S32, destination, destination)); }
1255 if source == RCX {
1256 self.emit_ins(X86Instruction::alu_immediate(size, 0xd3, opcode_extension, destination, 0, None));
1257 } else if destination == RCX {
1258 self.emit_ins(X86Instruction::push(source, None));
1259 self.emit_ins(X86Instruction::xchg(OperandSize::S64, source, RCX, None));
1260 self.emit_ins(X86Instruction::alu_immediate(size, 0xd3, opcode_extension, source, 0, None));
1261 self.emit_ins(X86Instruction::mov(OperandSize::S64, source, RCX));
1262 self.emit_ins(X86Instruction::pop(source));
1263 } else {
1264 self.emit_ins(X86Instruction::push(RCX, None));
1265 self.emit_ins(X86Instruction::mov(OperandSize::S64, source, RCX));
1266 self.emit_ins(X86Instruction::alu_immediate(size, 0xd3, opcode_extension, destination, 0, None));
1267 self.emit_ins(X86Instruction::pop(RCX));
1268 }
1269 }
1270
1271 #[allow(clippy::too_many_arguments)]
1272 fn emit_product_quotient_remainder(
1273 &mut self,
1274 size: OperandSize,
1275 alt_dst: bool,
1276 division: bool,
1277 signed: bool,
1278 src: X86Register,
1279 dst: X86Register,
1280 imm: Option<i64>,
1281 ) {
1282 if division {
1289 if imm.is_none() {
1291 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.pc as i64)); self.emit_ins(X86Instruction::test(size, src, src, None)); self.emit_ins(X86Instruction::conditional_jump_immediate(0x84, self.relative_to_anchor(ANCHOR_DIV_BY_ZERO, 6)));
1294 }
1295
1296 if signed && imm.unwrap_or(-1) == -1 {
1299 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, if let OperandSize::S64 = size { i64::MIN } else { i32::MIN as i64 }));
1300 self.emit_ins(X86Instruction::cmp(size, dst, REGISTER_SCRATCH, None)); if imm.is_none() {
1303 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, 0)); self.emit_ins(X86Instruction::cmov(size, 0x45, src, REGISTER_SCRATCH)); self.emit_ins(X86Instruction::cmp_immediate(size, src, -1, None)); self.emit_ins(X86Instruction::cmov(size, 0x45, src, REGISTER_SCRATCH)); self.emit_ins(X86Instruction::test(size, REGISTER_SCRATCH, REGISTER_SCRATCH, None)); }
1312
1313 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.pc as i64));
1315 self.emit_ins(X86Instruction::conditional_jump_immediate(0x84, self.relative_to_anchor(ANCHOR_DIV_OVERFLOW, 6)));
1316 }
1317 }
1318
1319 if let Some(imm) = imm {
1320 if self.should_sanitize_constant(imm) {
1321 self.emit_sanitized_load_immediate(REGISTER_SCRATCH, imm);
1322 } else {
1323 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, imm));
1324 }
1325 } else {
1326 self.emit_ins(X86Instruction::mov(OperandSize::S64, src, REGISTER_SCRATCH));
1327 }
1328 if dst != RAX {
1329 self.emit_ins(X86Instruction::push(RAX, None));
1330 self.emit_ins(X86Instruction::mov(OperandSize::S64, dst, RAX));
1331 }
1332 if dst != RDX {
1333 self.emit_ins(X86Instruction::push(RDX, None));
1334 }
1335 if division {
1336 if signed {
1337 self.emit_ins(X86Instruction::sign_extend_rax_rdx(size));
1338 } else {
1339 self.emit_ins(X86Instruction::alu(size, 0x31, RDX, RDX, None)); }
1341 }
1342
1343 self.emit_ins(X86Instruction::alu_immediate(size, 0xf7, 0x4 | ((division as u8) << 1) | signed as u8, REGISTER_SCRATCH, 0, None));
1344
1345 if dst != RDX {
1346 if alt_dst {
1347 self.emit_ins(X86Instruction::mov(OperandSize::S64, RDX, dst));
1348 }
1349 self.emit_ins(X86Instruction::pop(RDX));
1350 }
1351 if dst != RAX {
1352 if !alt_dst {
1353 self.emit_ins(X86Instruction::mov(OperandSize::S64, RAX, dst));
1354 }
1355 self.emit_ins(X86Instruction::pop(RAX));
1356 }
1357 if let OperandSize::S32 = size {
1358 if signed && !self.executable.get_sbpf_version().explicit_sign_extension_of_results() {
1359 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x63, dst, dst, None)); }
1361 }
1362 }
1363
1364 fn emit_set_exception_kind(&mut self, err: EbpfError) {
1365 let err_kind = unsafe { *std::ptr::addr_of!(err).cast::<u64>() };
1366 let err_discriminant = ProgramResult::Err(err).discriminant();
1367 self.emit_ins(X86Instruction::lea(OperandSize::S64, REGISTER_PTR_TO_VM, REGISTER_MAP[0], Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult)))));
1368 self.emit_ins(X86Instruction::store_immediate(OperandSize::S64, REGISTER_MAP[0], X86IndirectAccess::Offset(0), err_discriminant as i64)); self.emit_ins(X86Instruction::store_immediate(OperandSize::S64, REGISTER_MAP[0], X86IndirectAccess::Offset(std::mem::size_of::<u64>() as i32), err_kind as i64)); }
1371
1372 fn emit_result_is_err(&mut self, destination: X86Register) {
1373 let ok = ProgramResult::Ok(0);
1374 let ok_discriminant = ok.discriminant();
1375 self.emit_ins(X86Instruction::lea(OperandSize::S64, REGISTER_PTR_TO_VM, destination, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult)))));
1376 self.emit_ins(X86Instruction::cmp_immediate(OperandSize::S64, destination, ok_discriminant as i64, Some(X86IndirectAccess::Offset(0))));
1377 }
1378
1379 fn emit_subroutines(&mut self) {
1380 if self.config.enable_instruction_tracing {
1382 self.set_anchor(ANCHOR_TRACE);
1383 self.emit_ins(X86Instruction::push(REGISTER_SCRATCH, None));
1385 for reg in REGISTER_MAP.iter().rev() {
1386 self.emit_ins(X86Instruction::push(*reg, None));
1387 }
1388 self.emit_ins(X86Instruction::mov(OperandSize::S64, RSP, REGISTER_MAP[0]));
1389 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP, - 8 * 3, None)); self.emit_rust_call(Value::Constant64(C::trace as *const u8 as i64, false), &[
1391 Argument { index: 1, value: Value::Register(REGISTER_MAP[0]) }, Argument { index: 0, value: Value::RegisterIndirect(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ContextObjectPointer), false) },
1393 ], None);
1394 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP, 8 * 3, None)); self.emit_ins(X86Instruction::pop(REGISTER_MAP[0]));
1397 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP, 8 * (REGISTER_MAP.len() - 1) as i64, None)); self.emit_ins(X86Instruction::pop(REGISTER_SCRATCH));
1399 self.emit_ins(X86Instruction::return_near());
1400 }
1401
1402 self.set_anchor(ANCHOR_EPILOGUE);
1404 if self.config.enable_instruction_meter {
1405 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 5, REGISTER_INSTRUCTION_METER, 1, None)); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x29, REGISTER_SCRATCH, REGISTER_INSTRUCTION_METER, None)); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x2B, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::PreviousInstructionMeter))))); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xf7, 3, REGISTER_INSTRUCTION_METER, 0, None)); self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::DueInsnCount)))); }
1412 fn stopwatch_result(numerator: u64, denominator: u64) {
1414 println!("Stop watch: {} / {} = {}", numerator, denominator, if denominator == 0 { 0.0 } else { numerator as f64 / denominator as f64 });
1415 }
1416 if self.stopwatch_is_active {
1417 self.emit_rust_call(Value::Constant64(stopwatch_result as *const u8 as i64, false), &[
1418 Argument { index: 1, value: Value::RegisterIndirect(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::StopwatchDenominator), false) },
1419 Argument { index: 0, value: Value::RegisterIndirect(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::StopwatchNumerator), false) },
1420 ], None);
1421 }
1422 self.emit_ins(X86Instruction::load(OperandSize::S64, REGISTER_PTR_TO_VM, RSP, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::HostStackPointer))));
1424 self.emit_ins(X86Instruction::return_near());
1425
1426 self.set_anchor(ANCHOR_THROW_EXCEEDED_MAX_INSTRUCTIONS);
1428 self.emit_set_exception_kind(EbpfError::ExceededMaxInstructions);
1429 self.emit_ins(X86Instruction::mov(OperandSize::S64, REGISTER_INSTRUCTION_METER, REGISTER_SCRATCH)); self.set_anchor(ANCHOR_THROW_EXCEPTION_UNCHECKED);
1434 self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_SCRATCH, REGISTER_PTR_TO_VM, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::Registers) + 11 * std::mem::size_of::<u64>() as i32))); self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_EPILOGUE, 5)));
1436
1437 self.set_anchor(ANCHOR_EXIT);
1439 if self.config.enable_instruction_meter {
1440 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, REGISTER_INSTRUCTION_METER, 1, None)); }
1442 self.emit_ins(X86Instruction::lea(OperandSize::S64, REGISTER_PTR_TO_VM, REGISTER_SCRATCH, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult)))));
1443 self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_MAP[0], REGISTER_SCRATCH, X86IndirectAccess::Offset(std::mem::size_of::<u64>() as i32))); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x31, REGISTER_SCRATCH, REGISTER_SCRATCH, None)); self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_EPILOGUE, 5)));
1446
1447 self.set_anchor(ANCHOR_THROW_EXCEPTION);
1449 self.emit_validate_instruction_count(None);
1451 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION_UNCHECKED, 5)));
1452
1453 self.set_anchor(ANCHOR_CALL_DEPTH_EXCEEDED);
1455 self.emit_set_exception_kind(EbpfError::CallDepthExceeded);
1456 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));
1457
1458 self.set_anchor(ANCHOR_CALL_OUTSIDE_TEXT_SEGMENT);
1460 self.emit_set_exception_kind(EbpfError::CallOutsideTextSegment);
1461 self.emit_ins(X86Instruction::load(OperandSize::S64, RSP, REGISTER_SCRATCH, X86IndirectAccess::OffsetIndexShift(-8, RSP, 0)));
1462 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));
1463
1464 self.set_anchor(ANCHOR_DIV_BY_ZERO);
1466 self.emit_set_exception_kind(EbpfError::DivideByZero);
1467 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));
1468
1469 self.set_anchor(ANCHOR_DIV_OVERFLOW);
1471 self.emit_set_exception_kind(EbpfError::DivideOverflow);
1472 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));
1473
1474 self.set_anchor(ANCHOR_CALL_REG_UNSUPPORTED_INSTRUCTION);
1476 self.emit_ins(X86Instruction::load(OperandSize::S64, RSP, REGISTER_SCRATCH, X86IndirectAccess::OffsetIndexShift(-8, RSP, 0))); self.emit_ins(X86Instruction::pop(REGISTER_MAP[0])); self.set_anchor(ANCHOR_CALL_UNSUPPORTED_INSTRUCTION);
1482 if self.config.enable_instruction_tracing {
1483 self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_TRACE, 5)));
1484 }
1485 self.emit_set_exception_kind(EbpfError::UnsupportedInstruction);
1486 self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 5)));
1487
1488 self.set_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL);
1490 self.emit_ins(X86Instruction::push_immediate(OperandSize::S64, -1)); if self.config.enable_instruction_meter {
1492 self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_INSTRUCTION_METER, REGISTER_PTR_TO_VM, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::DueInsnCount)))); }
1494 self.emit_rust_call(Value::Register(REGISTER_SCRATCH), &[
1495 Argument { index: 5, value: Value::Register(ARGUMENT_REGISTERS[5]) },
1496 Argument { index: 4, value: Value::Register(ARGUMENT_REGISTERS[4]) },
1497 Argument { index: 3, value: Value::Register(ARGUMENT_REGISTERS[3]) },
1498 Argument { index: 2, value: Value::Register(ARGUMENT_REGISTERS[2]) },
1499 Argument { index: 1, value: Value::Register(ARGUMENT_REGISTERS[1]) },
1500 Argument { index: 0, value: Value::Register(REGISTER_PTR_TO_VM) },
1501 ], None);
1502 if self.config.enable_instruction_meter {
1503 self.emit_ins(X86Instruction::load(OperandSize::S64, REGISTER_PTR_TO_VM, REGISTER_INSTRUCTION_METER, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::PreviousInstructionMeter)))); }
1505
1506 self.emit_result_is_err(REGISTER_SCRATCH);
1508 self.emit_ins(X86Instruction::pop(REGISTER_SCRATCH));
1509 self.emit_ins(X86Instruction::conditional_jump_immediate(0x85, self.relative_to_anchor(ANCHOR_EPILOGUE, 6)));
1510 self.emit_ins(X86Instruction::lea(OperandSize::S64, REGISTER_PTR_TO_VM, REGISTER_SCRATCH, Some(X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult)))));
1512 self.emit_ins(X86Instruction::load(OperandSize::S64, REGISTER_SCRATCH, REGISTER_MAP[0], X86IndirectAccess::Offset(8)));
1513 self.emit_ins(X86Instruction::return_near());
1514
1515 self.set_anchor(ANCHOR_INTERNAL_FUNCTION_CALL_PROLOGUE);
1517 self.emit_validate_instruction_count(None);
1518 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 5, RSP, 8 * (SCRATCH_REGS + 1) as i64, None)); self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_SCRATCH, RSP, X86IndirectAccess::OffsetIndexShift(0, RSP, 0))); self.emit_ins(X86Instruction::load(OperandSize::S64, RSP, REGISTER_SCRATCH, X86IndirectAccess::OffsetIndexShift(8 * (SCRATCH_REGS + 1) as i32, RSP, 0))); for (i, reg) in REGISTER_MAP.iter().skip(FIRST_SCRATCH_REG).take(SCRATCH_REGS).enumerate() {
1522 self.emit_ins(X86Instruction::store(OperandSize::S64, *reg, RSP, X86IndirectAccess::OffsetIndexShift(8 * (SCRATCH_REGS - i + 1) as i32, RSP, 0))); }
1524 self.emit_ins(X86Instruction::store(OperandSize::S64, REGISTER_MAP[FRAME_PTR_REG], RSP, X86IndirectAccess::OffsetIndexShift(8, RSP, 0)));
1526 self.emit_ins(X86Instruction::xchg(OperandSize::S64, REGISTER_SCRATCH, RSP, Some(X86IndirectAccess::OffsetIndexShift(0, RSP, 0)))); let call_depth_access = X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::CallDepth));
1529 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, REGISTER_PTR_TO_VM, 1, Some(call_depth_access))); self.emit_ins(X86Instruction::cmp_immediate(OperandSize::S32, REGISTER_PTR_TO_VM, self.config.max_call_depth as i64, Some(call_depth_access)));
1532 self.emit_ins(X86Instruction::conditional_jump_immediate(0x83, self.relative_to_anchor(ANCHOR_CALL_DEPTH_EXCEEDED, 6)));
1533 if !self.executable.get_sbpf_version().dynamic_stack_frames() {
1535 let stack_frame_size = self.config.stack_frame_size as i64 * if self.config.enable_stack_frame_gaps { 2 } else { 1 };
1537 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, REGISTER_MAP[FRAME_PTR_REG], stack_frame_size, None)); }
1539 self.emit_ins(X86Instruction::return_near());
1540
1541 self.set_anchor(ANCHOR_INTERNAL_FUNCTION_CALL_REG);
1545 self.emit_ins(X86Instruction::push(REGISTER_MAP[0], None));
1546 self.emit_ins(X86Instruction::load_immediate(REGISTER_MAP[0], self.program_vm_addr as i64));
1548 self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x29, REGISTER_MAP[0], REGISTER_SCRATCH, None)); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 4, REGISTER_SCRATCH, !(INSN_SIZE as i64 - 1), None)); let number_of_instructions = self.result.pc_section.len();
1554 self.emit_ins(X86Instruction::cmp_immediate(OperandSize::S64, REGISTER_SCRATCH, (number_of_instructions * INSN_SIZE) as i64, None)); self.emit_ins(X86Instruction::conditional_jump_immediate(0x83, self.relative_to_anchor(ANCHOR_CALL_OUTSIDE_TEXT_SEGMENT, 6)));
1556 let shift_amount = INSN_SIZE.trailing_zeros();
1559 debug_assert_eq!(INSN_SIZE, 1 << shift_amount);
1560 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0xc1, 5, REGISTER_SCRATCH, shift_amount as i64, None)); self.emit_ins(X86Instruction::load_immediate(REGISTER_MAP[0], self.result.pc_section.as_ptr() as i64)); self.emit_ins(X86Instruction::load(OperandSize::S32, REGISTER_MAP[0], REGISTER_MAP[0], X86IndirectAccess::OffsetIndexShift(0, REGISTER_SCRATCH, 2))); self.emit_ins(X86Instruction::test_immediate(OperandSize::S32, REGISTER_MAP[0], 1 << 31, None)); self.emit_ins(X86Instruction::conditional_jump_immediate(0x85, self.relative_to_anchor(ANCHOR_CALL_REG_UNSUPPORTED_INSTRUCTION, 6))); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S32, 0x81, 4, REGISTER_MAP[0], i32::MAX as i64, None)); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x2b, REGISTER_INSTRUCTION_METER, RSP, Some(X86IndirectAccess::OffsetIndexShift(-8, RSP, 0)))); self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 5, REGISTER_INSTRUCTION_METER, 1, None)); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, REGISTER_SCRATCH, REGISTER_INSTRUCTION_METER, None)); self.emit_ins(X86Instruction::mov_mmx(OperandSize::S64, REGISTER_SCRATCH, MM0));
1574 self.emit_ins(X86Instruction::load_immediate(REGISTER_SCRATCH, self.result.text_section.as_ptr() as i64)); self.emit_ins(X86Instruction::alu(OperandSize::S64, 0x01, REGISTER_SCRATCH, REGISTER_MAP[0], None)); self.emit_ins(X86Instruction::mov_mmx(OperandSize::S64, MM0, REGISTER_SCRATCH));
1577 self.emit_ins(X86Instruction::xchg(OperandSize::S64, REGISTER_MAP[0], RSP, Some(X86IndirectAccess::OffsetIndexShift(0, RSP, 0)))); self.emit_ins(X86Instruction::return_near()); let lower_key = self.immediate_value_key as i32 as i64;
1583 for (anchor_base, len) in &[
1584 (0, 1i32), (0, 2i32), (0, 4i32), (0, 8i32),
1585 (4, 1i32), (4, 2i32), (4, 4i32), (4, 8i32),
1586 (8, 1i32), (8, 2i32), (8, 4i32), (8, 8i32),
1587 ] {
1588 let target_offset = *anchor_base + len.trailing_zeros() as usize;
1589 self.set_anchor(ANCHOR_TRANSLATE_MEMORY_ADDRESS + target_offset);
1590 if *anchor_base == 0 { let load = match len {
1593 1 => MemoryMapping::load::<u8> as *const u8 as i64,
1594 2 => MemoryMapping::load::<u16> as *const u8 as i64,
1595 4 => MemoryMapping::load::<u32> as *const u8 as i64,
1596 8 => MemoryMapping::load::<u64> as *const u8 as i64,
1597 _ => unreachable!()
1598 };
1599 self.emit_rust_call(Value::Constant64(load, false), &[
1600 Argument { index: 2, value: Value::Register(REGISTER_SCRATCH) }, Argument { index: 3, value: Value::Constant64(0, false) }, Argument { index: 1, value: Value::RegisterPlusConstant32(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::MemoryMapping), false) },
1603 Argument { index: 0, value: Value::RegisterPlusConstant32(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult), false) },
1604 ], None);
1605 } else { if *anchor_base == 8 {
1607 self.emit_ins(X86Instruction::alu_immediate(OperandSize::S64, 0x81, 0, RSP, lower_key, Some(X86IndirectAccess::OffsetIndexShift(-80, RSP, 0))));
1609 }
1610 let store = match len {
1611 1 => MemoryMapping::store::<u8> as *const u8 as i64,
1612 2 => MemoryMapping::store::<u16> as *const u8 as i64,
1613 4 => MemoryMapping::store::<u32> as *const u8 as i64,
1614 8 => MemoryMapping::store::<u64> as *const u8 as i64,
1615 _ => unreachable!()
1616 };
1617 self.emit_rust_call(Value::Constant64(store, false), &[
1618 Argument { index: 3, value: Value::Register(REGISTER_SCRATCH) }, Argument { index: 2, value: Value::RegisterIndirect(RSP, -8, false) },
1620 Argument { index: 4, value: Value::Constant64(0, false) }, Argument { index: 1, value: Value::RegisterPlusConstant32(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::MemoryMapping), false) },
1622 Argument { index: 0, value: Value::RegisterPlusConstant32(REGISTER_PTR_TO_VM, self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult), false) },
1623 ], None);
1624 }
1625
1626 self.emit_result_is_err(REGISTER_SCRATCH);
1628 self.emit_ins(X86Instruction::pop(REGISTER_SCRATCH)); self.emit_ins(X86Instruction::xchg(OperandSize::S64, REGISTER_SCRATCH, RSP, Some(X86IndirectAccess::OffsetIndexShift(0, RSP, 0)))); self.emit_ins(X86Instruction::conditional_jump_immediate(0x85, self.relative_to_anchor(ANCHOR_THROW_EXCEPTION, 6)));
1631
1632 if *anchor_base == 0 { self.emit_ins(X86Instruction::load(OperandSize::S64, REGISTER_PTR_TO_VM, REGISTER_SCRATCH, X86IndirectAccess::Offset(self.slot_in_vm(RuntimeEnvironmentSlot::ProgramResult) + std::mem::size_of::<u64>() as i32)));
1635 }
1636
1637 self.emit_ins(X86Instruction::return_near());
1638 }
1639 }
1640
1641 fn set_anchor(&mut self, anchor: usize) {
1642 self.anchors[anchor] = unsafe { self.result.text_section.as_ptr().add(self.offset_in_text_section) };
1643 }
1644
1645 fn relative_to_anchor(&self, anchor: usize, instruction_length: usize) -> i32 {
1648 let instruction_end = unsafe { self.result.text_section.as_ptr().add(self.offset_in_text_section).add(instruction_length) };
1649 let destination = self.anchors[anchor];
1650 debug_assert!(!destination.is_null());
1651 (unsafe { destination.offset_from(instruction_end) } as i32) }
1653
1654 fn relative_to_target_pc(&mut self, target_pc: usize, instruction_length: usize) -> i32 {
1655 let instruction_end = unsafe { self.result.text_section.as_ptr().add(self.offset_in_text_section).add(instruction_length) };
1656 let destination = if self.result.pc_section[target_pc] != 0 {
1657 &self.result.text_section[self.result.pc_section[target_pc] as usize & (i32::MAX as u32 as usize)] as *const u8
1659 } else {
1660 self.text_section_jumps.push(Jump { location: unsafe { instruction_end.sub(4) }, target_pc });
1662 return 0;
1663 };
1664 debug_assert!(!destination.is_null());
1665 (unsafe { destination.offset_from(instruction_end) } as i32) }
1667
1668 fn resolve_jumps(&mut self) {
1669 for jump in &self.text_section_jumps {
1671 let destination = &self.result.text_section[self.result.pc_section[jump.target_pc] as usize & (i32::MAX as u32 as usize)] as *const u8;
1672 let offset_value =
1673 unsafe { destination.offset_from(jump.location) } as i32 - mem::size_of::<i32>() as i32; unsafe { ptr::write_unaligned(jump.location as *mut i32, offset_value); }
1676 }
1677 }
1678}