LCOV - code coverage report
Current view: top level - flamenco/progcache - fd_progcache_rec.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 132 141 93.6 %
Date: 2026-06-29 05:51:35 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "fd_progcache.h"
       2             : #include "../vm/fd_vm.h" /* fd_vm_syscall_register_slot, fd_vm_validate */
       3             : #include "../../util/alloc/fd_alloc.h"
       4             : 
       5             : #include <stdlib.h>
       6             : 
       7             : /* Can be overridden by test executables */
       8             : __attribute__((weak)) int const fd_progcache_use_malloc = 0;
       9             : static inline _Bool
      10        5121 : use_malloc( void ) {
      11        5121 :   _Bool use_malloc = !!fd_progcache_use_malloc;
      12        5121 :   FD_COMPILER_FORGET( use_malloc ); /* prevent constant propagation */
      13        5121 :   return use_malloc;
      14        5121 : }
      15             : 
      16             : void *
      17             : fd_progcache_val_alloc( fd_progcache_rec_t *  rec,
      18             :                         fd_progcache_join_t * join,
      19             :                         ulong                 val_align,
      20        5016 :                         ulong                 val_footprint ) {
      21        5016 :   if( rec->data_gaddr ) fd_progcache_val_free( rec, join );
      22        5016 :   ulong  val_max = 0UL;
      23        5016 :   void * mem;
      24        5016 :   ulong  gaddr;
      25        5016 :   if( FD_UNLIKELY( use_malloc() ) ) { /* test only */
      26           0 :     mem = aligned_alloc( val_align, val_footprint );
      27           0 :     if( FD_UNLIKELY( !mem ) ) return NULL;
      28           0 :     val_max = val_footprint;
      29           0 :     gaddr   = (ulong)mem;
      30        5016 :   } else {
      31        5016 :     mem = fd_alloc_malloc_at_least( join->alloc, val_align, val_footprint, &val_max );
      32        5016 :     if( FD_UNLIKELY( !mem ) ) return NULL;
      33         108 :     FD_CHECK_CRIT( val_max<=UINT_MAX, "massive" ); /* unreachable */
      34         108 :     gaddr = fd_wksp_gaddr_fast( join->data_base, mem );
      35         108 :   }
      36         108 :   rec->data_gaddr = gaddr;
      37         108 :   rec->data_max   = (uint)val_max;
      38         108 :   return mem;
      39        5016 : }
      40             : 
      41             : void
      42             : fd_progcache_val_free1( fd_progcache_rec_t * rec,
      43             :                         void *               val,
      44         105 :                         fd_alloc_t *         alloc ) {
      45         105 :   if( FD_UNLIKELY( use_malloc() ) ) { /* test only */
      46           0 :     free( val );
      47         105 :   } else {
      48         105 :     fd_alloc_free( alloc, val );
      49         105 :   }
      50         105 :   rec->data_gaddr = 0UL;
      51         105 :   rec->data_max   = 0U;
      52         105 :   rec->rodata_off = 0U;
      53         105 :   rec->rodata_sz  = 0U;
      54         105 : }
      55             : 
      56             : void
      57             : fd_progcache_val_free( fd_progcache_rec_t *  rec,
      58          99 :                        fd_progcache_join_t * join ) {
      59          99 :   if( !rec->data_gaddr ) return;
      60          96 :   void * mem = fd_wksp_laddr_fast( join->data_base, rec->data_gaddr );
      61             : 
      62             :   /* Illegal to call val_free on a spill-allocated buffer */
      63          96 :   FD_TEST( !( (ulong)mem >= (ulong)join->shmem->spill.spad &&
      64          96 :               (ulong)mem <  (ulong)join->shmem->spill.spad+FD_PROGCACHE_SPAD_MAX ) );
      65             : 
      66          96 :   fd_progcache_val_free1( rec, mem, join->alloc );
      67          96 : }
      68             : 
      69             : FD_FN_PURE ulong
      70        7578 : fd_progcache_val_footprint( fd_sbpf_elf_info_t const * elf_info ) {
      71        7578 :   int   has_calldests = !fd_sbpf_enable_stricter_elf_headers_enabled( elf_info->sbpf_version );
      72        7578 :   ulong pc_max        = fd_ulong_max( 1UL, elf_info->text_cnt );
      73             : 
      74             :   /* load_buf_sz is the exact buffer the loader needs (peek-computed):
      75             :      text_off+text_sz for strict, the rodata image for lenient-fast, or bin_sz
      76             :      for legacy lenient. */
      77        7578 :   ulong l = FD_LAYOUT_INIT;
      78        7578 :   if( has_calldests ) {
      79        7578 :     l = FD_LAYOUT_APPEND( l, fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint( pc_max ) );
      80        7578 :   }
      81        7578 :   l = FD_LAYOUT_APPEND( l, 8UL, elf_info->load_buf_sz );
      82        7578 :   return FD_LAYOUT_FINI( l, fd_progcache_val_align() );
      83        7578 : }
      84             : 
      85             : /* Program loader wrapper */
      86             : 
      87             : fd_progcache_rec_t *
      88             : fd_progcache_rec_load( fd_progcache_rec_t *            rec,
      89             :                        fd_wksp_t *                     wksp,
      90             :                        fd_sbpf_elf_info_t const *      elf_info,
      91             :                        fd_sbpf_loader_config_t const * config,
      92             :                        ulong                           load_slot,
      93             :                        fd_features_t const *           features,
      94             :                        void const *                    progdata,
      95             :                        ulong                           progdata_sz,
      96             :                        void *                          scratch,
      97        2562 :                        ulong                           scratch_sz ) {
      98             : 
      99             :   /* Format object */
     100             : 
     101        2562 :   int has_calldests = !fd_sbpf_enable_stricter_elf_headers_enabled( elf_info->sbpf_version );
     102             : 
     103        2562 :   void * val           = fd_wksp_laddr_fast( wksp, rec->data_gaddr );
     104        2562 :   void * calldests_mem = NULL;
     105        2562 :   void * rodata_mem;
     106        2562 :   if( has_calldests ) {
     107             :     /* Lenient (v0-v2): [ calldests | rodata ] laid out inside val.  The rodata
     108             :        buffer is load_buf_sz (rodata image on the fast path, bin_sz on the
     109             :        legacy path); must match fd_progcache_val_footprint. */
     110        2562 :     FD_SCRATCH_ALLOC_INIT( l, val );
     111        2562 :     calldests_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint( fd_ulong_max( 1UL, elf_info->text_cnt ) ) );
     112        2562 :     rodata_mem    = FD_SCRATCH_ALLOC_APPEND( l, 8UL, elf_info->load_buf_sz );
     113        2562 :     FD_SCRATCH_ALLOC_FINI( l, fd_progcache_val_align() );
     114        2562 :     FD_TEST( _l-(ulong)val == fd_progcache_val_footprint( elf_info ) );
     115        2562 :   } else {
     116             :     /* Strict (v3+): no calldests, so rodata is just the start of val
     117             :        (val is fd_progcache_val_align()-aligned, which is >= 8). */
     118           0 :     rodata_mem = val;
     119           0 :   }
     120             : 
     121        2562 :   rec->calldests_off = has_calldests ? (uint)( (ulong)calldests_mem - (ulong)val ) : UINT_MAX;
     122        2562 :   rec->rodata_off    = (uint)( (ulong)rodata_mem - (ulong)val );
     123        2562 :   rec->entry_pc      = 0;
     124        2562 :   rec->rodata_sz     = 0;
     125             : 
     126        2562 :   rec->text_cnt      = elf_info->text_cnt;
     127        2562 :   rec->text_off      = elf_info->text_off;
     128        2562 :   rec->text_sz       = (uint)elf_info->text_sz;
     129        2562 :   rec->sbpf_version  = (uchar)elf_info->sbpf_version;
     130             : 
     131             :   /* Set up sbpf_loader (redirect writes into progcache_rec object) */
     132             : 
     133        2562 :   fd_sbpf_program_t prog[1] = {{
     134        2562 :     .info     = *elf_info,
     135        2562 :     .rodata   = rodata_mem,
     136        2562 :     .text     = (ulong *)((ulong)rodata_mem + elf_info->text_off), /* FIXME: WHAT IF MISALIGNED */
     137        2562 :     .entry_pc = ULONG_MAX
     138        2562 :   }};
     139        2562 :   if( has_calldests && elf_info->text_cnt>0UL ) {
     140        2562 :     prog->calldests_shmem = calldests_mem;
     141        2562 :     prog->calldests = fd_sbpf_calldests_join( fd_sbpf_calldests_new( calldests_mem, elf_info->text_cnt ) );
     142        2562 :   }
     143             : 
     144             :   /* Loader requires syscall table */
     145             : 
     146        2562 :   fd_sbpf_syscalls_t _syscalls[ FD_SBPF_SYSCALLS_SLOT_CNT ];
     147        2562 :   fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_join( fd_sbpf_syscalls_new( _syscalls ) );
     148        2562 :   int syscalls_err = fd_vm_syscall_register_slot( syscalls, load_slot, features, /* is_deploy */ 0 );
     149        2562 :   if( FD_UNLIKELY( syscalls_err!=FD_VM_SUCCESS ) ) FD_LOG_CRIT(( "fd_vm_syscall_register_slot failed" ));
     150             : 
     151             :   /* Run ELF loader.
     152             : 
     153             :      Scratch is needed only by the lenient (v0-v2) fallback path, which
     154             :      assembles the rodata segment via a scratch buffer.  The lenient fast
     155             :      path and strict (v3+) loads write directly into the destination buffer;
     156             :      passing NULL both selects the loader's fast/no-scratch path and faults
     157             :      loudly if it ever starts relying on scratch. */
     158             : 
     159        2562 :   int    use_scratch     = fd_sbpf_loader_is_legacy_lenient( elf_info );
     160        2562 :   void * load_scratch    = use_scratch ? scratch    : NULL;
     161        2562 :   ulong  load_scratch_sz = use_scratch ? scratch_sz : 0UL;
     162             : 
     163        2562 :   if( FD_UNLIKELY( 0!=fd_sbpf_program_load( prog, progdata, progdata_sz, syscalls, config, load_scratch, load_scratch_sz ) ) ) {
     164           0 :     return NULL;
     165           0 :   }
     166             : 
     167        2562 :   rec->entry_pc  = (uint)prog->entry_pc;
     168        2562 :   rec->rodata_sz = (uint)prog->rodata_sz;
     169             : 
     170             :   /* Run bytecode validator */
     171             : 
     172        2562 :   fd_vm_t _vm[1];
     173        2562 :   fd_vm_t * vm = fd_vm_join( fd_vm_new( _vm ) );
     174        2562 :   if( FD_UNLIKELY( !vm ) ) FD_LOG_CRIT(( "fd_vm_new failed" ));
     175        2562 :   vm = fd_vm_init( vm,
     176        2562 :                    NULL, /* OK since unused in `fd_vm_validate()` */
     177        2562 :                    0UL,
     178        2562 :                    0UL,
     179        2562 :                    prog->rodata,
     180        2562 :                    prog->rodata_sz,
     181        2562 :                    prog->text,
     182        2562 :                    prog->info.text_cnt,
     183        2562 :                    prog->info.text_off,
     184        2562 :                    prog->info.text_sz,
     185        2562 :                    prog->entry_pc,
     186        2562 :                    prog->calldests,
     187        2562 :                    elf_info->sbpf_version,
     188        2562 :                    syscalls,
     189        2562 :                    NULL,
     190        2562 :                    NULL,
     191        2562 :                    NULL,
     192        2562 :                    0U,
     193        2562 :                    NULL,
     194        2562 :                    0,
     195        2562 :                    FD_FEATURE_ACTIVE( load_slot, features, account_data_direct_mapping ),
     196        2562 :                    FD_FEATURE_ACTIVE( load_slot, features, syscall_parameter_address_restrictions ),
     197        2562 :                    FD_FEATURE_ACTIVE( load_slot, features, virtual_address_space_adjustments ),
     198        2562 :                    0,
     199        2562 :                    0UL );
     200        2562 :   if( FD_UNLIKELY( !vm ) ) FD_LOG_CRIT(( "fd_vm_init failed" ));
     201             : 
     202        2562 :   if( FD_UNLIKELY( fd_vm_validate( vm )!=FD_VM_SUCCESS ) ) return NULL;
     203             : 
     204        2559 :   return rec;
     205        2562 : }
     206             : 
     207             : fd_progcache_rec_t *
     208           3 : fd_progcache_rec_nx( fd_progcache_rec_t * rec ) {
     209           3 :   rec->data_gaddr    = 0UL;
     210           3 :   rec->data_max      = 0U;
     211           3 :   rec->entry_pc      = 0;
     212           3 :   rec->text_cnt      = 0;
     213           3 :   rec->text_off      = 0;
     214           3 :   rec->text_sz       = 0;
     215           3 :   rec->rodata_sz     = 0;
     216             :   rec->calldests_off = UINT_MAX;
     217           3 :   rec->rodata_off    = 0;
     218           3 :   rec->sbpf_version  = 0;
     219           3 :   return rec;
     220           3 : }

Generated by: LCOV version 1.14