LCOV - code coverage report
Current view: top level - ballet/sbpf - fd_sbpf_loader.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 727 976 74.5 %
Date: 2025-10-13 04:42:14 Functions: 25 25 100.0 %

          Line data    Source code
       1             : #include "fd_sbpf_loader.h"
       2             : #include "fd_sbpf_instr.h"
       3             : #include "fd_sbpf_opcodes.h"
       4             : #include "../../util/fd_util.h"
       5             : #include "../../util/bits/fd_sat.h"
       6             : #include "../murmur3/fd_murmur3.h"
       7             : 
       8             : #include <assert.h>
       9             : #include <stdio.h>
      10             : 
      11             : /* ELF loader, part 1 **************************************************
      12             : 
      13             :    Start with a static piece of scratch memory and do basic validation
      14             :    of the file content.  Walk the section table once and remember
      15             :    sections of interest.
      16             : 
      17             :    ### Terminology
      18             : 
      19             :    This source follows common ELF naming practices.
      20             : 
      21             :      section:  a named data region present in the ELF file
      22             :      segment:  a contiguous memory region containing sections
      23             :                (not necessarily contiguous in the ELF file)
      24             : 
      25             :      physical address (paddr): Byte offset into ELF file (uchar * bin)
      26             :      virtual  address (vaddr): VM memory address */
      27             : 
      28             : /* Provide convenient access to file header and ELF content */
      29             : 
      30             : __extension__ union fd_sbpf_elf {
      31             :   fd_elf64_ehdr ehdr;
      32             :   uchar         bin[0];
      33             : };
      34             : typedef union fd_sbpf_elf fd_sbpf_elf_t;
      35             : 
      36             : /* FD_SBPF_MM_{...}_ADDR are hardcoded virtual addresses of segments
      37             :    in the sBPF virtual machine.
      38             : 
      39             :    FIXME: These should be defined elsewhere */
      40             : 
      41           9 : #define FD_SBPF_MM_BYTECODE_ADDR (0x0UL)         /* bytecode */
      42         414 : #define FD_SBPF_MM_RODATA_ADDR   (0x100000000UL) /* readonly program data */
      43        9336 : #define FD_SBPF_MM_PROGRAM_ADDR  (0x100000000UL) /* readonly program data */
      44           9 : #define FD_SBPF_MM_STACK_ADDR    (0x200000000UL) /* stack */
      45           9 : #define FD_SBPF_MM_HEAP_ADDR     (0x300000000UL) /* heap */
      46          36 : #define FD_SBPF_MM_REGION_SZ     (0x100000000UL) /* max region size */
      47             : 
      48           9 : #define FD_SBPF_PF_X  (1U) /* executable */
      49          54 : #define FD_SBPF_PF_W  (2U) /* writable */
      50          27 : #define FD_SBPF_PF_R  (4U) /* readable */
      51          18 : #define FD_SBPF_PF_RW (FD_SBPF_PF_R|FD_SBPF_PF_W)
      52             : 
      53          54 : #define EXPECTED_PHDR_CNT (4U)
      54             : 
      55             : struct fd_sbpf_range {
      56             :   ulong lo;
      57             :   ulong hi;
      58             : };
      59             : typedef struct fd_sbpf_range fd_sbpf_range_t;
      60             : 
      61             : /* fd_sbpf_range_contains returns 1 if x is in the range
      62             :    [range.lo, range.hi) and 0 otherwise. */
      63             : static inline int
      64        5706 : fd_sbpf_range_contains( fd_sbpf_range_t const * range, ulong x ) {
      65        5706 :   return !!(( range->lo<=x ) & ( x<range->hi ));
      66        5706 : }
      67             : 
      68             : /* Mimics Elf64Shdr::file_range(). Returns a pointer to range (Some) if
      69             :    the section header type is not SHT_NOBITS, and sets range.{lo, hi} to
      70             :    the section header offset and offset + size, respectively. Returns
      71             :    NULL (None) otherwise, and sets both range.{lo, hi} to 0 (the default
      72             :    values for a Rust Range type).
      73             : 
      74             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L87-L93 */
      75             : 
      76             : static fd_sbpf_range_t *
      77             : fd_shdr_get_file_range( fd_elf64_shdr const * shdr,
      78        6045 :                         fd_sbpf_range_t *     range ) {
      79        6045 :   if( shdr->sh_type==FD_ELF_SHT_NOBITS ) {
      80           0 :     *range = (fd_sbpf_range_t) { .lo = 0UL, .hi = 0UL };
      81           0 :     return NULL;
      82        6045 :   } else {
      83        6045 :     *range = (fd_sbpf_range_t) { .lo = shdr->sh_offset, .hi = fd_ulong_sat_add( shdr->sh_offset, shdr->sh_size ) };
      84        6045 :     return range;
      85        6045 :   }
      86        6045 : }
      87             : 
      88             : /* Converts an ElfParserError code to an ElfError code.
      89             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L112-L132 */
      90             : static int
      91          12 : fd_sbpf_elf_parser_err_to_elf_err( int err ) {
      92          12 :   switch( err ) {
      93           9 :     case FD_SBPF_ELF_SUCCESS:
      94           9 :       return err;
      95           0 :     case FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS:
      96           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
      97           0 :     case FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER:
      98           0 :       return FD_SBPF_ELF_ERR_INVALID_PROGRAM_HEADER;
      99           3 :     default:
     100           3 :       return FD_SBPF_ELF_ERR_FAILED_TO_PARSE;
     101          12 :   }
     102          12 : }
     103             : 
     104             : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L11-L13 */
     105        1674 : #define FD_SBPF_SECTION_NAME_SZ_MAX (16UL)
     106             : #define FD_SBPF_SYMBOL_NAME_SZ_MAX  (64UL)
     107             : 
     108             : /* ELF loader, part 2 **************************************************
     109             : 
     110             :    Prepare a copy of a subrange of the ELF content: The rodata segment.
     111             :    Mangle the copy by applying dynamic relocations.  Then, zero out
     112             :    parts of the segment that are not interesting to the loader.
     113             : 
     114             :    ### Terminology
     115             : 
     116             :    Shorthands for relocation handling:
     117             : 
     118             :      S: Symbol value (typically an ELF physical address)
     119             :      A: Implicit addend, i.e. the original value of the field that the
     120             :         relocation handler is about to write to
     121             :      V: Virtual address, i.e. the target value that the relocation
     122             :         handler is about to write into where the implicit addend was
     123             :         previously stored */
     124             : 
     125             : ulong
     126          72 : fd_sbpf_program_align( void ) {
     127          72 :   return alignof( fd_sbpf_program_t );
     128          72 : }
     129             : 
     130             : ulong
     131          72 : fd_sbpf_program_footprint( fd_sbpf_elf_info_t const * info ) {
     132          72 :   FD_COMPILER_UNPREDICTABLE( info ); /* Make this appear as FD_FN_PURE (e.g. footprint might depened on info contents in future) */
     133          72 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( info->sbpf_version ) ) ) {
     134             :     /* SBPF v3+ no longer neeeds calldests bitmap */
     135           0 :     return FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,
     136           0 :       alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) ),
     137           0 :       alignof(fd_sbpf_program_t) );
     138           0 :   }
     139          72 :   return FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,
     140          72 :     alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) ),
     141          72 :     fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint( info->text_cnt ) ),  /* calldests bitmap */
     142          72 :     alignof(fd_sbpf_program_t) );
     143          72 : }
     144             : 
     145             : fd_sbpf_program_t *
     146             : fd_sbpf_program_new( void *                     prog_mem,
     147             :                      fd_sbpf_elf_info_t const * elf_info,
     148          72 :                      void *                     rodata ) {
     149             : 
     150          72 :   if( FD_UNLIKELY( !prog_mem ) ) {
     151           0 :     FD_LOG_WARNING(( "NULL prog_mem" ));
     152           0 :     return NULL;
     153           0 :   }
     154             : 
     155          72 :   if( FD_UNLIKELY( !elf_info ) ) {
     156           0 :     FD_LOG_WARNING(( "NULL elf_info" ));
     157           0 :     return NULL;
     158           0 :   }
     159             : 
     160          72 :   if( FD_UNLIKELY( ((elf_info->bin_sz)>0U) & (!rodata)) ) {
     161           0 :     FD_LOG_WARNING(( "NULL rodata" ));
     162           0 :     return NULL;
     163           0 :   }
     164             : 
     165             :   /* https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf_parser/mod.rs#L99 */
     166          72 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong) rodata, FD_SBPF_PROG_RODATA_ALIGN ) ) ){
     167           0 :     FD_LOG_WARNING(( "rodata is not 8-byte aligned" ));
     168           0 :     return NULL;
     169           0 :   }
     170             : 
     171             :   /* Initialize program struct */
     172             : 
     173          72 :   FD_SCRATCH_ALLOC_INIT( laddr, prog_mem );
     174          72 :   fd_sbpf_program_t * prog = FD_SCRATCH_ALLOC_APPEND( laddr, alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) );
     175             : 
     176             :   /* Note that entry_pc and rodata_sz get set during the loading phase. */
     177           0 :   *prog = (fd_sbpf_program_t) {
     178          72 :     .info      = *elf_info,
     179          72 :     .rodata    = rodata,
     180          72 :     .rodata_sz = 0UL,
     181          72 :     .text      = (ulong *)((ulong)rodata + elf_info->text_off), /* FIXME: WHAT IF MISALIGNED */
     182          72 :     .entry_pc  = ULONG_MAX,
     183          72 :   };
     184             : 
     185             :   /* If the text section is empty, then we do not need a calldests map. */
     186          72 :   ulong pc_max = elf_info->text_cnt;
     187          72 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( elf_info->sbpf_version ) || pc_max==0UL ) ) {
     188             :     /* No calldests map in SBPF v3+ or if text_cnt is 0. */
     189           6 :     prog->calldests_shmem = NULL;
     190           6 :     prog->calldests       = NULL;
     191          66 :   } else {
     192             :     /* Initialize calldests map. */
     193          66 :     prog->calldests_shmem = fd_sbpf_calldests_new(
     194          66 :           FD_SCRATCH_ALLOC_APPEND( laddr, fd_sbpf_calldests_align(),
     195          66 :                                           fd_sbpf_calldests_footprint( pc_max ) ),
     196           0 :           pc_max );
     197           0 :     prog->calldests = fd_sbpf_calldests_join( prog->calldests_shmem );
     198          66 :   }
     199             : 
     200          72 :   return prog;
     201          72 : }
     202             : 
     203             : void *
     204          30 : fd_sbpf_program_delete( fd_sbpf_program_t * mem ) {
     205             : 
     206          30 :   if( FD_LIKELY( mem->calldests ) ) {
     207          30 :     fd_sbpf_calldests_delete( fd_sbpf_calldests_leave( mem->calldests ) );
     208          30 :   }
     209          30 :   fd_memset( mem, 0, sizeof(fd_sbpf_program_t) );
     210             : 
     211          30 :   return (void *)mem;
     212          30 : }
     213             : 
     214             : /* fd_sbpf_loader_t contains various temporary state during loading. */
     215             : 
     216             : struct fd_sbpf_loader {
     217             :   /* External objects */
     218             :   ulong *              calldests; /* owned by program. NULL if text_cnt = 0 or SBPF v3+ */
     219             :   fd_sbpf_syscalls_t * syscalls;  /* owned by caller */
     220             : };
     221             : typedef struct fd_sbpf_loader fd_sbpf_loader_t;
     222             : 
     223             : /* fd_sbpf_slice_cstr_eq is a helper method for checking equality
     224             :    between a slice of memory to a null-terminated C-string.  Unlike
     225             :    strcmp, this function does not include the null-terminator in the
     226             :    comparison.  Returns 1 if the first slice_len bytes of the slice and
     227             :    cstr are equal, and 0 otherwise. */
     228             : static inline int
     229             : fd_sbpf_slice_cstr_eq( uchar const * slice,
     230             :                        ulong         slice_len,
     231       10317 :                        char const *  cstr ) {
     232       10317 :   return !!(slice_len==strlen( cstr ) && fd_memeq( slice, cstr, slice_len ));
     233       10317 : }
     234             : 
     235             : /* fd_sbpf_slice_cstr_start_with is a helper method for checking that a
     236             :    null-terminated C-string is a prefix of a slice of memory.  Returns 1
     237             :    if the first strlen(cstr) bytes of cstr is a prefix of slice, and 0
     238             :    otherwise. */
     239             : static inline int
     240             : fd_sbpf_slice_cstr_start_with( uchar const * slice,
     241             :                                ulong         slice_len,
     242        1020 :                                char const *  cstr ) {
     243        1020 :   ulong cstr_len = strlen( cstr );
     244        1020 :   return !!(slice_len>=cstr_len && fd_memeq( slice, cstr, cstr_len ));
     245        1020 : }
     246             : 
     247             : /* fd_sbpf_lenient_get_string_in_section queries a single string from a
     248             :    section which is marked as SHT_STRTAB.  Returns an ElfParserError on
     249             :    failure, and leaves *out_slice and *out_slice_len in an undefined
     250             :    state.  On success, returns 0 and sets *out_slice to a pointer into
     251             :    elf_bytes corresponding to the beginning of the string within the
     252             :    section.  *out_slice_len is set to the length of the resulting slice.
     253             :    Note that *out_slice_len does not include the null-terminator of the
     254             :    resulting string.
     255             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L467-L496 */
     256             : int
     257             : fd_sbpf_lenient_get_string_in_section( uchar const *         elf_bytes,
     258             :                                        ulong                 elf_bytes_len,
     259             :                                        fd_elf64_shdr const * section_header,
     260             :                                        uint                  offset_in_section,
     261             :                                        ulong                 maximum_length,
     262             :                                        uchar const **        out_slice,
     263        4263 :                                        ulong *               out_slice_len ) {
     264             :   /* This could be checked only once outside the loop, but to keep the code the same...
     265             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L474-L476 */
     266        4263 :   if( FD_UNLIKELY( section_header->sh_type!=FD_ELF_SHT_STRTAB ) ) {
     267           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
     268           0 :   }
     269             : 
     270             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L477-L482 */
     271        4263 :   ulong offset_in_file;
     272        4263 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( section_header->sh_offset, offset_in_section, &offset_in_file ) ) ) {
     273           0 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     274           0 :   }
     275             : 
     276        4263 :   ulong string_range_start = offset_in_file;
     277        4263 :   ulong string_range_end   = fd_ulong_min( section_header->sh_offset+section_header->sh_size, offset_in_file+maximum_length );
     278        4263 :   if( FD_UNLIKELY( string_range_end>elf_bytes_len ) ) {
     279           0 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     280           0 :   }
     281             :   /* In rust vec.get([n..n]) returns [], so this is accepted.
     282             :      vec.get([n..m]) with m<n returns None, so it throws ElfParserError::OutOfBounds. */
     283        4263 :   if( FD_UNLIKELY( string_range_end<string_range_start ) ) {
     284           0 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     285           0 :   }
     286             : 
     287             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L486-L495 */
     288        4263 :   uchar * null_terminator_ptr = memchr( (uchar const *)elf_bytes+string_range_start, 0, string_range_end-string_range_start );
     289        4263 :   if( FD_UNLIKELY( null_terminator_ptr==NULL ) ) {
     290           3 :     return FD_SBPF_ELF_PARSER_ERR_STRING_TOO_LONG;
     291           3 :   }
     292             : 
     293        4260 :   *out_slice     = elf_bytes+string_range_start;
     294        4260 :   *out_slice_len = (ulong)(null_terminator_ptr-*out_slice);
     295             : 
     296        4260 :   return FD_SBPF_ELF_SUCCESS;
     297        4263 : }
     298             : 
     299             : /* Registers a target PC into the calldests function registry. Returns
     300             :    0 on success, inserts the target PC into the calldests, and sets
     301             :    *opt_out_pc_hash to murmur3_32(target_pc) (if opt_out_pc_hash is
     302             :    non-NULL). Returns FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION on failure
     303             :    if the target PC is already in the syscalls registry and leaves
     304             :    out_pc_hash in an undefined state.
     305             : 
     306             :    An important note is that Agave's implementation uses a map to store
     307             :    key-value pairs of (murmur3_32(target_pc), target_pc) within the
     308             :    calldests. We optimize this by using a set containing
     309             :    target_pc (this is our calldests map), and then deriving
     310             :    the target PC on the fly given murmur3_32(target_pc) (provided as
     311             :    imm) in the VM by computing the inverse hash (since murmur3_32 is
     312             :    bijective for uints).
     313             : 
     314             :    Another important note is that if a key-value pair already exists in
     315             :    Agave's calldests map, they will only throw a symbol hash collision
     316             :    error if the target PC is different from the one already registered.
     317             :    We can omit this check because of the hash function's bijective
     318             :    property, since the key-value pairs are deterministically derived
     319             :    from one another.
     320             : 
     321             :    TODO: this function will have to be adapted to hash the target PC
     322             :    depending on the SBPF version (>= V3). That has not been implemented
     323             :    yet.
     324             : 
     325             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L142-L178 */
     326             : static int
     327             : fd_sbpf_register_function_hashed_legacy( fd_sbpf_loader_t *  loader,
     328             :                                          fd_sbpf_program_t * prog,
     329             :                                          uchar const *       name,
     330             :                                          ulong               name_len,
     331             :                                          ulong               target_pc,
     332        4743 :                                          uint *              opt_out_pc_hash ) {
     333             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L156-L160 */
     334        4743 :   uint pc_hash;
     335        4743 :   uchar is_entrypoint = fd_sbpf_slice_cstr_eq( name, name_len, "entrypoint" ) ||
     336        4743 :                         target_pc==FD_SBPF_ENTRYPOINT_PC;
     337        4743 :   if( FD_UNLIKELY( is_entrypoint ) ) {
     338          81 :     if( FD_UNLIKELY( prog->entry_pc!=ULONG_MAX && prog->entry_pc!=target_pc  ) ) {
     339             :       /* We already registered the entrypoint to a different target PC,
     340             :          so we cannot register it again. */
     341           0 :       return FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION;
     342           0 :     }
     343          81 :     prog->entry_pc = target_pc;
     344             : 
     345             :     /* Optimization for this constant value */
     346          81 :     pc_hash = FD_SBPF_ENTRYPOINT_HASH;
     347        4662 :   } else {
     348        4662 :     pc_hash = fd_pchash( (uint)target_pc );
     349        4662 :   }
     350             : 
     351             :   /* loader.get_function_registry() is their equivalent of our syscalls
     352             :      registry. Fail if the target PC is present there.
     353             : 
     354             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L161-L163 */
     355        4743 :   if( FD_UNLIKELY( fd_sbpf_syscalls_query( loader->syscalls, pc_hash, NULL ) ) ) {
     356           0 :     return FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION;
     357           0 :   }
     358             : 
     359             :   /* Insert the target PC into the calldests set if it's not the
     360             :      entrypoint. Due to the nature of our calldests, we also want to
     361             :      make sure that target_pc <= text_cnt, otherwise the insertion is
     362             :      UB. It's fine to skip inserting these entries because the calldests
     363             :      are write-only in the SBPF loader and only queried from the VM. */
     364        4743 :   if( FD_LIKELY( !is_entrypoint &&
     365        4743 :                   loader->calldests &&
     366        4743 :                   fd_sbpf_calldests_valid_idx( loader->calldests, target_pc ) ) ) {
     367        4662 :     fd_sbpf_calldests_insert( loader->calldests, target_pc );
     368        4662 :   }
     369             : 
     370        4743 :   if( opt_out_pc_hash ) *opt_out_pc_hash = pc_hash;
     371        4743 :   return FD_SBPF_ELF_SUCCESS;
     372        4743 : }
     373             : 
     374             : /* ELF Dynamic Relocations *********************************************
     375             : 
     376             :    ### Summary
     377             : 
     378             :    The sBPF ELF loader provides a limited dynamic relocation mechanism
     379             :    to fix up Clang-generated shared objects for execution in an sBPF VM.
     380             : 
     381             :    The relocation types themselves violate the eBPF and ELF specs in
     382             :    various ways.  In short, the relocation table (via DT_REL) is used to
     383             :    shift program code from zero-based addressing to the MM_PROGRAM
     384             :    segment in the VM memory map (at 0x1_0000_0000).
     385             : 
     386             :    As part of the Solana VM protocol it abides by strict determinism
     387             :    requirements.  This sadly means that we will have to replicate all
     388             :    edge cases and bugs in the Solana Labs ELF loader.
     389             : 
     390             :    Three relocation types are currently supported:
     391             :      - R_BPF_64_64: Sets an absolute address of a symbol as the
     392             :        64-bit immediate field of an lddw instruction
     393             :      - R_BPF_64_RELATIVE: Adds MM_PROGRAM_START (0x1_0000_0000) to ...
     394             :        a) ... the 64-bit imm field of an lddw instruction (if in text)
     395             :        b) ... a 64-bit integer (if not in text section)
     396             :      - R_BPF_64_32: Sets the 32-bit immediate field of a call
     397             :        instruction to ...
     398             :        a) the ID of a local function (Murmur3 hash of function PC address)
     399             :        b) the ID of a syscall
     400             : 
     401             :    Obviously invalid relocations (e.g. out-of-bounds of ELF file or
     402             :    unsupported reloc type) raise an error.
     403             :    Relocations that would corrupt ELF data structures are silently
     404             :    ignored (using the fd_sbpf_reloc_mask mechanism).
     405             : 
     406             :    ### History
     407             : 
     408             :    The use of relocations is technically redundant, as the Solana VM
     409             :    memory map has been hardcoded in program runtime v1 (so far the only
     410             :    runtime).  However, virtually all deployed programs as of April 2023
     411             :    are position-independent shared objects and make heavy use of such
     412             :    relocations.
     413             : 
     414             :    Relocations in the Solana VM have a complicated history.  Over the
     415             :    course of years, multiple protocol bugs have been added and fixed.
     416             :    The ELF loader needs to handle all these edge cases to avoid breaking
     417             :    "userspace".  I.e. any deployed programs which might be immutable
     418             :    must continue to function.
     419             : 
     420             :    While this complex logic will probably stick around for the next few
     421             :    years, the Solana protocol is getting increasingly restrictive for
     422             :    newly deployed ELFs.  Another proposed change is upgrading to
     423             :    position-dependent binaries without any dynamic relocations. */
     424             : 
     425             : /* R_BPF_64_64 relocates an absolute address into the extended imm field
     426             :    of an lddw-form instruction.  (Two instruction slots, low 32 bits in
     427             :    first immediate field, high 32 bits in second immediate field)
     428             : 
     429             :     Bits  0..32    32..64   64..96   96..128
     430             :          [ ... ] [ IMM_LO ] [ ... ] [ IMM_HI ]
     431             : 
     432             :     Returns 0 on success and writes the imm offset to the rodata.
     433             :     Returns the error code on failure.
     434             : 
     435             :     https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1069-L1141 */
     436             : 
     437             : static int
     438             : fd_sbpf_r_bpf_64_64( fd_sbpf_elf_t const *      elf,
     439             :                      ulong                      elf_sz,
     440             :                      uchar                    * rodata,
     441             :                      fd_sbpf_elf_info_t const * info,
     442             :                      fd_elf64_rel       const * dt_rel,
     443           6 :                      ulong                      r_offset ) {
     444             : 
     445           6 :   fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     446             : 
     447             :   /* Note that the sbpf_version variable is ALWAYS V0 (see Agave's code
     448             :      to understand why).
     449             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1070-L1080 */
     450           6 :   ulong imm_offset = fd_ulong_sat_add( r_offset, 4UL /* BYTE_OFFSET_IMMEDIATE */ );
     451             : 
     452             :   /* Bounds check.
     453             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1084-L1086 */
     454           6 :   if( FD_UNLIKELY( fd_ulong_sat_add( imm_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     455           0 :     return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     456           0 :   }
     457             : 
     458             :   /* Get the symbol entry from the dynamic symbol table.
     459             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1089-L1092 */
     460           6 :   fd_elf64_sym const * symbol = NULL;
     461           6 :   {
     462             :     /* Ensure the dynamic symbol table exists. */
     463           6 :     if( FD_UNLIKELY( info->shndx_dynsymtab<0 ) ) {
     464           0 :       return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
     465           0 :     }
     466             : 
     467             :     /* Get the dynamic symbol table section header. The section header
     468             :        was already validated in fd_sbpf_lenient_elf_parse() so we can
     469             :        directly get the symbol table. */
     470           6 :     fd_elf64_shdr const * sh_dynsym    = &shdrs[ info->shndx_dynsymtab ];
     471           6 :     fd_elf64_sym const *  dynsym_table = (fd_elf64_sym const *)( elf->bin + sh_dynsym->sh_offset );
     472           6 :     ulong                 dynsym_cnt   = (ulong)(sh_dynsym->sh_size / sizeof(fd_elf64_sym));
     473             : 
     474             :     /* The symbol table index is stored in the lower 4 bytes of r_info.
     475             :        Check the bounds of the symbol table index. */
     476           6 :     ulong r_sym = FD_ELF64_R_SYM( dt_rel->r_info );
     477           6 :     if( FD_UNLIKELY( r_sym>=dynsym_cnt ) ) {
     478           0 :       return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
     479           0 :     }
     480           6 :     symbol = &dynsym_table[ r_sym ];
     481           6 :   }
     482             : 
     483             :   /* Use the relative address as an offset to derive the relocated
     484             :      address.
     485             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1094-L1096 */
     486           6 :   uint  refd_addr = FD_LOAD( uint, &rodata[ imm_offset ] );
     487           6 :   ulong addr      = fd_ulong_sat_add( symbol->st_value, refd_addr );
     488             : 
     489             :   /* We need to normalize the address into the VM's memory space, which
     490             :      is rooted at 0x1_0000_0000 (the program ro-data region). If the
     491             :      linker hasn't normalized the addresses already, we treat addr as
     492             :      a relative offset into the program ro-data region. */
     493           6 :   if( addr<FD_SBPF_MM_PROGRAM_ADDR ) {
     494           6 :     addr = fd_ulong_sat_add( addr, FD_SBPF_MM_PROGRAM_ADDR );
     495           6 :   }
     496             : 
     497             :   /* Again, no need to check the sbpf_version because it's always V0.
     498             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1106-L1140 */
     499           6 :   ulong imm_low_offset  = imm_offset;
     500           6 :   ulong imm_high_offset = fd_ulong_sat_add( imm_low_offset, 8UL /* INSN_SIZE */ );
     501             : 
     502             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1116-L1122 */
     503           6 :   {
     504             :     /* Bounds check before writing to the rodata. */
     505           6 :     if( FD_UNLIKELY( fd_ulong_sat_add( imm_low_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     506           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     507           0 :     }
     508             : 
     509             :     /* Write back */
     510           6 :     FD_STORE( uint, rodata+imm_low_offset, (uint)addr );
     511           6 :   }
     512             : 
     513             :   /* Same as above, but for the imm high offset.
     514             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1125-L1134 */
     515           0 :   {
     516             :     /* Bounds check before writing to the rodata. */
     517           6 :     if( FD_UNLIKELY( fd_ulong_sat_add( imm_high_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     518           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     519           0 :     }
     520             : 
     521             :     /* Write back */
     522           6 :     FD_STORE( uint, rodata+imm_high_offset, (uint)(addr>>32UL) );
     523           6 :   }
     524             : 
     525             :   /* ...rest of this function is a no-op because
     526             :      enable_symbol_and_section_labels is disabled in production. */
     527             : 
     528           6 :   return FD_SBPF_ELF_SUCCESS;
     529           6 : }
     530             : 
     531             : /* R_BPF_64_RELATIVE is almost entirely Solana specific. Returns 0 on
     532             :    success and an ElfError on failure.
     533             : 
     534             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1142-L1247 */
     535             : 
     536             : static int
     537             : fd_sbpf_r_bpf_64_relative( fd_sbpf_elf_t const *      elf,
     538             :                            ulong                      elf_sz,
     539             :                            uchar                    * rodata,
     540             :                            fd_sbpf_elf_info_t const * info,
     541        5568 :                            ulong                      r_offset ) {
     542             : 
     543        5568 :   fd_elf64_shdr const * shdrs   = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     544        5568 :   fd_elf64_shdr const * sh_text = &shdrs[ info->shndx_text ];
     545             : 
     546             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1147-L1148 */
     547        5568 :   ulong imm_offset = fd_ulong_sat_add( r_offset, 4UL /* BYTE_OFFSET_IMMEDIATE */ );
     548             : 
     549             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1150-L1246 */
     550        5568 :   fd_sbpf_range_t text_section_range;
     551        5568 :   if( fd_shdr_get_file_range( sh_text, &text_section_range ) &&
     552        5568 :       fd_sbpf_range_contains( &text_section_range, r_offset ) ) {
     553             : 
     554             :     /* We are relocating a lddw (load double word) instruction which
     555             :        spans two instruction slots. The address top be relocated is
     556             :        split in two halves in the two imms of the instruction slots.
     557             : 
     558             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1159-L1162 */
     559        3756 :     ulong imm_low_offset  = imm_offset;
     560        3756 :     ulong imm_high_offset = fd_ulong_sat_add( r_offset,
     561        3756 :                                               4UL /* BYTE_OFFSET_IMMEDIATE */ + 8UL /* INSN_SIZE */ );
     562             : 
     563             :     /* Read the low side of the address. Perform a bounds check first.
     564             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1164-L1171 */
     565        3756 :     if( FD_UNLIKELY( fd_ulong_sat_add( imm_low_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     566           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     567           0 :     }
     568        3756 :     uint va_low = FD_LOAD( uint, rodata+imm_low_offset );
     569             : 
     570             :     /* Read the high side of the address. Perform a bounds check first.
     571             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1174-L1180 */
     572        3756 :     if( FD_UNLIKELY( fd_ulong_sat_add( imm_high_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     573           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     574           0 :     }
     575        3756 :     uint va_high = FD_LOAD( uint, rodata+imm_high_offset );
     576             : 
     577             :     /* Put the address back together.
     578             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1182-L1187 */
     579        3756 :     ulong refd_addr = ( (ulong)va_high<<32UL ) | va_low;
     580        3756 :     if( FD_UNLIKELY( refd_addr==0UL ) ) {
     581           0 :       return FD_SBPF_ELF_ERR_INVALID_VIRTUAL_ADDRESS;
     582           0 :     }
     583             : 
     584             :     /* We need to normalize the address into the VM's memory space, which
     585             :        is rooted at 0x1_0000_0000 (the program ro-data region). If the
     586             :        linker hasn't normalized the addresses already, we treat addr as
     587             :        a relative offset into the program ro-data region.
     588             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1189-L1193 */
     589        3756 :     if( refd_addr<FD_SBPF_MM_PROGRAM_ADDR ) {
     590        3756 :       refd_addr = fd_ulong_sat_add( refd_addr, FD_SBPF_MM_PROGRAM_ADDR );
     591        3756 :     }
     592             : 
     593             :     /* Write back the low half. Perform a bounds check first.
     594             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1195-L1202 */
     595        3756 :     if( FD_UNLIKELY( fd_ulong_sat_add( imm_low_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     596           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     597           0 :     }
     598        3756 :     FD_STORE( uint, rodata+imm_low_offset, (uint)refd_addr );
     599             : 
     600             :     /* Write back the high half. Perform a bounds check first.
     601             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1205-L1214 */
     602        3756 :     if( FD_UNLIKELY( fd_ulong_sat_add( imm_high_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     603           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     604           0 :     }
     605        3756 :     FD_STORE( uint, rodata+imm_high_offset, (uint)(refd_addr>>32UL) );
     606        3756 :   } else {
     607             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1216-L1228 */
     608        1812 :     ulong refd_addr = 0UL;
     609             : 
     610             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1230-L1239 */
     611        1812 :     if( FD_UNLIKELY( fd_ulong_sat_add( r_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     612           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     613           0 :     }
     614        1812 :     refd_addr = FD_LOAD( uint, rodata+imm_offset );
     615        1812 :     refd_addr = fd_ulong_sat_add( refd_addr, FD_SBPF_MM_PROGRAM_ADDR );
     616             : 
     617             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1242-L1245 */
     618        1812 :     if( FD_UNLIKELY( fd_ulong_sat_add( r_offset, sizeof(ulong) )>elf_sz ) ) {
     619           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     620           0 :     }
     621             : 
     622        1812 :     FD_STORE( ulong, rodata+r_offset, refd_addr );
     623        1812 :   }
     624             : 
     625        5568 :   return FD_SBPF_ELF_SUCCESS;
     626        5568 : }
     627             : 
     628             : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1248-L1301 */
     629             : static int
     630             : fd_sbpf_r_bpf_64_32( fd_sbpf_loader_t *              loader,
     631             :                      fd_sbpf_program_t *             prog,
     632             :                      fd_sbpf_elf_t const *           elf,
     633             :                      ulong                           elf_sz,
     634             :                      uchar *                         rodata,
     635             :                      fd_sbpf_elf_info_t const *      info,
     636             :                      fd_elf64_rel const *            dt_rel,
     637             :                      ulong                           r_offset,
     638        1920 :                      fd_sbpf_loader_config_t const * config ) {
     639             : 
     640        1920 :   fd_elf64_shdr const * shdrs                  = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     641        1920 :   fd_elf64_shdr const * sh_text                = &shdrs[ info->shndx_text ];
     642        1920 :   fd_elf64_shdr const * dyn_section_names_shdr = &shdrs[ info->shndx_dynstr ];
     643             : 
     644             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1253-L1254 */
     645        1920 :   ulong imm_offset = fd_ulong_sat_add( r_offset, 4UL /* BYTE_OFFSET_IMMEDIATE */ );
     646             : 
     647             :   /* Get the symbol entry from the dynamic symbol table.
     648             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1256-L1259 */
     649        1920 :   fd_elf64_sym const * symbol = NULL;
     650             : 
     651             :   /* Ensure the dynamic symbol table exists. */
     652        1920 :   if( FD_UNLIKELY( info->shndx_dynsymtab<0 ) ) {
     653           0 :     return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
     654           0 :   }
     655             : 
     656             :   /* Get the dynamic symbol table section header. The section header
     657             :      was already validated in fd_sbpf_lenient_elf_parse() so we can
     658             :      directly get the symbol table. */
     659        1920 :   fd_elf64_shdr const * sh_dynsym    = &shdrs[ info->shndx_dynsymtab ];
     660        1920 :   fd_elf64_sym const *  dynsym_table = (fd_elf64_sym const *)( elf->bin + sh_dynsym->sh_offset );
     661        1920 :   ulong                 dynsym_cnt   = (ulong)(sh_dynsym->sh_size / sizeof(fd_elf64_sym));
     662             : 
     663             :   /* The symbol table index is stored in the lower 4 bytes of r_info.
     664             :      Check the bounds of the symbol table index. */
     665        1920 :   ulong r_sym = FD_ELF64_R_SYM( dt_rel->r_info );
     666        1920 :   if( FD_UNLIKELY( r_sym>=dynsym_cnt ) ) {
     667           0 :     return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
     668           0 :   }
     669        1920 :   symbol = &dynsym_table[ r_sym ];
     670             : 
     671             :   /* Verify symbol name.
     672             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1261-L1263 */
     673        1920 :   uchar const * name;
     674        1920 :   ulong         name_len;
     675        1920 :   if( FD_UNLIKELY( fd_sbpf_lenient_get_string_in_section( elf->bin, elf_sz, dyn_section_names_shdr, symbol->st_name, FD_SBPF_SYMBOL_NAME_SZ_MAX, &name, &name_len ) ) ) {
     676           0 :     return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
     677           0 :   }
     678             : 
     679             :   /* If the symbol is defined, this is a bpf-to-bpf call.
     680             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1265-L1295 */
     681        1920 :   uint key = 0U;
     682        1920 :   int symbol_is_function = ( FD_ELF64_ST_TYPE( symbol->st_info )==FD_ELF_STT_FUNC );
     683        1920 :   {
     684        1920 :     if( symbol_is_function && symbol->st_value!=0UL ) {
     685             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1267-L1269 */
     686         138 :       fd_sbpf_range_t text_section_range = (fd_sbpf_range_t) {
     687         138 :           .lo = sh_text->sh_addr,
     688         138 :           .hi = fd_ulong_sat_add( sh_text->sh_addr, sh_text->sh_size ) };
     689         138 :       if( FD_UNLIKELY( !fd_sbpf_range_contains( &text_section_range, symbol->st_value ) ) ) {
     690           0 :         return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     691           0 :       }
     692             : 
     693             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1270-L1279 */
     694         138 :       ulong target_pc = fd_ulong_sat_sub( symbol->st_value, sh_text->sh_addr ) / 8UL;
     695         138 :       int err = fd_sbpf_register_function_hashed_legacy( loader, prog, name, name_len, target_pc, &key );
     696         138 :       if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
     697           0 :         return err;
     698           0 :       }
     699        1782 :     } else {
     700             :       /* Else, it's a syscall. Ensure that the syscall can be resolved.
     701             :          https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1281-L1294 */
     702        1782 :       key = fd_murmur3_32(name, name_len, 0UL );
     703        1782 :       if( FD_UNLIKELY( config->reject_broken_elfs &&
     704        1782 :                        fd_sbpf_syscalls_query( loader->syscalls, key, NULL )==NULL ) ) {
     705           0 :         return FD_SBPF_ELF_ERR_UNRESOLVED_SYMBOL;
     706           0 :       }
     707        1782 :     }
     708        1920 :   }
     709             : 
     710             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1297-L1300 */
     711        1920 :   if( FD_UNLIKELY( fd_ulong_sat_add( imm_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
     712           0 :     return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
     713           0 :   }
     714             : 
     715        1920 :   FD_STORE( uint, rodata+imm_offset, key );
     716             : 
     717        1920 :   return FD_SBPF_ELF_SUCCESS;
     718        1920 : }
     719             : 
     720             : static int
     721             : fd_sbpf_elf_peek_strict( fd_sbpf_elf_info_t * info,
     722             :                          void const *         bin,
     723           9 :                          ulong                bin_sz ) {
     724             : 
     725             :   /* Parse file header */
     726             : 
     727             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L425
     728             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L278
     729             :      (Agave does some extra checks on alignment, but they don't seem necessary) */
     730           9 :   if( FD_UNLIKELY( bin_sz<sizeof(fd_elf64_ehdr) ) ) {
     731           0 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     732           0 :   }
     733             : 
     734           9 :   fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
     735             : 
     736             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L430-L453 */
     737           9 :   ulong program_header_table_end = sizeof(fd_elf64_ehdr) + ehdr.e_phnum*sizeof(fd_elf64_phdr);
     738             : 
     739           9 :   int parse_ehdr_err =
     740           9 :       ( fd_uint_load_4( ehdr.e_ident )    != FD_ELF_MAG_LE         )
     741           9 :     | ( ehdr.e_ident[ FD_ELF_EI_CLASS   ] != FD_ELF_CLASS_64       )
     742           9 :     | ( ehdr.e_ident[ FD_ELF_EI_DATA    ] != FD_ELF_DATA_LE        )
     743           9 :     | ( ehdr.e_ident[ FD_ELF_EI_VERSION ] != 1                     )
     744           9 :     | ( ehdr.e_ident[ FD_ELF_EI_OSABI   ] != FD_ELF_OSABI_NONE     )
     745           9 :     | ( fd_ulong_load_8( ehdr.e_ident+8 ) != 0UL                   )
     746           9 :     | ( ehdr.e_type                       != FD_ELF_ET_DYN         )
     747           9 :     | ( ehdr.e_machine                    != FD_ELF_EM_SBPF        )
     748           9 :     | ( ehdr.e_version                    != 1                     )
     749             :     // | ( ehdr.e_entry )
     750           9 :     | ( ehdr.e_phoff                      != sizeof(fd_elf64_ehdr) )
     751             :     // | ( ehdr.e_shoff )
     752             :     // | ( ehdr.e_flags )
     753           9 :     | ( ehdr.e_ehsize                     != sizeof(fd_elf64_ehdr) )
     754           9 :     | ( ehdr.e_phentsize                  != sizeof(fd_elf64_phdr) )
     755           9 :     | ( ehdr.e_phnum                      <  EXPECTED_PHDR_CNT     ) /* SIMD-0189 says < instead of != */
     756           9 :     | ( program_header_table_end          >= bin_sz                )
     757           9 :     | ( ehdr.e_shentsize                  != sizeof(fd_elf64_shdr) )
     758             :     // | ( ehdr.e_shnum )
     759           9 :     | ( ehdr.e_shstrndx                   >= ehdr.e_shnum          )
     760           9 :   ;
     761           9 :   if( FD_UNLIKELY( parse_ehdr_err ) ) {
     762           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
     763           0 :   }
     764             : 
     765             :   /* Parse program headers (expecting 4 segments) */
     766             : 
     767             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L455-L487
     768             :      Note: Agave iterates with a zip, i.e. it cuts the loop to 4, even
     769             :            though the number of phdrs is allowed to be higher. */
     770           9 :   ulong expected_p_vaddr[ EXPECTED_PHDR_CNT ] = { FD_SBPF_MM_BYTECODE_ADDR, FD_SBPF_MM_RODATA_ADDR, FD_SBPF_MM_STACK_ADDR, FD_SBPF_MM_HEAP_ADDR };
     771           9 :   uint  expected_p_flags[ EXPECTED_PHDR_CNT ] = { FD_SBPF_PF_X,             FD_SBPF_PF_R,           FD_SBPF_PF_RW,         FD_SBPF_PF_RW        };
     772           9 :   fd_elf64_phdr     phdr[ EXPECTED_PHDR_CNT ];
     773          45 :   for( uint i=0; i<EXPECTED_PHDR_CNT; i++ ) {
     774          36 :     ulong phdr_off = sizeof(fd_elf64_ehdr) + i*sizeof(fd_elf64_phdr);
     775          36 :     phdr[ i ] = FD_LOAD( fd_elf64_phdr, bin+phdr_off );
     776             : 
     777          36 :     ulong p_filesz = ( expected_p_flags[ i ] & FD_SBPF_PF_W ) ? 0UL : phdr[ i ].p_memsz;
     778          36 :     int parse_phdr_err =
     779          36 :         ( phdr[ i ].p_type         != FD_ELF_PT_LOAD              )
     780          36 :       | ( phdr[ i ].p_flags        != expected_p_flags[ i ]       )
     781          36 :       | ( phdr[ i ].p_offset       <  program_header_table_end    )
     782          36 :       | ( phdr[ i ].p_offset       >= bin_sz                      )
     783          36 :       | ( phdr[ i ].p_offset % 8UL != 0UL                         )
     784          36 :       | ( phdr[ i ].p_vaddr        != expected_p_vaddr[ i ]       )
     785          36 :       | ( phdr[ i ].p_paddr        != expected_p_vaddr[ i ]       )
     786          36 :       | ( phdr[ i ].p_filesz       != p_filesz                    )
     787          36 :       | ( phdr[ i ].p_filesz       >  bin_sz - phdr[ i ].p_offset )
     788          36 :       | ( phdr[ i ].p_filesz % 8UL != 0UL                         )
     789          36 :       | ( phdr[ i ].p_memsz        >= FD_SBPF_MM_REGION_SZ        )
     790          36 :     ;
     791          36 :     if( FD_UNLIKELY( parse_phdr_err ) ) {
     792           0 :       return FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER;
     793           0 :     }
     794          36 :   }
     795             : 
     796             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L489-L506 */
     797           9 :   ulong vm_range_start = phdr[ 0 ].p_vaddr;
     798           9 :   ulong vm_range_end = phdr[ 0 ].p_vaddr + phdr[ 0 ].p_memsz;
     799           9 :   ulong entry_chk = ehdr.e_entry + 7UL;
     800           9 :   int parse_e_entry_err =
     801           9 :      !( vm_range_start <= entry_chk && entry_chk < vm_range_end ) /* rust contains includes min, excludes max*/
     802           9 :     | ( ehdr.e_entry % 8UL != 0UL                               )
     803           9 :   ;
     804           9 :   if( FD_UNLIKELY( parse_e_entry_err ) ) {
     805           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
     806           0 :   }
     807             : 
     808             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L507-L515 */
     809           9 :   ulong entry_pc = ( ehdr.e_entry - phdr[ 0 ].p_vaddr ) / 8UL;
     810           9 :   ulong insn = fd_ulong_load_8( (uchar const *) bin + phdr[ 0 ].p_offset + entry_pc*8UL );
     811             :   /* Entrypoint must be a valid function start (ADD64_IMM with dst=r10)
     812             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/ebpf.rs#L588 */
     813           9 :   if( FD_UNLIKELY( !fd_sbpf_is_function_start( fd_sbpf_instr( insn ) ) ) ) {
     814           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
     815           0 :   }
     816             : 
     817             :   /* config.enable_symbol_and_section_labels is false in production,
     818             :      so there's nothing else to do.
     819             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L519 */
     820             : 
     821           9 :   info->bin_sz   = bin_sz;
     822           9 :   info->text_off = (uint)phdr[ 0 ].p_offset;
     823           9 :   info->text_sz  = (uint)phdr[ 0 ].p_memsz;
     824           9 :   info->text_cnt = (uint)( phdr[ 0 ].p_memsz / 8UL );
     825             : 
     826           9 :   return FD_SBPF_ELF_SUCCESS;
     827           9 : }
     828             : 
     829             : static inline int
     830        2853 : fd_sbpf_check_overlap( ulong a_start, ulong a_end, ulong b_start, ulong b_end ) {
     831        2853 :   return !( ( a_end <= b_start || b_end <= a_start ) );
     832        2853 : }
     833             : 
     834             : /* Mirrors Elf64::parse() in Agave. Returns an ElfParserError code on
     835             :    failure and 0 on success.
     836             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L148 */
     837             : int
     838             : fd_sbpf_lenient_elf_parse( fd_sbpf_elf_info_t * info,
     839             :                            void const *         bin,
     840          93 :                            ulong                bin_sz ) {
     841             : 
     842             :   /* This documents the values that will be set in this function */
     843          93 :   info->bin_sz          = bin_sz;
     844          93 :   info->phndx_dyn       = -1;
     845          93 :   info->shndx_dyn       = -1;
     846          93 :   info->shndx_symtab    = -1;
     847          93 :   info->shndx_strtab    = -1;
     848          93 :   info->shndx_dynstr    = -1;
     849          93 :   info->shndx_dynsymtab = -1;
     850             : 
     851             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L149 */
     852          93 :   if( FD_UNLIKELY( bin_sz<sizeof(fd_elf64_ehdr) ) ) {
     853           0 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     854           0 :   }
     855             : 
     856          93 :   fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
     857          93 :   ulong ehdr_start = 0;
     858          93 :   ulong ehdr_end = sizeof(fd_elf64_ehdr);
     859             : 
     860             :   /* ELF header
     861             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L151-L162 */
     862          93 :   int parse_ehdr_err =
     863          93 :       ( fd_uint_load_4( ehdr.e_ident )    != FD_ELF_MAG_LE         )
     864          93 :     | ( ehdr.e_ident[ FD_ELF_EI_CLASS   ] != FD_ELF_CLASS_64       )
     865          93 :     | ( ehdr.e_ident[ FD_ELF_EI_DATA    ] != FD_ELF_DATA_LE        )
     866          93 :     | ( ehdr.e_ident[ FD_ELF_EI_VERSION ] != 1                     )
     867          93 :     | ( ehdr.e_version                    != 1                     )
     868          93 :     | ( ehdr.e_ehsize                     != sizeof(fd_elf64_ehdr) )
     869          93 :     | ( ehdr.e_phentsize                  != sizeof(fd_elf64_phdr) )
     870          93 :     | ( ehdr.e_shentsize                  != sizeof(fd_elf64_shdr) )
     871          93 :     | ( ehdr.e_shstrndx                   >= ehdr.e_shnum          )
     872          93 :   ;
     873          93 :   if( FD_UNLIKELY( parse_ehdr_err ) ) {
     874           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
     875           0 :   }
     876             : 
     877             :   /* Program headers
     878             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L164-L165 */
     879          93 :   ulong phdr_start = ehdr.e_phoff;
     880          93 :   ulong phdr_end, phdr_sz;
     881             :   /* Elf64::parse_program_header_table() */
     882          93 :   {
     883          93 :     if( FD_UNLIKELY( __builtin_umull_overflow( ehdr.e_phnum, sizeof(fd_elf64_phdr), &phdr_sz ) ) ) {
     884           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     885           0 :     }
     886             : 
     887          93 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( ehdr.e_phoff, phdr_sz, &phdr_end ) ) ) {
     888             :       /* ArithmeticOverflow -> ElfParserError::OutOfBounds
     889             :         https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L671-L675 */
     890           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     891           0 :     }
     892             : 
     893             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L301 */
     894          93 :     if( FD_UNLIKELY( fd_sbpf_check_overlap( ehdr_start, ehdr_end, phdr_start, phdr_end ) ) ) {
     895           0 :       return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
     896           0 :     }
     897             : 
     898             :     /* Ensure program header table range lies within the file, like
     899             :        slice_from_bytes. Unfortunately the checks have to be split up
     900             :        because Agave throws different error codes depending on which
     901             :        condition fails...
     902             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L302-L303 */
     903          93 :     if( FD_UNLIKELY( phdr_sz%sizeof(fd_elf64_phdr)!=0UL ) ) {
     904           0 :       return FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE;
     905           0 :     }
     906             : 
     907          93 :     if( FD_UNLIKELY( phdr_end>bin_sz ) ) {
     908           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     909           0 :     }
     910             : 
     911          93 :     if( FD_UNLIKELY( !fd_ulong_is_aligned( phdr_start, 8UL ) ) ) {
     912           0 :       return FD_SBPF_ELF_PARSER_ERR_INVALID_ALIGNMENT;
     913           0 :     }
     914          93 :   }
     915             : 
     916             :   /* Section headers
     917             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L167-L172 */
     918             : 
     919          93 :   ulong shdr_start = ehdr.e_shoff;
     920          93 :   ulong shdr_end, shdr_sz;
     921             :   /* Elf64::parse_section_header_table() */
     922          93 :   {
     923          93 :     if( FD_UNLIKELY( __builtin_umull_overflow( ehdr.e_shnum, sizeof(fd_elf64_shdr), &shdr_sz ) ) ) {
     924           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     925           0 :     }
     926             : 
     927             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L314-L317 */
     928          93 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( ehdr.e_shoff, shdr_sz, &shdr_end ) ) ) {
     929             :       /* ArithmeticOverflow -> ElfParserError::OutOfBounds
     930             :         https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L671-L675 */
     931           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     932           0 :     }
     933             : 
     934             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L318 */
     935          93 :     if( FD_UNLIKELY( fd_sbpf_check_overlap( ehdr_start, ehdr_end, shdr_start, shdr_end ) ) ) {
     936           0 :       return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
     937           0 :     }
     938             : 
     939             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L319 */
     940          93 :     if( FD_UNLIKELY( fd_sbpf_check_overlap( phdr_start, phdr_end, shdr_start, shdr_end ) ) ) {
     941           0 :       return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
     942           0 :     }
     943             : 
     944             :     /* Ensure section header table range lies within the file, like slice_from_bytes */
     945          93 :     if( FD_UNLIKELY( shdr_end > bin_sz ) ) {
     946           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     947           0 :     }
     948          93 :   }
     949             : 
     950             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L174-L177 */
     951          93 :   fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + ehdr.e_shoff );
     952          93 :   if( FD_UNLIKELY( shdr.sh_type != FD_ELF_SHT_NULL ) ) {
     953           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
     954           0 :   }
     955             : 
     956             :   /* Parse each program header
     957             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L179-L196 */
     958          93 :   ulong vaddr = 0UL;
     959         420 :   for( ulong i=0; i<ehdr.e_phnum; i++ ) {
     960         327 :     fd_elf64_phdr phdr = FD_LOAD( fd_elf64_phdr, bin + phdr_start + i*sizeof(fd_elf64_phdr) );
     961         327 :     if( FD_UNLIKELY( phdr.p_type != FD_ELF_PT_LOAD ) ) {
     962             :       /* Remember first PT_DYNAMIC program header for dynamic parsing */
     963          84 :       if( phdr.p_type==FD_ELF_PT_DYNAMIC && info->phndx_dyn == -1 ) {
     964          84 :         info->phndx_dyn = (int)i;
     965          84 :       }
     966          84 :       continue;
     967          84 :     }
     968         243 :     if( FD_UNLIKELY( phdr.p_vaddr<vaddr ) ) {
     969           0 :       return FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER;
     970           0 :     }
     971         243 :     ulong _offset_plus_size;
     972         243 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( phdr.p_offset, phdr.p_filesz, &_offset_plus_size ) ) ) {
     973           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     974           0 :     }
     975         243 :     if( FD_UNLIKELY( phdr.p_offset + phdr.p_filesz > bin_sz ) ) {
     976           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
     977           0 :     }
     978         243 :     vaddr = phdr.p_vaddr;
     979         243 :   }
     980             : 
     981             :   /* Parse each section header
     982             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L198-L216 */
     983          93 :   ulong offset = 0UL;
     984         960 :   for( ulong i=0; i<ehdr.e_shnum; i++ ) {
     985             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L200-L205 */
     986         867 :     fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
     987         867 :     if( FD_UNLIKELY( shdr.sh_type==FD_ELF_SHT_NOBITS ) ) {
     988           9 :       continue;
     989           9 :     }
     990             : 
     991             :     /* Remember first SHT_DYNAMIC section header for dynamic parsing */
     992         858 :     if( shdr.sh_type==FD_ELF_SHT_DYNAMIC && info->shndx_dyn == -1 ) {
     993          84 :       info->shndx_dyn = (int)i;
     994          84 :     }
     995             : 
     996         858 :     ulong sh_start = shdr.sh_offset;
     997         858 :     ulong sh_end;
     998         858 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( shdr.sh_offset, shdr.sh_size, &sh_end ) ) ) {
     999           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1000           0 :     }
    1001             : 
    1002             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L206-L208 */
    1003         858 :     if( FD_UNLIKELY( fd_sbpf_check_overlap( sh_start, sh_end, ehdr_start, ehdr_end ) ) ) {
    1004           0 :       return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
    1005           0 :     }
    1006         858 :     if( FD_UNLIKELY( fd_sbpf_check_overlap( sh_start, sh_end, phdr_start, phdr_end ) ) ) {
    1007           0 :       return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
    1008           0 :     }
    1009         858 :     if( FD_UNLIKELY( fd_sbpf_check_overlap( sh_start, sh_end, shdr_start, shdr_end ) ) ) {
    1010           0 :       return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
    1011           0 :     }
    1012             : 
    1013             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L209-L215 */
    1014         858 :     if( FD_UNLIKELY( sh_start < offset ) ) {
    1015           0 :       return FD_SBPF_ELF_PARSER_ERR_SECTION_NOT_IN_ORDER;
    1016           0 :     }
    1017         858 :     offset = sh_end;
    1018         858 :     if( FD_UNLIKELY( sh_end > bin_sz ) ) {
    1019           0 :       return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1020           0 :     }
    1021         858 :   }
    1022             : 
    1023             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L218-L224
    1024             :      section_header_table.get() returning ok is equivalent to ehdr.e_shstrndx < ehdr.e_shnum,
    1025             :      and this is already checked above. So, nothing to do here. */
    1026             : 
    1027             :   /* Parse sections
    1028             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L240 */
    1029          93 :   {
    1030             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L340-L342 */
    1031          93 :     if( FD_UNLIKELY( ehdr.e_shstrndx == 0 ) ) {
    1032           0 :       return FD_SBPF_ELF_PARSER_ERR_NO_SECTION_NAME_STRING_TABLE;
    1033           0 :     }
    1034             : 
    1035             :     /* Use section name string table to identify well-known sections */
    1036          93 :     ulong section_names_shdr_idx = ehdr.e_shstrndx;
    1037          93 :     fd_elf64_shdr section_names_shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + section_names_shdr_idx*sizeof(fd_elf64_shdr) );
    1038             :     /* Agave repeats the following validation all the times, we can do it once here
    1039             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L474-L476 */
    1040          93 :     if( FD_UNLIKELY( section_names_shdr.sh_type != FD_ELF_SHT_STRTAB ) ) {
    1041           0 :       return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
    1042           0 :     }
    1043             : 
    1044             :     /* Iterate sections and record indices for .text, .symtab, .strtab, .dyn, .dynstr */
    1045         936 :     for( ulong i=0; i<ehdr.e_shnum; i++ ) {
    1046             :       /* Again... */
    1047         846 :       fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
    1048             : 
    1049         846 :       uchar const * name;
    1050         846 :       ulong         name_len;
    1051         846 :       int res = fd_sbpf_lenient_get_string_in_section( bin, bin_sz, &section_names_shdr, shdr.sh_name, FD_SBPF_SECTION_NAME_SZ_MAX, &name, &name_len );
    1052         846 :       if( FD_UNLIKELY( res < 0 ) ) {
    1053           3 :         return res;
    1054           3 :       }
    1055             : 
    1056             :       /* Store the first section by name:
    1057             :          https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L350-L355
    1058             :          The rust code expands in:
    1059             :             match section_name {
    1060             :                 b".symtab" => {
    1061             :                     if self.symbol_section_header.is_some() {
    1062             :                         return Err(ElfParserError::InvalidSectionHeader);
    1063             :                     }
    1064             :                     self.symbol_section_header = Some(section_header);
    1065             :                 }
    1066             :                 ...
    1067             :                 _ => {}
    1068             :             }
    1069             :          Note that the number of bytes compared should not include the
    1070             :          null-terminator.
    1071             :         */
    1072         843 :       if( fd_sbpf_slice_cstr_eq( name, name_len, ".symtab" ) ) {
    1073          36 :         if( FD_UNLIKELY( info->shndx_symtab != -1 ) ) {
    1074           0 :           return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
    1075           0 :         }
    1076          36 :         info->shndx_symtab = (int)i;
    1077         807 :       } else if( fd_sbpf_slice_cstr_eq( name, name_len, ".strtab" ) ) {
    1078          36 :         if( FD_UNLIKELY( info->shndx_strtab != -1 ) ) {
    1079           0 :           return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
    1080           0 :         }
    1081          36 :         info->shndx_strtab = (int)i;
    1082         771 :       } else if( fd_sbpf_slice_cstr_eq( name, name_len, ".dynstr" ) ) {
    1083          81 :         if( FD_UNLIKELY( info->shndx_dynstr != -1 ) ) {
    1084           0 :           return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
    1085           0 :         }
    1086          81 :         info->shndx_dynstr = (int)i;
    1087          81 :       }
    1088         843 :     }
    1089          93 :   }
    1090             : 
    1091             :   /* Parse dynamic
    1092             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L241 */
    1093          90 :   {
    1094             :     /* Try PT_DYNAMIC first; if invalid or absent, fall back to SHT_DYNAMIC.
    1095             :        Note that only the first PT_DYNAMIC and SHT_DYNAMIC are used because of Rust iter().find().
    1096             :        Mirrors Rust logic:
    1097             :          - Try PT_DYNAMIC: https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L364-L372
    1098             :          - Fallback to SHT_DYNAMIC if PT missing/invalid: https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L374-L387
    1099             :        If neither exists, return OK (static file). If SHT_DYNAMIC exists but is invalid, error. */
    1100             : 
    1101          90 :     ulong dynamic_table_start = ULONG_MAX;
    1102          90 :     ulong dynamic_table_end = ULONG_MAX;
    1103             : 
    1104             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L364-L372 */
    1105          90 :     if( info->phndx_dyn >= 0 ) {
    1106          81 :       fd_elf64_phdr dyn_ph = FD_LOAD( fd_elf64_phdr, bin + phdr_start + (ulong)info->phndx_dyn*sizeof(fd_elf64_phdr) );
    1107          81 :       dynamic_table_start = dyn_ph.p_offset;
    1108          81 :       dynamic_table_end = dyn_ph.p_offset + dyn_ph.p_filesz;
    1109             : 
    1110             :       /* slice_from_program_header also checks that the size of the
    1111             :          slice is a multiple of the type size and that the alignment is
    1112             :          correct. */
    1113          81 :       if( FD_UNLIKELY( dynamic_table_end<dynamic_table_start ||
    1114          81 :                        dynamic_table_end>bin_sz ||
    1115          81 :                        dyn_ph.p_filesz%sizeof(fd_elf64_dyn)!=0UL ||
    1116          81 :                        !fd_ulong_is_aligned( dynamic_table_start, 8UL ) ) ) {
    1117             :         /* skip - try SHT_DYNAMIC instead */
    1118           0 :         dynamic_table_start = ULONG_MAX;
    1119           0 :         dynamic_table_end = ULONG_MAX;
    1120           0 :       }
    1121          81 :     }
    1122             : 
    1123             :     /* If PT_DYNAMIC did not validate, try SHT_DYNAMIC
    1124             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L376-L387 */
    1125          90 :     if( dynamic_table_start==ULONG_MAX && info->shndx_dyn >= 0 ) {
    1126           0 :       fd_elf64_shdr dyn_sh = FD_LOAD( fd_elf64_shdr, bin + shdr_start + (ulong)info->shndx_dyn*sizeof(fd_elf64_shdr) );
    1127           0 :       dynamic_table_start = dyn_sh.sh_offset;
    1128           0 :       dynamic_table_end = dyn_sh.sh_offset + dyn_sh.sh_size;
    1129           0 :       if( FD_UNLIKELY( ( dynamic_table_end < dynamic_table_start )
    1130           0 :                     |  ( dynamic_table_end > bin_sz )
    1131           0 :                     |  ( dyn_sh.sh_size % sizeof(fd_elf64_dyn) != 0UL ) ) ) {
    1132             :         /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L382-L385 */
    1133           0 :         return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
    1134           0 :       }
    1135           0 :     }
    1136             : 
    1137             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L393 */
    1138          90 :     if( dynamic_table_start==ULONG_MAX ) {
    1139           9 :       return FD_SBPF_ELF_SUCCESS;
    1140           9 :     }
    1141             : 
    1142             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L396-L407 */
    1143          81 :     ulong dynamic_table[ FD_ELF_DT_NUM ] = { 0UL };
    1144          81 :     ulong dyn_cnt = (dynamic_table_end - dynamic_table_start) / (ulong)sizeof(fd_elf64_dyn);
    1145         846 :     for( ulong i = 0UL; i<dyn_cnt; i++ ) {
    1146         846 :       fd_elf64_dyn dyn = FD_LOAD( fd_elf64_dyn, bin + dynamic_table_start + i*sizeof(fd_elf64_dyn) );
    1147             : 
    1148         846 :       if( FD_UNLIKELY( dyn.d_tag==FD_ELF_DT_NULL ) ) {
    1149          81 :         break;
    1150          81 :       }
    1151         765 :       if( FD_UNLIKELY( dyn.d_tag>=FD_ELF_DT_NUM ) ) {
    1152          72 :         continue;
    1153          72 :       }
    1154             : 
    1155         693 :       dynamic_table[ dyn.d_tag ] = dyn.d_un.d_val;
    1156         693 :     }
    1157             : 
    1158             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L409 */
    1159          81 :     do {
    1160          81 :       ulong vaddr = dynamic_table[ FD_ELF_DT_REL ];
    1161          81 :       if( FD_UNLIKELY( vaddr==0UL ) ) {
    1162          15 :         break; /* from this do-while */
    1163          15 :       }
    1164             : 
    1165          66 :       if ( FD_UNLIKELY( dynamic_table[ FD_ELF_DT_RELENT ] != sizeof(fd_elf64_rel) ) ) {
    1166           0 :         return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
    1167           0 :       }
    1168             : 
    1169          66 :       ulong size = dynamic_table[ FD_ELF_DT_RELSZ ];
    1170          66 :       if( FD_UNLIKELY( size==0UL ) ) {
    1171           0 :         return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
    1172           0 :       }
    1173             : 
    1174             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L430-L444 */
    1175          66 :       ulong offset = ULONG_MAX;
    1176         189 :       for( ulong i=0; i<ehdr.e_phnum; i++ ) {
    1177             :         /* Again... */
    1178         189 :         fd_elf64_phdr phdr = FD_LOAD( fd_elf64_phdr, bin + phdr_start + i*sizeof(fd_elf64_phdr) );
    1179         189 :         if( FD_UNLIKELY( phdr.p_vaddr+phdr.p_memsz<phdr.p_vaddr ) ) {
    1180           0 :           return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1181           0 :         }
    1182         189 :         if( phdr.p_vaddr<=vaddr && vaddr<phdr.p_vaddr+phdr.p_memsz ) {
    1183             :           /* vaddr - phdr.p_vaddr is guaranteed to be non-negative */
    1184          66 :           offset = vaddr-phdr.p_vaddr+phdr.p_offset;
    1185          66 :           if( FD_UNLIKELY( offset<phdr.p_offset ) ) {
    1186           0 :             return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1187           0 :           }
    1188          66 :           break;
    1189          66 :         }
    1190         189 :       }
    1191          66 :       if( FD_UNLIKELY( offset==ULONG_MAX ) ) {
    1192           0 :         for( ulong i=0; i<ehdr.e_shnum; i++ ) {
    1193             :           /* Again... */
    1194           0 :           fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
    1195           0 :           if( shdr.sh_addr == vaddr ) {
    1196           0 :             offset = shdr.sh_offset;
    1197           0 :             break;
    1198           0 :           }
    1199           0 :         }
    1200           0 :       }
    1201          66 :       if( FD_UNLIKELY( offset==ULONG_MAX ) ) {
    1202           0 :         return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
    1203           0 :       }
    1204             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L446-L448 */
    1205          66 :       ulong _offset_plus_size;
    1206          66 :       if( FD_UNLIKELY( __builtin_uaddl_overflow( offset, size, &_offset_plus_size ) ) ) {
    1207           0 :         return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1208           0 :       }
    1209             : 
    1210             :       /* slice_from_bytes checks that size is a multiple of the type
    1211             :          size and that the alignment of the bytes + offset is correct. */
    1212          66 :       if( FD_UNLIKELY( ( size%sizeof(fd_elf64_rel)!=0UL ) ||
    1213          66 :                        ( offset+size>bin_sz ) ||
    1214          66 :                        ( !fd_ulong_is_aligned( offset, 8UL ) ) ) ) {
    1215           0 :         return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
    1216           0 :       }
    1217             : 
    1218             :       /* Save the dynamic relocation table info */
    1219          66 :       info->dt_rel_off = (uint)offset;
    1220          66 :       info->dt_rel_sz  = (uint)size;
    1221          66 :     } while( 0 ); /* so we can break out */
    1222             : 
    1223             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L410 */
    1224          81 :     do {
    1225             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L452-L455 */
    1226          81 :       ulong vaddr = dynamic_table[ FD_ELF_DT_SYMTAB ];
    1227          81 :       if( FD_UNLIKELY( vaddr==0UL ) ) {
    1228           0 :         break; /* from this do-while */
    1229           0 :       }
    1230             : 
    1231          81 :       fd_elf64_shdr shdr_sym = { 0 };
    1232         426 :       for( ulong i=0; i<ehdr.e_shnum; i++ ) {
    1233             :         /* Again... */
    1234         426 :         shdr_sym = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
    1235         426 :         if( shdr_sym.sh_addr == vaddr ) {
    1236          81 :           info->shndx_dynsymtab = (int)i;
    1237          81 :           break;
    1238          81 :         }
    1239         426 :       }
    1240             : 
    1241             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L457-L461 */
    1242          81 :       if( FD_UNLIKELY( info->shndx_dynsymtab==-1 ) ) {
    1243           0 :         return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
    1244           0 :       }
    1245             : 
    1246             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L463-L464 */
    1247          81 :       {
    1248          81 :         if( FD_UNLIKELY( shdr_sym.sh_type != FD_ELF_SHT_SYMTAB && shdr_sym.sh_type != FD_ELF_SHT_DYNSYM ) ) {
    1249           0 :           return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
    1250           0 :         }
    1251          81 :         ulong shdr_sym_start = shdr_sym.sh_offset;
    1252          81 :         ulong shdr_sym_end = shdr_sym.sh_offset + shdr_sym.sh_size;
    1253          81 :         if( FD_UNLIKELY( ( shdr_sym_end < shdr_sym_start )
    1254          81 :                       |  ( shdr_sym_end > bin_sz )
    1255          81 :                       |  ( shdr_sym.sh_size % sizeof(fd_elf64_sym) != 0UL ) ) ) {
    1256           0 :           return FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE;
    1257           0 :         }
    1258          81 :       }
    1259          81 :     } while( 0 ); /* so we can break out */
    1260          81 :   }
    1261             : 
    1262          81 :   return FD_SBPF_ELF_SUCCESS;
    1263          81 : }
    1264             : 
    1265             : /* Performs validation checks on the ELF. Returns an ElfError on failure
    1266             :    and 0 on success.
    1267             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L719-L809 */
    1268             : int
    1269             : fd_sbpf_lenient_elf_validate( fd_sbpf_elf_info_t * info,
    1270             :                               void const *         bin,
    1271             :                               ulong                bin_sz,
    1272          90 :                               fd_elf64_shdr *      text_shdr ) {
    1273             : 
    1274             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L721-L736 */
    1275          90 :   fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
    1276          90 :   if( FD_UNLIKELY( ehdr.e_ident[ FD_ELF_EI_CLASS ] != FD_ELF_CLASS_64 ) ) {
    1277           0 :     return FD_SBPF_ELF_ERR_WRONG_CLASS;
    1278           0 :   }
    1279          90 :   if( FD_UNLIKELY( ehdr.e_ident[ FD_ELF_EI_DATA  ] != FD_ELF_DATA_LE ) ) {
    1280           0 :     return FD_SBPF_ELF_ERR_WRONG_ENDIANNESS;
    1281           0 :   }
    1282          90 :   if( FD_UNLIKELY( ehdr.e_ident[ FD_ELF_EI_OSABI ] != FD_ELF_OSABI_NONE ) ) {
    1283           0 :     return FD_SBPF_ELF_ERR_WRONG_ABI;
    1284           0 :   }
    1285          90 :   if( FD_UNLIKELY( ehdr.e_machine != FD_ELF_EM_BPF && ehdr.e_machine != FD_ELF_EM_SBPF ) ) {
    1286           0 :     return FD_SBPF_ELF_ERR_WRONG_MACHINE;
    1287           0 :   }
    1288          90 :   if( FD_UNLIKELY( ehdr.e_type != FD_ELF_ET_DYN ) ) {
    1289           0 :     return FD_SBPF_ELF_ERR_WRONG_TYPE;
    1290           0 :   }
    1291             : 
    1292             :   /* This code doesn't do anything:
    1293             :      1. version is already checked at the very beginning of elf_peek
    1294             :      2. the if condition is never true because sbpf_version is always v0
    1295             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L738-L763 */
    1296             : 
    1297          90 :   ulong shdr_start = ehdr.e_shoff;
    1298          90 :   ulong section_names_shdr_idx = ehdr.e_shstrndx;
    1299          90 :   fd_elf64_shdr section_names_shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + section_names_shdr_idx*sizeof(fd_elf64_shdr) );
    1300             : 
    1301             :   /* We do a single iteration over the section header table, collect all info
    1302             :      we need and return the errors later to match Agave. */
    1303             : 
    1304          90 :   int shndx_text    = -1;
    1305          90 :   int writeable_err = 0;
    1306          90 :   int oob_err       = 0;
    1307         918 :   for( ulong i=0UL; i<ehdr.e_shnum; i++ ) {
    1308             :     /* Again... */
    1309         828 :     fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + ehdr.e_shoff + i*sizeof(fd_elf64_shdr) );
    1310             : 
    1311         828 :     uchar const * name;
    1312         828 :     ulong         name_len;
    1313         828 :     int res = fd_sbpf_lenient_get_string_in_section( bin, bin_sz, &section_names_shdr, shdr.sh_name, FD_SBPF_SECTION_NAME_SZ_MAX, &name, &name_len );
    1314         828 :     if( FD_UNLIKELY( res ) ) {
    1315             :       /* this can never fail because it was checked above, but safer to keep it */
    1316           0 :       return fd_sbpf_elf_parser_err_to_elf_err( res );
    1317           0 :     }
    1318             : 
    1319             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L765-L775 */
    1320         828 :     if( FD_UNLIKELY( fd_sbpf_slice_cstr_eq( name, name_len, ".text" ) ) ) {
    1321          90 :       if( FD_LIKELY( shndx_text==-1 ) ) {
    1322          90 :         *text_shdr = shdr;  /* Store the text section header */
    1323          90 :         shndx_text = (int)i;
    1324          90 :       } else {
    1325           0 :         return FD_SBPF_ELF_ERR_NOT_ONE_TEXT_SECTION;
    1326           0 :       }
    1327          90 :     }
    1328             : 
    1329             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L780-L791 */
    1330         828 :     if( FD_UNLIKELY( fd_sbpf_slice_cstr_start_with( name, name_len, ".bss" ) ||
    1331         828 :                      ( ( ( shdr.sh_flags & (FD_ELF_SHF_ALLOC | FD_ELF_SHF_WRITE) ) == (FD_ELF_SHF_ALLOC | FD_ELF_SHF_WRITE) ) &&
    1332         828 :                            fd_sbpf_slice_cstr_start_with( name, name_len, ".data" ) &&
    1333         828 :                            !fd_sbpf_slice_cstr_start_with( name, name_len, ".data.rel" ) ) ) ) {
    1334             :       /* to match Agave return error we can't fail here */
    1335           6 :       writeable_err = 1;
    1336           6 :     }
    1337             : 
    1338             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L793-L802 */
    1339         828 :     ulong shdr_end;
    1340         828 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( shdr.sh_offset, shdr.sh_size, &shdr_end ) ||
    1341         828 :                      shdr_end>bin_sz ) ) {
    1342           0 :       oob_err = 1;
    1343           0 :     }
    1344         828 :   }
    1345             : 
    1346             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L776-L778 */
    1347          90 :   if( FD_UNLIKELY( shndx_text==-1 ) ) {
    1348           0 :     return FD_SBPF_ELF_ERR_NOT_ONE_TEXT_SECTION;
    1349           0 :   }
    1350             : 
    1351             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L786-L788 */
    1352          90 :   if( FD_UNLIKELY( writeable_err ) ) {
    1353           3 :     return FD_SBPF_ELF_ERR_WRITABLE_SECTION_NOT_SUPPORTED;
    1354           3 :   }
    1355             : 
    1356             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L798 */
    1357          87 :   if( FD_UNLIKELY( oob_err ) ) {
    1358           0 :     return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1359           0 :   }
    1360             : 
    1361             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L804-L806 */
    1362          87 :   if( FD_UNLIKELY( !(
    1363          87 :     text_shdr->sh_addr <= ehdr.e_entry && ehdr.e_entry < fd_ulong_sat_add( text_shdr->sh_addr, text_shdr->sh_size )
    1364          87 :   ) ) ) {
    1365           0 :     return FD_SBPF_ELF_ERR_ENTRYPOINT_OUT_OF_BOUNDS;
    1366           0 :   }
    1367             : 
    1368             :   /* Get text section file ranges to calculate the size. */
    1369          87 :   fd_sbpf_range_t text_section_range;
    1370          87 :   fd_shdr_get_file_range( text_shdr, &text_section_range );
    1371             : 
    1372          87 :   info->text_off   = (uint)text_shdr->sh_addr;
    1373          87 :   info->text_sz    = text_section_range.hi-text_section_range.lo;
    1374          87 :   info->text_cnt   = (uint)( info->text_sz/8UL );
    1375          87 :   info->shndx_text = shndx_text;
    1376             : 
    1377          87 :   return FD_SBPF_ELF_SUCCESS;
    1378          87 : }
    1379             : 
    1380             : /* First part of Agave's load_with_lenient_parser(). We split up this
    1381             :    function into two parts so we know how much memory we need to
    1382             :    allocate for the loading step. Returns an ElfError on failure and 0
    1383             :    on success.
    1384             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L593-L638 */
    1385             : static int
    1386             : fd_sbpf_elf_peek_lenient( fd_sbpf_elf_info_t *            info,
    1387             :                           void const *                    bin,
    1388             :                           ulong                           bin_sz,
    1389          93 :                           fd_sbpf_loader_config_t const * config ) {
    1390             : 
    1391             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L607 */
    1392          93 :   int res = fd_sbpf_lenient_elf_parse( info, bin, bin_sz );
    1393          93 :   if( FD_UNLIKELY( res<0 ) ) {
    1394           3 :     return fd_sbpf_elf_parser_err_to_elf_err( res );
    1395           3 :   }
    1396             : 
    1397             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L617 */
    1398          90 :   fd_elf64_shdr text_shdr = { 0 };
    1399          90 :   res = fd_sbpf_lenient_elf_validate( info, bin, bin_sz, &text_shdr );
    1400          90 :   if( FD_UNLIKELY( res<0 ) ) {
    1401           3 :     return res;
    1402           3 :   }
    1403             : 
    1404             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L620-L638 */
    1405          87 :   {
    1406          87 :     ulong text_section_vaddr = fd_ulong_sat_add( text_shdr.sh_addr, FD_SBPF_MM_RODATA_ADDR );
    1407          87 :     ulong vaddr_end          = text_section_vaddr;
    1408             : 
    1409             :     /* Validate bounds and text section addrs / offsets.
    1410             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L632-L638 */
    1411          87 :     if( FD_UNLIKELY( ( config->reject_broken_elfs && text_shdr.sh_addr!=text_shdr.sh_offset ) ||
    1412          87 :                        vaddr_end>FD_SBPF_MM_STACK_ADDR ) ) {
    1413           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1414           0 :     }
    1415          87 :   }
    1416             : 
    1417             :   /* Peek (vs load) stops here
    1418             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L638 */
    1419             : 
    1420          87 :   return FD_SBPF_ELF_SUCCESS;
    1421          87 : }
    1422             : 
    1423             : static int
    1424             : fd_sbpf_program_get_sbpf_version_or_err( void const *                    bin,
    1425             :                                          ulong                           bin_sz,
    1426         153 :                                          fd_sbpf_loader_config_t const * config ) {
    1427             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L376-L381 */
    1428         153 :   const ulong E_FLAGS_OFFSET = 48UL;
    1429             : 
    1430         153 :   if( FD_UNLIKELY( bin_sz<E_FLAGS_OFFSET+sizeof(uint) ) ) {
    1431           9 :     return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1432           9 :   }
    1433         144 :   uint e_flags = FD_LOAD( uint, bin+E_FLAGS_OFFSET );
    1434             : 
    1435         144 :   uint sbpf_version = 0U;
    1436         144 :   if( FD_UNLIKELY( config->sbpf_max_version==FD_SBPF_V0 ) ) {
    1437             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L384-L388 */
    1438          12 :     sbpf_version = e_flags==E_FLAGS_SBPF_V2 ? FD_SBPF_RESERVED : FD_SBPF_V0;
    1439         132 :   } else {
    1440             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L390-L396 */
    1441         132 :     sbpf_version = e_flags < FD_SBPF_VERSION_COUNT ? e_flags : FD_SBPF_RESERVED;
    1442         132 :   }
    1443             : 
    1444             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L399-L401 */
    1445         144 :   if( FD_UNLIKELY( !( config->sbpf_min_version <= sbpf_version && sbpf_version <= config->sbpf_max_version ) ) ) {
    1446          42 :     return FD_SBPF_ELF_ERR_UNSUPPORTED_SBPF_VERSION;
    1447          42 :   }
    1448             : 
    1449             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L403-L407 */
    1450         102 :   return (int)sbpf_version;
    1451         144 : }
    1452             : 
    1453             : int
    1454             : fd_sbpf_elf_peek( fd_sbpf_elf_info_t *            info,
    1455             :                   void const *                    bin,
    1456             :                   ulong                           bin_sz,
    1457         153 :                   fd_sbpf_loader_config_t const * config ) {
    1458             :   /* Extract sbpf_version (or error)
    1459             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L376-L401 */
    1460         153 :   int maybe_sbpf_version = fd_sbpf_program_get_sbpf_version_or_err( bin, bin_sz, config );
    1461         153 :   if( FD_UNLIKELY( maybe_sbpf_version<0 ) ) {
    1462          51 :     return maybe_sbpf_version;
    1463          51 :   }
    1464             : 
    1465             :   /* Initialize info struct */
    1466         102 :   *info = (fd_sbpf_elf_info_t) {
    1467         102 :     .bin_sz          = 0U,
    1468         102 :     .text_off        = 0U,
    1469         102 :     .text_cnt        = 0U,
    1470         102 :     .text_sz         = 0UL,
    1471         102 :     .shndx_text      = -1,
    1472         102 :     .shndx_symtab    = -1,
    1473         102 :     .shndx_strtab    = -1,
    1474         102 :     .shndx_dyn       = -1,
    1475         102 :     .shndx_dynstr    = -1,
    1476         102 :     .shndx_dynsymtab = -1,
    1477         102 :     .phndx_dyn       = -1,
    1478         102 :     .dt_rel_off      = 0UL,
    1479         102 :     .dt_rel_sz       = 0UL,
    1480         102 :     .sbpf_version    = (uint)maybe_sbpf_version,
    1481             :     /* !!! Keep this in sync with -Werror=missing-field-initializers */
    1482         102 :   };
    1483             : 
    1484             :   /* Invoke strict vs lenient parser. The strict parser is used for
    1485             :      SBPF version >= 3. The strict parser also returns an ElfParserError
    1486             :      while the lenient parser returns an ElfError, so we have to map
    1487             :      the strict parser's error code.
    1488             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L403-L407 */
    1489         102 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( info->sbpf_version ) ) ) {
    1490           9 :     return fd_sbpf_elf_parser_err_to_elf_err( fd_sbpf_elf_peek_strict( info, bin, bin_sz ) );
    1491           9 :   }
    1492          93 :   return fd_sbpf_elf_peek_lenient( info, bin, bin_sz, config );
    1493         102 : }
    1494             : 
    1495             : /* Parses and concatenates the readonly data sections. This function
    1496             :    also computes and sets the rodata_sz field inside the SBPF program
    1497             :    struct.
    1498             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L812-L987 */
    1499             : static int
    1500             : fd_sbpf_parse_ro_sections( fd_sbpf_program_t *             prog,
    1501             :                            void const *                    bin,
    1502             :                            ulong                           bin_sz,
    1503          72 :                            fd_sbpf_loader_config_t const * config ) {
    1504             : 
    1505          72 :   fd_sbpf_elf_t const * elf                = (fd_sbpf_elf_t const *)bin;
    1506          72 :   fd_elf64_shdr const * shdrs              = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
    1507          72 :   fd_elf64_shdr const * section_names_shdr = &shdrs[ elf->ehdr.e_shstrndx ];
    1508          72 :   uchar *               rodata             = prog->rodata;
    1509             : 
    1510             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L818-L834 */
    1511          72 :   ulong lowest_addr          = ULONG_MAX; /* Lowest section address */
    1512          72 :   ulong highest_addr         = 0UL;       /* Highest section address */
    1513          72 :   ulong ro_fill_length       = 0UL;       /* Aggregated section length, excluding gaps between sections */
    1514          72 :   uchar invalid_offsets      = 0;         /* Whether the section has invalid offsets */
    1515             : 
    1516             :   /* Store the section header indices of ro slices to fill later. */
    1517          72 :   ulong ro_slices_shidxs[ elf->ehdr.e_shnum ];
    1518          72 :   ulong ro_slices_cnt = 0UL;
    1519             : 
    1520             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L837-L909 */
    1521         741 :   for( uint i=0U; i<elf->ehdr.e_shnum; i++ ) {
    1522         669 :     fd_elf64_shdr const * section_header = &shdrs[ i ];
    1523             : 
    1524             :     /* Match the section name.
    1525             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L838-L845 */
    1526         669 :     uchar const * name;
    1527         669 :     ulong         name_len;
    1528         669 :     if( FD_UNLIKELY( fd_sbpf_lenient_get_string_in_section( bin, bin_sz, section_names_shdr, section_header->sh_name, FD_SBPF_SECTION_NAME_SZ_MAX, &name, &name_len ) ) ) {
    1529           0 :       continue;
    1530           0 :     }
    1531             : 
    1532         669 :     if( FD_UNLIKELY( !fd_sbpf_slice_cstr_eq( name, name_len, ".text" ) &&
    1533         669 :                      !fd_sbpf_slice_cstr_eq( name, name_len, ".rodata" ) &&
    1534         669 :                      !fd_sbpf_slice_cstr_eq( name, name_len, ".data.rel.ro" ) &&
    1535         669 :                      !fd_sbpf_slice_cstr_eq( name, name_len, ".eh_frame" ) ) ) {
    1536         510 :       continue;
    1537         510 :     }
    1538             : 
    1539         159 :     ulong section_addr = section_header->sh_addr;
    1540             : 
    1541             :     /* Handling for the section header offsets. If ELF vaddrs are
    1542             :        enabled, the section header addresses are allowed to be > the
    1543             :        section header offsets, as long as address - offset is constant
    1544             :        across all sections. Otherwise, the section header addresses
    1545             :        and offsets must match.
    1546             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L865-L884 */
    1547         159 :     if( FD_LIKELY( !invalid_offsets ) ) {
    1548             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L866-L880 */
    1549         159 :       if( FD_UNLIKELY( section_addr!=section_header->sh_offset ) ) {
    1550           6 :         invalid_offsets = 1;
    1551           6 :       }
    1552         159 :     }
    1553             : 
    1554             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L886-L897 */
    1555         159 :     ulong vaddr_end = section_addr;
    1556         159 :     if( section_addr<FD_SBPF_MM_RODATA_ADDR ) {
    1557         159 :       vaddr_end = fd_ulong_sat_add( section_addr, FD_SBPF_MM_RODATA_ADDR );
    1558         159 :     }
    1559             : 
    1560         159 :     if( FD_UNLIKELY( ( config->reject_broken_elfs && invalid_offsets ) ||
    1561         159 :                        vaddr_end>FD_SBPF_MM_STACK_ADDR ) ) {
    1562           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1563           0 :     }
    1564             : 
    1565             :     /* Append the ro slices vector and update the lowest / highest addr
    1566             :        and ro_fill_length variables. Agave stores three fields in the
    1567             :        ro slices array that can all be derived from the section header,
    1568             :        so we just need to store the indices.
    1569             : 
    1570             :        The call to fd_shdr_get_file_range() is allowed to fail (Agave's
    1571             :        unwrap_or_default() call returns a range of 0..0 in this case).
    1572             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L899-L908 */
    1573         159 :     fd_sbpf_range_t section_header_range;
    1574         159 :     fd_shdr_get_file_range( section_header, &section_header_range );
    1575         159 :     if( FD_UNLIKELY( section_header_range.hi>bin_sz ) ) {
    1576           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1577           0 :     }
    1578         159 :     ulong section_data_len = section_header_range.hi-section_header_range.lo;
    1579             : 
    1580         159 :     lowest_addr    = fd_ulong_min( lowest_addr, section_addr );
    1581         159 :     highest_addr   = fd_ulong_max( highest_addr, fd_ulong_sat_add( section_addr, section_data_len ) );
    1582         159 :     ro_fill_length = fd_ulong_sat_add( ro_fill_length, section_data_len );
    1583         159 :     ro_slices_shidxs[ ro_slices_cnt++ ] = i;
    1584         159 :   }
    1585             : 
    1586             :   /* This checks that the ro sections are not overlapping. This check
    1587             :      is incomplete, however, because it does not account for the
    1588             :      existence of gaps between sections in calculations.
    1589             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L910-L913 */
    1590          72 :   if( FD_UNLIKELY( config->reject_broken_elfs &&
    1591          72 :                    fd_ulong_sat_add( lowest_addr, ro_fill_length )>highest_addr ) ) {
    1592           0 :     return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1593           0 :   }
    1594             : 
    1595             :   /* Note that optimize_rodata is always false.
    1596             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L923-L984 */
    1597          72 :  {
    1598             :     /* Readonly / non-readonly sections are mixed, so non-readonly
    1599             :        sections must be zeroed and the readonly sections must be copied
    1600             :        at their respective offsets.
    1601             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L950-L983 */
    1602          72 :     lowest_addr = 0UL;
    1603             : 
    1604             :     /* Bounds check. */
    1605          72 :     ulong buf_len = highest_addr;
    1606          72 :     if( FD_UNLIKELY( buf_len>bin_sz ) ) {
    1607           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1608           0 :     }
    1609             : 
    1610             :     /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L971-L976 */
    1611          72 :     uchar ro_section[ buf_len ]; fd_memset( ro_section, 0, buf_len );
    1612         231 :     for( ulong i=0UL; i<ro_slices_cnt; i++ ) {
    1613         159 :       ulong sh_idx                       = ro_slices_shidxs[ i ];
    1614         159 :       fd_elf64_shdr const * shdr         = &shdrs[ sh_idx ];
    1615         159 :       ulong                 section_addr = shdr->sh_addr;
    1616             : 
    1617             :       /* This was checked above and should never fail. */
    1618         159 :       fd_sbpf_range_t slice_range;
    1619         159 :       fd_shdr_get_file_range( shdr, &slice_range );
    1620         159 :       if( FD_UNLIKELY( slice_range.hi>bin_sz ) ) {
    1621           0 :         return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1622           0 :       }
    1623             : 
    1624         159 :       ulong buf_offset_start = fd_ulong_sat_sub( section_addr, lowest_addr );
    1625         159 :       ulong slice_len        = slice_range.hi-slice_range.lo;
    1626         159 :       if( FD_UNLIKELY( slice_len>buf_len ) ) {
    1627           0 :         return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1628           0 :       }
    1629             : 
    1630         159 :       fd_memcpy( ro_section+buf_offset_start, rodata+slice_range.lo, slice_len );
    1631         159 :     }
    1632             : 
    1633             :     /* Copy the rodata section back in. */
    1634          72 :     prog->rodata_sz = buf_len;
    1635          72 :     fd_memcpy( rodata, ro_section, buf_len );
    1636          72 :   }
    1637             : 
    1638          72 :   return FD_SBPF_ELF_SUCCESS;
    1639          72 : }
    1640             : 
    1641             : /* Applies ELF relocations in-place. Returns 0 on success and an
    1642             :    ElfError error code on failure.
    1643             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L990-L1331 */
    1644             : static int
    1645             : fd_sbpf_program_relocate( fd_sbpf_program_t *             prog,
    1646             :                           void const *                    bin,
    1647             :                           ulong                           bin_sz,
    1648             :                           fd_sbpf_loader_config_t const * config,
    1649          72 :                           fd_sbpf_loader_t *              loader ) {
    1650          72 :   fd_sbpf_elf_info_t const * elf_info = &prog->info;
    1651          72 :   fd_sbpf_elf_t const *      elf      = (fd_sbpf_elf_t const *)bin;
    1652          72 :   uchar *                    rodata   = prog->rodata;
    1653          72 :   fd_elf64_shdr const *      shdrs    = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
    1654          72 :   fd_elf64_shdr const *      shtext   = &shdrs[ elf_info->shndx_text ];
    1655             : 
    1656             :   /* Copy rodata segment */
    1657          72 :   fd_memcpy( rodata, elf->bin, elf_info->bin_sz );
    1658             : 
    1659             :   /* Fixup all program counter relative call instructions
    1660             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1005-L1041 */
    1661          72 :   {
    1662             :     /* Validate the bytes range of the text section.
    1663             :        https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1006-L1008 */
    1664          72 :     fd_sbpf_range_t text_section_range;
    1665          72 :     fd_shdr_get_file_range( shtext, &text_section_range );
    1666             : 
    1667          72 :     ulong insn_cnt = (text_section_range.hi-text_section_range.lo)/8UL;
    1668          72 :     if( FD_UNLIKELY( shtext->sh_size+shtext->sh_offset>bin_sz ) ) {
    1669           0 :       return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1670           0 :     }
    1671             : 
    1672          72 :     uchar * ptr = rodata + shtext->sh_offset;
    1673             : 
    1674      138309 :     for( ulong i=0UL; i<insn_cnt; i++, ptr+=8UL ) {
    1675      138237 :       ulong insn = FD_LOAD( ulong, ptr );
    1676             : 
    1677             :       /* Check for call instruction.  If immediate is UINT_MAX, assume
    1678             :          that compiler generated a relocation instead.
    1679             :          https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1015 */
    1680      138237 :       ulong opc  = insn & 0xFF;
    1681      138237 :       int   imm  = (int)(insn >> 32UL);
    1682      138237 :       if( (opc!=FD_SBPF_OP_CALL_IMM) || (imm==-1) ) continue;
    1683             : 
    1684             :       /* Calculate and check the target PC
    1685             :          https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1016-L1021 */
    1686        4533 :       long target_pc = fd_long_sat_add( fd_long_sat_add( (long)i, 1L ), imm);
    1687        4533 :       if( FD_UNLIKELY( target_pc<0L || target_pc>=(long)insn_cnt ) ) {
    1688           0 :         return FD_SBPF_ELF_ERR_RELATIVE_JUMP_OUT_OF_BOUNDS;
    1689           0 :       }
    1690             : 
    1691             :       /* Update the calldests
    1692             :          https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1027-L1032 */
    1693        4533 :       uint pc_hash;
    1694        4533 :       int err = fd_sbpf_register_function_hashed_legacy( loader, prog, NULL, 0UL, (ulong)target_pc, &pc_hash );
    1695        4533 :       if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
    1696           0 :         return err;
    1697           0 :       }
    1698             : 
    1699             :       /* Store PC hash in text section. Check for writes outside the
    1700             :          text section.
    1701             :          https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1034-L1038 */
    1702        4533 :       ulong offset = fd_ulong_sat_add( fd_ulong_sat_mul( i, 8UL ), 4UL ); // offset in text section
    1703        4533 :       if( FD_UNLIKELY( offset+4UL>shtext->sh_size ) ) {
    1704           0 :         return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
    1705           0 :       }
    1706             : 
    1707        4533 :       FD_STORE( uint, ptr+4UL, pc_hash );
    1708        4533 :     }
    1709          72 :   }
    1710             : 
    1711             :   /* Fixup all the relocations in the relocation section if exists. The
    1712             :      dynamic relocations table was already parsed and validated in
    1713             :      fd_sbpf_lenient_elf_parse().
    1714             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1046-L1304 */
    1715          72 :   {
    1716          72 :     fd_elf64_rel const *  dt_rels    = (fd_elf64_rel const *)( elf->bin + elf_info->dt_rel_off );
    1717          72 :     uint                  dt_rel_cnt = elf_info->dt_rel_sz / sizeof(fd_elf64_rel);
    1718             : 
    1719        7566 :     for( uint i=0U; i<dt_rel_cnt; i++ ) {
    1720        7494 :       fd_elf64_rel const * dt_rel   = &dt_rels[ i ];
    1721        7494 :       ulong                r_offset = dt_rel->r_offset;
    1722             : 
    1723             :       /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1068-L1303 */
    1724        7494 :       int err;
    1725        7494 :       switch( FD_ELF64_R_TYPE( dt_rel->r_info ) ) {
    1726           6 :         case FD_ELF_R_BPF_64_64:
    1727           6 :           err = fd_sbpf_r_bpf_64_64( elf, bin_sz, rodata, elf_info, dt_rel, r_offset );
    1728           6 :           break;
    1729        5568 :         case FD_ELF_R_BPF_64_RELATIVE:
    1730        5568 :           err = fd_sbpf_r_bpf_64_relative(elf, bin_sz, rodata, elf_info, r_offset );
    1731        5568 :           break;
    1732        1920 :         case FD_ELF_R_BPF_64_32:
    1733        1920 :           err = fd_sbpf_r_bpf_64_32( loader, prog, elf, bin_sz, rodata, elf_info, dt_rel, r_offset, config );
    1734        1920 :           break;
    1735           0 :         default:
    1736           0 :           return FD_SBPF_ELF_ERR_UNKNOWN_RELOCATION;
    1737        7494 :       }
    1738             : 
    1739        7494 :       if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
    1740           0 :         return err;
    1741           0 :       }
    1742        7494 :     }
    1743          72 :   }
    1744             : 
    1745             :   /* ...rest of this function is a no-op because
    1746             :      enable_symbol_and_section_labels is disabled in production. */
    1747             : 
    1748          72 :   return FD_SBPF_ELF_SUCCESS;
    1749          72 : }
    1750             : 
    1751             : /* Second part of load_with_lenient_parser().
    1752             : 
    1753             :    This function is responsible for "loading" an sBPF program. This
    1754             :    means...
    1755             :    1. Applies any relocations in-place to the rodata section.
    1756             :    2. Registers the program entrypoint and other valid calldests.
    1757             :    3. Parses and validates the rodata sections, zeroing out any gaps
    1758             :       between sections.
    1759             : 
    1760             :    Returns 0 on success and an ElfError error code on failure.
    1761             : 
    1762             :    https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L640-L689
    1763             :  */
    1764             : static int
    1765             : fd_sbpf_program_load_lenient( fd_sbpf_program_t *             prog,
    1766             :                               void const *                    bin,
    1767             :                               ulong                           bin_sz,
    1768             :                               fd_sbpf_loader_t *              loader,
    1769          72 :                               fd_sbpf_loader_config_t const * config ) {
    1770             : 
    1771             :   /* Load (vs peek) starts here
    1772             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L641 */
    1773             : 
    1774          72 :   fd_sbpf_elf_t const * elf      = (fd_sbpf_elf_t const *)bin;
    1775          72 :   fd_sbpf_elf_info_t *  elf_info = &prog->info;
    1776          72 :   fd_elf64_shdr const * shdrs    = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
    1777          72 :   fd_elf64_shdr const * sh_text  = &shdrs[ elf_info->shndx_text ];
    1778             : 
    1779             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L642-L647 */
    1780          72 :   int err = fd_sbpf_program_relocate( prog, bin, bin_sz, config, loader );
    1781          72 :   if( FD_UNLIKELY( err ) ) return err;
    1782             : 
    1783             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L649-L653 */
    1784          72 :   ulong offset = fd_ulong_sat_sub( elf->ehdr.e_entry, sh_text->sh_addr );
    1785          72 :   if( FD_UNLIKELY( offset&0x7UL ) ) { /* offset % 8 != 0 */
    1786           0 :     return FD_SBPF_ELF_ERR_INVALID_ENTRYPOINT;
    1787           0 :   }
    1788             : 
    1789             :   /* Unregister the entrypoint from the calldests, and register the
    1790             :      entry_pc. Our behavior slightly diverges from Agave's because we
    1791             :      rely on an explicit entry_pc field within the elf_info struct
    1792             :      to handle the b"entrypoint" symbol, and rely on PC hash inverses
    1793             :      for any other CALL_IMM targets.
    1794             : 
    1795             :      Note that even though we won't use the calldests value for the
    1796             :      entry pc, we still need to "register" it to check for any potential
    1797             :      symbol collisions and report errors accordingly. We unregister it
    1798             :      first by setting it to ULONG_MAX.
    1799             : 
    1800             :      TODO: Add special casing for static syscalls enabled. For now, it
    1801             :      is not implemented.
    1802             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L654-L667 */
    1803          72 :   prog->entry_pc = ULONG_MAX;
    1804          72 :   ulong entry_pc = offset/8UL;
    1805          72 :   err = fd_sbpf_register_function_hashed_legacy(
    1806          72 :       loader,
    1807          72 :       prog,
    1808          72 :       (uchar const *)"entrypoint",
    1809          72 :       strlen( "entrypoint" ),
    1810          72 :       entry_pc,
    1811          72 :       NULL );
    1812          72 :   if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
    1813           0 :     return err;
    1814           0 :   }
    1815             : 
    1816             :   /* Parse the ro sections.
    1817             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L669-L676 */
    1818          72 :   err = fd_sbpf_parse_ro_sections( prog, bin, bin_sz, config );
    1819          72 :   if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
    1820           0 :     return err;
    1821           0 :   }
    1822             : 
    1823          72 :   return FD_SBPF_ELF_SUCCESS;
    1824          72 : }
    1825             : 
    1826             : int
    1827             : fd_sbpf_program_load( fd_sbpf_program_t *             prog,
    1828             :                       void const *                    bin,
    1829             :                       ulong                           bin_sz,
    1830             :                       fd_sbpf_syscalls_t *            syscalls,
    1831          72 :                       fd_sbpf_loader_config_t const * config ) {
    1832          72 :   fd_sbpf_loader_t loader = {
    1833          72 :     .calldests = prog->calldests,
    1834          72 :     .syscalls  = syscalls,
    1835          72 :   };
    1836             : 
    1837             :   /* Invoke strict vs lenient loader
    1838             :      Note: info.sbpf_version is already set by fd_sbpf_program_parse()
    1839             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L403-L409 */
    1840          72 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( prog->info.sbpf_version ) ) ) {
    1841             :     /* There is nothing else to do in the strict case except updating
    1842             :        the prog->rodata_sz field from phdr[ 1 ].p_memsz, and setting
    1843             :        the entry_pc. */
    1844           0 :     fd_elf64_ehdr ehdr     = FD_LOAD( fd_elf64_ehdr, bin );
    1845           0 :     fd_elf64_phdr phdr_0   = FD_LOAD( fd_elf64_phdr, bin+sizeof(fd_elf64_ehdr) );
    1846           0 :     fd_elf64_phdr phdr_1   = FD_LOAD( fd_elf64_phdr, bin+sizeof(fd_elf64_ehdr)+sizeof(fd_elf64_phdr) );
    1847           0 :     prog->rodata_sz        = phdr_1.p_memsz;
    1848           0 :     prog->entry_pc         = ( ehdr.e_entry-phdr_0.p_vaddr )/8UL;
    1849           0 :     return FD_SBPF_ELF_SUCCESS;
    1850           0 :   }
    1851          72 :   int res = fd_sbpf_program_load_lenient( prog, bin, bin_sz, &loader, config );
    1852          72 :   if( FD_UNLIKELY( res!=FD_SBPF_ELF_SUCCESS ) ) {
    1853           0 :     return res;
    1854           0 :   }
    1855             : 
    1856          72 :   return FD_SBPF_ELF_SUCCESS;
    1857          72 : }
    1858             : 
    1859             : #undef ERR
    1860             : #undef FAIL
    1861             : #undef REQUIRE

Generated by: LCOV version 1.14