LCOV - code coverage report
Current view: top level - ballet/sbpf - fd_sbpf_loader.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 580 602 96.3 %
Date: 2024-11-13 11:58:15 Functions: 24 24 100.0 %

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

Generated by: LCOV version 1.14