LCOV - code coverage report
Current view: top level - ballet/sbpf - fd_sbpf_loader.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 19 20 95.0 %
Date: 2025-09-18 04:41:32 Functions: 2 117 1.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_ballet_sbpf_fd_sbpf_loader_h
       2             : #define HEADER_fd_src_ballet_sbpf_fd_sbpf_loader_h
       3             : 
       4             : /* fd_sbpf_loader prepares an sBPF program for execution.  This involves
       5             :    parsing and dynamic relocation.
       6             : 
       7             :    Due to historical reasons, this loader is neither a pure static
       8             :    linker nor a real dynamic loader.  For instance, it will ignore the
       9             :    program header table and instead load specific sections at predefined
      10             :    addresses.  However, it will perform dynamic relocation. */
      11             : 
      12             : #include "../../util/fd_util_base.h"
      13             : #include "../elf/fd_elf64.h"
      14             : 
      15             : /* Error types ********************************************************/
      16             : 
      17             : /* FIXME make error types more specific */
      18             : #define FD_SBPF_ERR_INVALID_ELF (1)
      19           3 : #define FD_SBPF_PROG_RODATA_ALIGN 8UL
      20             : 
      21             : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L17 */
      22           6 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER           (-1)
      23           0 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER        (-2)
      24             : #define FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER        (-3)
      25             : #define FD_SBPF_ELF_PARSER_ERR_INVALID_STRING                (-4)
      26             : #define FD_SBPF_ELF_PARSER_ERR_STRING_TOO_LONG               (-5)
      27           9 : #define FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS                 (-6)
      28             : #define FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE                  (-7)
      29             : #define FD_SBPF_ELF_PARSER_ERR_OVERLAP                       (-8)
      30             : #define FD_SBPF_ELF_PARSER_ERR_SECTION_NOT_IN_ORDER          (-9)
      31             : #define FD_SBPF_ELF_PARSER_ERR_NO_SECTION_NAME_STRING_TABLE  (-10)
      32             : #define FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE (-11)
      33             : #define FD_SBPF_ELF_PARSER_ERR_INVALID_RELOCATION_TABLE      (-12)
      34             : #define FD_SBPF_ELF_PARSER_ERR_INVALID_ALIGNMENT             (-13)
      35             : #define FD_SBPF_ELF_PARSER_ERR_NO_STRING_TABLE               (-14)
      36             : #define FD_SBPF_ELF_PARSER_ERR_NO_DYNAMIC_STRING_TABLE       (-15)
      37             : 
      38             : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L40 */
      39          42 : #define FD_SBPF_ELF_ERR_UNSUPPORTED_SBPF_VERSION             (-22)
      40             : 
      41             : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs
      42             :    FD_SBPF_VERSION_COUNT represents the latest active version,
      43             :    which is V3 for Agave 2.3 and V4 for Agave 3.x.
      44             :    To build for Agave 3.x, set FD_SBPF_VERSION_COUNT to 5U. */
      45       39060 : #define FD_SBPF_VERSION_COUNT (4U)
      46         270 : #define FD_SBPF_V0            (0U)
      47       17775 : #define FD_SBPF_V1            (1U)
      48     2338953 : #define FD_SBPF_V2            (2U)
      49   805613352 : #define FD_SBPF_V3            (3U)
      50             : #define FD_SBPF_V4            (4U)
      51         165 : #define FD_SBPF_RESERVED      (FD_SBPF_VERSION_COUNT+1U)
      52             : 
      53             : /* Program struct *****************************************************/
      54             : 
      55             : /* fd_sbpf_calldests is a bit vector of valid call destinations.
      56             :    Should be configured to fit any possible program counter.  The max
      57             :    program counter is <size of ELF binary> divided by 8. */
      58             : 
      59             : #define SET_NAME fd_sbpf_calldests
      60             : #include "../../util/tmpl/fd_set_dynamic.c"
      61             : 
      62             : /* fd_sbpf_syscall_func_t is a callback implementing an sBPF syscall.
      63             :    vm is a handle to the running VM.  Returns 0 on suceess or an integer
      64             :    error code on failure.
      65             : 
      66             :    IMPORTANT SAFETY TIP!  See notes in
      67             :    flamenco/vm/syscall/fd_vm_syscall.h on what a syscall should expect
      68             :    to see and expect to return. */
      69             : 
      70             : /* FIXME: THIS BELONGS IN FLAMENCO/VM */
      71             : 
      72             : typedef int
      73             : (*fd_sbpf_syscall_func_t)( void *  vm,
      74             :                            ulong   arg0,
      75             :                            ulong   arg1,
      76             :                            ulong   arg2,
      77             :                            ulong   arg3,
      78             :                            ulong   arg4,
      79             :                            ulong * _ret );
      80             : 
      81             : /* fd_sbpf_syscalls_t maps syscall IDs => a name and a VM specific
      82             :    context.  FIXME: THIS ALSO PROBABLY BELONGS IN FLAMENCO/VM */
      83             : 
      84     1020324 : #define FD_SBPF_SYSCALLS_LG_SLOT_CNT (7)
      85             : #define FD_SBPF_SYSCALLS_SLOT_CNT    (1UL<<FD_SBPF_SYSCALLS_LG_SLOT_CNT)
      86             : 
      87             : /* The syscalls map keys should technically be of type uint since they are
      88             :    just murmur32 hashes. However, Agave's BTree allows the full range to be
      89             :    used as a key [0, UINT_MAX]. So we need to define a wider key type to
      90             :    allow for a NULL value that is outside this range. We use ulong here. */
      91             : 
      92             : struct fd_sbpf_syscalls {
      93             :   ulong                  key;  /* Murmur3-32 hash of function name */
      94             :   fd_sbpf_syscall_func_t func; /* Function pointer */
      95             :   char const *           name; /* Infinite lifetime pointer to function name */
      96             : };
      97             : 
      98             : typedef struct fd_sbpf_syscalls fd_sbpf_syscalls_t;
      99             : 
     100             : #define MAP_NAME              fd_sbpf_syscalls
     101       14682 : #define MAP_T                 fd_sbpf_syscalls_t
     102        6960 : #define MAP_HASH_T            ulong
     103      992649 : #define MAP_KEY_NULL          ULONG_MAX         /* Any number greater than UINT_MAX works */
     104       10989 : #define MAP_KEY_INVAL(k)      ( k > UINT_MAX )  /* Force keys to uint size */
     105        8775 : #define MAP_KEY_EQUAL(k0,k1)  (k0)==(k1)
     106             : #define MAP_KEY_EQUAL_IS_SLOW 0
     107        6960 : #define MAP_KEY_HASH(k)       (k)
     108             : #define MAP_MEMOIZE           0
     109     1020324 : #define MAP_LG_SLOT_CNT       FD_SBPF_SYSCALLS_LG_SLOT_CNT
     110             : #include "../../util/tmpl/fd_map.c"
     111             : 
     112             : #define FD_SBPF_SYSCALLS_FOOTPRINT (sizeof(fd_sbpf_syscalls_t) * (1UL<<FD_SBPF_SYSCALLS_LG_SLOT_CNT))
     113             : #define FD_SBPF_SYSCALLS_ALIGN     alignof(fd_sbpf_syscalls_t)
     114             : 
     115             : /* fd_sbpf_elf_info_t contains basic information extracted from an ELF
     116             :    binary. Indicates how much scratch memory and buffer size is required
     117             :    to fully load the program. */
     118             : 
     119             : struct fd_sbpf_elf_info {
     120             :   uint text_off;    /* File offset of .text section (overlaps rodata segment) */
     121             :   uint text_cnt;    /* Instruction count */
     122             :   ulong text_sz;    /* Length of text segment */
     123             : 
     124             :   uint dynstr_off;  /* File offset of .dynstr section (0=missing) */
     125             :   uint dynstr_sz;   /* Dynstr char count */
     126             : 
     127             :   uint rodata_sz;         /* size of rodata segment */
     128             :   uint rodata_footprint;  /* size of ELF binary */
     129             : 
     130             :   /* Known section indices
     131             :      In [-1,USHORT_MAX) where -1 means "not found" */
     132             :   int shndx_text;
     133             :   int shndx_symtab;
     134             :   int shndx_strtab;
     135             :   int shndx_dyn;
     136             :   int shndx_dynstr;
     137             : 
     138             :   /* Known program header indices (like shndx_*) */
     139             :   int phndx_dyn;
     140             : 
     141             :   uint entry_pc;  /* Program counter of entry point
     142             :                      NOTE: MIGHT BE OUT OF BOUNDS! */
     143             : 
     144             :   /* Bitmap of sections to be loaded (LSB => MSB) */
     145             :   ulong loaded_sections[ 1024UL ];
     146             : 
     147             :   /* SBPF version, SIMD-0161 */
     148             :   ulong sbpf_version;
     149             : };
     150             : typedef struct fd_sbpf_elf_info fd_sbpf_elf_info_t;
     151             : 
     152             : /* fd_sbpf_program_t describes a loaded program in memory.
     153             : 
     154             :    [rodata,rodata+rodata_sz) is an externally allocated buffer holding
     155             :    the read-only segment to be loaded into the VM.  WARNING: The rodata
     156             :    area required doing load (rodata_footprint) is larger than the area
     157             :    mapped into the VM (rodata_sz).
     158             : 
     159             :    [text,text+8*text_cnt) is a sub-region of the read-only segment
     160             :    containing executable code. */
     161             : 
     162             : struct __attribute__((aligned(32UL))) fd_sbpf_program {
     163             :   fd_sbpf_elf_info_t info;
     164             : 
     165             :   /* rodata segment to be mapped into VM memory */
     166             :   void * rodata;     /* rodata segment data */
     167             :   ulong  rodata_sz;  /* size of data */
     168             : 
     169             :   /* text section within rodata segment */
     170             :   ulong * text;
     171             :   ulong   text_cnt;  /* instruction count */
     172             :   ulong   text_off;  /* instruction offset for use in CALL_REG instructions */
     173             :   ulong   text_sz;   /* size of text segment */
     174             :   ulong   entry_pc;  /* entrypoint PC (at text[ entry_pc - start_pc ]) ... FIXME: HMMMM ... CODE SEEMS TO USE TEXT[ ENTRY_PC ] */
     175             : 
     176             :   /* Bit vector of valid call destinations (bit count is rodata_sz) */
     177             :   void * calldests_shmem;
     178             :   /* Local join to bit vector of valid call destinations */
     179             :   fd_sbpf_calldests_t * calldests;
     180             : };
     181             : typedef struct fd_sbpf_program fd_sbpf_program_t;
     182             : 
     183             : struct fd_sbpf_loader_config {
     184             :   int elf_deploy_checks;
     185             :   uint sbpf_min_version;
     186             :   uint sbpf_max_version;
     187             :   int enable_symbol_and_section_labels;
     188             : };
     189             : typedef struct fd_sbpf_loader_config fd_sbpf_loader_config_t;
     190             : 
     191             : /* Prototypes *********************************************************/
     192             : 
     193             : FD_PROTOTYPES_BEGIN
     194             : 
     195             : /* fd_sbpf_elf_peek partially parses the given ELF file in memory region
     196             :    [bin,bin+bin_sz)  Populates `info`.  Returns `info` on success.  On
     197             :    failure, returns NULL.
     198             : 
     199             :    elf_deploy_checks: The Agave ELF loader introduced additional checks
     200             :    that would fail on (certain) existing mainnet programs. Since it is
     201             :    impossible to retroactively enforce these checks on already deployed programs,
     202             :    a guard flag is used to enable these checks only when deploying programs.
     203             : 
     204             :    sbpf_min_version, sbpf_max_version: determine the min, max SBPF version
     205             :    allowed, version is retrieved from the ELF header. See SIMD-0161. */
     206             : 
     207             : int
     208             : fd_sbpf_elf_peek( fd_sbpf_elf_info_t *            info,
     209             :                   void const *                    bin,
     210             :                   ulong                           bin_sz,
     211             :                   fd_sbpf_loader_config_t const * config );
     212             : 
     213             : /* fd_sbpf_program_{align,footprint} return the alignment and size
     214             :    requirements of the memory region backing the fd_sbpf_program_t
     215             :    object. */
     216             : 
     217             : FD_FN_CONST ulong
     218             : fd_sbpf_program_align( void );
     219             : 
     220             : FD_FN_PURE ulong
     221             : fd_sbpf_program_footprint( fd_sbpf_elf_info_t const * info );
     222             : 
     223             : /* fd_sbpf_program_new formats prog_mem to hold an fd_sbpf_program_t.
     224             :    prog_mem must match footprint requirements of the given elf_info.
     225             :    elf_info may be deallocated on return.
     226             : 
     227             :    rodata is the read-only segment buffer that the program is configured
     228             :    against and must be valid for the lifetime of the program object. It
     229             :    should also meet the alignment requirements of the program object.
     230             :    */
     231             : 
     232             : fd_sbpf_program_t *
     233             : fd_sbpf_program_new( void *                     prog_mem,
     234             :                      fd_sbpf_elf_info_t const * elf_info,
     235             :                      void *                     rodata );
     236             : 
     237             : /* fd_sbpf_program_load loads an eBPF program for execution.
     238             : 
     239             :    prog is a program object allocated with fd_sbpf_program_new and must
     240             :    match the footprint requirements of this ELF file.
     241             : 
     242             :    Initializes and populates the program struct with information about
     243             :    the program and prepares the read-only segment provided in
     244             :    fd_sbpf_program_new.
     245             : 
     246             :    Memory region [bin,bin+bin_sz) contains the ELF file to be loaded.
     247             : 
     248             :    On success, returns 0.
     249             :    On error, returns FD_SBPF_ERR_* and leaves prog in an undefined
     250             :    state.
     251             : 
     252             :    ### Compliance
     253             : 
     254             :    This loader does not yet adhere to Solana protocol specs.
     255             :    It is mostly compatible with solana-labs/rbpf v0.3.0 with the
     256             :    following config:
     257             : 
     258             :      new_elf_parser:     true
     259             :      enable_elf_vaddr:   false
     260             :      reject_broken_elfs: elf_deploy_checks
     261             : 
     262             :    For documentation on these config params, see:
     263             :    https://github.com/anza-xyz/sbpf/blob/v0.3.0/src/vm.rs#L198
     264             : 
     265             :    Solana/Agave equivalent:
     266             :    https://github.com/anza-xyz/sbpf/blob/v0.8.0/src/elf.rs#L361
     267             :    */
     268             : 
     269             : int
     270             : fd_sbpf_program_load( fd_sbpf_program_t *             prog,
     271             :                       void const *                    bin,
     272             :                       ulong                           bin_sz,
     273             :                       fd_sbpf_syscalls_t *            syscalls,
     274             :                       fd_sbpf_loader_config_t const * config );
     275             : 
     276             : /* fd_sbpf_program_delete destroys the program object and unformats the
     277             :    memory regions holding it. */
     278             : 
     279             : void *
     280             : fd_sbpf_program_delete( fd_sbpf_program_t * program );
     281             : 
     282             : /* fd_csv_strerror: Returns a cstr describing the source line and error
     283             :    kind after the last call to `fd_sbpf_program_load` from the same
     284             :    thread returned non-zero.
     285             :    Always returns a valid cstr, though the content is undefined in case
     286             :    the last call to `fd_sbpf_program_load` returned zero (success). */
     287             : 
     288             : char const *
     289             : fd_sbpf_strerror( void );
     290             : 
     291             : /* SIMD-0189 */
     292   805457241 : static inline int fd_sbpf_enable_stricter_elf_headers( ulong sbpf_version ) { return sbpf_version >= FD_SBPF_V3; }
     293             : 
     294             : FD_PROTOTYPES_END
     295             : 
     296             : #endif /* HEADER_fd_src_ballet_sbpf_fd_sbpf_loader_h */

Generated by: LCOV version 1.14