Skip to content
This repository was archived by the owner on Jan 10, 2025. It is now read-only.

Commit 73f0e76

Browse files
authored
Add new syscall instruction to interpreter and jitter (#621)
1 parent dec6a6b commit 73f0e76

File tree

3 files changed

+261
-398
lines changed

3 files changed

+261
-398
lines changed

src/interpreter.rs

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::{
1616
ebpf::{self, STACK_PTR_REG},
1717
elf::Executable,
1818
error::{EbpfError, ProgramResult},
19+
program::BuiltinFunction,
1920
vm::{Config, ContextObject, EbpfVm},
2021
};
2122

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

527528
// Do not delegate the check to the verifier, since self.registered functions can be
528529
// changed after the program has been verified.
529-
ebpf::CALL_IMM
530-
| ebpf::SYSCALL if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => {
531-
let mut resolved = false;
532-
let (external, internal) = if self.executable.get_sbpf_version().static_syscalls() {
533-
(insn.src == 0, insn.src != 0)
534-
} else {
535-
(true, true)
536-
};
537-
538-
if external {
539-
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) {
540-
resolved = true;
541-
542-
self.vm.due_insn_count = self.vm.previous_instruction_meter - self.vm.due_insn_count;
543-
self.vm.registers[0..6].copy_from_slice(&self.reg[0..6]);
544-
self.vm.invoke_function(function);
545-
self.vm.due_insn_count = 0;
546-
self.reg[0] = match &self.vm.program_result {
547-
ProgramResult::Ok(value) => *value,
548-
ProgramResult::Err(_err) => return false,
549-
};
550-
}
551-
}
552-
553-
if internal && !resolved {
554-
if let Some((_function_name, target_pc)) =
530+
ebpf::CALL_IMM => {
531+
if let (false, Some((_, function))) =
532+
(self.executable.get_sbpf_version().static_syscalls(),
533+
self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32)) {
534+
// SBPFv1 syscall
535+
self.reg[0] = match self.dispatch_syscall(function) {
536+
ProgramResult::Ok(value) => *value,
537+
ProgramResult::Err(_err) => return false,
538+
};
539+
} else if let Some((_, target_pc)) =
555540
self.executable
556541
.get_function_registry()
557542
.lookup_by_key(
558543
self
559544
.executable
560545
.get_sbpf_version()
561546
.calculate_call_imm_target_pc(self.reg[11] as usize, insn.imm)
562-
)
563-
{
564-
resolved = true;
565-
566-
// make BPF to BPF call
567-
if !self.push_frame(config) {
568-
return false;
569-
}
570-
check_pc!(self, next_pc, target_pc as u64);
547+
) {
548+
// make BPF to BPF call
549+
if !self.push_frame(config) {
550+
return false;
571551
}
572-
}
573-
574-
if !resolved {
552+
check_pc!(self, next_pc, target_pc as u64);
553+
} else {
575554
throw_error!(self, EbpfError::UnsupportedInstruction);
576555
}
577556
}
557+
ebpf::SYSCALL if self.executable.get_sbpf_version().static_syscalls() => {
558+
if let Some((_, function)) = self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32) {
559+
// SBPFv2 syscall
560+
self.reg[0] = match self.dispatch_syscall(function) {
561+
ProgramResult::Ok(value) => *value,
562+
ProgramResult::Err(_err) => return false,
563+
};
564+
} else {
565+
debug_assert!(false, "Invalid syscall should have been detected in the verifier.");
566+
}
567+
},
578568
ebpf::RETURN
579569
| ebpf::EXIT => {
580570
if (insn.opc == ebpf::EXIT && self.executable.get_sbpf_version().static_syscalls())
@@ -609,4 +599,12 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
609599
self.reg[11] = next_pc;
610600
true
611601
}
602+
603+
fn dispatch_syscall(&mut self, function: BuiltinFunction<C>) -> &ProgramResult {
604+
self.vm.due_insn_count = self.vm.previous_instruction_meter - self.vm.due_insn_count;
605+
self.vm.registers[0..6].copy_from_slice(&self.reg[0..6]);
606+
self.vm.invoke_function(function);
607+
self.vm.due_insn_count = 0;
608+
&self.vm.program_result
609+
}
612610
}

src/jit.rs

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::{
3131
allocate_pages, free_pages, get_system_page_size, protect_pages, round_to_page_size,
3232
},
3333
memory_region::{AccessType, MemoryMapping},
34+
program::BuiltinFunction,
3435
vm::{get_runtime_environment_key, Config, ContextObject, EbpfVm},
3536
x86::*,
3637
};
@@ -705,48 +706,36 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
705706
ebpf::JSLT_REG => self.emit_conditional_branch_reg(0x8c, false, src, dst, target_pc),
706707
ebpf::JSLE_IMM => self.emit_conditional_branch_imm(0x8e, false, insn.imm, dst, target_pc),
707708
ebpf::JSLE_REG => self.emit_conditional_branch_reg(0x8e, false, src, dst, target_pc),
708-
ebpf::CALL_IMM | ebpf::SYSCALL
709-
if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => {
709+
ebpf::CALL_IMM => {
710710
// For JIT, external functions MUST be registered at compile time.
711-
712-
let mut resolved = false;
713-
let (external, internal) = if self.executable.get_sbpf_version().static_syscalls() {
714-
(insn.src == 0, insn.src != 0)
715-
} else {
716-
(true, true)
717-
};
718-
719-
if external {
720-
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) {
721-
self.emit_validate_and_profile_instruction_count(false, Some(0));
722-
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_SCRATCH, function as usize as i64));
723-
self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL, 5)));
724-
self.emit_undo_profile_instruction_count(0);
725-
resolved = true;
726-
}
727-
}
728-
729-
if internal {
730-
if let Some((_function_name, target_pc)) =
711+
if let (false, Some((_, function))) =
712+
(self.executable.get_sbpf_version().static_syscalls(),
713+
self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32)) {
714+
// SBPFv1 syscall
715+
self.emit_syscall_dispatch(function);
716+
} else if let Some((_function_name, target_pc)) =
731717
self.executable
732718
.get_function_registry()
733719
.lookup_by_key(
734720
self
735721
.executable
736722
.get_sbpf_version()
737723
.calculate_call_imm_target_pc(self.pc, insn.imm)
738-
)
739-
{
740-
self.emit_internal_call(Value::Constant64(target_pc as i64, true));
741-
resolved = true;
742-
}
743-
}
744-
745-
if !resolved {
724+
) {
725+
// BPF to BPF call
726+
self.emit_internal_call(Value::Constant64(target_pc as i64, true));
727+
} else {
746728
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_SCRATCH, self.pc as i64));
747729
self.emit_ins(X86Instruction::jump_immediate(self.relative_to_anchor(ANCHOR_CALL_UNSUPPORTED_INSTRUCTION, 5)));
748730
}
749731
},
732+
ebpf::SYSCALL if self.executable.get_sbpf_version().static_syscalls() => {
733+
if let Some((_, function)) = self.executable.get_loader().get_function_registry(self.executable.get_sbpf_version()).lookup_by_key(insn.imm as u32) {
734+
self.emit_syscall_dispatch(function);
735+
} else {
736+
debug_assert!(false, "Invalid syscall should have been detected in the verifier.")
737+
}
738+
},
750739
ebpf::CALL_REG => {
751740
let target_pc = if self.executable.get_sbpf_version().callx_uses_src_reg() {
752741
src
@@ -1144,6 +1133,14 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
11441133
}
11451134
}
11461135

1136+
#[inline]
1137+
fn emit_syscall_dispatch(&mut self, function: BuiltinFunction<C>) {
1138+
self.emit_validate_and_profile_instruction_count(false, Some(0));
1139+
self.emit_ins(X86Instruction::load_immediate(OperandSize::S64, REGISTER_SCRATCH, function as usize as i64));
1140+
self.emit_ins(X86Instruction::call_immediate(self.relative_to_anchor(ANCHOR_EXTERNAL_FUNCTION_CALL, 5)));
1141+
self.emit_undo_profile_instruction_count(0);
1142+
}
1143+
11471144
#[inline]
11481145
fn emit_address_translation(&mut self, dst: Option<u8>, vm_addr: Value, len: u64, value: Option<Value>) {
11491146
debug_assert_ne!(dst.is_some(), value.is_some());
@@ -1705,7 +1702,7 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
17051702
mod tests {
17061703
use super::*;
17071704
use crate::{
1708-
program::{BuiltinFunction, BuiltinProgram, FunctionRegistry, SBPFVersion},
1705+
program::{BuiltinProgram, FunctionRegistry, SBPFVersion},
17091706
syscalls,
17101707
vm::TestContextObject,
17111708
};
@@ -1752,12 +1749,10 @@ mod tests {
17521749

17531750
fn create_mockup_executable(config: Config, program: &[u8]) -> Executable<TestContextObject> {
17541751
let sbpf_version = *config.enabled_sbpf_versions.end();
1755-
let mut function_registry =
1756-
FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
1757-
function_registry
1758-
.register_function_hashed(*b"gather_bytes", syscalls::SyscallGatherBytes::vm)
1752+
let mut loader = BuiltinProgram::new_loader_with_dense_registration(config);
1753+
loader
1754+
.register_function("gather_bytes", 1, syscalls::SyscallGatherBytes::vm)
17591755
.unwrap();
1760-
let loader = BuiltinProgram::new_loader(config, function_registry);
17611756
let mut function_registry = FunctionRegistry::default();
17621757
function_registry
17631758
.register_function(8, *b"function_foo", 8)
@@ -1844,6 +1839,10 @@ mod tests {
18441839
opcode = 0x85;
18451840
(0x88, 0x91020CDD)
18461841
}
1842+
0x95 => {
1843+
// Put a valid syscall
1844+
(0, 1)
1845+
}
18471846
0xD4 | 0xDC => (0x88, 16),
18481847
_ => (0x88, 0xFFFFFFFF),
18491848
};

0 commit comments

Comments
 (0)