solana_sbpf/
elf.rs

1//! This module relocates a BPF ELF
2
3// Note: Typically ELF shared objects are loaded using the program headers and
4// not the section headers.  Since we are leveraging the elfkit crate its much
5// easier to use the section headers.  There are cases (reduced size, obfuscation)
6// where the section headers may be removed from the ELF.  If that happens then
7// this loader will need to be re-written to use the program headers instead.
8
9use 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/// Error definitions
39#[derive(Debug, thiserror::Error, PartialEq, Eq)]
40pub enum ElfError {
41    /// Failed to parse ELF file
42    #[error("Failed to parse ELF file: {0}")]
43    FailedToParse(String),
44    /// Entrypoint out of bounds
45    #[error("Entrypoint out of bounds")]
46    EntrypointOutOfBounds,
47    /// Invalid entrypoint
48    #[error("Invalid entrypoint")]
49    InvalidEntrypoint,
50    /// Failed to get section
51    #[error("Failed to get section {0}")]
52    FailedToGetSection(String),
53    /// Unresolved symbol
54    #[error("Unresolved symbol ({0}) at instruction #{1:?} (ELF file offset {2:#x})")]
55    UnresolvedSymbol(String, usize, usize),
56    /// Section not found
57    #[error("Section not found: {0}")]
58    SectionNotFound(String),
59    /// Relative jump out of bounds
60    #[error("Relative jump out of bounds at instruction #{0}")]
61    RelativeJumpOutOfBounds(usize),
62    /// Symbol hash collision
63    #[error("Symbol hash collision {0:#x}")]
64    SymbolHashCollision(u32),
65    /// Incompatible ELF: wrong endianess
66    #[error("Incompatible ELF: wrong endianess")]
67    WrongEndianess,
68    /// Incompatible ELF: wrong ABI
69    #[error("Incompatible ELF: wrong ABI")]
70    WrongAbi,
71    /// Incompatible ELF: wrong machine
72    #[error("Incompatible ELF: wrong machine")]
73    WrongMachine,
74    /// Incompatible ELF: wrong class
75    #[error("Incompatible ELF: wrong class")]
76    WrongClass,
77    /// Not one text section
78    #[error("Multiple or no text sections, consider removing llc option: -function-sections")]
79    NotOneTextSection,
80    /// Read-write data not supported
81    #[error("Found writable section ({0}) in ELF, read-write data not supported")]
82    WritableSectionNotSupported(String),
83    /// Relocation failed, no loadable section contains virtual address
84    #[error("Relocation failed, no loadable section contains virtual address {0:#x}")]
85    AddressOutsideLoadableSection(u64),
86    /// Relocation failed, invalid referenced virtual address
87    #[error("Relocation failed, invalid referenced virtual address {0:#x}")]
88    InvalidVirtualAddress(u64),
89    /// Relocation failed, unknown type
90    #[error("Relocation failed, unknown type {0:?}")]
91    UnknownRelocation(u32),
92    /// Failed to read relocation info
93    #[error("Failed to read relocation info")]
94    FailedToReadRelocationInfo,
95    /// Incompatible ELF: wrong type
96    #[error("Incompatible ELF: wrong type")]
97    WrongType,
98    /// Unknown symbol
99    #[error("Unknown symbol with index {0}")]
100    UnknownSymbol(usize),
101    /// Offset or value is out of bounds
102    #[error("Offset or value is out of bounds")]
103    ValueOutOfBounds,
104    /// Detected sbpf_version required by the executable which are not enabled
105    #[error("Detected sbpf_version required by the executable which are not enabled")]
106    UnsupportedSBPFVersion,
107    /// Invalid program header
108    #[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
148// For more information on the BPF instruction set:
149// https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
150
151// msb                                                        lsb
152// +------------------------+----------------+----+----+--------+
153// |immediate               |offset          |src |dst |opcode  |
154// +------------------------+----------------+----+----+--------+
155
156// From least significant to most significant bit:
157//   8 bit opcode
158//   4 bit destination register (dst)
159//   4 bit source register (src)
160//   16 bit offset
161//   32 bit immediate (imm)
162
163/// Byte offset of the immediate field in the instruction
164const BYTE_OFFSET_IMMEDIATE: usize = 4;
165/// Byte length of the immediate field
166const BYTE_LENGTH_IMMEDIATE: usize = 4;
167
168/// BPF relocation types.
169#[allow(non_camel_case_types)]
170#[derive(Debug, PartialEq, Copy, Clone)]
171enum BpfRelocationType {
172    /// No relocation, placeholder
173    R_Bpf_None = 0,
174    /// R_BPF_64_64 relocation type is used for ld_imm64 instruction.
175    /// The actual to-be-relocated data (0 or section offset) is
176    /// stored at r_offset + 4 and the read/write data bitsize is 32
177    /// (4 bytes). The relocation can be resolved with the symbol
178    /// value plus implicit addend.
179    R_Bpf_64_64 = 1,
180    /// 64 bit relocation of a ldxdw instruction.  The ldxdw
181    /// instruction occupies two instruction slots. The 64-bit address
182    /// to load from is split into the 32-bit imm field of each
183    /// slot. The first slot's pre-relocation imm field contains the
184    /// virtual address (typically same as the file offset) of the
185    /// location to load. Relocation involves calculating the
186    /// post-load 64-bit physical address referenced by the imm field
187    /// and writing that physical address back into the imm fields of
188    /// the ldxdw instruction.
189    R_Bpf_64_Relative = 8,
190    /// Relocation of a call instruction.  The existing imm field
191    /// contains either an offset of the instruction to jump to (think
192    /// local function call) or a special value of "-1".  If -1 the
193    /// symbol must be looked up in the symbol table.  The relocation
194    /// entry contains the symbol number to call.  In order to support
195    /// both local jumps and calling external symbols a 32-bit hash is
196    /// computed and stored in the the call instruction's 32-bit imm
197    /// field.  The hash is used later to look up the 64-bit address
198    /// to jump to.  In the case of a local jump the hash is
199    /// calculated using the current program counter and in the case
200    /// of a symbol the hash is calculated using the name of the
201    /// symbol.
202    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/// ELF section
217#[derive(Debug, PartialEq)]
218pub enum Section {
219    /// Owned section data.
220    ///
221    /// The first field is virtual address of the section.
222    /// The second field is the actual section data.
223    Owned(usize, Vec<u8>),
224    /// Borrowed section data.
225    ///
226    /// The first field is virtual address of the section.
227    /// The second field can be used to index the input ELF buffer to
228    /// retrieve the section data.
229    Borrowed(usize, Range<usize>),
230}
231
232/// Elf loader/relocator
233#[derive(Debug, PartialEq)]
234pub struct Executable<C: ContextObject> {
235    /// Loaded and executable elf
236    elf_bytes: AlignedMemory<{ HOST_ALIGN }>,
237    /// Required SBPF capabilities
238    sbpf_version: SBPFVersion,
239    /// Read-only section
240    ro_section: Section,
241    /// Text section virtual address
242    text_section_vaddr: u64,
243    /// Text section range in `elf_bytes`
244    text_section_range: Range<usize>,
245    /// Address of the entry point
246    entry_pc: usize,
247    /// Call resolution map (hash, pc, name)
248    function_registry: FunctionRegistry<usize>,
249    /// Loader built-in program
250    loader: Arc<BuiltinProgram<C>>,
251    /// Compiled program and argument
252    #[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    /// Get the configuration settings
258    pub fn get_config(&self) -> &Config {
259        self.loader.get_config()
260    }
261
262    /// Get the executable sbpf_version
263    pub fn get_sbpf_version(&self) -> SBPFVersion {
264        self.sbpf_version
265    }
266
267    /// Get the .text section virtual address and bytes
268    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    /// Get the concatenated read-only sections (including the text section)
276    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    /// Get a memory region that can be used to access the merged readonly section
286    pub fn get_ro_region(&self) -> MemoryRegion {
287        get_ro_region(&self.ro_section, self.elf_bytes.as_slice())
288    }
289
290    /// Get the entry point offset into the text section
291    pub fn get_entrypoint_instruction_offset(&self) -> usize {
292        self.entry_pc
293    }
294
295    /// Get the text section offset in the ELF file
296    #[cfg(feature = "debugger")]
297    pub fn get_text_section_offset(&self) -> u64 {
298        self.text_section_range.start as u64
299    }
300
301    /// Get the loader built-in program
302    pub fn get_loader(&self) -> &Arc<BuiltinProgram<C>> {
303        &self.loader
304    }
305
306    /// Get the JIT compiled program
307    #[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    /// Verify the executable
313    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    /// JIT compile the executable
325    #[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    /// Get the function registry
333    pub fn get_function_registry(&self) -> &FunctionRegistry<usize> {
334        &self.function_registry
335    }
336
337    /// Create from raw text section bytes (list of instructions)
338    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    /// Fully loads an ELF
375    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    /// Loads an ELF without relocation
412    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_entry
443            || file_header.e_phoff != mem::size_of::<Elf64Ehdr>() as u64
444            // file_header.e_shoff
445            // file_header.e_flags
446            || 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_shnum
452            || 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), // byte code
459            (PT_LOAD, PF_R, ebpf::MM_RODATA_START),   // read only data
460            (PT_GNU_STACK, PF_R | PF_W, ebpf::MM_STACK_START), // stack
461            (PT_LOAD, PF_R | PF_W, ebpf::MM_HEAP_START), // heap
462            (PT_NULL, 0, 0xFFFFFFFF00000000),         // dynamic symbol table
463        ];
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, // Is set in Self::load()
597            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    /// Loads an ELF with relocation
609    fn load_with_lenient_parser(
610        bytes: &[u8],
611        loader: Arc<BuiltinProgram<C>>,
612    ) -> Result<Self, ElfError> {
613        // We always need one memory copy to take ownership and for relocations
614        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                // We might need another memory copy to ensure alignment
620                (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        // calculate the text section info
635        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        // relocate symbols
656        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        // calculate entrypoint offset into the text section
665        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    /// Calculate the total memory size of the executable
708    #[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            // elf bytes
714            .saturating_add(self.elf_bytes.mem_size())
715            // ro section
716            .saturating_add(match &self.ro_section {
717                Section::Owned(_, data) => data.capacity(),
718                Section::Borrowed(_, _) => 0,
719            })
720            // bpf functions
721            .saturating_add(self.function_registry.mem_size());
722
723        #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
724        {
725            // compiled programs
726            total = total.saturating_add(self.compiled_program.as_ref().map_or(0, |program| program.mem_size()));
727        }
728
729        total
730    }
731
732    // Functions exposed for tests
733
734    /// Validates the ELF
735    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                // When optimize_rodata=false, we allocate a vector and copy all
765                // rodata sections into it. In that case we can't allow virtual
766                // addresses or we'd potentially have to do huge allocations.
767                return Err(ElfError::UnsupportedSBPFVersion);
768            }
769
770            // The toolchain currently emits up to 4 program headers. 10 is a
771            // future proof nice round number.
772            //
773            // program_headers() returns an ExactSizeIterator so count doesn't
774            // actually iterate again.
775            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    /// Parses and concatenates the readonly data sections
827    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        // the lowest section address
834        let mut lowest_addr = usize::MAX;
835        // the highest section address
836        let mut highest_addr = 0;
837        // the aggregated section length, not including gaps between sections
838        let mut ro_fill_length = 0usize;
839        let mut invalid_offsets = false;
840        // when sbpf_version.enable_elf_vaddr()=true, we allow section_addr != sh_offset
841        // if section_addr - sh_offset is constant across all sections. That is,
842        // we allow sections to be translated by a fixed virtual offset.
843        let mut addr_file_offset = None;
844
845        // keep track of where ro sections are so we can tell whether they're
846        // contiguous
847        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            // sh_offset handling:
871            //
872            // If sbpf_version.enable_elf_vaddr()=true, we allow section_addr >
873            // sh_offset, if section_addr - sh_offset is constant across all
874            // sections. That is, we allow the linker to align rodata to a
875            // positive base address (MM_RODATA_START) as long as the mapping
876            // to sh_offset(s) stays linear.
877            //
878            // If sbpf_version.enable_elf_vaddr()=false, section_addr must match
879            // sh_offset for backwards compatibility
880            if !invalid_offsets {
881                if sbpf_version.enable_elf_vaddr() {
882                    // This is enforced in validate()
883                    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                            // The sections are not all translated by the same
890                            // constant. We won't be able to borrow, but unless
891                            // config.reject_broken_elf=true, we're still going
892                            // to accept this file for backwards compatibility.
893                            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            // Read only sections are grouped together with no intermixed non-ro
940            // sections. We can borrow.
941
942            // When sbpf_version.enable_elf_vaddr()=true, section addresses and their
943            // corresponding buffer offsets can be translated by a constant
944            // amount. Subtract the constant to get buffer positions.
945            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                // The first field of Section::Borrowed is an offset from
952                // ebpf::MM_RODATA_START so if the linker has already put the
953                // sections within ebpf::MM_RODATA_START, we need to subtract
954                // it now.
955                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            // Read only and other non-ro sections are mixed. Zero the non-ro
966            // sections and and copy the ro ones at their intended offsets.
967
968            if config.optimize_rodata {
969                // The rodata region starts at MM_RODATA_START + offset,
970                // [MM_RODATA_START, MM_RODATA_START + offset) is not
971                // mappable. We only need to allocate highest_addr - lowest_addr
972                // bytes.
973                highest_addr = highest_addr.saturating_sub(lowest_addr);
974            } else {
975                // For backwards compatibility, the whole [MM_RODATA_START,
976                // MM_RODATA_START + highest_addr) range is mappable. We need
977                // to allocate the whole address range.
978                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    /// Relocates the ELF in-place
1005    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        // Fixup all program counter relative call instructions
1020        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        // Fixup all the relocations in the relocation section if exists
1061        for relocation in elf.dynamic_relocations_table().unwrap_or_default().iter() {
1062            let mut r_offset = relocation.r_offset as usize;
1063
1064            // When sbpf_version.enable_elf_vaddr()=true, we allow section.sh_addr !=
1065            // section.sh_offset so we need to bring r_offset to the correct
1066            // byte offset.
1067            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                    // Offset of the immediate field
1086                    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                    // Read the instruction's immediate field which contains virtual
1098                    // address to convert to physical
1099                    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                    // The relocated address is relative to the address of the
1110                    // symbol at index `r_sym`
1111                    let mut addr = symbol.st_value.saturating_add(refd_addr);
1112
1113                    // The "physical address" from the VM's perspective is rooted
1114                    // at `MM_RODATA_START`. If the linker hasn't already put
1115                    // the symbol within `MM_RODATA_START`, we need to do so
1116                    // now.
1117                    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                        // Write the low side of the relocate address
1131                        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                        // Write the high side of the relocate address
1140                        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                    // Relocation between different sections, where the target
1159                    // memory is not associated to a symbol (eg some compiler
1160                    // generated rodata that doesn't have an explicit symbol).
1161
1162                    // Offset of the immediate field
1163                    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                        // We're relocating a lddw instruction, which spans two
1171                        // instruction slots. The address to be relocated is
1172                        // split in two halves in the two imms of the
1173                        // instruction slots.
1174                        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                        // Read the low side of the address
1180                        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                        // Read the high side of the address
1189                        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                        // Put the address back together
1198                        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                            // The linker hasn't already placed rodata within
1206                            // MM_RODATA_START, so we do so now
1207                            refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1208                        }
1209
1210                        // Write back the low half
1211                        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                        // Write back the high half
1220                        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                            // We're relocating an address inside a data section (eg .rodata). The
1233                            // address is encoded as a simple u64.
1234
1235                            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                                // Not within MM_RODATA_START, do it now
1241                                refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1242                            }
1243                            refd_addr
1244                        } else {
1245                            // There used to be a bug in toolchains before
1246                            // https://github.com/solana-labs/llvm-project/pull/35 where for 64 bit
1247                            // relocations we were encoding only the low 32 bits, shifted 32 bits to
1248                            // the left. Our relocation code used to be compatible with that, so we
1249                            // need to keep supporting this case for backwards compatibility.
1250                            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                    // The .text section has an unresolved call to symbol instruction
1265                    // Hash the symbol name and stick it into the call instruction's imm
1266                    // field.  Later that hash will be used to look up the function location.
1267
1268                    // Offset of the immediate field
1269                    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                    // If the symbol is defined, this is a bpf-to-bpf call
1281                    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                        // Else it's a syscall
1297                        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            // Register all known function names from the symbol table
1323            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
1363/// Creates a [MemoryRegion] for the given [Section]
1364pub 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    // If offset > 0, the region will start at MM_RODATA_START + the offset of
1371    // the first read only byte. [MM_RODATA_START, MM_RODATA_START + offset)
1372    // will be unmappable, see MemoryRegion::vm_to_host.
1373    MemoryRegion::new_readonly(ro_data, offset as u64)
1374}