1use {
3 crate::{
4 ebpf,
5 elf::ElfError,
6 vm::{Config, ContextObject, EncryptedHostAddressToEbpfVm},
7 },
8 std::collections::{btree_map::Entry, BTreeMap},
9};
10
11#[derive(Debug, PartialEq, PartialOrd, Eq, Clone, Copy)]
13pub enum SBPFVersion {
14 V0,
16 V1,
18 V2,
20 V3,
22 V4,
24 Reserved,
26}
27
28impl SBPFVersion {
29 pub fn manual_stack_frame_bump(self) -> bool {
33 self == SBPFVersion::V1 || self == SBPFVersion::V2
34 }
35 pub fn stack_frame_gaps(self) -> bool {
37 self == SBPFVersion::V0
38 }
39
40 pub fn enable_pqr(self) -> bool {
42 self == SBPFVersion::V2
43 }
44 pub fn explicit_sign_extension_of_results(self) -> bool {
46 self == SBPFVersion::V2
47 }
48 pub fn swap_sub_reg_imm_operands(self) -> bool {
50 self == SBPFVersion::V2
51 }
52 pub fn disable_neg(self) -> bool {
54 self == SBPFVersion::V2
55 }
56
57 pub fn callx_uses_src_reg(self) -> bool {
59 self == SBPFVersion::V2
60 }
61 pub fn disable_lddw(self) -> bool {
63 self == SBPFVersion::V2
64 }
65 pub fn disable_le(self) -> bool {
67 self == SBPFVersion::V2
68 }
69 pub fn move_memory_instruction_classes(self) -> bool {
71 self == SBPFVersion::V2
72 }
73
74 pub fn static_syscalls(self) -> bool {
76 self >= SBPFVersion::V3
77 }
78 pub fn enable_stricter_elf_headers(self) -> bool {
80 self >= SBPFVersion::V3
81 }
82 pub fn enable_lower_rodata_vaddr(self) -> bool {
84 self >= SBPFVersion::V3
85 }
86 pub fn enable_jmp32(self) -> bool {
88 self >= SBPFVersion::V3
89 }
90 pub fn callx_uses_dst_reg(self) -> bool {
92 self >= SBPFVersion::V3
93 }
94
95 pub fn calculate_call_imm_target_pc(self, pc: usize, imm: i64) -> u32 {
98 if self.static_syscalls() {
99 (pc as i64).saturating_add(imm).saturating_add(1) as u32
100 } else {
101 imm as u32
102 }
103 }
104}
105
106#[derive(Debug, PartialEq, Eq)]
108pub struct FunctionRegistry<T> {
109 pub(crate) map: BTreeMap<u32, (Vec<u8>, T)>,
110}
111
112impl<T> Default for FunctionRegistry<T> {
113 fn default() -> Self {
114 Self {
115 map: BTreeMap::new(),
116 }
117 }
118}
119
120impl<T: Copy + PartialEq> FunctionRegistry<T> {
121 pub fn register_function(
123 &mut self,
124 key: u32,
125 name: impl Into<Vec<u8>>,
126 value: T,
127 ) -> Result<(), ElfError> {
128 match self.map.entry(key) {
129 Entry::Vacant(entry) => {
130 entry.insert((name.into(), value));
131 }
132 Entry::Occupied(entry) => {
133 if entry.get().1 != value {
134 return Err(ElfError::SymbolHashCollision(key));
135 }
136 }
137 }
138 Ok(())
139 }
140
141 pub(crate) fn register_function_hashed_legacy<C: ContextObject>(
143 &mut self,
144 loader: &BuiltinProgram<C>,
145 hash_symbol_name: bool,
146 name: impl Into<Vec<u8>>,
147 value: T,
148 ) -> Result<u32, ElfError>
149 where
150 usize: From<T>,
151 {
152 let name = name.into();
153 let config = loader.get_config();
154 let key = if hash_symbol_name {
155 let hash = if name == b"entrypoint" {
156 ebpf::hash_symbol_name(b"entrypoint")
157 } else {
158 ebpf::hash_symbol_name(&usize::from(value).to_le_bytes())
159 };
160 if loader.get_function_registry().lookup_by_key(hash).is_some() {
161 return Err(ElfError::SymbolHashCollision(hash));
162 }
163 hash
164 } else {
165 usize::from(value) as u32
166 };
167 self.register_function(
168 key,
169 if config.enable_symbol_and_section_labels || name == b"entrypoint" {
170 name
171 } else {
172 Vec::default()
173 },
174 value,
175 )?;
176 Ok(key)
177 }
178
179 pub fn unregister_function(&mut self, key: u32) -> bool {
181 self.map.remove(&key).is_some()
182 }
183
184 pub fn keys(&self) -> impl Iterator<Item = u32> + '_ {
186 self.map.keys().copied()
187 }
188
189 pub fn iter(&self) -> impl Iterator<Item = (u32, (&[u8], T))> + '_ {
191 self.map
192 .iter()
193 .map(|(key, (name, value))| (*key, (name.as_slice(), *value)))
194 }
195
196 pub fn lookup_by_key(&self, key: u32) -> Option<(&[u8], T)> {
198 self.map
200 .get(&key)
201 .map(|(function_name, value)| (function_name.as_slice(), *value))
202 }
203
204 pub fn lookup_by_name(&self, name: &[u8]) -> Option<(&[u8], T)> {
206 self.map
207 .values()
208 .find(|(function_name, _value)| function_name == name)
209 .map(|(function_name, value)| (function_name.as_slice(), *value))
210 }
211
212 pub fn mem_size(&self) -> usize {
214 std::mem::size_of::<Self>().saturating_add(self.map.iter().fold(
215 0,
216 |state: usize, (_, (name, value))| {
217 state.saturating_add(
218 std::mem::size_of_val(value).saturating_add(
219 std::mem::size_of_val(name).saturating_add(name.capacity()),
220 ),
221 )
222 },
223 ))
224 }
225}
226
227pub type BuiltinFunction<C> = fn(EncryptedHostAddressToEbpfVm<C>, u64, u64, u64, u64, u64);
229#[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
231pub type JitCompiler<'a, C> = crate::jit::JitCompiler<'a, C>;
232#[cfg(not(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64")))]
234pub struct JitCompiler<'a, C> {
235 _phantom: std::marker::PhantomData<&'a C>,
236}
237#[cfg(not(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64")))]
238impl<'a, C: ContextObject> JitCompiler<'a, C> {
239 #[allow(dead_code)]
241 pub fn emit_external_call(&mut self, _function: BuiltinFunction<C>) {}
242}
243pub type BuiltinCodegen<C> = fn(&mut JitCompiler<C>);
245
246#[derive(Eq)]
248pub struct BuiltinProgram<C: ContextObject> {
249 config: Option<Box<Config>>,
251 sparse_registry: FunctionRegistry<(BuiltinFunction<C>, BuiltinCodegen<C>)>,
253}
254
255impl<C: ContextObject> PartialEq for BuiltinProgram<C> {
256 fn eq(&self, other: &Self) -> bool {
257 self.config.eq(&other.config) && self.sparse_registry.eq(&other.sparse_registry)
258 }
259}
260
261impl<C: ContextObject> BuiltinProgram<C> {
262 pub fn new_loader(config: Config) -> Self {
264 Self {
265 config: Some(Box::new(config)),
266 sparse_registry: FunctionRegistry::default(),
267 }
268 }
269
270 pub fn new_builtin() -> Self {
272 Self {
273 config: None,
274 sparse_registry: FunctionRegistry::default(),
275 }
276 }
277
278 pub fn new_mock() -> Self {
280 Self {
281 config: Some(Box::default()),
282 sparse_registry: FunctionRegistry::default(),
283 }
284 }
285
286 pub fn get_config(&self) -> &Config {
288 self.config.as_ref().unwrap()
289 }
290
291 pub fn get_function_registry(
293 &self,
294 ) -> &FunctionRegistry<(BuiltinFunction<C>, BuiltinCodegen<C>)> {
295 &self.sparse_registry
296 }
297
298 pub fn mem_size(&self) -> usize {
300 std::mem::size_of::<Self>()
301 .saturating_add(if self.config.is_some() {
302 std::mem::size_of::<Config>()
303 } else {
304 0
305 })
306 .saturating_add(self.sparse_registry.mem_size())
307 }
308
309 pub fn register_function(
313 &mut self,
314 name: &str,
315 entry: (BuiltinFunction<C>, BuiltinCodegen<C>),
316 ) -> Result<(), ElfError> {
317 let key = ebpf::hash_symbol_name(name.as_bytes());
318 self.sparse_registry
319 .register_function(key, name, entry)
320 .map(|_| ())
321 }
322
323 pub fn register_definition<BFD: BuiltinFunctionDefinition<C>>(
325 &mut self,
326 name: &str,
327 ) -> Result<(), ElfError> {
328 self.register_function(name, (BFD::vm, BFD::codegen))
329 }
330
331 pub fn unregister_function(&mut self, name: &str) -> bool {
333 let key = ebpf::hash_symbol_name(name.as_bytes());
334 self.sparse_registry.unregister_function(key)
335 }
336}
337
338pub trait BuiltinFunctionDefinition<C>
340where
341 C: crate::vm::ContextObject,
342{
343 type Error: Into<Box<dyn core::error::Error>>;
345
346 fn rust(
350 vm: &mut C,
351 arg_a: u64,
352 arg_b: u64,
353 arg_c: u64,
354 arg_d: u64,
355 arg_e: u64,
356 ) -> Result<u64, Self::Error>;
357
358 #[expect(clippy::arithmetic_side_effects)]
360 fn vm(mut vm: EncryptedHostAddressToEbpfVm<C>, a: u64, b: u64, c: u64, d: u64, e: u64) {
361 unsafe {
362 vm.with_vm(|vm| {
364 let enable_insn_meter = vm.loader.get_config().enable_instruction_meter;
365 if enable_insn_meter {
366 let used_cus = vm.previous_instruction_meter - vm.due_insn_count;
367 vm.context().consume(used_cus);
368 }
369 let converted_result: crate::error::ProgramResult =
370 Self::rust(vm.context(), a, b, c, d, e)
371 .map_err(|err| crate::error::EbpfError::SyscallError(err.into()))
372 .into();
373 vm.program_result = converted_result;
374 if enable_insn_meter {
375 vm.previous_instruction_meter = vm.context().get_remaining();
376 }
377 })
378 }
379 }
380
381 fn codegen(jit: &mut JitCompiler<C>) {
386 jit.emit_external_call(Self::vm);
387 }
388
389 fn register(program: &mut BuiltinProgram<C>, name: &str) -> Result<(), ElfError>
391 where
392 Self: Sized,
393 {
394 program.register_definition::<Self>(name)
395 }
396}
397
398impl<C: ContextObject> std::fmt::Debug for BuiltinProgram<C> {
399 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
400 f.debug_struct("BuiltinProgram")
401 .field("registry", unsafe {
402 std::mem::transmute::<
403 &FunctionRegistry<(BuiltinFunction<C>, BuiltinCodegen<C>)>,
404 &FunctionRegistry<(usize, usize)>,
405 >(&self.sparse_registry)
406 })
407 .finish()
408 }
409}
410
411#[macro_export]
413macro_rules! declare_builtin_function {
414 (
415 $(#[$attr:meta])*
416 $name:ident $(<$($generic_ident:tt : $generic_type:tt),+>)?,
417 fn rust(
418 $vm:ident : &mut $ContextObject:ty,
419 $arg_a:ident : u64,
420 $arg_b:ident : u64,
421 $arg_c:ident : u64,
422 $arg_d:ident : u64,
423 $arg_e:ident : u64,
424 ) -> Result<$Ok:ty, $Err:ty> {
425 $($rust:tt)*
426 }
427 $(fn codegen(
428 $jit:ident : &mut $crate::program::JitCompiler<$ContextObject2:ty>,
429 ) {
430 $($codegen:tt)*
431 })?
432 ) => {
433 $(#[$attr])*
434 pub struct $name $(<$($generic_ident),+>)? (
435 $(std::marker::PhantomData<($($generic_ident,)+)>)?
436 );
437 impl $(<$($generic_ident : $generic_type),+>)?
438 $crate::program::BuiltinFunctionDefinition<$ContextObject> for
439 $name $(<$($generic_ident),+>)?
440 {
441 type Error = $Err;
442 fn rust(
443 $vm: &mut $ContextObject,
444 $arg_a: u64,
445 $arg_b: u64,
446 $arg_c: u64,
447 $arg_d: u64,
448 $arg_e: u64,
449 ) -> core::result::Result<$Ok, $Err> {
450 $($rust)*
451 }
452 $(fn codegen(
453 $jit: &mut $crate::program::JitCompiler<$ContextObject2>,
454 ) {
455 $($codegen)*
456 })?
457 }
458 };
459}