Skip to content

Commit

Permalink
Add new syscall instruction to interpreter and jitter (#621)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasSte authored Nov 6, 2024
1 parent dec6a6b commit 73f0e76
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 398 deletions.
74 changes: 36 additions & 38 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::{
ebpf::{self, STACK_PTR_REG},
elf::Executable,
error::{EbpfError, ProgramResult},
program::BuiltinFunction,
vm::{Config, ContextObject, EbpfVm},
};

Expand Down Expand Up @@ -526,55 +527,44 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {

// Do not delegate the check to the verifier, since self.registered functions can be
// changed after the program has been verified.
ebpf::CALL_IMM
| ebpf::SYSCALL if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => {
let mut resolved = false;
let (external, internal) = if self.executable.get_sbpf_version().static_syscalls() {
(insn.src == 0, insn.src != 0)
} else {
(true, true)
};

if external {
if let Some((_function_name, function)) = self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32) {
resolved = true;

self.vm.due_insn_count = self.vm.previous_instruction_meter - self.vm.due_insn_count;
self.vm.registers[0..6].copy_from_slice(&self.reg[0..6]);
self.vm.invoke_function(function);
self.vm.due_insn_count = 0;
self.reg[0] = match &self.vm.program_result {
ProgramResult::Ok(value) => *value,
ProgramResult::Err(_err) => return false,
};
}
}

if internal && !resolved {
if let Some((_function_name, target_pc)) =
ebpf::CALL_IMM => {
if let (false, Some((_, function))) =
(self.executable.get_sbpf_version().static_syscalls(),
self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32)) {
// SBPFv1 syscall
self.reg[0] = match self.dispatch_syscall(function) {
ProgramResult::Ok(value) => *value,
ProgramResult::Err(_err) => return false,
};
} else if let Some((_, target_pc)) =
self.executable
.get_function_registry()
.lookup_by_key(
self
.executable
.get_sbpf_version()
.calculate_call_imm_target_pc(self.reg[11] as usize, insn.imm)
)
{
resolved = true;

// make BPF to BPF call
if !self.push_frame(config) {
return false;
}
check_pc!(self, next_pc, target_pc as u64);
) {
// make BPF to BPF call
if !self.push_frame(config) {
return false;
}
}

if !resolved {
check_pc!(self, next_pc, target_pc as u64);
} else {
throw_error!(self, EbpfError::UnsupportedInstruction);
}
}
ebpf::SYSCALL if self.executable.get_sbpf_version().static_syscalls() => {
if let Some((_, function)) = self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32) {
// SBPFv2 syscall
self.reg[0] = match self.dispatch_syscall(function) {
ProgramResult::Ok(value) => *value,
ProgramResult::Err(_err) => return false,
};
} else {
debug_assert!(false, "Invalid syscall should have been detected in the verifier.");
}
},
ebpf::RETURN
| ebpf::EXIT => {
if (insn.opc == ebpf::EXIT && self.executable.get_sbpf_version().static_syscalls())
Expand Down Expand Up @@ -609,4 +599,12 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
self.reg[11] = next_pc;
true
}

fn dispatch_syscall(&mut self, function: BuiltinFunction<C>) -> &ProgramResult {
self.vm.due_insn_count = self.vm.previous_instruction_meter - self.vm.due_insn_count;
self.vm.registers[0..6].copy_from_slice(&self.reg[0..6]);
self.vm.invoke_function(function);
self.vm.due_insn_count = 0;
&self.vm.program_result
}
}
71 changes: 35 additions & 36 deletions src/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::{
allocate_pages, free_pages, get_system_page_size, protect_pages, round_to_page_size,
},
memory_region::{AccessType, MemoryMapping},
program::BuiltinFunction,
vm::{get_runtime_environment_key, Config, ContextObject, EbpfVm},
x86::*,
};
Expand Down Expand Up @@ -705,48 +706,36 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
ebpf::JSLT_REG => self.emit_conditional_branch_reg(0x8c, false, src, dst, target_pc),
ebpf::JSLE_IMM => self.emit_conditional_branch_imm(0x8e, false, insn.imm, dst, target_pc),
ebpf::JSLE_REG => self.emit_conditional_branch_reg(0x8e, false, src, dst, target_pc),
ebpf::CALL_IMM | ebpf::SYSCALL
if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => {
ebpf::CALL_IMM => {
// For JIT, external functions MUST be registered at compile time.

let mut resolved = false;
let (external, internal) = if self.executable.get_sbpf_version().static_syscalls() {
(insn.src == 0, insn.src != 0)
} else {
(true, true)
};

if external {
if let Some((_function_name, function)) = self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32) {
self.emit_validate_and_profile_instruction_count(false, Some(0));
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_SCRATCH, function as usize as i64));
self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL, 5)));
self.emit_undo_profile_instruction_count(0);
resolved = true;
}
}

if internal {
if let Some((_function_name, target_pc)) =
if let (false, Some((_, function))) =
(self.executable.get_sbpf_version().static_syscalls(),
self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32)) {
// SBPFv1 syscall
self.emit_syscall_dispatch(function);
} else if let Some((_function_name, target_pc)) =
self.executable
.get_function_registry()
.lookup_by_key(
self
.executable
.get_sbpf_version()
.calculate_call_imm_target_pc(self.pc, insn.imm)
)
{
self.emit_internal_call(Value::Constant64(target_pc as i64, true));
resolved = true;
}
}

if !resolved {
) {
// BPF to BPF call
self.emit_internal_call(Value::Constant64(target_pc as i64, true));
} else {
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_SCRATCH, self.pc as i64));
self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_CALL_UNSUPPORTED_INSTRUCTION, 5)));
}
},
ebpf::SYSCALL if self.executable.get_sbpf_version().static_syscalls() => {
if let Some((_, function)) = self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32) {
self.emit_syscall_dispatch(function);
} else {
debug_assert!(false, "Invalid syscall should have been detected in the verifier.")
}
},
ebpf::CALL_REG => {
let target_pc = if self.executable.get_sbpf_version().callx_uses_src_reg() {
src
Expand Down Expand Up @@ -1144,6 +1133,14 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
}
}

#[inline]
fn emit_syscall_dispatch(&mut self, function: BuiltinFunction<C>) {
self.emit_validate_and_profile_instruction_count(false, Some(0));
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_SCRATCH, function as usize as i64));
self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL, 5)));
self.emit_undo_profile_instruction_count(0);
}

#[inline]
fn emit_address_translation(&mut self, dst: Option<u8>, vm_addr: Value, len: u64, value: Option<Value>) {
debug_assert_ne!(dst.is_some(), value.is_some());
Expand Down Expand Up @@ -1705,7 +1702,7 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
mod tests {
use super::*;
use crate::{
program::{BuiltinFunction, BuiltinProgram, FunctionRegistry, SBPFVersion},
program::{BuiltinProgram, FunctionRegistry, SBPFVersion},
syscalls,
vm::TestContextObject,
};
Expand Down Expand Up @@ -1752,12 +1749,10 @@ mod tests {

fn create_mockup_executable(config: Config, program: &[u8]) -> Executable<TestContextObject> {
let sbpf_version = *config.enabled_sbpf_versions.end();
let mut function_registry =
FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
function_registry
.register_function_hashed(*b"gather_bytes", syscalls::SyscallGatherBytes::vm)
let mut loader = BuiltinProgram::new_loader_with_dense_registration(config);
loader
.register_function("gather_bytes", 1, syscalls::SyscallGatherBytes::vm)
.unwrap();
let loader = BuiltinProgram::new_loader(config, function_registry);
let mut function_registry = FunctionRegistry::default();
function_registry
.register_function(8, *b"function_foo", 8)
Expand Down Expand Up @@ -1844,6 +1839,10 @@ mod tests {
opcode = 0x85;
(0x88, 0x91020CDD)
}
0x95 => {
// Put a valid syscall
(0, 1)
}
0xD4 | 0xDC => (0x88, 16),
_ => (0x88, 0xFFFFFFFF),
};
Expand Down
Loading

0 comments on commit 73f0e76

Please sign in to comment.