1use crate::{
10 aligned_memory::{is_memory_aligned, AlignedMemory},
11 ebpf::{self, EF_SBPF_V2, HOST_ALIGN, INSN_SIZE},
12 elf_parser::{
13 consts::{
14 ELFCLASS64, ELFDATA2LSB, ELFOSABI_NONE, EM_BPF, EM_SBPF, ET_DYN, R_X86_64_32,
15 R_X86_64_64, R_X86_64_NONE, R_X86_64_RELATIVE,
16 },
17 types::{Elf64Phdr, Elf64Shdr, Elf64Word},
18 Elf64, ElfParserError,
19 },
20 error::EbpfError,
21 memory_region::MemoryRegion,
22 program::{BuiltinProgram, FunctionRegistry, SBPFVersion},
23 verifier::Verifier,
24 vm::{Config, ContextObject},
25};
26
27#[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
28use crate::jit::{JitCompiler, JitProgram};
29use byteorder::{ByteOrder, LittleEndian};
30use std::{collections::BTreeMap, fmt::Debug, mem, ops::Range, str};
31
32#[cfg(not(feature = "shuttle-test"))]
33use std::sync::Arc;
34
35#[cfg(feature = "shuttle-test")]
36use shuttle::sync::Arc;
37
38#[derive(Debug, thiserror::Error, PartialEq, Eq)]
40pub enum ElfError {
41 #[error("Failed to parse ELF file: {0}")]
43 FailedToParse(String),
44 #[error("Entrypoint out of bounds")]
46 EntrypointOutOfBounds,
47 #[error("Invalid entrypoint")]
49 InvalidEntrypoint,
50 #[error("Failed to get section {0}")]
52 FailedToGetSection(String),
53 #[error("Unresolved symbol ({0}) at instruction #{1:?} (ELF file offset {2:#x})")]
55 UnresolvedSymbol(String, usize, usize),
56 #[error("Section not found: {0}")]
58 SectionNotFound(String),
59 #[error("Relative jump out of bounds at instruction #{0}")]
61 RelativeJumpOutOfBounds(usize),
62 #[error("Symbol hash collision {0:#x}")]
64 SymbolHashCollision(u32),
65 #[error("Incompatible ELF: wrong endianess")]
67 WrongEndianess,
68 #[error("Incompatible ELF: wrong ABI")]
70 WrongAbi,
71 #[error("Incompatible ELF: wrong machine")]
73 WrongMachine,
74 #[error("Incompatible ELF: wrong class")]
76 WrongClass,
77 #[error("Multiple or no text sections, consider removing llc option: -function-sections")]
79 NotOneTextSection,
80 #[error("Found writable section ({0}) in ELF, read-write data not supported")]
82 WritableSectionNotSupported(String),
83 #[error("Relocation failed, no loadable section contains virtual address {0:#x}")]
85 AddressOutsideLoadableSection(u64),
86 #[error("Relocation failed, invalid referenced virtual address {0:#x}")]
88 InvalidVirtualAddress(u64),
89 #[error("Relocation failed, unknown type {0:?}")]
91 UnknownRelocation(u32),
92 #[error("Failed to read relocation info")]
94 FailedToReadRelocationInfo,
95 #[error("Incompatible ELF: wrong type")]
97 WrongType,
98 #[error("Unknown symbol with index {0}")]
100 UnknownSymbol(usize),
101 #[error("Offset or value is out of bounds")]
103 ValueOutOfBounds,
104 #[error("Detected sbpf_version required by the executable which are not enabled")]
106 UnsupportedSBPFVersion,
107 #[error("Invalid ELF program header")]
109 InvalidProgramHeader,
110}
111
112impl From<ElfParserError> for ElfError {
113 fn from(err: ElfParserError) -> Self {
114 match err {
115 ElfParserError::InvalidSectionHeader
116 | ElfParserError::InvalidString
117 | ElfParserError::InvalidSize
118 | ElfParserError::Overlap
119 | ElfParserError::SectionNotInOrder
120 | ElfParserError::NoSectionNameStringTable
121 | ElfParserError::InvalidDynamicSectionTable
122 | ElfParserError::InvalidRelocationTable
123 | ElfParserError::InvalidAlignment
124 | ElfParserError::NoStringTable
125 | ElfParserError::NoDynamicStringTable
126 | ElfParserError::InvalidFileHeader
127 | ElfParserError::StringTooLong(_, _) => ElfError::FailedToParse(err.to_string()),
128 ElfParserError::InvalidProgramHeader => ElfError::InvalidProgramHeader,
129 ElfParserError::OutOfBounds => ElfError::ValueOutOfBounds,
130 }
131 }
132}
133
134fn get_section(elf: &Elf64, name: &[u8]) -> Result<Elf64Shdr, ElfError> {
135 for section_header in elf.section_header_table() {
136 if elf.section_name(section_header.sh_name)? == name {
137 return Ok(section_header.clone());
138 }
139 }
140
141 Err(ElfError::SectionNotFound(
142 std::str::from_utf8(name)
143 .unwrap_or("UTF-8 error")
144 .to_string(),
145 ))
146}
147
148const BYTE_OFFSET_IMMEDIATE: usize = 4;
165const BYTE_LENGTH_IMMEDIATE: usize = 4;
167
168#[allow(non_camel_case_types)]
170#[derive(Debug, PartialEq, Copy, Clone)]
171enum BpfRelocationType {
172 R_Bpf_None = 0,
174 R_Bpf_64_64 = 1,
180 R_Bpf_64_Relative = 8,
190 R_Bpf_64_32 = 10,
203}
204impl BpfRelocationType {
205 fn from_x86_relocation_type(from: u32) -> Option<BpfRelocationType> {
206 match from {
207 R_X86_64_NONE => Some(BpfRelocationType::R_Bpf_None),
208 R_X86_64_64 => Some(BpfRelocationType::R_Bpf_64_64),
209 R_X86_64_RELATIVE => Some(BpfRelocationType::R_Bpf_64_Relative),
210 R_X86_64_32 => Some(BpfRelocationType::R_Bpf_64_32),
211 _ => None,
212 }
213 }
214}
215
216#[derive(Debug, PartialEq)]
218pub enum Section {
219 Owned(usize, Vec<u8>),
224 Borrowed(usize, Range<usize>),
230}
231
232#[derive(Debug, PartialEq)]
234pub struct Executable<C: ContextObject> {
235 elf_bytes: AlignedMemory<{ HOST_ALIGN }>,
237 sbpf_version: SBPFVersion,
239 ro_section: Section,
241 text_section_vaddr: u64,
243 text_section_range: Range<usize>,
245 entry_pc: usize,
247 function_registry: FunctionRegistry<usize>,
249 loader: Arc<BuiltinProgram<C>>,
251 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
253 compiled_program: Option<JitProgram>,
254}
255
256impl<C: ContextObject> Executable<C> {
257 pub fn get_config(&self) -> &Config {
259 self.loader.get_config()
260 }
261
262 pub fn get_sbpf_version(&self) -> SBPFVersion {
264 self.sbpf_version
265 }
266
267 pub fn get_text_bytes(&self) -> (u64, &[u8]) {
269 (
270 self.text_section_vaddr,
271 &self.elf_bytes.as_slice()[self.text_section_range.clone()],
272 )
273 }
274
275 pub fn get_ro_section(&self) -> &[u8] {
277 match &self.ro_section {
278 Section::Owned(_offset, data) => data.as_slice(),
279 Section::Borrowed(_offset, byte_range) => {
280 &self.elf_bytes.as_slice()[byte_range.clone()]
281 }
282 }
283 }
284
285 pub fn get_ro_region(&self) -> MemoryRegion {
287 get_ro_region(&self.ro_section, self.elf_bytes.as_slice())
288 }
289
290 pub fn get_entrypoint_instruction_offset(&self) -> usize {
292 self.entry_pc
293 }
294
295 #[cfg(feature = "debugger")]
297 pub fn get_text_section_offset(&self) -> u64 {
298 self.text_section_range.start as u64
299 }
300
301 pub fn get_loader(&self) -> &Arc<BuiltinProgram<C>> {
303 &self.loader
304 }
305
306 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
308 pub fn get_compiled_program(&self) -> Option<&JitProgram> {
309 self.compiled_program.as_ref()
310 }
311
312 pub fn verify<V: Verifier>(&self) -> Result<(), EbpfError> {
314 <V as Verifier>::verify(
315 self.get_text_bytes().1,
316 self.get_config(),
317 self.get_sbpf_version(),
318 self.get_function_registry(),
319 self.loader.get_function_registry(),
320 )?;
321 Ok(())
322 }
323
324 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
326 pub fn jit_compile(&mut self) -> Result<(), crate::error::EbpfError> {
327 let jit = JitCompiler::<C>::new(self)?;
328 self.compiled_program = Some(jit.compile()?);
329 Ok(())
330 }
331
332 pub fn get_function_registry(&self) -> &FunctionRegistry<usize> {
334 &self.function_registry
335 }
336
337 pub fn new_from_text_bytes(
339 text_bytes: &[u8],
340 loader: Arc<BuiltinProgram<C>>,
341 sbpf_version: SBPFVersion,
342 mut function_registry: FunctionRegistry<usize>,
343 ) -> Result<Self, ElfError> {
344 let elf_bytes = AlignedMemory::from_slice(text_bytes);
345 let entry_pc = if let Some((_name, pc)) = function_registry.lookup_by_name(b"entrypoint") {
346 pc
347 } else {
348 function_registry.register_function_hashed_legacy(
349 &loader,
350 !sbpf_version.static_syscalls(),
351 *b"entrypoint",
352 0,
353 )?;
354 0
355 };
356 Ok(Self {
357 elf_bytes,
358 sbpf_version,
359 ro_section: Section::Borrowed(ebpf::MM_RODATA_START as usize, 0..text_bytes.len()),
360 text_section_vaddr: if sbpf_version.enable_lower_bytecode_vaddr() {
361 ebpf::MM_BYTECODE_START
362 } else {
363 ebpf::MM_RODATA_START
364 },
365 text_section_range: 0..text_bytes.len(),
366 entry_pc,
367 function_registry,
368 loader,
369 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
370 compiled_program: None,
371 })
372 }
373
374 pub fn load(bytes: &[u8], loader: Arc<BuiltinProgram<C>>) -> Result<Self, ElfError> {
376 const E_FLAGS_OFFSET: usize = 48;
377 let e_flags = LittleEndian::read_u32(
378 bytes
379 .get(E_FLAGS_OFFSET..E_FLAGS_OFFSET.saturating_add(std::mem::size_of::<u32>()))
380 .ok_or(ElfParserError::OutOfBounds)?,
381 );
382 let config = loader.get_config();
383 let sbpf_version = if config.enabled_sbpf_versions.end() == &SBPFVersion::V0 {
384 if e_flags == EF_SBPF_V2 {
385 SBPFVersion::Reserved
386 } else {
387 SBPFVersion::V0
388 }
389 } else {
390 match e_flags {
391 0 => SBPFVersion::V0,
392 1 => SBPFVersion::V1,
393 2 => SBPFVersion::V2,
394 3 => SBPFVersion::V3,
395 _ => SBPFVersion::Reserved,
396 }
397 };
398 if !config.enabled_sbpf_versions.contains(&sbpf_version) {
399 return Err(ElfError::UnsupportedSBPFVersion);
400 }
401
402 let mut executable = if sbpf_version.enable_stricter_elf_headers() {
403 Self::load_with_strict_parser(bytes, loader)?
404 } else {
405 Self::load_with_lenient_parser(bytes, loader)?
406 };
407 executable.sbpf_version = sbpf_version;
408 Ok(executable)
409 }
410
411 pub fn load_with_strict_parser(
413 bytes: &[u8],
414 loader: Arc<BuiltinProgram<C>>,
415 ) -> Result<Self, ElfParserError> {
416 use crate::elf_parser::{
417 consts::{
418 ELFMAG, EV_CURRENT, PF_R, PF_W, PF_X, PT_GNU_STACK, PT_LOAD, PT_NULL, SHN_UNDEF,
419 STT_FUNC,
420 },
421 types::{Elf64Ehdr, Elf64Shdr, Elf64Sym},
422 };
423
424 let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
425 let elf_bytes = aligned_memory.as_slice();
426
427 let (file_header_range, file_header) = Elf64::parse_file_header(elf_bytes)?;
428 let program_header_table_range = mem::size_of::<Elf64Ehdr>()
429 ..mem::size_of::<Elf64Phdr>()
430 .saturating_mul(file_header.e_phnum as usize)
431 .saturating_add(mem::size_of::<Elf64Ehdr>());
432 if file_header.e_ident.ei_mag != ELFMAG
433 || file_header.e_ident.ei_class != ELFCLASS64
434 || file_header.e_ident.ei_data != ELFDATA2LSB
435 || file_header.e_ident.ei_version != EV_CURRENT as u8
436 || file_header.e_ident.ei_osabi != ELFOSABI_NONE
437 || file_header.e_ident.ei_abiversion != 0x00
438 || file_header.e_ident.ei_pad != [0x00; 7]
439 || file_header.e_type != ET_DYN
440 || file_header.e_machine != EM_SBPF
441 || file_header.e_version != EV_CURRENT
442 || file_header.e_phoff != mem::size_of::<Elf64Ehdr>() as u64
444 || file_header.e_ehsize != mem::size_of::<Elf64Ehdr>() as u16
447 || file_header.e_phentsize != mem::size_of::<Elf64Phdr>() as u16
448 || file_header.e_phnum < EXPECTED_PROGRAM_HEADERS.len() as u16
449 || program_header_table_range.end >= elf_bytes.len()
450 || file_header.e_shentsize != mem::size_of::<Elf64Shdr>() as u16
451 || file_header.e_shstrndx >= file_header.e_shnum
453 {
454 return Err(ElfParserError::InvalidFileHeader);
455 }
456
457 const EXPECTED_PROGRAM_HEADERS: [(u32, u32, u64); 5] = [
458 (PT_LOAD, PF_X, ebpf::MM_BYTECODE_START), (PT_LOAD, PF_R, ebpf::MM_RODATA_START), (PT_GNU_STACK, PF_R | PF_W, ebpf::MM_STACK_START), (PT_LOAD, PF_R | PF_W, ebpf::MM_HEAP_START), (PT_NULL, 0, 0xFFFFFFFF00000000), ];
464 let program_header_table =
465 Elf64::slice_from_bytes::<Elf64Phdr>(elf_bytes, program_header_table_range.clone())?;
466 for (program_header, (p_type, p_flags, p_vaddr)) in program_header_table
467 .iter()
468 .zip(EXPECTED_PROGRAM_HEADERS.iter())
469 {
470 let p_filesz = if (*p_flags & PF_W) != 0 {
471 0
472 } else {
473 program_header.p_memsz
474 };
475 if program_header.p_type != *p_type
476 || program_header.p_flags != *p_flags
477 || program_header.p_offset < program_header_table_range.end as u64
478 || program_header.p_offset >= elf_bytes.len() as u64
479 || program_header.p_offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
480 || program_header.p_vaddr != *p_vaddr
481 || program_header.p_paddr != *p_vaddr
482 || program_header.p_filesz != p_filesz
483 || program_header.p_filesz
484 > (elf_bytes.len() as u64).saturating_sub(program_header.p_offset)
485 || program_header.p_memsz >= ebpf::MM_REGION_SIZE
486 {
487 return Err(ElfParserError::InvalidProgramHeader);
488 }
489 }
490
491 let config = loader.get_config();
492 let symbol_names_section_header = if config.enable_symbol_and_section_labels {
493 let (_section_header_table_range, section_header_table) =
494 Elf64::parse_section_header_table(
495 elf_bytes,
496 file_header_range.clone(),
497 file_header,
498 program_header_table_range.clone(),
499 )?;
500 let section_names_section_header = (file_header.e_shstrndx != SHN_UNDEF)
501 .then(|| {
502 section_header_table
503 .get(file_header.e_shstrndx as usize)
504 .ok_or(ElfParserError::OutOfBounds)
505 })
506 .transpose()?
507 .ok_or(ElfParserError::NoSectionNameStringTable)?;
508 let mut symbol_names_section_header = None;
509 for section_header in section_header_table.iter() {
510 let section_name = Elf64::get_string_in_section(
511 elf_bytes,
512 section_names_section_header,
513 section_header.sh_name,
514 64,
515 )?;
516 if section_name == b".dynstr" {
517 symbol_names_section_header = Some(section_header);
518 }
519 }
520 symbol_names_section_header
521 } else {
522 None
523 };
524 let bytecode_header = &program_header_table[0];
525 let rodata_header = &program_header_table[1];
526 let dynamic_symbol_table: &[Elf64Sym] =
527 Elf64::slice_from_program_header(elf_bytes, &program_header_table[4])?;
528 let mut function_registry = FunctionRegistry::<usize>::default();
529 let mut expected_symbol_address = bytecode_header.p_vaddr;
530 for symbol in dynamic_symbol_table {
531 if symbol.st_info & STT_FUNC == 0 {
532 continue;
533 }
534 if symbol.st_value != expected_symbol_address {
535 return Err(ElfParserError::OutOfBounds);
536 }
537 if symbol.st_size == 0 || symbol.st_size.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
538 {
539 return Err(ElfParserError::InvalidSize);
540 }
541 if symbol.st_size
542 > bytecode_header
543 .vm_range()
544 .end
545 .saturating_sub(symbol.st_value)
546 {
547 return Err(ElfParserError::OutOfBounds);
548 }
549 let target_pc = symbol
550 .st_value
551 .saturating_sub(bytecode_header.p_vaddr)
552 .checked_div(ebpf::INSN_SIZE as u64)
553 .unwrap_or_default() as usize;
554 let name = if config.enable_symbol_and_section_labels {
555 Elf64::get_string_in_section(
556 elf_bytes,
557 symbol_names_section_header
558 .as_ref()
559 .ok_or(ElfParserError::NoStringTable)?,
560 symbol.st_name as Elf64Word,
561 u8::MAX as usize,
562 )?
563 } else {
564 &[]
565 };
566 function_registry
567 .register_function(target_pc as u32, name, target_pc)
568 .unwrap();
569 expected_symbol_address = symbol.st_value.saturating_add(symbol.st_size);
570 }
571 if expected_symbol_address != bytecode_header.vm_range().end {
572 return Err(ElfParserError::OutOfBounds);
573 }
574 if !bytecode_header.vm_range().contains(&file_header.e_entry)
575 || file_header.e_entry.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
576 {
577 return Err(ElfParserError::InvalidFileHeader);
578 }
579 let entry_pc = file_header
580 .e_entry
581 .saturating_sub(bytecode_header.p_vaddr)
582 .checked_div(ebpf::INSN_SIZE as u64)
583 .unwrap_or_default() as usize;
584 if function_registry.lookup_by_key(entry_pc as u32).is_none() {
585 return Err(ElfParserError::InvalidFileHeader);
586 }
587
588 let text_section_vaddr = bytecode_header.p_vaddr;
589 let text_section_range = bytecode_header.file_range().unwrap_or_default();
590 let ro_section = Section::Borrowed(
591 rodata_header.p_vaddr as usize,
592 rodata_header.file_range().unwrap_or_default(),
593 );
594 Ok(Self {
595 elf_bytes: aligned_memory,
596 sbpf_version: SBPFVersion::Reserved, ro_section,
598 text_section_vaddr,
599 text_section_range,
600 entry_pc,
601 function_registry,
602 loader,
603 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
604 compiled_program: None,
605 })
606 }
607
608 fn load_with_lenient_parser(
610 bytes: &[u8],
611 loader: Arc<BuiltinProgram<C>>,
612 ) -> Result<Self, ElfError> {
613 let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
615 let (mut elf_bytes, unrelocated_elf_bytes) =
616 if is_memory_aligned(bytes.as_ptr() as usize, HOST_ALIGN) {
617 (aligned_memory, bytes)
618 } else {
619 (aligned_memory.clone(), aligned_memory.as_slice())
621 };
622 let elf = Elf64::parse(unrelocated_elf_bytes)?;
623
624 let config = loader.get_config();
625 let header = elf.file_header();
626 let sbpf_version = if header.e_flags == EF_SBPF_V2 {
627 SBPFVersion::Reserved
628 } else {
629 SBPFVersion::V0
630 };
631
632 Self::validate(config, &elf, elf_bytes.as_slice())?;
633
634 let text_section = get_section(&elf, b".text")?;
636 let text_section_vaddr =
637 if sbpf_version.enable_elf_vaddr() && text_section.sh_addr >= ebpf::MM_RODATA_START {
638 text_section.sh_addr
639 } else {
640 text_section.sh_addr.saturating_add(ebpf::MM_RODATA_START)
641 };
642 let vaddr_end = if sbpf_version.reject_rodata_stack_overlap() {
643 text_section_vaddr.saturating_add(text_section.sh_size)
644 } else {
645 text_section_vaddr
646 };
647 if (config.reject_broken_elfs
648 && !sbpf_version.enable_elf_vaddr()
649 && text_section.sh_addr != text_section.sh_offset)
650 || vaddr_end > ebpf::MM_STACK_START
651 {
652 return Err(ElfError::ValueOutOfBounds);
653 }
654
655 let mut function_registry = FunctionRegistry::default();
657 Self::relocate(
658 &mut function_registry,
659 &loader,
660 &elf,
661 elf_bytes.as_slice_mut(),
662 )?;
663
664 let offset = header.e_entry.saturating_sub(text_section.sh_addr);
666 if offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0) {
667 return Err(ElfError::InvalidEntrypoint);
668 }
669 let entry_pc = if let Some(entry_pc) = (offset as usize).checked_div(ebpf::INSN_SIZE) {
670 if !sbpf_version.static_syscalls() {
671 function_registry.unregister_function(ebpf::hash_symbol_name(b"entrypoint"));
672 }
673 function_registry.register_function_hashed_legacy(
674 &loader,
675 !sbpf_version.static_syscalls(),
676 *b"entrypoint",
677 entry_pc,
678 )?;
679 entry_pc
680 } else {
681 return Err(ElfError::InvalidEntrypoint);
682 };
683
684 let ro_section = Self::parse_ro_sections(
685 config,
686 &sbpf_version,
687 elf.section_header_table()
688 .iter()
689 .map(|s| (elf.section_name(s.sh_name).ok(), s)),
690 elf_bytes.as_slice(),
691 )?;
692
693 Ok(Self {
694 elf_bytes,
695 sbpf_version,
696 ro_section,
697 text_section_vaddr,
698 text_section_range: text_section.file_range().unwrap_or_default(),
699 entry_pc,
700 function_registry,
701 loader,
702 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
703 compiled_program: None,
704 })
705 }
706
707 #[rustfmt::skip]
709 #[allow(clippy::size_of_ref)]
710 pub fn mem_size(&self) -> usize {
711 let mut total = mem::size_of::<Self>();
712 total = total
713 .saturating_add(self.elf_bytes.mem_size())
715 .saturating_add(match &self.ro_section {
717 Section::Owned(_, data) => data.capacity(),
718 Section::Borrowed(_, _) => 0,
719 })
720 .saturating_add(self.function_registry.mem_size());
722
723 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
724 {
725 total = total.saturating_add(self.compiled_program.as_ref().map_or(0, |program| program.mem_size()));
727 }
728
729 total
730 }
731
732 pub fn validate(config: &Config, elf: &Elf64, elf_bytes: &[u8]) -> Result<(), ElfError> {
736 let header = elf.file_header();
737 if header.e_ident.ei_class != ELFCLASS64 {
738 return Err(ElfError::WrongClass);
739 }
740 if header.e_ident.ei_data != ELFDATA2LSB {
741 return Err(ElfError::WrongEndianess);
742 }
743 if header.e_ident.ei_osabi != ELFOSABI_NONE {
744 return Err(ElfError::WrongAbi);
745 }
746 if header.e_machine != EM_BPF && header.e_machine != EM_SBPF {
747 return Err(ElfError::WrongMachine);
748 }
749 if header.e_type != ET_DYN {
750 return Err(ElfError::WrongType);
751 }
752
753 let sbpf_version = if header.e_flags == EF_SBPF_V2 {
754 SBPFVersion::Reserved
755 } else {
756 SBPFVersion::V0
757 };
758 if !config.enabled_sbpf_versions.contains(&sbpf_version) {
759 return Err(ElfError::UnsupportedSBPFVersion);
760 }
761
762 if sbpf_version.enable_elf_vaddr() {
763 if !config.optimize_rodata {
764 return Err(ElfError::UnsupportedSBPFVersion);
768 }
769
770 if elf.program_header_table().iter().count() >= 10 {
776 return Err(ElfError::InvalidProgramHeader);
777 }
778 }
779
780 let num_text_sections =
781 elf.section_header_table()
782 .iter()
783 .fold(0, |count: usize, section_header| {
784 if let Ok(this_name) = elf.section_name(section_header.sh_name) {
785 if this_name == b".text" {
786 return count.saturating_add(1);
787 }
788 }
789 count
790 });
791 if 1 != num_text_sections {
792 return Err(ElfError::NotOneTextSection);
793 }
794
795 for section_header in elf.section_header_table().iter() {
796 if let Ok(name) = elf.section_name(section_header.sh_name) {
797 if name.starts_with(b".bss")
798 || (section_header.is_writable()
799 && (name.starts_with(b".data") && !name.starts_with(b".data.rel")))
800 {
801 return Err(ElfError::WritableSectionNotSupported(
802 String::from_utf8_lossy(name).to_string(),
803 ));
804 }
805 }
806 }
807
808 for section_header in elf.section_header_table().iter() {
809 let start = section_header.sh_offset as usize;
810 let end = section_header
811 .sh_offset
812 .checked_add(section_header.sh_size)
813 .ok_or(ElfError::ValueOutOfBounds)? as usize;
814 let _ = elf_bytes
815 .get(start..end)
816 .ok_or(ElfError::ValueOutOfBounds)?;
817 }
818 let text_section = get_section(elf, b".text")?;
819 if !text_section.vm_range().contains(&header.e_entry) {
820 return Err(ElfError::EntrypointOutOfBounds);
821 }
822
823 Ok(())
824 }
825
826 pub fn parse_ro_sections<'a, S: IntoIterator<Item = (Option<&'a [u8]>, &'a Elf64Shdr)>>(
828 config: &Config,
829 sbpf_version: &SBPFVersion,
830 sections: S,
831 elf_bytes: &[u8],
832 ) -> Result<Section, ElfError> {
833 let mut lowest_addr = usize::MAX;
835 let mut highest_addr = 0;
837 let mut ro_fill_length = 0usize;
839 let mut invalid_offsets = false;
840 let mut addr_file_offset = None;
844
845 let mut first_ro_section = 0;
848 let mut last_ro_section = 0;
849 let mut n_ro_sections = 0usize;
850
851 let mut ro_slices = vec![];
852 for (i, (name, section_header)) in sections.into_iter().enumerate() {
853 match name {
854 Some(name)
855 if name == b".text"
856 || name == b".rodata"
857 || name == b".data.rel.ro"
858 || name == b".eh_frame" => {}
859 _ => continue,
860 }
861
862 if n_ro_sections == 0 {
863 first_ro_section = i;
864 }
865 last_ro_section = i;
866 n_ro_sections = n_ro_sections.saturating_add(1);
867
868 let section_addr = section_header.sh_addr;
869
870 if !invalid_offsets {
881 if sbpf_version.enable_elf_vaddr() {
882 debug_assert!(config.optimize_rodata);
884 if section_addr < section_header.sh_offset {
885 invalid_offsets = true;
886 } else {
887 let offset = section_addr.saturating_sub(section_header.sh_offset);
888 if *addr_file_offset.get_or_insert(offset) != offset {
889 invalid_offsets = true;
894 }
895 }
896 } else if section_addr != section_header.sh_offset {
897 invalid_offsets = true;
898 }
899 }
900
901 let mut vaddr_end =
902 if sbpf_version.enable_elf_vaddr() && section_addr >= ebpf::MM_RODATA_START {
903 section_addr
904 } else {
905 section_addr.saturating_add(ebpf::MM_RODATA_START)
906 };
907 if sbpf_version.reject_rodata_stack_overlap() {
908 vaddr_end = vaddr_end.saturating_add(section_header.sh_size);
909 }
910 if (config.reject_broken_elfs && invalid_offsets) || vaddr_end > ebpf::MM_STACK_START {
911 return Err(ElfError::ValueOutOfBounds);
912 }
913
914 let section_data = elf_bytes
915 .get(section_header.file_range().unwrap_or_default())
916 .ok_or(ElfError::ValueOutOfBounds)?;
917
918 let section_addr = section_addr as usize;
919 lowest_addr = lowest_addr.min(section_addr);
920 highest_addr = highest_addr.max(section_addr.saturating_add(section_data.len()));
921 ro_fill_length = ro_fill_length.saturating_add(section_data.len());
922
923 ro_slices.push((section_addr, section_data));
924 }
925
926 if config.reject_broken_elfs && lowest_addr.saturating_add(ro_fill_length) > highest_addr {
927 return Err(ElfError::ValueOutOfBounds);
928 }
929
930 let can_borrow = !invalid_offsets
931 && last_ro_section
932 .saturating_add(1)
933 .saturating_sub(first_ro_section)
934 == n_ro_sections;
935 if sbpf_version.enable_elf_vaddr() && !can_borrow {
936 return Err(ElfError::ValueOutOfBounds);
937 }
938 let ro_section = if config.optimize_rodata && can_borrow {
939 let buf_offset_start =
946 lowest_addr.saturating_sub(addr_file_offset.unwrap_or(0) as usize);
947 let buf_offset_end =
948 highest_addr.saturating_sub(addr_file_offset.unwrap_or(0) as usize);
949
950 let addr_offset = if lowest_addr >= ebpf::MM_RODATA_START as usize {
951 lowest_addr
956 } else {
957 if sbpf_version.enable_elf_vaddr() {
958 return Err(ElfError::ValueOutOfBounds);
959 }
960 lowest_addr.saturating_add(ebpf::MM_RODATA_START as usize)
961 };
962
963 Section::Borrowed(addr_offset, buf_offset_start..buf_offset_end)
964 } else {
965 if config.optimize_rodata {
969 highest_addr = highest_addr.saturating_sub(lowest_addr);
974 } else {
975 lowest_addr = 0;
979 };
980
981 let buf_len = highest_addr;
982 if buf_len > elf_bytes.len() {
983 return Err(ElfError::ValueOutOfBounds);
984 }
985
986 let mut ro_section = vec![0; buf_len];
987 for (section_addr, slice) in ro_slices.iter() {
988 let buf_offset_start = section_addr.saturating_sub(lowest_addr);
989 ro_section[buf_offset_start..buf_offset_start.saturating_add(slice.len())]
990 .copy_from_slice(slice);
991 }
992
993 let addr_offset = if lowest_addr >= ebpf::MM_RODATA_START as usize {
994 lowest_addr
995 } else {
996 lowest_addr.saturating_add(ebpf::MM_RODATA_START as usize)
997 };
998 Section::Owned(addr_offset, ro_section)
999 };
1000
1001 Ok(ro_section)
1002 }
1003
1004 fn relocate(
1006 function_registry: &mut FunctionRegistry<usize>,
1007 loader: &BuiltinProgram<C>,
1008 elf: &Elf64,
1009 elf_bytes: &mut [u8],
1010 ) -> Result<(), ElfError> {
1011 let mut syscall_cache = BTreeMap::new();
1012 let text_section = get_section(elf, b".text")?;
1013 let sbpf_version = if elf.file_header().e_flags == EF_SBPF_V2 {
1014 SBPFVersion::Reserved
1015 } else {
1016 SBPFVersion::V0
1017 };
1018
1019 let config = loader.get_config();
1021 let text_bytes = elf_bytes
1022 .get_mut(text_section.file_range().unwrap_or_default())
1023 .ok_or(ElfError::ValueOutOfBounds)?;
1024 let instruction_count = text_bytes
1025 .len()
1026 .checked_div(ebpf::INSN_SIZE)
1027 .ok_or(ElfError::ValueOutOfBounds)?;
1028 for i in 0..instruction_count {
1029 let insn = ebpf::get_insn(text_bytes, i);
1030 if insn.opc == ebpf::CALL_IMM && insn.imm != -1 {
1031 let target_pc = (i as isize)
1032 .saturating_add(1)
1033 .saturating_add(insn.imm as isize);
1034 if target_pc < 0 || target_pc >= instruction_count as isize {
1035 return Err(ElfError::RelativeJumpOutOfBounds(i));
1036 }
1037 let name = if config.enable_symbol_and_section_labels {
1038 format!("function_{target_pc}")
1039 } else {
1040 String::default()
1041 };
1042 let key = function_registry.register_function_hashed_legacy(
1043 loader,
1044 !sbpf_version.static_syscalls(),
1045 name.as_bytes(),
1046 target_pc as usize,
1047 )?;
1048 if !sbpf_version.static_syscalls() {
1049 let offset = i.saturating_mul(ebpf::INSN_SIZE).saturating_add(4);
1050 let checked_slice = text_bytes
1051 .get_mut(offset..offset.saturating_add(4))
1052 .ok_or(ElfError::ValueOutOfBounds)?;
1053 LittleEndian::write_u32(checked_slice, key);
1054 }
1055 }
1056 }
1057
1058 let mut program_header: Option<&Elf64Phdr> = None;
1059
1060 for relocation in elf.dynamic_relocations_table().unwrap_or_default().iter() {
1062 let mut r_offset = relocation.r_offset as usize;
1063
1064 if sbpf_version.enable_elf_vaddr() {
1068 match program_header {
1069 Some(header) if header.vm_range().contains(&(r_offset as u64)) => {}
1070 _ => {
1071 program_header = elf
1072 .program_header_table()
1073 .iter()
1074 .find(|header| header.vm_range().contains(&(r_offset as u64)))
1075 }
1076 }
1077 let header = program_header.as_ref().ok_or(ElfError::ValueOutOfBounds)?;
1078 r_offset = r_offset
1079 .saturating_sub(header.p_vaddr as usize)
1080 .saturating_add(header.p_offset as usize);
1081 }
1082
1083 match BpfRelocationType::from_x86_relocation_type(relocation.r_type()) {
1084 Some(BpfRelocationType::R_Bpf_64_64) => {
1085 let imm_offset = if text_section
1087 .file_range()
1088 .unwrap_or_default()
1089 .contains(&r_offset)
1090 || sbpf_version == SBPFVersion::V0
1091 {
1092 r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE)
1093 } else {
1094 r_offset
1095 };
1096
1097 let checked_slice = elf_bytes
1100 .get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1101 .ok_or(ElfError::ValueOutOfBounds)?;
1102 let refd_addr = LittleEndian::read_u32(checked_slice) as u64;
1103
1104 let symbol = elf
1105 .dynamic_symbol_table()
1106 .and_then(|table| table.get(relocation.r_sym() as usize).cloned())
1107 .ok_or_else(|| ElfError::UnknownSymbol(relocation.r_sym() as usize))?;
1108
1109 let mut addr = symbol.st_value.saturating_add(refd_addr);
1112
1113 if addr < ebpf::MM_RODATA_START {
1118 addr = ebpf::MM_RODATA_START.saturating_add(addr);
1119 }
1120
1121 if text_section
1122 .file_range()
1123 .unwrap_or_default()
1124 .contains(&r_offset)
1125 || sbpf_version == SBPFVersion::V0
1126 {
1127 let imm_low_offset = imm_offset;
1128 let imm_high_offset = imm_low_offset.saturating_add(INSN_SIZE);
1129
1130 let imm_slice = elf_bytes
1132 .get_mut(
1133 imm_low_offset
1134 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1135 )
1136 .ok_or(ElfError::ValueOutOfBounds)?;
1137 LittleEndian::write_u32(imm_slice, (addr & 0xFFFFFFFF) as u32);
1138
1139 let imm_slice = elf_bytes
1141 .get_mut(
1142 imm_high_offset
1143 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1144 )
1145 .ok_or(ElfError::ValueOutOfBounds)?;
1146 LittleEndian::write_u32(
1147 imm_slice,
1148 addr.checked_shr(32).unwrap_or_default() as u32,
1149 );
1150 } else {
1151 let imm_slice = elf_bytes
1152 .get_mut(imm_offset..imm_offset.saturating_add(8))
1153 .ok_or(ElfError::ValueOutOfBounds)?;
1154 LittleEndian::write_u64(imm_slice, addr);
1155 }
1156 }
1157 Some(BpfRelocationType::R_Bpf_64_Relative) => {
1158 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1164
1165 if text_section
1166 .file_range()
1167 .unwrap_or_default()
1168 .contains(&r_offset)
1169 {
1170 let imm_low_offset = imm_offset;
1175 let imm_high_offset = r_offset
1176 .saturating_add(INSN_SIZE)
1177 .saturating_add(BYTE_OFFSET_IMMEDIATE);
1178
1179 let imm_slice = elf_bytes
1181 .get(
1182 imm_low_offset
1183 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1184 )
1185 .ok_or(ElfError::ValueOutOfBounds)?;
1186 let va_low = LittleEndian::read_u32(imm_slice) as u64;
1187
1188 let imm_slice = elf_bytes
1190 .get(
1191 imm_high_offset
1192 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1193 )
1194 .ok_or(ElfError::ValueOutOfBounds)?;
1195 let va_high = LittleEndian::read_u32(imm_slice) as u64;
1196
1197 let mut refd_addr = va_high.checked_shl(32).unwrap_or_default() | va_low;
1199
1200 if refd_addr == 0 {
1201 return Err(ElfError::InvalidVirtualAddress(refd_addr));
1202 }
1203
1204 if refd_addr < ebpf::MM_RODATA_START {
1205 refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1208 }
1209
1210 let imm_slice = elf_bytes
1212 .get_mut(
1213 imm_low_offset
1214 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1215 )
1216 .ok_or(ElfError::ValueOutOfBounds)?;
1217 LittleEndian::write_u32(imm_slice, (refd_addr & 0xFFFFFFFF) as u32);
1218
1219 let imm_slice = elf_bytes
1221 .get_mut(
1222 imm_high_offset
1223 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1224 )
1225 .ok_or(ElfError::ValueOutOfBounds)?;
1226 LittleEndian::write_u32(
1227 imm_slice,
1228 refd_addr.checked_shr(32).unwrap_or_default() as u32,
1229 );
1230 } else {
1231 let refd_addr = if sbpf_version != SBPFVersion::V0 {
1232 let addr_slice = elf_bytes
1236 .get(r_offset..r_offset.saturating_add(mem::size_of::<u64>()))
1237 .ok_or(ElfError::ValueOutOfBounds)?;
1238 let mut refd_addr = LittleEndian::read_u64(addr_slice);
1239 if refd_addr < ebpf::MM_RODATA_START {
1240 refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1242 }
1243 refd_addr
1244 } else {
1245 let addr_slice = elf_bytes
1251 .get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1252 .ok_or(ElfError::ValueOutOfBounds)?;
1253 let refd_addr = LittleEndian::read_u32(addr_slice) as u64;
1254 ebpf::MM_RODATA_START.saturating_add(refd_addr)
1255 };
1256
1257 let addr_slice = elf_bytes
1258 .get_mut(r_offset..r_offset.saturating_add(mem::size_of::<u64>()))
1259 .ok_or(ElfError::ValueOutOfBounds)?;
1260 LittleEndian::write_u64(addr_slice, refd_addr);
1261 }
1262 }
1263 Some(BpfRelocationType::R_Bpf_64_32) => {
1264 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1270
1271 let symbol = elf
1272 .dynamic_symbol_table()
1273 .and_then(|table| table.get(relocation.r_sym() as usize).cloned())
1274 .ok_or_else(|| ElfError::UnknownSymbol(relocation.r_sym() as usize))?;
1275
1276 let name = elf
1277 .dynamic_symbol_name(symbol.st_name as Elf64Word)
1278 .map_err(|_| ElfError::UnknownSymbol(symbol.st_name as usize))?;
1279
1280 let key = if symbol.is_function() && symbol.st_value != 0 {
1282 if !text_section.vm_range().contains(&symbol.st_value) {
1283 return Err(ElfError::ValueOutOfBounds);
1284 }
1285 let target_pc = (symbol.st_value.saturating_sub(text_section.sh_addr)
1286 as usize)
1287 .checked_div(ebpf::INSN_SIZE)
1288 .unwrap_or_default();
1289 function_registry.register_function_hashed_legacy(
1290 loader,
1291 !sbpf_version.static_syscalls(),
1292 name,
1293 target_pc,
1294 )?
1295 } else {
1296 let hash = *syscall_cache
1298 .entry(symbol.st_name)
1299 .or_insert_with(|| ebpf::hash_symbol_name(name));
1300 if config.reject_broken_elfs
1301 && loader.get_function_registry().lookup_by_key(hash).is_none()
1302 {
1303 return Err(ElfError::UnresolvedSymbol(
1304 String::from_utf8_lossy(name).to_string(),
1305 r_offset.checked_div(ebpf::INSN_SIZE).unwrap_or(0),
1306 r_offset,
1307 ));
1308 }
1309 hash
1310 };
1311
1312 let checked_slice = elf_bytes
1313 .get_mut(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1314 .ok_or(ElfError::ValueOutOfBounds)?;
1315 LittleEndian::write_u32(checked_slice, key);
1316 }
1317 _ => return Err(ElfError::UnknownRelocation(relocation.r_type())),
1318 }
1319 }
1320
1321 if config.enable_symbol_and_section_labels {
1322 for symbol in elf.symbol_table().ok().flatten().unwrap_or_default().iter() {
1324 if symbol.st_info & 0xEF != 0x02 {
1325 continue;
1326 }
1327 if !text_section.vm_range().contains(&symbol.st_value) {
1328 return Err(ElfError::ValueOutOfBounds);
1329 }
1330 let target_pc = (symbol.st_value.saturating_sub(text_section.sh_addr) as usize)
1331 .checked_div(ebpf::INSN_SIZE)
1332 .unwrap_or_default();
1333 let name = elf
1334 .symbol_name(symbol.st_name as Elf64Word)
1335 .map_err(|_| ElfError::UnknownSymbol(symbol.st_name as usize))?;
1336 function_registry.register_function_hashed_legacy(
1337 loader,
1338 !sbpf_version.static_syscalls(),
1339 name,
1340 target_pc,
1341 )?;
1342 }
1343 }
1344
1345 Ok(())
1346 }
1347
1348 #[allow(dead_code)]
1349 fn dump_data(name: &str, prog: &[u8]) {
1350 let mut eight_bytes: Vec<u8> = Vec::new();
1351 println!("{name}");
1352 for i in prog.iter() {
1353 if eight_bytes.len() >= 7 {
1354 println!("{eight_bytes:02X?}");
1355 eight_bytes.clear();
1356 } else {
1357 eight_bytes.push(*i);
1358 }
1359 }
1360 }
1361}
1362
1363pub fn get_ro_region(ro_section: &Section, elf: &[u8]) -> MemoryRegion {
1365 let (offset, ro_data) = match ro_section {
1366 Section::Owned(offset, data) => (*offset, data.as_slice()),
1367 Section::Borrowed(offset, byte_range) => (*offset, &elf[byte_range.clone()]),
1368 };
1369
1370 MemoryRegion::new_readonly(ro_data, offset as u64)
1374}