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

Generated by: LCOV version 1.14