LCOV - code coverage report
Current view: top level - ballet/sbpf - fd_sbpf_loader.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 683 754 90.6 %
Date: 2025-09-18 04:41:32 Functions: 29 29 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             : /* Error handling *****************************************************/
      12             : 
      13             : /* Thread local storage last error value */
      14             : 
      15             : static FD_TL int ldr_errno     =  0;
      16             : static FD_TL int ldr_err_srcln = -1;
      17             : #define FD_SBPF_ERRBUF_SZ (128UL)
      18             : static FD_TL char fd_sbpf_errbuf[ FD_SBPF_ERRBUF_SZ ] = {0};
      19             : 
      20             : /* fd_sbpf_loader_seterr remembers the error ID and line number of the
      21             :    current file at which the last error occurred. */
      22             : 
      23             : __attribute__((cold,noinline)) static int
      24             : fd_sbpf_loader_seterr( int err,
      25          72 :                        int srcln ) {
      26          72 :   ldr_errno     = err;
      27          72 :   ldr_err_srcln = srcln;
      28          72 :   return err;
      29          72 : }
      30             : 
      31             : /* Macros for returning an error from the current function while also
      32             :    remembering the error code. */
      33             : 
      34           6 : #define ERR( err ) return fd_sbpf_loader_seterr( (err), __LINE__ )
      35           6 : #define FAIL()  ERR( FD_SBPF_ERR_INVALID_ELF )
      36       44532 : #define REQUIRE(x) do { if ( FD_UNLIKELY( !(x) ) ) FAIL(); } while (0)
      37             : 
      38             : char const *
      39           9 : fd_sbpf_strerror( void ) {
      40           9 :   if( FD_UNLIKELY( ldr_errno==0 ) )
      41           9 :     strcpy( fd_sbpf_errbuf, "ok" );
      42           0 :   else
      43           9 :     snprintf( fd_sbpf_errbuf, FD_SBPF_ERRBUF_SZ,
      44           9 :               "code %d at %s(%d)", ldr_errno, __FILE__, ldr_err_srcln );
      45           9 :   return fd_sbpf_errbuf;
      46           9 : }
      47             : 
      48             : /* ELF loader, part 1 **************************************************
      49             : 
      50             :    Start with a static piece of scratch memory and do basic validation
      51             :    of the file content.  Walk the section table once and remember
      52             :    sections of interest.
      53             : 
      54             :    ### Terminology
      55             : 
      56             :    This source follows common ELF naming practices.
      57             : 
      58             :      section:  a named data region present in the ELF file
      59             :      segment:  a contiguous memory region containing sections
      60             :                (not necessarily contiguous in the ELF file)
      61             : 
      62             :      physical address (paddr): Byte offset into ELF file (uchar * bin)
      63             :      virtual  address (vaddr): VM memory address */
      64             : 
      65             : /* Provide convenient access to file header and ELF content */
      66             : 
      67             : __extension__ union fd_sbpf_elf {
      68             :   fd_elf64_ehdr ehdr;
      69             :   uchar         bin[0];
      70             : };
      71             : typedef union fd_sbpf_elf fd_sbpf_elf_t;
      72             : 
      73             : /* FD_SBPF_MM_{...}_ADDR are hardcoded virtual addresses of segments
      74             :    in the sBPF virtual machine.
      75             : 
      76             :    FIXME: These should be defined elsewhere */
      77             : 
      78           9 : #define FD_SBPF_MM_BYTECODE_ADDR (0x0UL)         /* bytecode */
      79           9 : #define FD_SBPF_MM_RODATA_ADDR   (0x100000000UL) /* readonly program data */
      80        9423 : #define FD_SBPF_MM_PROGRAM_ADDR  (0x100000000UL) /* readonly program data */
      81           9 : #define FD_SBPF_MM_STACK_ADDR    (0x200000000UL) /* stack */
      82           9 : #define FD_SBPF_MM_HEAP_ADDR     (0x300000000UL) /* heap */
      83          36 : #define FD_SBPF_MM_REGION_SZ     (0x100000000UL) /* max region size */
      84             : 
      85           9 : #define FD_SBPF_PF_X  (1U) /* executable */
      86          54 : #define FD_SBPF_PF_W  (2U) /* writable */
      87          27 : #define FD_SBPF_PF_R  (4U) /* readable */
      88          18 : #define FD_SBPF_PF_RW (FD_SBPF_PF_R|FD_SBPF_PF_W)
      89             : 
      90          54 : #define EXPECTED_PHDR_CNT (4U)
      91             : 
      92             : /* _fd_int_store_if_negative stores x to *p if *p is negative (branchless) */
      93             : 
      94             : static inline int
      95             : _fd_int_store_if_negative( int * p,
      96         168 :                       int   x ) {
      97         168 :   return (*p = fd_int_if( (*p)<0, x, *p ));
      98         168 : }
      99             : 
     100             : /* fd_sbpf_check_ehdr verifies the ELF file header. */
     101             : 
     102             : static int
     103             : fd_sbpf_check_ehdr( fd_elf64_ehdr const * ehdr,
     104             :                     ulong                 elf_sz,
     105             :                     uint                  min_version,
     106          87 :                     uint                  max_version ) {
     107             : 
     108             :   /* Validate ELF magic */
     109          87 :   REQUIRE( ( fd_uint_load_4( ehdr->e_ident )==0x464c457fU          )
     110             :   /* Validate file type/target identification
     111             :      Solana/Agave performs header checks across two places:
     112             :       - Elf64::parse https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf_parser/mod.rs#L108
     113             :       - Executable::validate https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf.rs#L518
     114             :      These two sections are executed in close proximity, with no modifications to the header in between.
     115             :      We can therefore consolidate the checks in one place.
     116             :   */
     117          87 :          & ( ehdr->e_ident[ FD_ELF_EI_CLASS   ]==FD_ELF_CLASS_64   )
     118          87 :          & ( ehdr->e_ident[ FD_ELF_EI_DATA    ]==FD_ELF_DATA_LE    )
     119          87 :          & ( ehdr->e_ident[ FD_ELF_EI_VERSION ]==1                 )
     120          87 :          & ( ehdr->e_ident[ FD_ELF_EI_OSABI   ]==FD_ELF_OSABI_NONE )
     121          87 :          & ( ehdr->e_type                      ==FD_ELF_ET_DYN     )
     122          87 :          & ( ( ehdr->e_machine                 ==FD_ELF_EM_BPF   )
     123          87 :            | ( ehdr->e_machine                 ==FD_ELF_EM_SBPF  ) )
     124          87 :          & ( ehdr->e_version                   ==1                 )
     125             :   /* Coherence checks */
     126          87 :          & ( ehdr->e_ehsize   ==sizeof(fd_elf64_ehdr)              )
     127          87 :          & ( ehdr->e_phentsize==sizeof(fd_elf64_phdr)              )
     128          87 :          & ( ehdr->e_shentsize==sizeof(fd_elf64_shdr)              )
     129          87 :          & ( ehdr->e_shstrndx < ehdr->e_shnum                      )
     130          87 :          & ( ehdr->e_flags >= min_version                          )
     131          87 :          & ( max_version
     132          87 :              ? ( ehdr->e_flags <= max_version )
     133          87 :              : ( ehdr->e_flags != FD_ELF_EF_SBPF_V2 )
     134          87 :            )
     135          87 :   );
     136             : 
     137             :   /* Bounds check program header table */
     138             : 
     139          87 :   ulong const phoff = ehdr->e_phoff;
     140          87 :   ulong const phnum = ehdr->e_phnum;
     141          87 :   REQUIRE( ( fd_ulong_is_aligned( phoff, 8UL ) )
     142          87 :          & ( phoff<=elf_sz ) ); /* out of bounds */
     143             : 
     144          87 :   REQUIRE( phnum<=(ULONG_MAX/sizeof(fd_elf64_phdr)) ); /* overflow */
     145          87 :   ulong const phsz = phnum*sizeof(fd_elf64_phdr);
     146             : 
     147          87 :   ulong const phoff_end = phoff+phsz;
     148          87 :   REQUIRE( ( phoff_end>=phoff  )    /* overflow */
     149          87 :          & ( phoff_end<=elf_sz )    /* out of bounds */
     150          87 :          & ( (phoff_end==0UL)       /* overlaps file header */
     151          87 :            | (phoff>=sizeof(fd_elf64_ehdr)) ) );
     152             : 
     153             :   /* Bounds check section header table */
     154             : 
     155          87 :   ulong const shoff = ehdr->e_shoff;
     156          87 :   ulong const shnum = ehdr->e_shnum;
     157          87 :   REQUIRE( ( fd_ulong_is_aligned( shoff, 8UL ) )
     158          87 :          & ( shoff>=sizeof(fd_elf64_ehdr)      )    /* overlaps file header */
     159          87 :          & ( shoff< elf_sz                     )    /* out of bounds */
     160          87 :          & ( shnum> 0UL                        ) ); /* not enough sections */
     161             : 
     162          87 :   REQUIRE( shoff<=(ULONG_MAX/sizeof(fd_elf64_shdr)) ); /* overflow */
     163          87 :   ulong const shsz = shnum*sizeof(fd_elf64_shdr);
     164             : 
     165          87 :   ulong const shoff_end = shoff+shsz;
     166          87 :   REQUIRE( ( shoff_end>=shoff  )    /* overflow */
     167          87 :          & ( shoff_end<=elf_sz ) ); /* out of bounds */
     168             : 
     169             :   /* Overlap checks */
     170             : 
     171          87 :   REQUIRE( (phoff>=shoff_end) | (shoff>=phoff_end) ); /* overlap shdrs<>phdrs */
     172             : 
     173          87 :   return 0;
     174          87 : }
     175             : 
     176             : /* shdr_get_loaded_size returns the loaded size of a section, i.e. the
     177             :    number of bytes loaded into the rodata segment.  sBPF ELFs grossly
     178             :    misuse the sh_size parameter.  When SHT_NOBITS is set, the actual
     179             :    section size is zero, and the section size is ignored. */
     180             : 
     181             : static ulong
     182        5925 : shdr_get_loaded_size( fd_elf64_shdr const * shdr ) {
     183        5925 :   return fd_ulong_if( shdr->sh_type==FD_ELF_SHT_NOBITS, 0UL, shdr->sh_size );
     184        5925 : }
     185             : 
     186             : /* check_cstr verifies a string in a string table.  Returns non-NULL if
     187             :    the string is null terminated and contains at most max non-NULL
     188             :    characters.  Returns NULL if the off is out of bounds, or if the max
     189             :    or EOF are reached before the null terminator. */
     190             : 
     191             : static char const *
     192             : check_cstr( uchar const * bin,
     193             :             ulong         bin_sz,
     194             :             ulong         off,
     195             :             ulong         max,
     196        2730 :             ulong *       opt_sz ) {
     197        2730 :   if( FD_UNLIKELY( off>=bin_sz ) ) return NULL;
     198        2730 :   max += 1UL;                              /* include NULL terminator */
     199        2730 :   max  = fd_ulong_min( max, bin_sz-off );  /* truncate to available size */
     200        2730 :   char const * cstr = (char const *)( bin+off );
     201        2730 :   ulong len = strnlen( cstr, max );
     202        2730 :   if( opt_sz ) *opt_sz = len;
     203        2730 :   return len<max ? cstr : NULL;
     204        2730 : }
     205             : 
     206             : /* fd_sbpf_load_phdrs walks the program header table.  Remembers info
     207             :    along the way, and performs various validations.
     208             : 
     209             :    Assumes that ...
     210             :    - table does not overlap with file header or section header table and
     211             :      is within bounds
     212             :    - offset of program header table is 8 byte aligned */
     213             : 
     214             : static int
     215             : fd_sbpf_load_phdrs( fd_sbpf_elf_info_t *  info,
     216             :                     fd_sbpf_elf_t const * elf,
     217          87 :                     ulong                 elf_sz ) {
     218             : 
     219          87 :   ulong const pht_offset = elf->ehdr.e_phoff;
     220          87 :   ulong const pht_cnt    = elf->ehdr.e_phnum;
     221             : 
     222             :   /* Virtual address of last seen program header */
     223          87 :   ulong p_load_vaddr = 0UL;
     224             : 
     225             :   /* Read program header table */
     226          87 :   fd_elf64_phdr const * phdr = (fd_elf64_phdr const *)( elf->bin + pht_offset );
     227         408 :   for( ulong i=0; i<pht_cnt; i++ ) {
     228         321 :     switch( phdr[i].p_type ) {
     229          84 :     case FD_ELF_PT_DYNAMIC:
     230             :       /* Remember first PT_DYNAMIC segment */
     231          84 :       _fd_int_store_if_negative( &info->phndx_dyn, (int)i );
     232          84 :       break;
     233         237 :     case FD_ELF_PT_LOAD:
     234             :       /* LOAD segments must be ordered */
     235         237 :       REQUIRE( phdr[ i ].p_vaddr >= p_load_vaddr );
     236         237 :       p_load_vaddr = phdr[ i ].p_vaddr;
     237             :       /* Segment must be within bounds */
     238         237 :       REQUIRE( ( phdr[ i ].p_offset + phdr[ i ].p_filesz >= phdr[ i ].p_offset )
     239         237 :              & ( phdr[ i ].p_offset + phdr[ i ].p_filesz <= elf_sz             ) );
     240             :       /* No overlap checks */
     241         237 :       break;
     242         237 :     default:
     243             :       /* Ignore other segment types */
     244           0 :       break;
     245         321 :     }
     246         321 :   }
     247             : 
     248          87 :   return 0;
     249          87 : }
     250             : 
     251             : /* FD_SBPF_SECTION_NAME_SZ_MAX is the maximum length of a symbol name cstr
     252             :    including zero terminator.
     253             :    https://github.com/solana-labs/rbpf/blob/c168a8715da668a71584ea46696d85f25c8918f6/src/elf_parser/mod.rs#L12 */
     254        1617 : #define FD_SBPF_SECTION_NAME_SZ_MAX (16UL)
     255             : 
     256             : /* fd_sbpf_load_shdrs walks the section header table.  Remembers info
     257             :    along the way, and performs various validations.
     258             : 
     259             :    Assumes that ...
     260             :    - table does not overlap with file header or program header table and
     261             :      is within bounds
     262             :    - offset of section header table is 8 byte aligned
     263             :    - section header table has at least one entry */
     264             : 
     265             : static int
     266             : fd_sbpf_load_shdrs( fd_sbpf_elf_info_t *  info,
     267             :                     fd_sbpf_elf_t const * elf,
     268             :                     ulong                 elf_sz,
     269          87 :                     int                   elf_deploy_checks ) {
     270             : 
     271             :   /* File Header */
     272          87 :   ulong const eh_offset = 0UL;
     273          87 :   ulong const eh_offend = sizeof(fd_elf64_ehdr);
     274             : 
     275             :   /* Section Header Table */
     276          87 :   ulong const sht_offset = elf->ehdr.e_shoff;
     277          87 :   ulong const sht_cnt    = elf->ehdr.e_shnum;
     278          87 :   ulong const sht_sz     = sht_cnt*sizeof(fd_elf64_shdr);
     279          87 :   ulong const sht_offend = sht_offset + sht_sz;
     280             : 
     281          87 :   fd_elf64_shdr const * shdr = (fd_elf64_shdr const *)( elf->bin + sht_offset );
     282             : 
     283             :   /* Program Header Table */
     284          87 :   ulong const pht_offset = elf->ehdr.e_phoff;
     285          87 :   ulong const pht_cnt    = elf->ehdr.e_phnum;
     286          87 :   ulong const pht_offend = pht_offset + (pht_cnt*sizeof(fd_elf64_phdr));
     287             : 
     288             :   /* Overlap checks */
     289          87 :   REQUIRE( (sht_offset>=eh_offend ) | (sht_offend<=eh_offset ) ); /* overlaps ELF file header */
     290          87 :   REQUIRE( (sht_offset>=pht_offend) | (sht_offend<=pht_offset) ); /* overlaps program header table */
     291             : 
     292             :   /* Require SHT_STRTAB for section name table */
     293             : 
     294          87 :   REQUIRE( elf->ehdr.e_shstrndx < sht_cnt ); /* out of bounds */
     295          87 :   REQUIRE( shdr[ elf->ehdr.e_shstrndx ].sh_type==FD_ELF_SHT_STRTAB );
     296             : 
     297          87 :   ulong shstr_off = shdr[ elf->ehdr.e_shstrndx ].sh_offset;
     298          87 :   ulong shstr_sz  = shdr[ elf->ehdr.e_shstrndx ].sh_size;
     299          87 :   REQUIRE( shstr_off<elf_sz );
     300          87 :   shstr_sz = fd_ulong_min( shstr_sz, elf_sz-shstr_off );
     301             : 
     302             :   /* Clear the "loaded sections" bitmap */
     303             : 
     304          87 :   fd_memset( info->loaded_sections, 0, sizeof(info->loaded_sections) );
     305             : 
     306             :   /* Validate section header table.
     307             :      Check that all sections are in bounds, ordered, and don't overlap. */
     308             : 
     309          87 :   ulong min_sh_offset = 0UL;  /* lowest permitted section offset */
     310             : 
     311             :   /* Keep track of the physical (file address) end of all relevant
     312             :      sections to determine rodata_sz */
     313          87 :   ulong psegment_end          = 0UL;     /* Upper bound of physical (file) addressing  */
     314             : 
     315             :   /* While validating section header table, also figure out size
     316             :      of the rodata segment.  This is the minimal virtual address range
     317             :      that spans all sections. */
     318          87 :   ulong vsegment_start  = FD_SBPF_MM_PROGRAM_ADDR;  /* Lower bound of segment virtual address */
     319          87 :   ulong vsegment_end    = 0UL;  /* Upper bound of segment virtual address */
     320             : 
     321          87 :   ulong tot_section_sz = 0UL;  /* Size of all sections */
     322          87 :   ulong lowest_addr    = 0UL;
     323          87 :   ulong highest_addr   = 0UL;
     324             : 
     325         891 :   for( ulong i=0UL; i<sht_cnt; i++ ) {
     326         810 :     uint  sh_type   = shdr[ i ].sh_type;
     327         810 :     uint  sh_name   = shdr[ i ].sh_name;
     328         810 :     ulong sh_addr   = shdr[ i ].sh_addr;
     329         810 :     ulong sh_offset = shdr[ i ].sh_offset;
     330         810 :     ulong sh_size   = shdr[ i ].sh_size;
     331         810 :     ulong sh_offend = sh_offset + sh_size;
     332             : 
     333             :     /* First section must be SHT_NULL */
     334         810 :     REQUIRE( i>0UL || sh_type==FD_ELF_SHT_NULL );
     335             : 
     336             :     /* check that physical range has no overflow and is within bounds */
     337         810 :     REQUIRE( sh_offend >= sh_offset );
     338         810 :     REQUIRE( sh_offend <= elf_sz    ); // https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf_parser/mod.rs#L180
     339             : 
     340         810 :     if( sh_type!=FD_ELF_SHT_NOBITS ) {
     341             :       /* Overlap checks */
     342         807 :       REQUIRE( (sh_offset>=eh_offend ) | (sh_offend<=eh_offset ) ); /* overlaps ELF file header */
     343         807 :       REQUIRE( (sh_offset>=pht_offend) | (sh_offend<=pht_offset) ); /* overlaps program header table */
     344         807 :       REQUIRE( (sh_offset>=sht_offend) | (sh_offend<=sht_offset) ); /* overlaps section header table */
     345             : 
     346             :       /* Ordering and overlap check
     347             :          https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf_parser/mod.rs#L177
     348             :       */
     349         807 :       REQUIRE( sh_offset >= min_sh_offset );
     350         807 :       min_sh_offset = sh_offend;
     351         807 :     }
     352             : 
     353         810 :     if( sh_type==FD_ELF_SHT_DYNAMIC ) {
     354             :       /* Remember first SHT_DYNAMIC segment */
     355          84 :       _fd_int_store_if_negative( &info->shndx_dyn, (int)i );
     356          84 :     }
     357             : 
     358         810 :     ulong name_off = shstr_off + (ulong)sh_name;
     359         810 :     REQUIRE( ( name_off<elf_sz   ) /* out of bounds */
     360         810 :            & ( sh_name <shstr_sz ) );
     361             : 
     362             :     /* Create name cstr */
     363             : 
     364         810 :     char const * name_ptr = check_cstr( elf->bin + shstr_off, shstr_sz, sh_name, FD_SBPF_SECTION_NAME_SZ_MAX-1UL, NULL );
     365         810 :     REQUIRE( name_ptr );
     366         807 :     char __attribute__((aligned(16UL))) name[ FD_SBPF_SECTION_NAME_SZ_MAX ] = {0};
     367         807 :     strncpy( name, name_ptr, FD_SBPF_SECTION_NAME_SZ_MAX-1UL );
     368             : 
     369             :     /* Check name */
     370             :     /* TODO switch table for this? */
     371             :     /* TODO reject duplicate sections */
     372             : 
     373             :     /* https://github.com/firedancer-io/sbpf/blob/sbpf-v0.11.1-patches/src/elf.rs#L855 */
     374         807 :     if( FD_LIKELY( strncmp( name, ".text", sizeof(".text") )==0 ||
     375         807 :                    strncmp( name, ".rodata", sizeof(".rodata") )==0 ||
     376         807 :                    strncmp( name, ".data.rel.ro", sizeof(".data.rel.ro") )==0 ||
     377         807 :                    strncmp( name, ".eh_frame", sizeof(".eh_frame") )==0 ) ) {
     378         210 :       lowest_addr  = fd_ulong_min( lowest_addr, sh_addr );
     379         210 :       highest_addr = fd_ulong_max( highest_addr, fd_ulong_sat_add( sh_addr, sh_size ) );
     380         210 :     }
     381             : 
     382         807 :     int load = 0;  /* should section be loaded? */
     383             : 
     384         807 :     /**/ if( 0==memcmp( name, ".text", 6UL /* equals */ ) ) {
     385          87 :       REQUIRE( (info->shndx_text)<0 ); /* check for duplicate */
     386          87 :       info->shndx_text = (int)i;
     387          87 :       load = 1;
     388          87 :     }
     389         720 :     else if( (0==memcmp( name, ".rodata",       8UL /* equals */ ) )
     390         720 :            | (0==memcmp( name, ".data.rel.ro", 13UL /* equals */ ) )
     391         720 :            | (0==memcmp( name, ".eh_frame",    10UL /* equals */ ) ) ) {
     392         123 :       load = 1;
     393         123 :     }
     394         597 :     else if( 0==memcmp( name, ".symtab",   8UL /* equals     */ ) ) {
     395          33 :       REQUIRE( (info->shndx_symtab)<0 );
     396          33 :       info->shndx_symtab = (int)i;
     397          33 :     }
     398         564 :     else if( 0==memcmp( name, ".strtab",   8UL /* equals     */ ) ) {
     399          33 :       REQUIRE( (info->shndx_strtab)<0 );
     400          33 :       info->shndx_strtab = (int)i;
     401          33 :     }
     402         531 :     else if( 0==memcmp( name, ".dynstr",   8UL /* equals     */ ) ) {
     403          81 :       REQUIRE( (info->shndx_dynstr)<0 );
     404          81 :       info->shndx_dynstr = (int)i;
     405          81 :     }
     406         450 :     else if( 0==memcmp( name, ".bss",      4UL /* has prefix */ ) ) {
     407           0 :       FAIL();
     408           0 :     }
     409         450 :     else if( 0==memcmp( name, ".data.rel", 9UL  /* has prefix */ ) ) {} /* ignore */
     410         450 :     else if( (0==memcmp( name, ".data",    5UL  /* has prefix */ ) )
     411         450 :            & ( ( shdr[ i ].sh_flags & (FD_ELF_SHF_ALLOC|FD_ELF_SHF_WRITE) )
     412         450 :                                     ==(FD_ELF_SHF_ALLOC|FD_ELF_SHF_WRITE)   ) ) {
     413           0 :       FAIL();
     414           0 :     }
     415         450 :     else                                            {} /* ignore */
     416             :     /* else ignore */
     417             : 
     418         807 :     if( load ) {
     419             :       /* Remember that section should be loaded */
     420             : 
     421         210 :       info->loaded_sections[ i>>6UL ] |= (1UL)<<(i&63UL);
     422             : 
     423             :       /* Check that virtual address range is in MM_PROGRAM bounds */
     424             : 
     425         210 :       ulong sh_actual_size = shdr_get_loaded_size( &shdr[ i ] );
     426         210 :       ulong sh_virtual_end = sh_addr + sh_actual_size;
     427             : 
     428             :       /* https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf.rs#L426 */
     429         210 :       if ( FD_UNLIKELY( elf_deploy_checks ) ){
     430          57 :         REQUIRE( sh_addr == sh_offset );
     431          57 :       }
     432         207 :       REQUIRE( sh_addr        <  FD_SBPF_MM_PROGRAM_ADDR ); /* overflow check */
     433         207 :       REQUIRE( sh_actual_size <  FD_SBPF_MM_PROGRAM_ADDR ); /* overflow check */
     434         207 :       REQUIRE( sh_virtual_end <= FD_SBPF_MM_STACK_ADDR-FD_SBPF_MM_PROGRAM_ADDR ); /* check overlap with stack */
     435             : 
     436             :       /* Check that physical address range is in bounds
     437             :         (Seems redundant?) */
     438         207 :       ulong paddr_end = sh_offset + sh_actual_size;
     439         207 :       REQUIRE( paddr_end >= sh_offset );
     440         207 :       REQUIRE( paddr_end <= elf_sz    );
     441             : 
     442         207 :       vsegment_start = fd_ulong_min( vsegment_start, sh_addr );
     443             :       /* Expand range to fit section */
     444         207 :       psegment_end = fd_ulong_max( psegment_end, paddr_end );
     445         207 :       vsegment_end = fd_ulong_max( vsegment_end, sh_virtual_end );
     446             : 
     447             :       /* Coherence check sum of section sizes */
     448         207 :       REQUIRE( tot_section_sz + sh_actual_size >= tot_section_sz ); /* overflow check */
     449         207 :       tot_section_sz += sh_actual_size;
     450         207 :     }
     451         807 :   }
     452             : 
     453             :   /* https://github.com/firedancer-io/sbpf/blob/sbpf-v0.11.1-patches/src/elf.rs#L982 */
     454          81 :   REQUIRE( fd_ulong_sat_sub( highest_addr, lowest_addr ) <= elf_sz ); /* addr out of bounds */
     455             : 
     456             :   /* More coherence checks */
     457          81 :   REQUIRE( psegment_end <= elf_sz ); // https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf.rs#L782
     458             : 
     459             : 
     460             :   /* Check that the rodata segment is within bounds
     461             :      https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf.rs#L725 */
     462          81 :   if ( FD_UNLIKELY( elf_deploy_checks ) ){
     463          18 :     REQUIRE( fd_ulong_sat_add( vsegment_start, tot_section_sz) <= vsegment_end );
     464          18 :   }
     465             : 
     466             :   /* Require .text section */
     467             : 
     468          81 :   REQUIRE( (info->shndx_text)>=0 );
     469          81 :   fd_elf64_shdr const * shdr_text = &shdr[ info->shndx_text ];
     470          81 :   REQUIRE( (shdr_text->sh_addr <= elf->ehdr.e_entry)
     471             :            /* check that entrypoint is in text VM range */
     472          81 :          & (elf->ehdr.e_entry  <  fd_ulong_sat_add( shdr_text->sh_addr, shdr_text->sh_size ) ) );
     473             :   /* NOTE: Does NOT check that the entrypoint is in text section file
     474             :            range (which may be 0 sz if SHT_NOBITS).  This check is
     475             :            separately done in the sBPF verifier. */
     476             : 
     477          81 :   info->text_off = (uint)shdr_text->sh_offset;
     478          81 :   ulong text_size = shdr_get_loaded_size( shdr_text );
     479          81 :   info->text_sz = text_size;
     480          81 :   info->text_cnt = (uint) text_size / 8U;
     481             : 
     482             : 
     483             :   /* Convert entrypoint offset to program counter */
     484             : 
     485          81 :   info->rodata_sz        = (uint)psegment_end;
     486          81 :   info->rodata_footprint = (uint)elf_sz;
     487             : 
     488          81 :   ulong entry_off = fd_ulong_sat_sub( elf->ehdr.e_entry, shdr_text->sh_addr );
     489          81 :   ulong entry_pc = entry_off / 8UL;
     490             : 
     491             :   /* Follows https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf.rs#L443 */
     492          81 :   REQUIRE( fd_ulong_is_aligned( entry_off, 8UL ) );
     493          81 :   REQUIRE( entry_pc < ( info->rodata_sz / 8UL ) );
     494          81 :   info->entry_pc = (uint)entry_pc;
     495             : 
     496          81 :   if( (info->shndx_dynstr)>=0 ) {
     497          81 :     fd_elf64_shdr const * shdr_dynstr = &shdr[ info->shndx_dynstr ];
     498          81 :     ulong sh_offset = shdr_dynstr->sh_offset;
     499          81 :     ulong sh_size   = shdr_dynstr->sh_size;
     500          81 :     REQUIRE( (sh_offset+sh_size>=sh_offset) & (sh_offset+sh_size<=info->rodata_footprint) );
     501          81 :     info->dynstr_off = (uint)sh_offset;
     502          81 :     info->dynstr_sz  = (uint)sh_size;
     503          81 :   }
     504             : 
     505          81 :   return 0;
     506          81 : }
     507             : 
     508             : fd_sbpf_elf_info_t *
     509             : fd_sbpf_elf_peek_old( fd_sbpf_elf_info_t * info,
     510             :                       void const *         bin,
     511             :                       ulong                elf_sz,
     512             :                       int                  elf_deploy_checks,
     513             :                       uint                 sbpf_min_version,
     514          87 :                       uint                 sbpf_max_version ) {
     515             : 
     516             :   /* Reject overlong ELFs (using uint addressing internally).
     517             :      This is well beyond Solana's max account size of 10 MB. */
     518          87 :   if( FD_UNLIKELY( elf_sz>UINT_MAX ) )
     519           0 :     return NULL;
     520             : 
     521          87 :   fd_sbpf_elf_t const * elf = (fd_sbpf_elf_t const *)bin;
     522          87 :   int err;
     523             : 
     524             :   /* Validate file header */
     525          87 :   if( FD_UNLIKELY( (err=fd_sbpf_check_ehdr( &elf->ehdr, elf_sz, sbpf_min_version, sbpf_max_version ))!=0 ) )
     526           0 :     return NULL;
     527             : 
     528             :   /* Program headers */
     529          87 :   if( FD_UNLIKELY( (err=fd_sbpf_load_phdrs( info, elf,  elf_sz ))!=0 ) )
     530           0 :     return NULL;
     531             : 
     532             :   /* Section headers */
     533          87 :   if( FD_UNLIKELY( (err=fd_sbpf_load_shdrs( info, elf,  elf_sz, elf_deploy_checks ))!=0 ) )
     534           6 :     return NULL;
     535             : 
     536             :   /* Set SBPF version from ELF e_flags */
     537          81 :   info->sbpf_version = sbpf_max_version ? elf->ehdr.e_flags : 0UL;
     538             : 
     539          81 :   return info;
     540          87 : }
     541             : 
     542             : /* ELF loader, part 2 **************************************************
     543             : 
     544             :    Prepare a copy of a subrange of the ELF content: The rodata segment.
     545             :    Mangle the copy by applying dynamic relocations.  Then, zero out
     546             :    parts of the segment that are not interesting to the loader.
     547             : 
     548             :    ### Terminology
     549             : 
     550             :    Shorthands for relocation handling:
     551             : 
     552             :      S: Symbol value (typically an ELF physical address)
     553             :      A: Implicit addend, i.e. the original value of the field that the
     554             :         relocation handler is about to write to
     555             :      V: Virtual address, i.e. the target value that the relocation
     556             :         handler is about to write into where the implicit addend was
     557             :         previously stored */
     558             : 
     559             : ulong
     560          66 : fd_sbpf_program_align( void ) {
     561          66 :   return alignof( fd_sbpf_program_t );
     562          66 : }
     563             : 
     564             : ulong
     565          66 : fd_sbpf_program_footprint( fd_sbpf_elf_info_t const * info ) {
     566          66 :   FD_COMPILER_UNPREDICTABLE( info ); /* Make this appear as FD_FN_PURE (e.g. footprint might depened on info contents in future) */
     567          66 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers( info->sbpf_version ) ) ) {
     568             :     /* SBPF v3+ no longer neeeds calldests bitmap */
     569           0 :     return FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,
     570           0 :       alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) ),
     571           0 :       alignof(fd_sbpf_program_t) );
     572           0 :   }
     573          66 :   return FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,
     574          66 :     alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) ),
     575          66 :     fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint( info->rodata_sz / 8UL ) ),  /* calldests bitmap */
     576          66 :     alignof(fd_sbpf_program_t) );
     577          66 : }
     578             : 
     579             : fd_sbpf_program_t *
     580             : fd_sbpf_program_new( void *                     prog_mem,
     581             :                      fd_sbpf_elf_info_t const * elf_info,
     582          66 :                      void *                     rodata ) {
     583             : 
     584          66 :   if( FD_UNLIKELY( !prog_mem ) ) {
     585           0 :     FD_LOG_WARNING(( "NULL prog_mem" ));
     586           0 :     return NULL;
     587           0 :   }
     588             : 
     589          66 :   if( FD_UNLIKELY( !elf_info ) ) {
     590           0 :     FD_LOG_WARNING(( "NULL elf_info" ));
     591           0 :     return NULL;
     592           0 :   }
     593             : 
     594          66 :   if( FD_UNLIKELY( ((elf_info->rodata_footprint)>0U) & (!rodata)) ) {
     595           0 :     FD_LOG_WARNING(( "NULL rodata" ));
     596           0 :     return NULL;
     597           0 :   }
     598             : 
     599             :   /* https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf_parser/mod.rs#L99 */
     600          66 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong) rodata, FD_SBPF_PROG_RODATA_ALIGN ) ) ){
     601           0 :     FD_LOG_WARNING(( "rodata is not 8-byte aligned" ));
     602           0 :     return NULL;
     603           0 :   }
     604             : 
     605             :   /* Initialize program struct */
     606             : 
     607          66 :   FD_SCRATCH_ALLOC_INIT( laddr, prog_mem );
     608          66 :   fd_sbpf_program_t * prog = FD_SCRATCH_ALLOC_APPEND( laddr, alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) );
     609             : 
     610           0 :   *prog = (fd_sbpf_program_t) {
     611          66 :     .info      = *elf_info,
     612          66 :     .rodata    = rodata,
     613          66 :     .rodata_sz = elf_info->rodata_sz,
     614          66 :     .text      = (ulong *)((ulong)rodata + elf_info->text_off), /* FIXME: WHAT IF MISALIGNED */
     615          66 :     .text_off  = elf_info->text_off,
     616          66 :     .text_cnt  = elf_info->text_cnt,
     617          66 :     .text_sz   = elf_info->text_sz,
     618          66 :     .entry_pc  = elf_info->entry_pc
     619          66 :   };
     620             : 
     621          66 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers( elf_info->sbpf_version ) ) ) {
     622             :     /* No calldests map in SBPF v3+ */
     623           0 :     prog->calldests_shmem = NULL;
     624           0 :     prog->calldests = NULL;
     625          66 :   } else {
     626             :     /* Initialize calldests map */
     627          66 :     ulong pc_max = elf_info->rodata_sz / 8UL;
     628          66 :     prog->calldests_shmem = fd_sbpf_calldests_new(
     629          66 :           FD_SCRATCH_ALLOC_APPEND( laddr, fd_sbpf_calldests_align(),
     630          66 :                                           fd_sbpf_calldests_footprint( pc_max ) ),
     631           0 :           pc_max );
     632           0 :     prog->calldests = fd_sbpf_calldests_join( prog->calldests_shmem );
     633          66 :   }
     634             : 
     635          66 :   return prog;
     636          66 : }
     637             : 
     638             : void *
     639          30 : fd_sbpf_program_delete( fd_sbpf_program_t * mem ) {
     640             : 
     641          30 :   fd_sbpf_calldests_delete( fd_sbpf_calldests_leave( mem->calldests ) );
     642          30 :   fd_memset( mem, 0, sizeof(fd_sbpf_program_t) );
     643             : 
     644          30 :   return (void *)mem;
     645          30 : }
     646             : 
     647             : /* fd_sbpf_loader_t contains various temporary state during loading */
     648             : 
     649             : struct fd_sbpf_loader {
     650             :   /* External objects */
     651             :   ulong *              calldests;  /* owned by program */
     652             :   fd_sbpf_syscalls_t * syscalls;   /* owned by caller */
     653             : 
     654             :   /* Dynamic table */
     655             :   uint dyn_off;  /* File offset of dynamic table (UINT_MAX=missing) */
     656             :   uint dyn_cnt;  /* Number of dynamic table entries */
     657             : 
     658             :   /* Dynamic table entries */
     659             :   ulong dt_rel;
     660             :   ulong dt_relent;
     661             :   ulong dt_relsz;
     662             :   ulong dt_symtab;
     663             : 
     664             :   /* Dynamic symbols */
     665             :   uint dynsym_off;  /* File offset of .dynsym section (0=missing) */
     666             :   uint dynsym_cnt;  /* Symbol count */
     667             : 
     668             :   int elf_deploy_checks;
     669             : };
     670             : typedef struct fd_sbpf_loader fd_sbpf_loader_t;
     671             : 
     672             : /* FD_SBPF_SYM_NAME_SZ_MAX is the maximum length of a symbol name cstr
     673             :    including zero terminator.
     674             :    https://github.com/solana-labs/rbpf/blob/c168a8715da668a71584ea46696d85f25c8918f6/src/elf_parser/mod.rs#L13 */
     675        1920 : #define FD_SBPF_SYM_NAME_SZ_MAX (64UL)
     676             : 
     677             : 
     678             : static int
     679             : fd_sbpf_find_dynamic( fd_sbpf_loader_t *         loader,
     680             :                       fd_sbpf_elf_t const *      elf,
     681             :                       ulong                      elf_sz,
     682          66 :                       fd_sbpf_elf_info_t const * info ) {
     683             : 
     684          66 :   fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     685          66 :   fd_elf64_phdr const * phdrs = (fd_elf64_phdr const *)( elf->bin + elf->ehdr.e_phoff );
     686             : 
     687             :   /* Try first PT_DYNAMIC in program header table */
     688             : 
     689          66 :   if( (info->phndx_dyn)>=0 ) {
     690          66 :     ulong dyn_off = phdrs[ info->phndx_dyn ].p_offset;
     691          66 :     ulong dyn_sz  = phdrs[ info->phndx_dyn ].p_filesz;
     692          66 :     ulong dyn_end = dyn_off+dyn_sz;
     693             : 
     694             :     /* Fall through to SHT_DYNAMIC if invalid */
     695             : 
     696          66 :     if( FD_LIKELY(   ( dyn_end>=dyn_off )                /* overflow      */
     697          66 :                    & ( dyn_end<=elf_sz  )                /* out of bounds */
     698          66 :                    & fd_ulong_is_aligned( dyn_off, 8UL ) /* misaligned    */
     699          66 :                    & fd_ulong_is_aligned( dyn_sz, sizeof(fd_elf64_dyn) ) /* misaligned sz */ ) ) {
     700          66 :       loader->dyn_off = (uint)dyn_off;
     701          66 :       loader->dyn_cnt = (uint)(dyn_sz / sizeof(fd_elf64_dyn));
     702          66 :       return 0;
     703          66 :     }
     704          66 :   }
     705             : 
     706             :   /* Try first SHT_DYNAMIC in section header table */
     707             : 
     708           0 :   if( (info->shndx_dyn)>0 ) {
     709           0 :     ulong dyn_off = shdrs[ info->shndx_dyn ].sh_offset;
     710           0 :     ulong dyn_sz  = shdrs[ info->shndx_dyn ].sh_size;
     711           0 :     ulong dyn_end = dyn_off+dyn_sz;
     712             : 
     713             :     /* This time, don't tolerate errors */
     714             : 
     715           0 :     REQUIRE( ( dyn_end>=dyn_off )                /* overflow      */
     716           0 :            & ( dyn_end<=elf_sz  )                /* out of bounds */
     717           0 :            & fd_ulong_is_aligned( dyn_off, 8UL ) /* misaligned    */
     718           0 :            & fd_ulong_is_aligned( dyn_sz, sizeof(fd_elf64_dyn) ) /* misaligned sz */ );
     719             : 
     720           0 :     loader->dyn_off = (uint)dyn_off;
     721           0 :     loader->dyn_cnt = (uint)(dyn_sz / sizeof(fd_elf64_dyn));
     722           0 :     return 0;
     723           0 :   }
     724             : 
     725             :   /* Missing or invalid PT_DYNAMIC and missing SHT_DYNAMIC, skip. */
     726           0 :   return 0;
     727           0 : }
     728             : 
     729             : static int
     730             : fd_sbpf_load_dynamic( fd_sbpf_loader_t *         loader,
     731             :                       fd_sbpf_elf_t const *      elf,
     732          66 :                       ulong                      elf_sz ) {
     733             : 
     734          66 :   fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     735             : 
     736             :   /* Skip if no dynamic table was found */
     737             : 
     738          66 :   if( !loader->dyn_cnt ) return 0;
     739             : 
     740             :   /* Walk dynamic table */
     741             : 
     742          66 :   fd_elf64_dyn const * dyn     = (fd_elf64_dyn const *)( elf->bin + loader->dyn_off );
     743          66 :   ulong const          dyn_cnt = loader->dyn_cnt;
     744             : 
     745         681 :   for( ulong i=0; i<dyn_cnt; i++ ) {
     746         681 :     if( FD_UNLIKELY( dyn[i].d_tag==FD_ELF_DT_NULL ) ) break;
     747             : 
     748         615 :     ulong d_val = dyn[i].d_un.d_val;
     749         615 :     switch( dyn[i].d_tag ) {
     750          51 :     case FD_ELF_DT_REL:    loader->dt_rel   =d_val; break;
     751          51 :     case FD_ELF_DT_RELENT: loader->dt_relent=d_val; break;
     752          51 :     case FD_ELF_DT_RELSZ:  loader->dt_relsz =d_val; break;
     753          66 :     case FD_ELF_DT_SYMTAB: loader->dt_symtab=d_val; break;
     754         615 :     }
     755         615 :   }
     756             : 
     757             :   /* Load dynamic symbol table */
     758             : 
     759          66 :   if( loader->dt_symtab ) {
     760             :     /* Search for dynamic symbol table
     761             :       FIXME unfortunate bounded O(n^2) -- could convert to binary search */
     762             : 
     763             :     /* FIXME this could be clobbered by relocations, causing strict
     764             :              aliasing violations */
     765             : 
     766          66 :     fd_elf64_shdr const * shdr_dynsym = NULL;
     767             : 
     768         336 :     for( ulong i=0; i<elf->ehdr.e_shnum; i++ ) {
     769         336 :       if( shdrs[ i ].sh_addr == loader->dt_symtab ) {
     770             :         /* TODO: verify this ... */
     771             :         /* Check section type */
     772          66 :         uint sh_type = shdrs[ i ].sh_type;
     773             :         // https://github.com/solana-labs/rbpf/blob/v0.8.5/src/elf_parser/mod.rs#L500
     774          66 :         REQUIRE( (sh_type==FD_ELF_SHT_SYMTAB) | (sh_type==FD_ELF_SHT_DYNSYM) );
     775             : 
     776          66 :         shdr_dynsym = &shdrs[ i ];
     777          66 :         break;
     778          66 :       }
     779         336 :     }
     780          66 :     REQUIRE( shdr_dynsym );
     781             : 
     782             :     /* Check if out of bounds or misaligned */
     783             : 
     784          66 :     ulong sh_offset = shdr_dynsym->sh_offset;
     785          66 :     ulong sh_size   = shdr_dynsym->sh_size;
     786             : 
     787          66 :     REQUIRE( ( sh_offset+sh_size>=sh_offset           )
     788          66 :            & ( sh_offset+sh_size<=elf_sz              )
     789          66 :            & ( fd_ulong_is_aligned( sh_offset, 8UL )  )
     790          66 :            & ( sh_size % sizeof (fd_elf64_sym) == 0UL ) );
     791             : 
     792          66 :     loader->dynsym_off = (uint)sh_offset;
     793          66 :     loader->dynsym_cnt = (uint)(sh_size/sizeof(fd_elf64_sym));
     794          66 :   }
     795             : 
     796          66 :   return 0;
     797          66 : }
     798             : 
     799             : /* ELF Dynamic Relocations *********************************************
     800             : 
     801             :    ### Summary
     802             : 
     803             :    The sBPF ELF loader provides a limited dynamic relocation mechanism
     804             :    to fix up Clang-generated shared objects for execution in an sBPF VM.
     805             : 
     806             :    The relocation types themselves violate the eBPF and ELF specs in
     807             :    various ways.  In short, the relocation table (via DT_REL) is used to
     808             :    shift program code from zero-based addressing to the MM_PROGRAM
     809             :    segment in the VM memory map (at 0x1_0000_0000).
     810             : 
     811             :    As part of the Solana VM protocol it abides by strict determinism
     812             :    requirements.  This sadly means that we will have to replicate all
     813             :    edge cases and bugs in the Solana Labs ELF loader.
     814             : 
     815             :    Three relocation types are currently supported:
     816             :      - R_BPF_64_64: Sets an absolute address of a symbol as the
     817             :        64-bit immediate field of an lddw instruction
     818             :      - R_BPF_64_RELATIVE: Adds MM_PROGRAM_START (0x1_0000_0000) to ...
     819             :        a) ... the 64-bit imm field of an lddw instruction (if in text)
     820             :        b) ... a 64-bit integer (if not in text section)
     821             :      - R_BPF_64_32: Sets the 32-bit immediate field of a call
     822             :        instruction to ...
     823             :        a) the ID of a local function (Murmur3 hash of function PC address)
     824             :        b) the ID of a syscall
     825             : 
     826             :    Obviously invalid relocations (e.g. out-of-bounds of ELF file or
     827             :    unsupported reloc type) raise an error.
     828             :    Relocations that would corrupt ELF data structures are silently
     829             :    ignored (using the fd_sbpf_reloc_mask mechanism).
     830             : 
     831             :    ### History
     832             : 
     833             :    The use of relocations is technically redundant, as the Solana VM
     834             :    memory map has been hardcoded in program runtime v1 (so far the only
     835             :    runtime).  However, virtually all deployed programs as of April 2023
     836             :    are position-independent shared objects and make heavy use of such
     837             :    relocations.
     838             : 
     839             :    Relocations in the Solana VM have a complicated history.  Over the
     840             :    course of years, multiple protocol bugs have been added and fixed.
     841             :    The ELF loader needs to handle all these edge cases to avoid breaking
     842             :    "userspace".  I.e. any deployed programs which might be immutable
     843             :    must continue to function.
     844             : 
     845             :    While this complex logic will probably stick around for the next few
     846             :    years, the Solana protocol is getting increasingly restrictive for
     847             :    newly deployed ELFs.  Another proposed change is upgrading to
     848             :    position-dependent binaries without any dynamic relocations. */
     849             : 
     850             : /* R_BPF_64_64 relocates an absolute address into the extended imm field
     851             :    of an lddw-form instruction.  (Two instruction slots, low 32 bits in
     852             :    first immediate field, high 32 bits in second immediate field)
     853             : 
     854             :     Bits  0..32    32..64   64..96   96..128
     855             :          [ ... ] [ IMM_LO ] [ ... ] [ IMM_HI ] */
     856             : 
     857             : static int
     858             : fd_sbpf_r_bpf_64_64( fd_sbpf_loader_t   const * loader,
     859             :                      fd_sbpf_elf_t      const * elf,
     860             :                      ulong                      elf_sz,
     861             :                      uchar                    * rodata,
     862             :                      fd_sbpf_elf_info_t const * info,
     863           6 :                      fd_elf64_rel       const * rel ) {
     864             : 
     865           6 :   (void)info;
     866             : 
     867           6 :   uint  r_sym    = FD_ELF64_R_SYM( rel->r_info );
     868           6 :   ulong r_offset = rel->r_offset;
     869             : 
     870             :   /* Bounds check */
     871           6 :   REQUIRE( ( r_offset+16UL> r_offset )
     872           6 :          & ( r_offset+16UL<=elf_sz   ) );
     873             : 
     874             :   /* Offsets of implicit addend (immediate fields) */
     875           6 :   ulong A_off_lo = r_offset+ 4UL;
     876           6 :   ulong A_off_hi = r_offset+12UL;
     877             : 
     878             :   /* Read implicit addend (imm field of first insn slot) */
     879             :   // SBF_V2: ulong A_off = is_text ? r_offset+4UL : r_offset;
     880           6 :   REQUIRE( A_off_lo+4UL<elf_sz );
     881             : 
     882             :   /* Lookup symbol */
     883           6 :   REQUIRE( r_sym < loader->dynsym_cnt );
     884           6 :   fd_elf64_sym const * dynsyms = (fd_elf64_sym const *)( elf->bin + loader->dynsym_off );
     885           6 :   fd_elf64_sym const * sym     = &dynsyms[ r_sym ];
     886           6 :   ulong S = sym->st_value;
     887             : 
     888             :   /* Relocate */
     889           6 :   ulong A = FD_LOAD( uint, &rodata[ A_off_lo ] );
     890           6 :   ulong V = fd_ulong_sat_add( S, A );
     891           6 :   if( V<FD_SBPF_MM_PROGRAM_ADDR ) V+=FD_SBPF_MM_PROGRAM_ADDR;
     892             : 
     893             :   /* Write back */
     894           6 :   FD_STORE( uint, &rodata[ A_off_lo ], (uint)(V      ) );
     895           6 :   FD_STORE( uint, &rodata[ A_off_hi ], (uint)(V>>32UL) );
     896             : 
     897           6 :   return 0;
     898           6 : }
     899             : 
     900             : /* R_BPF_64_RELATIVE is almost entirely Solana specific. */
     901             : 
     902             : static int
     903             : fd_sbpf_r_bpf_64_relative( fd_sbpf_elf_t      const * elf,
     904             :                            ulong                      elf_sz,
     905             :                            uchar                    * rodata,
     906             :                            fd_sbpf_elf_info_t const * info,
     907        5568 :                            fd_elf64_rel       const * rel ) {
     908             : 
     909        5568 :   ulong r_offset = rel->r_offset;
     910             : 
     911             :   /* Is reloc target in .text section? */
     912        5568 :   fd_elf64_shdr const * shdrs     = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     913        5568 :   fd_elf64_shdr const * shdr_text = &shdrs[ info->shndx_text ];
     914        5568 :   int is_text = ( ( r_offset >= shdr_text->sh_offset ) &
     915        5568 :                   ( r_offset <  shdr_text->sh_offset +
     916        5568 :                                 shdr_get_loaded_size( shdr_text ) ) );
     917             : 
     918        5568 :   if( is_text ) {
     919             :     /* If reloc target is in .text, behave like R_BPF_64_64, except:
     920             :        - R_SYM(r_info) is ignored
     921             :        - If implicit addend looks like a physical address, make it
     922             :          a virtual address (by adding a constant offset)
     923             : 
     924             :        This relocation type seems to make little sense but is required
     925             :        for most programs. */
     926             : 
     927        3756 :     REQUIRE( (r_offset+16UL>r_offset) & (r_offset+16UL<=elf_sz) );
     928        3756 :     ulong imm_lo_off = r_offset+ 4UL;
     929        3756 :     ulong imm_hi_off = r_offset+12UL;
     930             : 
     931             :     /* Read implicit addend */
     932        3756 :     uint  va_lo = FD_LOAD( uint, rodata+imm_lo_off );
     933        3756 :     uint  va_hi = FD_LOAD( uint, rodata+imm_hi_off );
     934        3756 :     ulong va    = ( (ulong)va_hi<<32UL ) | va_lo;
     935             : 
     936        3756 :     REQUIRE( va!=0UL );
     937        3756 :     va = va<FD_SBPF_MM_PROGRAM_ADDR ? va+FD_SBPF_MM_PROGRAM_ADDR : va;
     938             : 
     939             :     /* Write back
     940             :        Skip bounds check as .text is guaranteed to be writable */
     941        3756 :     FD_STORE( uint, rodata+imm_lo_off, (uint)( va       ) );
     942        3756 :     FD_STORE( uint, rodata+imm_hi_off, (uint)( va>>32UL ) );
     943        3756 :   } else {
     944             :     /* Outside .text do a 64-bit write */
     945             : 
     946             :     /* Bounds checks */
     947        1812 :     REQUIRE( (r_offset+8UL>r_offset) & (r_offset+8UL<=elf_sz) );
     948             : 
     949             :     /* Read implicit addend */
     950        1812 :     ulong va = FD_LOAD( uint, rodata+r_offset+4UL );
     951             : 
     952             :     /* Relocate */
     953        1812 :     va = fd_ulong_sat_add( va, FD_SBPF_MM_PROGRAM_ADDR );
     954             : 
     955             :     /* Write back */
     956        1812 :     FD_STORE( ulong, rodata+r_offset, va );
     957        1812 :   }
     958             : 
     959        5568 :   return 0;
     960        5568 : }
     961             : 
     962             : static int
     963             : fd_sbpf_r_bpf_64_32( fd_sbpf_loader_t   const * loader,
     964             :                      fd_sbpf_elf_t      const * elf,
     965             :                      ulong                      elf_sz,
     966             :                      uchar                    * rodata,
     967             :                      fd_sbpf_elf_info_t const * info,
     968        1920 :                      fd_elf64_rel       const * rel ) {
     969             : 
     970        1920 :   uint  r_sym    = FD_ELF64_R_SYM( rel->r_info );
     971        1920 :   ulong r_offset = rel->r_offset;
     972             : 
     973             :   /* Lookup symbol */
     974        1920 :   REQUIRE( r_sym < loader->dynsym_cnt );
     975        1920 :   fd_elf64_shdr const * shdrs   = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
     976        1920 :   fd_elf64_sym  const * dynsyms = (fd_elf64_sym const *)( elf->bin + loader->dynsym_off );
     977        1920 :   fd_elf64_sym  const * sym     = &dynsyms[ r_sym ];
     978        1920 :   ulong S = sym->st_value;
     979             : 
     980             :   /* Verify .dynstr (TODO can we lift this out of the reloc handler?) */
     981        1920 :   REQUIRE( info->shndx_dynstr > 0 );
     982        1920 :   REQUIRE( shdrs[ info->shndx_dynstr ].sh_type == FD_ELF_SHT_STRTAB );
     983             : 
     984             :   /* Verify symbol name */
     985        1920 :   ulong name_len;
     986        1920 :   char const * name = check_cstr( elf->bin + info->dynstr_off, info->dynstr_sz, sym->st_name, FD_SBPF_SYM_NAME_SZ_MAX-1UL, &name_len );
     987        1920 :   REQUIRE( name );
     988             : 
     989             :   /* Value to write into relocated field */
     990        1920 :   uint V;
     991             : 
     992        1920 :   int is_func_call = ( FD_ELF64_ST_TYPE( sym->st_info ) == FD_ELF_STT_FUNC )
     993        1920 :                    & ( S!=0UL );
     994        1920 :   if( is_func_call ) {
     995             :     /* Check whether function call is in virtual memory range of text section. */
     996         138 :     fd_elf64_shdr const * shdr_text = &shdrs[ info->shndx_text ];
     997         138 :     ulong sh_addr = shdr_text->sh_addr;
     998         138 :     ulong sh_size = shdr_text->sh_size;
     999         138 :     REQUIRE( (S>=sh_addr) & (S<sh_addr+sh_size) );
    1000             : 
    1001             :     /* Note: The above check is broken, as sh_size is interpreted as 0
    1002             :        for SHT_NOBITS section. Yet, this is the "correct" loading
    1003             :        behavior according to protocol rules. */
    1004             : 
    1005             :     /* Register function call */
    1006         138 :     ulong target_pc = (S-sh_addr) / 8UL;
    1007             : 
    1008             :     /* TODO bounds check the target? */
    1009             : 
    1010             :     /* Register new entry */
    1011         138 :     uint hash;
    1012         138 :     if( name_len >= 10UL && 0==strncmp( name, "entrypoint", name_len ) ) {
    1013             :       /* Skip insertion of "entrypoint" relocation entries to calldests. This
    1014             :          emulates Solana/Agave's behavior of unregistering these entries before
    1015             :          registering the entrypoint manually.
    1016             :          Entrypoint is registered in fd_sbpf_program_load.
    1017             :          Hash is still applied. */
    1018           9 :       hash = 0x71e3cf81;
    1019         129 :     } else {
    1020         129 :       hash = fd_pchash( (uint)target_pc );
    1021         129 :       if( FD_LIKELY( target_pc < (info->rodata_sz / 8UL ) ) )
    1022         129 :         fd_sbpf_calldests_insert( loader->calldests, target_pc );
    1023         129 :     }
    1024             : 
    1025             :     /* Check for collision with syscall ID
    1026             :        https://github.com/solana-labs/rbpf/blob/57139e9e1fca4f01155f7d99bc55cdcc25b0bc04/src/program.rs#L142-L146 */
    1027         138 :     REQUIRE( !fd_sbpf_syscalls_query( loader->syscalls, (ulong)hash, NULL ) );
    1028         138 :     V = (uint)hash;
    1029        1782 :   } else {
    1030             :     /* FIXME Should cache Murmur hashes.
    1031             :              If max ELF size is 10MB, can fit about 640k relocs.
    1032             :              Each reloc could point to a symbol with the same st_name,
    1033             :              which results in 640MB hash input data without caching.  */
    1034        1782 :     uint hash = fd_murmur3_32( name, name_len, 0UL );
    1035             :     /* Ensure that requested syscall ID exists only when deploying
    1036             :        https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf.rs#L1097 */
    1037        1782 :     if ( FD_UNLIKELY( loader->elf_deploy_checks ) ) {
    1038          66 :       REQUIRE( fd_sbpf_syscalls_query( loader->syscalls, (ulong)hash, NULL ) );
    1039          66 :     }
    1040             : 
    1041        1782 :     V = hash;
    1042        1782 :   }
    1043             : 
    1044             :   /* Bounds checks */
    1045        1920 :   REQUIRE( (r_offset+8UL>r_offset) & (r_offset+8UL<=elf_sz) );
    1046        1920 :   ulong A_off = r_offset+4UL;
    1047             : 
    1048             :   /* Apply relocation */
    1049        1920 :   FD_STORE( uint, rodata+A_off, V );
    1050             : 
    1051        1920 :   return 0;
    1052        1920 : }
    1053             : 
    1054             : static int
    1055             : fd_sbpf_apply_reloc( fd_sbpf_loader_t   const * loader,
    1056             :                      fd_sbpf_elf_t      const * elf,
    1057             :                      ulong                      elf_sz,
    1058             :                      uchar                    * rodata,
    1059             :                      fd_sbpf_elf_info_t const * info,
    1060        7494 :                      fd_elf64_rel       const * rel ) {
    1061        7494 :   switch( FD_ELF64_R_TYPE( rel->r_info ) ) {
    1062           6 :   case FD_ELF_R_BPF_64_64:
    1063           6 :     return fd_sbpf_r_bpf_64_64      ( loader, elf, elf_sz, rodata, info, rel );
    1064        5568 :   case FD_ELF_R_BPF_64_RELATIVE:
    1065        5568 :     return fd_sbpf_r_bpf_64_relative(         elf, elf_sz, rodata, info, rel );
    1066        1920 :   case FD_ELF_R_BPF_64_32:
    1067        1920 :     return fd_sbpf_r_bpf_64_32      ( loader, elf, elf_sz, rodata, info, rel );
    1068           0 :   default:
    1069           0 :     ERR( FD_SBPF_ERR_INVALID_ELF );
    1070        7494 :   }
    1071        7494 : }
    1072             : 
    1073             : /* fd_sbpf_hash_calls converts local call instructions in the "LLVM
    1074             :    form" (immediate is a program counter offset) to eBPF form (immediate
    1075             :    is a hash of the target program counter).  Corresponds to
    1076             :    fixup_relative calls in solana-labs/rbpf.
    1077             : 
    1078             :    Assumes that the text section range exists, is within bounds, and
    1079             :    does not overlap with the ELF file header, program header table, or
    1080             :    section header table. */
    1081             : 
    1082             : static int
    1083             : fd_sbpf_hash_calls( fd_sbpf_loader_t *    loader,
    1084             :                     fd_sbpf_program_t *   prog,
    1085          66 :                     fd_sbpf_elf_t const * elf ) {
    1086             : 
    1087          66 :   fd_elf64_shdr const * shdrs  = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
    1088          66 :   fd_sbpf_elf_info_t *  info   = &prog->info;
    1089          66 :   uchar *               rodata = prog->rodata;
    1090             : 
    1091          66 :   fd_elf64_shdr const * shtext    = &shdrs[ info->shndx_text ];
    1092          66 :   fd_sbpf_calldests_t * calldests = loader->calldests;
    1093             : 
    1094          66 :   uchar * ptr      = rodata + shtext->sh_offset;
    1095          66 :   ulong   insn_cnt = shdr_get_loaded_size( shtext ) / 8UL;
    1096             : 
    1097      138303 :   for( ulong i=0; i<insn_cnt; i++, ptr+=8UL ) {
    1098      138237 :     ulong insn = *((ulong *) ptr);
    1099             : 
    1100             :     /* Check for call instruction.  If immediate is UINT_MAX, assume
    1101             :        that compiler generated a relocation instead. */
    1102      138237 :     ulong opc  = insn & 0xFF;
    1103      138237 :     int   imm  = (int)(insn >> 32UL);
    1104      138237 :     if( (opc!=0x85) | (imm==-1) )
    1105      133704 :       continue;
    1106             : 
    1107             :     /* Mark function call destination */
    1108        4533 :     long target_pc_s;
    1109        4533 :     REQUIRE( 0==__builtin_saddl_overflow( (long)i+1L, imm, &target_pc_s ) );
    1110        4533 :     ulong target_pc = (ulong)target_pc_s;
    1111        4533 :     REQUIRE( target_pc<insn_cnt );  /* bounds check target */
    1112             : 
    1113        4533 :     fd_sbpf_calldests_insert( calldests, target_pc );
    1114             : 
    1115             :     /* Replace immediate with hash */
    1116        4533 :     uint pc_hash = fd_pchash( (uint)target_pc );
    1117             :     /* Check for collision with syscall ID
    1118             :        https://github.com/solana-labs/rbpf/blob/57139e9e1fca4f01155f7d99bc55cdcc25b0bc04/src/program.rs#L142-L146 */
    1119        4533 :     REQUIRE( !fd_sbpf_syscalls_query( loader->syscalls, (ulong)pc_hash, NULL ) );
    1120             : 
    1121        4533 :     FD_STORE( uint, ptr+4UL, pc_hash );
    1122        4533 :   }
    1123             : 
    1124          66 :   return 0;
    1125          66 : }
    1126             : 
    1127             : static int
    1128             : fd_sbpf_relocate( fd_sbpf_loader_t   const * loader,
    1129             :                   fd_sbpf_elf_t      const * elf,
    1130             :                   ulong                      elf_sz,
    1131             :                   uchar                    * rodata,
    1132          66 :                   fd_sbpf_elf_info_t const * info ) {
    1133             : 
    1134          66 :   ulong const dt_rel    = loader->dt_rel;
    1135          66 :   ulong const dt_relent = loader->dt_relent;
    1136          66 :   ulong const dt_relsz  = loader->dt_relsz;
    1137             : 
    1138             :   /* Skip relocation if DT_REL is missing */
    1139             : 
    1140          66 :   if( dt_rel == 0UL ) return 0;
    1141             : 
    1142             :   /* Validate reloc table params */
    1143             : 
    1144          51 :   REQUIRE(  dt_relent==sizeof(fd_elf64_rel)       );
    1145          51 :   REQUIRE(  dt_relsz !=0UL                        );
    1146          51 :   REQUIRE( (dt_relsz % sizeof(fd_elf64_rel))==0UL );
    1147             : 
    1148             :   /* Resolve DT_REL virtual address to file offset
    1149             :      First, attempt to find segment containing DT_REL */
    1150             : 
    1151          51 :   ulong rel_off = ULONG_MAX;
    1152             : 
    1153          51 :   fd_elf64_phdr const * phdrs = (fd_elf64_phdr const *)( elf->bin + elf->ehdr.e_phoff );
    1154          51 :   ulong rel_phnum;
    1155         144 :   for( rel_phnum=0; rel_phnum < elf->ehdr.e_phnum; rel_phnum++ ) {
    1156         144 :     ulong va_lo = phdrs[ rel_phnum ].p_vaddr;
    1157         144 :     ulong va_hi = phdrs[ rel_phnum ].p_memsz + va_lo;
    1158         144 :     REQUIRE( va_hi>=va_lo );
    1159         144 :     if( (dt_rel>=va_lo) & (dt_rel<va_hi) ) {
    1160             :       /* Found */
    1161          51 :       ulong va_off = dt_rel - va_lo;
    1162          51 :       ulong pa_lo  = phdrs[ rel_phnum ].p_offset + va_off;
    1163             :       /* Overflow checks */
    1164          51 :       REQUIRE( (va_off<=dt_rel)
    1165          51 :              & (pa_lo >=va_off)
    1166          51 :              & (pa_lo < elf_sz) );
    1167          51 :       rel_off = pa_lo;
    1168          51 :       break;
    1169          51 :     }
    1170         144 :   }
    1171             : 
    1172             :   /* DT_REL not contained in any segment.  Fallback to section header
    1173             :      table for finding first dynamic reloc section. */
    1174             : 
    1175          51 :   if( rel_phnum == elf->ehdr.e_phnum ) {
    1176           0 :     fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
    1177           0 :     ulong rel_shnum;
    1178           0 :     for( rel_shnum=0; rel_shnum < elf->ehdr.e_shnum; rel_shnum++ )
    1179           0 :       if( shdrs[ rel_shnum ].sh_addr==dt_rel )
    1180           0 :         break;
    1181           0 :     REQUIRE( rel_shnum < elf->ehdr.e_shnum );
    1182           0 :     rel_off = shdrs[ rel_shnum ].sh_offset;
    1183           0 :   }
    1184             : 
    1185          51 :   REQUIRE( fd_ulong_is_aligned( rel_off, 8UL ) );
    1186          51 :   REQUIRE( (rel_off            <  elf_sz)
    1187          51 :          & (dt_relsz           <= elf_sz)
    1188          51 :          & ((rel_off+dt_relsz) <= elf_sz) );
    1189             : 
    1190             :   /* Load section and reloc tables
    1191             :      Assume section header already validated at this point */
    1192             : 
    1193          51 :   fd_elf64_rel const * rel     = (fd_elf64_rel const *)( elf->bin + rel_off );
    1194          51 :   ulong                rel_cnt = dt_relsz/sizeof(fd_elf64_rel);
    1195             : 
    1196             :   /* Apply each reloc */
    1197             : 
    1198        7545 :   for( ulong i=0; i<rel_cnt; i++ ) {
    1199        7494 :     int res = fd_sbpf_apply_reloc( loader, elf, elf_sz, rodata, info, &rel[ i ] );
    1200        7494 :     if( res!=0 ) return res;
    1201        7494 :   }
    1202             : 
    1203          51 :   return 0;
    1204          51 : }
    1205             : 
    1206             : static int
    1207             : fd_sbpf_zero_rodata( fd_sbpf_elf_t *            elf,
    1208             :                      uchar *                    rodata,
    1209          66 :                      fd_sbpf_elf_info_t const * info ) {
    1210             : 
    1211          66 :   fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
    1212             : 
    1213             :   /* memset gaps between sections to zero.
    1214             :       Assume section sh_addrs are monotonically increasing.
    1215             :       Assume section virtual address ranges equal physical address ranges.
    1216             :       Assume ranges are not overflowing. */
    1217             :   /* FIXME match Solana more closely here */
    1218             : 
    1219          66 :   ulong cursor = 0UL;
    1220         717 :   for( ulong i=0; i<elf->ehdr.e_shnum; i++ ) {
    1221         651 :     if( !( info->loaded_sections[ i>>6UL ] & (1UL<<(i&63UL)) ) ) continue;
    1222             : 
    1223         153 :     fd_elf64_shdr const * shdr = &shdrs[ i ];
    1224             : 
    1225             :     /* NOBITS sections are included in rodata, but may have invalid
    1226             :        offsets, thus we can't trust the shdr->sh_offset field. */
    1227         153 :     if( FD_UNLIKELY( shdr->sh_type==FD_ELF_SHT_NOBITS ) ) continue;
    1228             : 
    1229         153 :     ulong off = shdr->sh_offset;
    1230         153 :     ulong sz  = shdr->sh_size;
    1231         153 :     assert( cursor<=off             );  /* Invariant: Monotonically increasing offsets */
    1232         153 :     assert( off+sz>=off             );  /* Invariant: No integer overflow */
    1233         153 :     assert( off+sz<=info->rodata_sz );  /* Invariant: No buffer overflow */
    1234             : 
    1235             :     /* Fill gap with zeros */
    1236         153 :     ulong gap = off - cursor;
    1237         153 :     fd_memset( rodata+cursor, 0, gap );
    1238             : 
    1239         153 :     cursor = off+sz;
    1240         153 :   }
    1241             : 
    1242          66 :   fd_memset( rodata+cursor, 0, info->rodata_sz - cursor );
    1243             : 
    1244          66 :   return 0;
    1245          66 : }
    1246             : 
    1247             : int
    1248             : fd_sbpf_program_load_old( fd_sbpf_program_t *  prog,
    1249             :                           void const *         _bin,
    1250             :                           ulong                elf_sz,
    1251             :                           fd_sbpf_syscalls_t * syscalls,
    1252          66 :                           int                  elf_deploy_checks ) {
    1253          66 :   fd_sbpf_loader_seterr( 0, 0 );
    1254             : 
    1255          66 :   int err;
    1256          66 :   fd_sbpf_elf_t * elf = (fd_sbpf_elf_t *)_bin;
    1257             : 
    1258          66 :   fd_sbpf_loader_t loader = {
    1259          66 :     .calldests = prog->calldests,
    1260          66 :     .syscalls  = syscalls,
    1261             : 
    1262          66 :     .dyn_off   = 0U,
    1263          66 :     .dyn_cnt   = 0U,
    1264             : 
    1265          66 :     .dt_rel    = 0UL,
    1266          66 :     .dt_relent = 0UL,
    1267          66 :     .dt_relsz  = 0UL,
    1268          66 :     .dt_symtab = 0UL,
    1269             : 
    1270          66 :     .dynsym_off = 0U,
    1271          66 :     .dynsym_cnt = 0U,
    1272          66 :     .elf_deploy_checks = elf_deploy_checks
    1273          66 :   };
    1274             : 
    1275             :   /* Find dynamic section */
    1276          66 :   if( FD_UNLIKELY( (err=fd_sbpf_find_dynamic( &loader, elf, elf_sz, &prog->info ))!=0 ) )
    1277           0 :     return err;
    1278             : 
    1279             :   /* Load dynamic section */
    1280          66 :   if( FD_UNLIKELY( (err=fd_sbpf_load_dynamic( &loader, elf, elf_sz ))!=0 ) )
    1281           0 :     return err;
    1282             : 
    1283             :   /* Register entrypoint to calldests. */
    1284          66 :   fd_sbpf_calldests_insert( prog->calldests, prog->entry_pc );
    1285             : 
    1286             :   /* Copy rodata segment */
    1287          66 :   fd_memcpy( prog->rodata, elf->bin, prog->info.rodata_footprint );
    1288             : 
    1289             :   /* Convert calls with PC relative immediate to hashes */
    1290          66 :   if( FD_UNLIKELY( (err=fd_sbpf_hash_calls  ( &loader, prog, elf ))!=0 ) )
    1291           0 :     return err;
    1292             : 
    1293             :   /* Apply relocations */
    1294          66 :   if( FD_UNLIKELY( (err=fd_sbpf_relocate    ( &loader, elf, elf_sz, prog->rodata, &prog->info ))!=0 ) )
    1295           0 :     return err;
    1296             : 
    1297             :   /* Create read-only segment */
    1298          66 :   if( FD_UNLIKELY( (err=fd_sbpf_zero_rodata( elf, prog->rodata, &prog->info ))!=0 ) )
    1299           0 :     return err;
    1300             : 
    1301          66 :   return 0;
    1302          66 : }
    1303             : 
    1304             : int
    1305             : fd_sbpf_program_get_sbpf_version_or_err( void const *                    bin,
    1306             :                                          ulong                           bin_sz,
    1307         147 :                                          fd_sbpf_loader_config_t const * config ) {
    1308             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L376-L381 */
    1309         147 :   const ulong E_FLAGS_OFFSET = 48UL;
    1310         147 :   const uint  E_FLAGS_SBPF_V2 = 0x20;
    1311             : 
    1312         147 :   if( FD_UNLIKELY( bin_sz < E_FLAGS_OFFSET+sizeof(uint) ) ) {
    1313           9 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1314           9 :   }
    1315         138 :   uint e_flags = fd_uint_load_4( (uchar const *)bin + E_FLAGS_OFFSET );
    1316             : 
    1317         138 :   uint sbpf_version = 0U;
    1318         138 :   if( FD_UNLIKELY( config->sbpf_max_version==FD_SBPF_V0 ) ) {
    1319             :     /* https://github.com/anza-xyz/sbpf/blob/main/src/elf.rs#L384-L388 */
    1320          12 :     sbpf_version = e_flags==E_FLAGS_SBPF_V2 ? FD_SBPF_RESERVED : FD_SBPF_V0;
    1321         126 :   } else {
    1322             :     /* https://github.com/anza-xyz/sbpf/blob/main/src/elf.rs#L390-L396 */
    1323         126 :     sbpf_version = e_flags < FD_SBPF_VERSION_COUNT ? e_flags : FD_SBPF_RESERVED;
    1324         126 :   }
    1325             : 
    1326             :   /* https://github.com/anza-xyz/sbpf/blob/main/src/elf.rs#L399-L401 */
    1327         138 :   if( FD_UNLIKELY( !( config->sbpf_min_version <= sbpf_version && sbpf_version <= config->sbpf_max_version ) ) ) {
    1328          42 :     return FD_SBPF_ELF_ERR_UNSUPPORTED_SBPF_VERSION;
    1329          42 :   }
    1330             : 
    1331             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L403-L407 */
    1332          96 :   return (int)sbpf_version;
    1333         138 : }
    1334             : 
    1335             : int
    1336             : fd_sbpf_elf_peek_strict( fd_sbpf_elf_info_t *            info,
    1337             :                          void const *                    bin,
    1338             :                          ulong                           bin_sz,
    1339           9 :                          fd_sbpf_loader_config_t const * config ) {
    1340           9 :   (void)config;
    1341             : 
    1342             :   /* Parse file header */
    1343             : 
    1344             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L425
    1345             :      https://github.com/anza-xyz/sbpf/blob/main/src/elf_parser/mod.rs#L278
    1346             :      (Agave does some extra checks on alignment, but they don't seem necessary) */
    1347           9 :   if( FD_UNLIKELY( bin_sz<sizeof(fd_elf64_ehdr) ) ) {
    1348           0 :     return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
    1349           0 :   }
    1350             : 
    1351           9 :   fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
    1352             : 
    1353             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L430-L453 */
    1354           9 :   ulong program_header_table_end = sizeof(fd_elf64_ehdr) + ehdr.e_phnum*sizeof(fd_elf64_phdr);
    1355             : 
    1356           9 :   int parse_ehdr_err =
    1357           9 :       ( fd_uint_load_4( ehdr.e_ident )    != FD_ELF_MAG_LE         )
    1358           9 :     | ( ehdr.e_ident[ FD_ELF_EI_CLASS   ] != FD_ELF_CLASS_64       )
    1359           9 :     | ( ehdr.e_ident[ FD_ELF_EI_DATA    ] != FD_ELF_DATA_LE        )
    1360           9 :     | ( ehdr.e_ident[ FD_ELF_EI_VERSION ] != 1                     )
    1361           9 :     | ( ehdr.e_ident[ FD_ELF_EI_OSABI   ] != FD_ELF_OSABI_NONE     )
    1362           9 :     | ( fd_ulong_load_8( ehdr.e_ident+8 ) != 0UL                   )
    1363           9 :     | ( ehdr.e_type                       != FD_ELF_ET_DYN         )
    1364           9 :     | ( ehdr.e_machine                    != FD_ELF_EM_SBPF        )
    1365           9 :     | ( ehdr.e_version                    != 1                     )
    1366             :     // | ( ehdr.e_entry )
    1367           9 :     | ( ehdr.e_phoff                      != sizeof(fd_elf64_ehdr) )
    1368             :     // | ( ehdr.e_shoff )
    1369             :     // | ( ehdr.e_flags )
    1370           9 :     | ( ehdr.e_ehsize                     != sizeof(fd_elf64_ehdr) )
    1371           9 :     | ( ehdr.e_phentsize                  != sizeof(fd_elf64_phdr) )
    1372           9 :     | ( ehdr.e_phnum                      <  EXPECTED_PHDR_CNT     ) /* SIMD-0189 says < instead of != */
    1373           9 :     | ( program_header_table_end          >= bin_sz                )
    1374           9 :     | ( ehdr.e_shentsize                  != sizeof(fd_elf64_shdr) )
    1375             :     // | ( ehdr.e_shnum )
    1376           9 :     | ( ehdr.e_shstrndx                   >= ehdr.e_shnum          )
    1377           9 :   ;
    1378           9 :   if( FD_UNLIKELY( parse_ehdr_err ) ) {
    1379           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
    1380           0 :   }
    1381             : 
    1382             :   /* Parse program headers (expecting 4 segments) */
    1383             : 
    1384             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L455-L487
    1385             :      Note: Agave iterates with a zip, i.e. it cuts the loop to 4, even
    1386             :            though the number of phdrs is allowed to be higher. */
    1387           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 };
    1388           9 :   uint  expected_p_flags[ EXPECTED_PHDR_CNT ] = { FD_SBPF_PF_X,             FD_SBPF_PF_R,           FD_SBPF_PF_RW,         FD_SBPF_PF_RW        };
    1389           9 :   fd_elf64_phdr     phdr[ EXPECTED_PHDR_CNT ];
    1390          45 :   for( uint i=0; i<EXPECTED_PHDR_CNT; i++ ) {
    1391          36 :     ulong phdr_off = sizeof(fd_elf64_ehdr) + i*sizeof(fd_elf64_phdr);
    1392          36 :     phdr[ i ] = FD_LOAD( fd_elf64_phdr, bin+phdr_off );
    1393             : 
    1394          36 :     ulong p_filesz = ( expected_p_flags[ i ] & FD_SBPF_PF_W ) ? 0UL : phdr[ i ].p_memsz;
    1395          36 :     int parse_phdr_err =
    1396          36 :         ( phdr[ i ].p_type         != FD_ELF_PT_LOAD              )
    1397          36 :       | ( phdr[ i ].p_flags        != expected_p_flags[ i ]       )
    1398          36 :       | ( phdr[ i ].p_offset       <  program_header_table_end    )
    1399          36 :       | ( phdr[ i ].p_offset       >= bin_sz                      )
    1400          36 :       | ( phdr[ i ].p_offset % 8UL != 0UL                         )
    1401          36 :       | ( phdr[ i ].p_vaddr        != expected_p_vaddr[ i ]       )
    1402          36 :       | ( phdr[ i ].p_paddr        != expected_p_vaddr[ i ]       )
    1403          36 :       | ( phdr[ i ].p_filesz       != p_filesz                    )
    1404          36 :       | ( phdr[ i ].p_filesz       >  bin_sz - phdr[ i ].p_offset )
    1405          36 :       | ( phdr[ i ].p_filesz % 8UL != 0UL                         )
    1406          36 :       | ( phdr[ i ].p_memsz        >= FD_SBPF_MM_REGION_SZ        )
    1407          36 :     ;
    1408          36 :     if( FD_UNLIKELY( parse_phdr_err ) ) {
    1409           0 :       return FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER;
    1410           0 :     }
    1411          36 :   }
    1412             : 
    1413             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L489-L506 */
    1414           9 :   ulong vm_range_start = phdr[ 0 ].p_vaddr;
    1415           9 :   ulong vm_range_end = phdr[ 0 ].p_vaddr + phdr[ 0 ].p_memsz;
    1416           9 :   ulong entry_chk = ehdr.e_entry + 7UL;
    1417           9 :   int parse_e_entry_err =
    1418           9 :      !( vm_range_start <= entry_chk && entry_chk < vm_range_end ) /* rust contains includes min, excludes max*/
    1419           9 :     | ( ehdr.e_entry % 8UL != 0UL                               )
    1420           9 :   ;
    1421           9 :   if( FD_UNLIKELY( parse_e_entry_err ) ) {
    1422           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
    1423           0 :   }
    1424             : 
    1425             :   /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L507-L515 */
    1426           9 :   ulong entry_pc = ( ehdr.e_entry - phdr[ 0 ].p_vaddr ) / 8UL;
    1427           9 :   ulong insn = fd_ulong_load_8( (uchar const *) bin + phdr[ 0 ].p_offset + entry_pc*8UL );
    1428             :   /* Entrypoint must be a valid function start (ADD64_IMM with dst=r10)
    1429             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/ebpf.rs#L588 */
    1430           9 :   if( FD_UNLIKELY( !fd_sbpf_is_function_start( fd_sbpf_instr( insn ) ) ) ) {
    1431           0 :     return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
    1432           0 :   }
    1433             : 
    1434             :   /* config.enable_symbol_and_section_labels is false in production,
    1435             :      so there's nothing else to do.
    1436             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L519 */
    1437             : 
    1438           9 :   info->rodata_sz        = (uint)phdr[ 1 ].p_memsz;
    1439           9 :   info->rodata_footprint = (uint)bin_sz;
    1440           9 :   info->entry_pc         = (uint)entry_pc;
    1441           9 :   info->text_off         = (uint)phdr[ 0 ].p_offset;
    1442           9 :   info->text_sz          = (uint)phdr[ 0 ].p_memsz;
    1443           9 :   info->text_cnt         = (uint)( phdr[ 0 ].p_memsz / 8UL );
    1444             : 
    1445           9 :   return 0;
    1446           9 : }
    1447             : 
    1448             : int
    1449             : fd_sbpf_elf_peek_lenient( fd_sbpf_elf_info_t *            info,
    1450             :                           void const *                    bin,
    1451             :                           ulong                           bin_sz,
    1452          87 :                           fd_sbpf_loader_config_t const * config ) {
    1453             :   // FIXME
    1454          87 :   fd_sbpf_elf_info_t * res = fd_sbpf_elf_peek_old( info, bin, bin_sz, config->elf_deploy_checks, config->sbpf_min_version, config->sbpf_max_version );
    1455             : 
    1456             :   /* Peek (vs load) stops here
    1457             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L639 */
    1458          87 :   return res==NULL ? FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER : 0;
    1459          87 : }
    1460             : 
    1461             : int
    1462             : fd_sbpf_elf_peek( fd_sbpf_elf_info_t *            info,
    1463             :                   void const *                    bin,
    1464             :                   ulong                           bin_sz,
    1465         147 :                   fd_sbpf_loader_config_t const * config ) {
    1466             :   /* Extract sbpf_version (or error)
    1467             :      https://github.com/anza-xyz/sbpf/blob/main/src/elf.rs#L376-L401 */
    1468         147 :   int maybe_sbpf_version = fd_sbpf_program_get_sbpf_version_or_err( bin, bin_sz, config );
    1469         147 :   if( FD_UNLIKELY( maybe_sbpf_version<0 ) ) {
    1470          51 :     return maybe_sbpf_version;
    1471          51 :   }
    1472             : 
    1473             :   /* Initialize info struct */
    1474          96 :   *info = (fd_sbpf_elf_info_t) {
    1475          96 :     .text_off         = 0U,
    1476          96 :     .text_cnt         = 0U,
    1477          96 :     .text_sz          = 0UL,
    1478          96 :     .dynstr_off       = 0U,
    1479          96 :     .dynstr_sz        = 0U,
    1480          96 :     .rodata_sz        = 0U,
    1481          96 :     .rodata_footprint = 0U,
    1482          96 :     .shndx_text       = -1,
    1483          96 :     .shndx_symtab     = -1,
    1484          96 :     .shndx_strtab     = -1,
    1485          96 :     .shndx_dyn        = -1,
    1486          96 :     .shndx_dynstr     = -1,
    1487          96 :     .phndx_dyn        = -1,
    1488          96 :     .entry_pc         = 0U,
    1489          96 :     .sbpf_version     = (uint)maybe_sbpf_version,
    1490             :     /* !!! Keep this in sync with -Werror=missing-field-initializers */
    1491          96 :   };
    1492             : 
    1493             :   /* Invoke strict vs lenient parser
    1494             :      https://github.com/anza-xyz/sbpf/blob/main/src/elf.rs#L403-L407 */
    1495          96 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers( info->sbpf_version ) ) ) {
    1496           9 :     return fd_sbpf_elf_peek_strict( info, bin, bin_sz, config );
    1497           9 :   }
    1498          87 :   return fd_sbpf_elf_peek_lenient( info, bin, bin_sz, config );
    1499          96 : }
    1500             : 
    1501             : int
    1502             : fd_sbpf_program_load_lenient( fd_sbpf_program_t *             prog,
    1503             :                               void const *                    bin,
    1504             :                               ulong                           bin_sz,
    1505             :                               fd_sbpf_syscalls_t *            syscalls,
    1506          66 :                               fd_sbpf_loader_config_t const * config ) {
    1507             :   /* Load (vs peek) starts here
    1508             :      https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L639 */
    1509             : 
    1510             :   // FIXME
    1511          66 :   return fd_sbpf_program_load_old( prog, bin, bin_sz, syscalls, config->elf_deploy_checks );
    1512          66 : }
    1513             : 
    1514             : int
    1515             : fd_sbpf_program_load( fd_sbpf_program_t *             prog,
    1516             :                       void const *                    bin,
    1517             :                       ulong                           bin_sz,
    1518             :                       fd_sbpf_syscalls_t *            syscalls,
    1519          66 :                       fd_sbpf_loader_config_t const * config ) {
    1520             :   /* Invoke strict vs lenient loader
    1521             :      Note: info.sbpf_version is already set by fd_sbpf_program_parse()
    1522             :      https://github.com/anza-xyz/sbpf/blob/main/src/elf.rs#L409-L413 */
    1523          66 :   if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers( prog->info.sbpf_version ) ) ) {
    1524             :     /* There is nothing else to do in the strict case*/
    1525           0 :     return 0;
    1526           0 :   }
    1527          66 :   return fd_sbpf_program_load_lenient( prog, bin, bin_sz, syscalls, config );
    1528          66 : }
    1529             : 
    1530             : #undef ERR
    1531             : #undef FAIL
    1532             : #undef REQUIRE

Generated by: LCOV version 1.14