1use crate::{
10 aligned_memory::{is_memory_aligned, AlignedMemory},
11 ebpf::{self, 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)]
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: std::sync::Mutex<Option<Arc<JitProgram>>>,
254}
255
256impl<C: PartialEq + ContextObject> PartialEq for Executable<C> {
257 fn eq(&self, other: &Self) -> bool {
258 self.elf_bytes == other.elf_bytes
259 && self.sbpf_version == other.sbpf_version
260 && self.ro_section == other.ro_section
261 && self.text_section_vaddr == other.text_section_vaddr
262 && self.text_section_range == other.text_section_range
263 && self.entry_pc == other.entry_pc
264 && self.function_registry == other.function_registry
265 && *self.loader == *other.loader
266 && {
267 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
268 {
269 let other = other.get_compiled_program();
272 let this = self
273 .compiled_program
274 .lock()
275 .unwrap_or_else(|e| e.into_inner());
276 *this == other
277 }
278 #[cfg(not(all(
279 feature = "jit",
280 not(target_os = "windows"),
281 target_arch = "x86_64"
282 )))]
283 true
284 }
285 }
286}
287
288impl<C: ContextObject> Executable<C> {
289 pub fn get_config(&self) -> &Config {
291 self.loader.get_config()
292 }
293
294 pub fn get_sbpf_version(&self) -> SBPFVersion {
296 self.sbpf_version
297 }
298
299 pub fn get_text_bytes(&self) -> (u64, &[u8]) {
301 (
302 self.text_section_vaddr,
303 &self.elf_bytes.as_slice()[self.text_section_range.clone()],
304 )
305 }
306
307 pub fn get_ro_section(&self) -> &[u8] {
309 match &self.ro_section {
310 Section::Owned(_offset, data) => data.as_slice(),
311 Section::Borrowed(_offset, byte_range) => {
312 &self.elf_bytes.as_slice()[byte_range.clone()]
313 }
314 }
315 }
316
317 pub fn get_ro_region(&self) -> MemoryRegion {
319 get_ro_region(&self.ro_section, self.elf_bytes.as_slice())
320 }
321
322 pub fn get_entrypoint_instruction_offset(&self) -> usize {
324 self.entry_pc
325 }
326
327 #[cfg(feature = "debugger")]
329 pub fn get_text_section_offset(&self) -> u64 {
330 self.text_section_range.start as u64
331 }
332
333 pub fn get_loader(&self) -> &Arc<BuiltinProgram<C>> {
335 &self.loader
336 }
337
338 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
343 pub fn get_compiled_program(&self) -> Option<Arc<JitProgram>> {
344 let guard = self
345 .compiled_program
346 .lock()
347 .unwrap_or_else(|e| e.into_inner());
348 guard.as_ref().map(Arc::clone)
349 }
350
351 pub fn verify<V: Verifier>(&self) -> Result<(), EbpfError> {
353 <V as Verifier>::verify(
354 self.get_text_bytes().1,
355 self.get_config(),
356 self.get_sbpf_version(),
357 )?;
358 Ok(())
359 }
360
361 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
373 pub fn jit_compile(&self) -> Result<(), crate::error::EbpfError> {
374 let jit = JitCompiler::<C>::new(self)?;
375 let compiled = Arc::new(jit.compile()?);
376 let mut guard = self
377 .compiled_program
378 .lock()
379 .unwrap_or_else(|e| e.into_inner());
380 *guard = Some(compiled);
381 Ok(())
382 }
383
384 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
390 pub fn take_compiled_program(&self) -> Option<Arc<JitProgram>> {
391 let mut guard = self
392 .compiled_program
393 .lock()
394 .unwrap_or_else(|e| e.into_inner());
395 guard.take()
396 }
397
398 pub fn get_function_registry(&self) -> &FunctionRegistry<usize> {
400 &self.function_registry
401 }
402
403 pub fn new_from_text_bytes(
405 text_bytes: &[u8],
406 loader: Arc<BuiltinProgram<C>>,
407 sbpf_version: SBPFVersion,
408 mut function_registry: FunctionRegistry<usize>,
409 ) -> Result<Self, ElfError> {
410 let elf_bytes = AlignedMemory::from_slice(text_bytes);
411 let entry_pc = if let Some((_name, pc)) = function_registry.lookup_by_name(b"entrypoint") {
412 pc
413 } else {
414 function_registry.register_function_hashed_legacy(
415 &loader,
416 !sbpf_version.static_syscalls(),
417 *b"entrypoint",
418 0,
419 )?;
420 0
421 };
422 Ok(Self {
423 elf_bytes,
424 sbpf_version,
425 ro_section: Section::Borrowed(
426 if sbpf_version.enable_lower_rodata_vaddr() {
427 ebpf::MM_RODATA_START
428 } else {
429 ebpf::MM_BYTECODE_START
430 } as usize,
431 0..text_bytes.len(),
432 ),
433 text_section_vaddr: ebpf::MM_BYTECODE_START,
434 text_section_range: 0..text_bytes.len(),
435 entry_pc,
436 function_registry,
437 loader,
438 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
439 compiled_program: None.into(),
440 })
441 }
442
443 pub fn load(bytes: &[u8], loader: Arc<BuiltinProgram<C>>) -> Result<Self, ElfError> {
445 const E_FLAGS_OFFSET: usize = 48;
446 let e_flags = LittleEndian::read_u32(
447 bytes
448 .get(E_FLAGS_OFFSET..E_FLAGS_OFFSET.saturating_add(std::mem::size_of::<u32>()))
449 .ok_or(ElfParserError::OutOfBounds)?,
450 );
451 let config = loader.get_config();
452 let sbpf_version = match e_flags {
453 0 => SBPFVersion::V0,
454 1 => SBPFVersion::V1,
455 2 => SBPFVersion::V2,
456 3 => SBPFVersion::V3,
457 4 => SBPFVersion::V4,
458 _ => SBPFVersion::Reserved,
459 };
460 if !config.enabled_sbpf_versions.contains(&sbpf_version) {
461 return Err(ElfError::UnsupportedSBPFVersion);
462 }
463
464 let mut executable = if sbpf_version.enable_stricter_elf_headers() {
465 Self::load_with_strict_parser(bytes, loader)?
466 } else {
467 Self::load_with_lenient_parser(bytes, loader)?
468 };
469 executable.sbpf_version = sbpf_version;
470 Ok(executable)
471 }
472
473 pub fn load_with_strict_parser(
475 bytes: &[u8],
476 loader: Arc<BuiltinProgram<C>>,
477 ) -> Result<Self, ElfParserError> {
478 use crate::elf_parser::{
479 consts::{ELFMAG, EV_CURRENT, PF_R, PF_X, PT_LOAD, SHN_UNDEF, STT_FUNC},
480 types::{Elf64Ehdr, Elf64Sym},
481 };
482
483 let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
484 let elf_bytes = aligned_memory.as_slice();
485
486 let (file_header_range, file_header) = Elf64::parse_file_header(elf_bytes)?;
487 let program_header_table_range = mem::size_of::<Elf64Ehdr>()
488 ..mem::size_of::<Elf64Phdr>()
489 .saturating_mul(file_header.e_phnum as usize)
490 .saturating_add(mem::size_of::<Elf64Ehdr>());
491 if file_header.e_ident.ei_mag != ELFMAG
492 || file_header.e_ident.ei_class != ELFCLASS64
493 || file_header.e_ident.ei_data != ELFDATA2LSB
494 || file_header.e_ident.ei_version != EV_CURRENT as u8
495 || file_header.e_ident.ei_osabi != ELFOSABI_NONE
496 || file_header.e_ident.ei_abiversion != 0x00
497 || file_header.e_ident.ei_pad != [0x00; 7]
498 || file_header.e_machine != EM_BPF
500 || file_header.e_version != EV_CURRENT
501 || file_header.e_phoff != mem::size_of::<Elf64Ehdr>() as u64
503 || file_header.e_ehsize != mem::size_of::<Elf64Ehdr>() as u16
506 || file_header.e_phentsize != mem::size_of::<Elf64Phdr>() as u16
507 || file_header.e_phnum == 0
508 || program_header_table_range.end > elf_bytes.len()
509 {
513 return Err(ElfParserError::InvalidFileHeader);
514 }
515
516 const EXPECTED_PROGRAM_HEADERS: [(u32, u64); 2] = [
517 (PF_R, ebpf::MM_RODATA_START), (PF_X, ebpf::MM_BYTECODE_START), ];
520 let program_header_table =
521 Elf64::slice_from_bytes::<Elf64Phdr>(elf_bytes, program_header_table_range.clone())?;
522 let mut expected_program_headers = EXPECTED_PROGRAM_HEADERS.iter();
523 let skip_rodata_program_header =
524 program_header_table[0].p_flags != EXPECTED_PROGRAM_HEADERS[0].0;
525 if skip_rodata_program_header {
526 expected_program_headers.next();
529 } else if file_header.e_phnum < 2 {
530 return Err(ElfParserError::InvalidFileHeader);
531 }
532 let mut expected_offset = program_header_table_range.end as u64;
533 for (program_header, (p_flags, p_vaddr)) in
534 program_header_table.iter().zip(expected_program_headers)
535 {
536 if program_header.p_type != PT_LOAD
537 || program_header.p_flags != *p_flags
538 || program_header.p_offset != expected_offset
539 || program_header.p_offset >= elf_bytes.len() as u64
540 || program_header.p_offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
541 || program_header.p_vaddr != *p_vaddr
542 || program_header.p_paddr != *p_vaddr
543 || program_header.p_filesz != program_header.p_memsz
544 || program_header.p_filesz
545 > (elf_bytes.len() as u64).saturating_sub(program_header.p_offset)
546 || program_header.p_filesz.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
547 || program_header.p_memsz >= ebpf::MM_REGION_SIZE
548 {
549 return Err(ElfParserError::InvalidProgramHeader);
550 }
551 expected_offset = expected_offset.saturating_add(program_header.p_filesz);
552 }
553
554 let (ro_section_range, bytecode_header) = if skip_rodata_program_header {
555 (
556 program_header_table_range.end..program_header_table_range.end,
557 &program_header_table[0],
558 )
559 } else {
560 (
561 program_header_table[0].file_range().unwrap_or_default(),
562 &program_header_table[1],
563 )
564 };
565 let ro_section = Section::Borrowed(ebpf::MM_RODATA_START as usize, ro_section_range);
566 let text_section_vaddr = bytecode_header.p_vaddr;
567 let text_section_range = bytecode_header.file_range().unwrap_or_default();
568
569 if !bytecode_header.vm_range().contains(
570 &file_header
571 .e_entry
572 .saturating_add(ebpf::INSN_SIZE as u64)
573 .saturating_sub(1),
574 ) || file_header.e_entry.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
575 {
576 return Err(ElfParserError::InvalidFileHeader);
577 }
578 let entry_pc = file_header
579 .e_entry
580 .saturating_sub(bytecode_header.p_vaddr)
581 .checked_div(ebpf::INSN_SIZE as u64)
582 .unwrap_or_default() as usize;
583
584 let mut function_registry = FunctionRegistry::<usize>::default();
585 let config = loader.get_config();
586 if config.enable_symbol_and_section_labels {
587 let (_section_header_table_range, section_header_table) =
588 Elf64::parse_section_header_table(
589 elf_bytes,
590 file_header_range.clone(),
591 file_header,
592 program_header_table_range.clone(),
593 )
594 .unwrap();
595 let section_names_section_header = (file_header.e_shstrndx != SHN_UNDEF)
596 .then(|| {
597 section_header_table
598 .get(file_header.e_shstrndx as usize)
599 .ok_or(ElfParserError::OutOfBounds)
600 })
601 .transpose()?
602 .unwrap();
603 let mut symbol_names_section_header = None;
604 let mut symbol_table_section_header = None;
605 for section_header in section_header_table.iter() {
606 let section_name = Elf64::get_string_in_section(
607 elf_bytes,
608 section_names_section_header,
609 section_header.sh_name,
610 64,
611 )
612 .unwrap();
613 if section_name == b".strtab" {
614 symbol_names_section_header = Some(section_header);
615 }
616 if section_name == b".symtab" {
617 symbol_table_section_header = Some(section_header);
618 }
619 }
620 let symbol_names_section_header = symbol_names_section_header.unwrap();
621 let symbol_table: &[Elf64Sym] =
622 Elf64::slice_from_section_header(elf_bytes, symbol_table_section_header.unwrap())
623 .unwrap();
624 for symbol in symbol_table {
625 if symbol.st_info & STT_FUNC == 0 {
626 continue;
627 }
628 let target_pc = symbol
629 .st_value
630 .saturating_sub(bytecode_header.p_vaddr)
631 .checked_div(ebpf::INSN_SIZE as u64)
632 .unwrap_or_default() as usize;
633 let name = Elf64::get_string_in_section(
634 elf_bytes,
635 symbol_names_section_header,
636 symbol.st_name as Elf64Word,
637 u8::MAX as usize,
638 )
639 .unwrap();
640 function_registry
641 .register_function(target_pc as u32, name, target_pc)
642 .unwrap();
643 }
644 }
645
646 Ok(Self {
647 elf_bytes: aligned_memory,
648 sbpf_version: SBPFVersion::Reserved, ro_section,
650 text_section_vaddr,
651 text_section_range,
652 entry_pc,
653 function_registry,
654 loader,
655 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
656 compiled_program: None.into(),
657 })
658 }
659
660 fn load_with_lenient_parser(
662 bytes: &[u8],
663 loader: Arc<BuiltinProgram<C>>,
664 ) -> Result<Self, ElfError> {
665 let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
667 let (mut elf_bytes, unrelocated_elf_bytes) =
668 if is_memory_aligned(bytes.as_ptr() as usize, HOST_ALIGN) {
669 (aligned_memory, bytes)
670 } else {
671 (aligned_memory.clone(), aligned_memory.as_slice())
673 };
674 let elf = Elf64::parse(unrelocated_elf_bytes)?;
675
676 let config = loader.get_config();
677 let header = elf.file_header();
678
679 Self::validate(&elf, elf_bytes.as_slice())?;
680
681 let text_section = get_section(&elf, b".text")?;
683 let text_section_vaddr = text_section.sh_addr.saturating_add(ebpf::MM_REGION_SIZE);
684 if (config.reject_broken_elfs && text_section.sh_addr != text_section.sh_offset)
685 || text_section_vaddr > ebpf::MM_STACK_START
686 {
687 return Err(ElfError::ValueOutOfBounds);
688 }
689
690 let mut function_registry = FunctionRegistry::default();
692 Self::relocate(
693 &mut function_registry,
694 &loader,
695 &elf,
696 elf_bytes.as_slice_mut(),
697 )?;
698
699 let offset = header.e_entry.saturating_sub(text_section.sh_addr);
701 if offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0) {
702 return Err(ElfError::InvalidEntrypoint);
703 }
704 let entry_pc = if let Some(entry_pc) = (offset as usize).checked_div(ebpf::INSN_SIZE) {
705 function_registry.unregister_function(ebpf::hash_symbol_name(b"entrypoint"));
706 function_registry.register_function_hashed_legacy(
707 &loader,
708 true,
709 *b"entrypoint",
710 entry_pc,
711 )?;
712 entry_pc
713 } else {
714 return Err(ElfError::InvalidEntrypoint);
715 };
716
717 let ro_section = Self::parse_ro_sections(
718 config,
719 elf.section_header_table()
720 .iter()
721 .map(|s| (elf.section_name(s.sh_name).ok(), s)),
722 elf_bytes.as_slice(),
723 )?;
724 let ro_section_vaddr = match &ro_section {
725 Section::Owned(offset, _data) => *offset,
726 Section::Borrowed(offset, _byte_range) => *offset,
727 } as u64;
728 if config.optimize_rodata {
729 let ro_section_index = ro_section_vaddr
730 .checked_shr(ebpf::VIRTUAL_ADDRESS_BITS as u32)
731 .unwrap_or(0);
732 if ro_section_index != 1 {
733 return Err(ElfError::ValueOutOfBounds);
734 }
735 } else {
736 debug_assert_eq!(ro_section_vaddr, ebpf::MM_REGION_SIZE);
737 }
738
739 Ok(Self {
740 elf_bytes,
741 sbpf_version: SBPFVersion::Reserved, ro_section,
743 text_section_vaddr,
744 text_section_range: text_section.file_range().unwrap_or_default(),
745 entry_pc,
746 function_registry,
747 loader,
748 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
749 compiled_program: None.into(),
750 })
751 }
752
753 #[rustfmt::skip]
755 #[allow(clippy::size_of_ref)]
756 pub fn mem_size(&self) -> usize {
757 let mut total = mem::size_of::<Self>();
758 total = total
759 .saturating_add(self.elf_bytes.mem_size())
761 .saturating_add(match &self.ro_section {
763 Section::Owned(_, data) => data.capacity(),
764 Section::Borrowed(_, _) => 0,
765 })
766 .saturating_add(self.function_registry.mem_size());
768
769 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
770 {
771 let prog = self.compiled_program.lock().unwrap_or_else(|e| e.into_inner());
773 total = total.saturating_add(prog.as_ref().map_or(0, |program| program.mem_size()));
774 }
775
776 total
777 }
778
779 pub fn validate(elf: &Elf64, elf_bytes: &[u8]) -> Result<(), ElfError> {
783 let header = elf.file_header();
784 if header.e_ident.ei_class != ELFCLASS64 {
785 return Err(ElfError::WrongClass);
786 }
787 if header.e_ident.ei_data != ELFDATA2LSB {
788 return Err(ElfError::WrongEndianess);
789 }
790 if header.e_ident.ei_osabi != ELFOSABI_NONE {
791 return Err(ElfError::WrongAbi);
792 }
793 if header.e_machine != EM_BPF && header.e_machine != EM_SBPF {
794 return Err(ElfError::WrongMachine);
795 }
796 if header.e_type != ET_DYN {
797 return Err(ElfError::WrongType);
798 }
799
800 let num_text_sections =
801 elf.section_header_table()
802 .iter()
803 .fold(0, |count: usize, section_header| {
804 if let Ok(this_name) = elf.section_name(section_header.sh_name) {
805 if this_name == b".text" {
806 return count.saturating_add(1);
807 }
808 }
809 count
810 });
811 if 1 != num_text_sections {
812 return Err(ElfError::NotOneTextSection);
813 }
814
815 for section_header in elf.section_header_table().iter() {
816 if let Ok(name) = elf.section_name(section_header.sh_name) {
817 if name.starts_with(b".bss")
818 || (section_header.is_writable()
819 && (name.starts_with(b".data") && !name.starts_with(b".data.rel")))
820 {
821 return Err(ElfError::WritableSectionNotSupported(
822 String::from_utf8_lossy(name).to_string(),
823 ));
824 }
825 }
826 }
827
828 for section_header in elf.section_header_table().iter() {
829 let start = section_header.sh_offset as usize;
830 let end = section_header
831 .sh_offset
832 .checked_add(section_header.sh_size)
833 .ok_or(ElfError::ValueOutOfBounds)? as usize;
834 let _ = elf_bytes
835 .get(start..end)
836 .ok_or(ElfError::ValueOutOfBounds)?;
837 }
838 let text_section = get_section(elf, b".text")?;
839 if !text_section.vm_range().contains(&header.e_entry) {
840 return Err(ElfError::EntrypointOutOfBounds);
841 }
842
843 Ok(())
844 }
845
846 pub fn parse_ro_sections<'a, S: IntoIterator<Item = (Option<&'a [u8]>, &'a Elf64Shdr)>>(
848 config: &Config,
849 sections: S,
850 elf_bytes: &[u8],
851 ) -> Result<Section, ElfError> {
852 let mut lowest_addr = usize::MAX;
854 let mut highest_addr = 0;
856 let mut ro_fill_length = 0usize;
858 let mut invalid_offsets = false;
859
860 let mut first_ro_section = 0;
863 let mut last_ro_section = 0;
864 let mut n_ro_sections = 0usize;
865
866 let mut ro_slices = vec![];
867 for (i, (name, section_header)) in sections.into_iter().enumerate() {
868 match name {
869 Some(name)
870 if name == b".text"
871 || name == b".rodata"
872 || name == b".data.rel.ro"
873 || name == b".eh_frame" => {}
874 _ => continue,
875 }
876
877 if n_ro_sections == 0 {
878 first_ro_section = i;
879 }
880 last_ro_section = i;
881 n_ro_sections = n_ro_sections.saturating_add(1);
882
883 let section_addr = section_header.sh_addr;
884
885 if !invalid_offsets && section_addr != section_header.sh_offset {
888 invalid_offsets = true;
889 }
890
891 let vaddr_end = section_addr.saturating_add(ebpf::MM_REGION_SIZE);
892 if (config.reject_broken_elfs && invalid_offsets) || vaddr_end > ebpf::MM_STACK_START {
893 return Err(ElfError::ValueOutOfBounds);
894 }
895
896 let section_data = elf_bytes
897 .get(section_header.file_range().unwrap_or_default())
898 .ok_or(ElfError::ValueOutOfBounds)?;
899
900 let section_addr = section_addr as usize;
901 lowest_addr = lowest_addr.min(section_addr);
902 highest_addr = highest_addr.max(section_addr.saturating_add(section_data.len()));
903 ro_fill_length = ro_fill_length.saturating_add(section_data.len());
904
905 ro_slices.push((section_addr, section_data));
906 }
907
908 if config.reject_broken_elfs && lowest_addr.saturating_add(ro_fill_length) > highest_addr {
909 return Err(ElfError::ValueOutOfBounds);
910 }
911
912 let can_borrow = !invalid_offsets
913 && last_ro_section
914 .saturating_add(1)
915 .saturating_sub(first_ro_section)
916 == n_ro_sections;
917 let ro_section = if config.optimize_rodata && can_borrow {
918 let addr_offset = if lowest_addr >= ebpf::MM_REGION_SIZE as usize {
922 lowest_addr
927 } else {
928 lowest_addr.saturating_add(ebpf::MM_REGION_SIZE as usize)
929 };
930
931 Section::Borrowed(addr_offset, lowest_addr..highest_addr)
932 } else {
933 if config.optimize_rodata {
937 highest_addr = highest_addr.saturating_sub(lowest_addr);
942 } else {
943 lowest_addr = 0;
947 };
948
949 let buf_len = highest_addr;
950 if buf_len > elf_bytes.len() {
951 return Err(ElfError::ValueOutOfBounds);
952 }
953
954 let mut ro_section = vec![0; buf_len];
955 for (section_addr, slice) in ro_slices.iter() {
956 let buf_offset_start = section_addr.saturating_sub(lowest_addr);
957 ro_section[buf_offset_start..buf_offset_start.saturating_add(slice.len())]
958 .copy_from_slice(slice);
959 }
960
961 let addr_offset = if lowest_addr >= ebpf::MM_REGION_SIZE as usize {
962 lowest_addr
963 } else {
964 lowest_addr.saturating_add(ebpf::MM_REGION_SIZE as usize)
965 };
966 Section::Owned(addr_offset, ro_section)
967 };
968
969 Ok(ro_section)
970 }
971
972 fn relocate(
974 function_registry: &mut FunctionRegistry<usize>,
975 loader: &BuiltinProgram<C>,
976 elf: &Elf64,
977 elf_bytes: &mut [u8],
978 ) -> Result<(), ElfError> {
979 let mut syscall_cache = BTreeMap::new();
980 let text_section = get_section(elf, b".text")?;
981
982 let config = loader.get_config();
984 let text_bytes = elf_bytes
985 .get_mut(text_section.file_range().unwrap_or_default())
986 .ok_or(ElfError::ValueOutOfBounds)?;
987 let instruction_count = text_bytes
988 .len()
989 .checked_div(ebpf::INSN_SIZE)
990 .ok_or(ElfError::ValueOutOfBounds)?;
991 for i in 0..instruction_count {
992 let insn = ebpf::get_insn(text_bytes, i);
993 if insn.opc == ebpf::CALL_IMM && insn.imm != -1 {
994 let target_pc = (i as isize)
995 .saturating_add(1)
996 .saturating_add(insn.imm as isize);
997 if target_pc < 0 || target_pc >= instruction_count as isize {
998 return Err(ElfError::RelativeJumpOutOfBounds(i));
999 }
1000 let name = if config.enable_symbol_and_section_labels {
1001 format!("function_{target_pc}")
1002 } else {
1003 String::default()
1004 };
1005 let key = function_registry.register_function_hashed_legacy(
1006 loader,
1007 true,
1008 name.as_bytes(),
1009 target_pc as usize,
1010 )?;
1011 let offset = i.saturating_mul(ebpf::INSN_SIZE).saturating_add(4);
1012 let checked_slice = text_bytes
1013 .get_mut(offset..offset.saturating_add(4))
1014 .ok_or(ElfError::ValueOutOfBounds)?;
1015 LittleEndian::write_u32(checked_slice, key);
1016 }
1017 }
1018
1019 for relocation in elf.dynamic_relocations_table().unwrap_or_default().iter() {
1021 let r_offset = relocation.r_offset as usize;
1022
1023 match BpfRelocationType::from_x86_relocation_type(relocation.r_type()) {
1024 Some(BpfRelocationType::R_Bpf_64_64) => {
1025 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1027
1028 let checked_slice = elf_bytes
1031 .get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1032 .ok_or(ElfError::ValueOutOfBounds)?;
1033 let refd_addr = LittleEndian::read_u32(checked_slice) as u64;
1034
1035 let symbol = elf
1036 .dynamic_symbol_table()
1037 .and_then(|table| table.get(relocation.r_sym() as usize).cloned())
1038 .ok_or_else(|| ElfError::UnknownSymbol(relocation.r_sym() as usize))?;
1039
1040 let mut addr = symbol.st_value.saturating_add(refd_addr);
1043
1044 if addr < ebpf::MM_REGION_SIZE {
1049 addr = ebpf::MM_REGION_SIZE.saturating_add(addr);
1050 }
1051
1052 let imm_low_offset = imm_offset;
1053 let imm_high_offset = imm_low_offset.saturating_add(INSN_SIZE);
1054
1055 let imm_slice = elf_bytes
1057 .get_mut(
1058 imm_low_offset..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1059 )
1060 .ok_or(ElfError::ValueOutOfBounds)?;
1061 LittleEndian::write_u32(imm_slice, (addr & 0xFFFFFFFF) as u32);
1062
1063 let imm_slice = elf_bytes
1065 .get_mut(
1066 imm_high_offset..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1067 )
1068 .ok_or(ElfError::ValueOutOfBounds)?;
1069 LittleEndian::write_u32(
1070 imm_slice,
1071 addr.checked_shr(32).unwrap_or_default() as u32,
1072 );
1073 }
1074 Some(BpfRelocationType::R_Bpf_64_Relative) => {
1075 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1081
1082 if text_section
1083 .file_range()
1084 .unwrap_or_default()
1085 .contains(&r_offset)
1086 {
1087 let imm_low_offset = imm_offset;
1092 let imm_high_offset = r_offset
1093 .saturating_add(INSN_SIZE)
1094 .saturating_add(BYTE_OFFSET_IMMEDIATE);
1095
1096 let imm_slice = elf_bytes
1098 .get(
1099 imm_low_offset
1100 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1101 )
1102 .ok_or(ElfError::ValueOutOfBounds)?;
1103 let va_low = LittleEndian::read_u32(imm_slice) as u64;
1104
1105 let imm_slice = elf_bytes
1107 .get(
1108 imm_high_offset
1109 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1110 )
1111 .ok_or(ElfError::ValueOutOfBounds)?;
1112 let va_high = LittleEndian::read_u32(imm_slice) as u64;
1113
1114 let mut refd_addr = va_high.checked_shl(32).unwrap_or_default() | va_low;
1116
1117 if refd_addr == 0 {
1118 return Err(ElfError::InvalidVirtualAddress(refd_addr));
1119 }
1120
1121 if refd_addr < ebpf::MM_REGION_SIZE {
1122 refd_addr = ebpf::MM_REGION_SIZE.saturating_add(refd_addr);
1125 }
1126
1127 let imm_slice = elf_bytes
1129 .get_mut(
1130 imm_low_offset
1131 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1132 )
1133 .ok_or(ElfError::ValueOutOfBounds)?;
1134 LittleEndian::write_u32(imm_slice, (refd_addr & 0xFFFFFFFF) as u32);
1135
1136 let imm_slice = elf_bytes
1138 .get_mut(
1139 imm_high_offset
1140 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1141 )
1142 .ok_or(ElfError::ValueOutOfBounds)?;
1143 LittleEndian::write_u32(
1144 imm_slice,
1145 refd_addr.checked_shr(32).unwrap_or_default() as u32,
1146 );
1147 } else {
1148 let addr_slice = elf_bytes
1154 .get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1155 .ok_or(ElfError::ValueOutOfBounds)?;
1156 let mut refd_addr = LittleEndian::read_u32(addr_slice) as u64;
1157 refd_addr = ebpf::MM_REGION_SIZE.saturating_add(refd_addr);
1158
1159 let addr_slice = elf_bytes
1160 .get_mut(r_offset..r_offset.saturating_add(mem::size_of::<u64>()))
1161 .ok_or(ElfError::ValueOutOfBounds)?;
1162 LittleEndian::write_u64(addr_slice, refd_addr);
1163 }
1164 }
1165 Some(BpfRelocationType::R_Bpf_64_32) => {
1166 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1172
1173 let symbol = elf
1174 .dynamic_symbol_table()
1175 .and_then(|table| table.get(relocation.r_sym() as usize).cloned())
1176 .ok_or_else(|| ElfError::UnknownSymbol(relocation.r_sym() as usize))?;
1177
1178 let name = elf
1179 .dynamic_symbol_name(symbol.st_name as Elf64Word)
1180 .map_err(|_| ElfError::UnknownSymbol(symbol.st_name as usize))?;
1181
1182 let key = if symbol.is_function() && symbol.st_value != 0 {
1184 if !text_section.vm_range().contains(&symbol.st_value) {
1185 return Err(ElfError::ValueOutOfBounds);
1186 }
1187 let target_pc = (symbol.st_value.saturating_sub(text_section.sh_addr)
1188 as usize)
1189 .checked_div(ebpf::INSN_SIZE)
1190 .unwrap_or_default();
1191 function_registry
1192 .register_function_hashed_legacy(loader, true, name, target_pc)?
1193 } else {
1194 let hash = *syscall_cache
1196 .entry(symbol.st_name)
1197 .or_insert_with(|| ebpf::hash_symbol_name(name));
1198 if config.reject_broken_elfs
1199 && loader.get_function_registry().lookup_by_key(hash).is_none()
1200 {
1201 return Err(ElfError::UnresolvedSymbol(
1202 String::from_utf8_lossy(name).to_string(),
1203 r_offset.checked_div(ebpf::INSN_SIZE).unwrap_or(0),
1204 r_offset,
1205 ));
1206 }
1207 hash
1208 };
1209
1210 let checked_slice = elf_bytes
1211 .get_mut(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1212 .ok_or(ElfError::ValueOutOfBounds)?;
1213 LittleEndian::write_u32(checked_slice, key);
1214 }
1215 _ => return Err(ElfError::UnknownRelocation(relocation.r_type())),
1216 }
1217 }
1218
1219 if config.enable_symbol_and_section_labels {
1220 for symbol in elf.symbol_table().ok().flatten().unwrap_or_default().iter() {
1222 if symbol.st_info & 0xEF != 0x02 {
1223 continue;
1224 }
1225 if !text_section.vm_range().contains(&symbol.st_value) {
1226 return Err(ElfError::ValueOutOfBounds);
1227 }
1228 let target_pc = (symbol.st_value.saturating_sub(text_section.sh_addr) as usize)
1229 .checked_div(ebpf::INSN_SIZE)
1230 .unwrap_or_default();
1231 let name = elf
1232 .symbol_name(symbol.st_name as Elf64Word)
1233 .map_err(|_| ElfError::UnknownSymbol(symbol.st_name as usize))?;
1234 function_registry.register_function_hashed_legacy(loader, true, name, target_pc)?;
1235 }
1236 }
1237
1238 Ok(())
1239 }
1240
1241 #[allow(dead_code)]
1242 fn dump_data(name: &str, prog: &[u8]) {
1243 let mut eight_bytes: Vec<u8> = Vec::new();
1244 println!("{name}");
1245 for i in prog.iter() {
1246 if eight_bytes.len() >= 7 {
1247 println!("{eight_bytes:02X?}");
1248 eight_bytes.clear();
1249 } else {
1250 eight_bytes.push(*i);
1251 }
1252 }
1253 }
1254}
1255
1256pub fn get_ro_region(ro_section: &Section, elf: &[u8]) -> MemoryRegion {
1258 let (offset, ro_data) = match ro_section {
1259 Section::Owned(offset, data) => (*offset, data.as_slice()),
1260 Section::Borrowed(offset, byte_range) => (*offset, &elf[byte_range.clone()]),
1261 };
1262
1263 MemoryRegion::new_readonly(ro_data, offset as u64)
1267}