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 12 : #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 : #define FD_SBPF_ELF_PARSER_SUCCESS ( 0) 23 24 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER (-1) 24 12 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER (-2) 25 0 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER (-3) 26 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_STRING (-4) 27 3 : #define FD_SBPF_ELF_PARSER_ERR_STRING_TOO_LONG (-5) 28 6 : #define FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS (-6) 29 0 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE (-7) 30 0 : #define FD_SBPF_ELF_PARSER_ERR_OVERLAP (-8) 31 0 : #define FD_SBPF_ELF_PARSER_ERR_SECTION_NOT_IN_ORDER (-9) 32 0 : #define FD_SBPF_ELF_PARSER_ERR_NO_SECTION_NAME_STRING_TABLE (-10) 33 0 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE (-11) 34 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_RELOCATION_TABLE (-12) 35 0 : #define FD_SBPF_ELF_PARSER_ERR_INVALID_ALIGNMENT (-13) 36 : #define FD_SBPF_ELF_PARSER_ERR_NO_STRING_TABLE (-14) 37 : #define FD_SBPF_ELF_PARSER_ERR_NO_DYNAMIC_STRING_TABLE (-15) 38 : 39 : /* Map Rust ElfError (elf.rs v0.12.2) to C error codes */ 40 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L40-L66 */ 41 128547 : #define FD_SBPF_ELF_SUCCESS ( 0) 42 27 : #define FD_SBPF_ELF_ERR_FAILED_TO_PARSE ( -1) 43 0 : #define FD_SBPF_ELF_ERR_ENTRYPOINT_OUT_OF_BOUNDS ( -2) 44 0 : #define FD_SBPF_ELF_ERR_INVALID_ENTRYPOINT ( -3) 45 : #define FD_SBPF_ELF_ERR_FAILED_TO_GET_SECTION ( -4) 46 0 : #define FD_SBPF_ELF_ERR_UNRESOLVED_SYMBOL ( -5) 47 : #define FD_SBPF_ELF_ERR_SECTION_NOT_FOUND ( -6) 48 0 : #define FD_SBPF_ELF_ERR_RELATIVE_JUMP_OUT_OF_BOUNDS ( -7) 49 0 : #define FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION ( -8) 50 0 : #define FD_SBPF_ELF_ERR_WRONG_ENDIANNESS ( -9) 51 0 : #define FD_SBPF_ELF_ERR_WRONG_ABI (-10) 52 0 : #define FD_SBPF_ELF_ERR_WRONG_MACHINE (-11) 53 0 : #define FD_SBPF_ELF_ERR_WRONG_CLASS (-12) 54 0 : #define FD_SBPF_ELF_ERR_NOT_ONE_TEXT_SECTION (-13) 55 0 : #define FD_SBPF_ELF_ERR_WRITABLE_SECTION_NOT_SUPPORTED (-14) 56 : #define FD_SBPF_ELF_ERR_ADDRESS_OUTSIDE_LOADABLE_SECTION (-15) 57 0 : #define FD_SBPF_ELF_ERR_INVALID_VIRTUAL_ADDRESS (-16) 58 0 : #define FD_SBPF_ELF_ERR_UNKNOWN_RELOCATION (-17) 59 : #define FD_SBPF_ELF_ERR_FAILED_TO_READ_RELOCATION_INFO (-18) 60 0 : #define FD_SBPF_ELF_ERR_WRONG_TYPE (-19) 61 0 : #define FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL (-20) 62 6 : #define FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS (-21) 63 51 : #define FD_SBPF_ELF_ERR_UNSUPPORTED_SBPF_VERSION (-22) 64 6 : #define FD_SBPF_ELF_ERR_INVALID_PROGRAM_HEADER (-23) 65 : 66 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs */ 67 2775 : #define FD_SBPF_VERSION_COUNT (5U) 68 5295 : #define FD_SBPF_V0 (0U) 69 117063 : #define FD_SBPF_V1 (1U) 70 3522174 : #define FD_SBPF_V2 (2U) 71 1121985 : #define FD_SBPF_V3 (3U) 72 : #define FD_SBPF_V4 (4U) 73 39 : #define FD_SBPF_RESERVED (FD_SBPF_VERSION_COUNT) 74 : 75 : /* Hardcoded constant for the murmur3_32 hash of the entrypoint. */ 76 39456 : #define FD_SBPF_ENTRYPOINT_PC (0xb00c380U) 77 2652 : #define FD_SBPF_ENTRYPOINT_HASH (0x71e3cf81U) /* fd_pchash( FD_SBPF_ENTRYPOINT_PC ) */ 78 : 79 : #define E_FLAGS_SBPF_V2 (0x20U) 80 : 81 : /* Program struct *****************************************************/ 82 : 83 : /* fd_sbpf_calldests is a bit vector of valid call destinations. 84 : Should be configured to fit any possible program counter. The max 85 : program counter is <size of ELF binary> divided by 8. */ 86 : 87 : #define SET_NAME fd_sbpf_calldests 88 : #include "../../util/tmpl/fd_set_dynamic.c" 89 : 90 : /* The sbpf program footprint is large when stricter elf headers are 91 : not enabled due to the calldests bitmap being included. So, the 92 : total footprint of the sbpf_program is the size of the sbpf_program 93 : struct plus the calldests bitmap. 94 : 95 : The calldests bitmap is variable with the text_cnt. A loose bound on 96 : the textcnt is the max size of an account / 8. So, the max possible 97 : text_cnt is 1310720. So the footprint of the sbpf_calldests is as 98 : follows: 99 : sizeof(SET_(private_t))-sizeof(SET_(t)) + sizeof(SET_(t))*SET_(private_word_cnt)( max ) 100 : private_word_cnt(1310720) = 20480 101 : sizeof(SET_(t)) = 8 (ulong) 102 : sizeof(SET_(private_t)) = 32 103 : */ 104 : #define FD_SBPF_TEXT_CNT_MAX (FD_RUNTIME_ACC_SZ_MAX / 8UL) 105 : #define FD_SBPF_CALLDESTS_PRIVATE_WORD_CNT ( (FD_SBPF_TEXT_CNT_MAX +63UL)>>6 ) 106 : #define FD_SBPF_PROGRAM_FOOTPRINT (sizeof(fd_sbpf_program_t) + sizeof(fd_sbpf_calldests_private_t)-sizeof(ulong) + sizeof(ulong)*FD_SBPF_CALLDESTS_PRIVATE_WORD_CNT ) 107 : 108 : /* fd_sbpf_syscall_func_t is a callback implementing an sBPF syscall. 109 : vm is a handle to the running VM. Returns 0 on success or an integer 110 : error code on failure. 111 : 112 : IMPORTANT SAFETY TIP! See notes in 113 : flamenco/vm/syscall/fd_vm_syscall.h on what a syscall should expect 114 : to see and expect to return. */ 115 : 116 : /* FIXME: THIS BELONGS IN FLAMENCO/VM */ 117 : 118 : typedef int 119 : (*fd_sbpf_syscall_func_t)( void * vm, 120 : ulong arg0, 121 : ulong arg1, 122 : ulong arg2, 123 : ulong arg3, 124 : ulong arg4, 125 : ulong * _ret ); 126 : 127 : /* fd_sbpf_syscalls_t maps syscall IDs => a name and a VM specific 128 : context. FIXME: THIS ALSO PROBABLY BELONGS IN FLAMENCO/VM */ 129 : 130 2877186 : #define FD_SBPF_SYSCALLS_LG_SLOT_CNT (7) 131 : #define FD_SBPF_SYSCALLS_SLOT_CNT (1UL<<FD_SBPF_SYSCALLS_LG_SLOT_CNT) 132 : 133 : /* The syscalls map keys should technically be of type uint since they are 134 : just murmur32 hashes. However, Agave's BTree allows the full range to be 135 : used as a key [0, UINT_MAX]. So we need to define a wider key type to 136 : allow for a NULL value that is outside this range. We use ulong here. */ 137 : 138 : struct fd_sbpf_syscalls { 139 : ulong key; /* Murmur3-32 hash of function name */ 140 : fd_sbpf_syscall_func_t func; /* Function pointer */ 141 : char const * name; /* Infinite lifetime pointer to function name */ 142 : }; 143 : 144 : typedef struct fd_sbpf_syscalls fd_sbpf_syscalls_t; 145 : 146 : #define MAP_NAME fd_sbpf_syscalls 147 220812 : #define MAP_T fd_sbpf_syscalls_t 148 207240 : #define MAP_HASH_T ulong 149 2381208 : #define MAP_KEY_NULL ULONG_MAX /* Any number greater than UINT_MAX works */ 150 284424 : #define MAP_KEY_INVAL(k) ( k > UINT_MAX ) /* Force keys to uint size */ 151 98481 : #define MAP_KEY_EQUAL(k0,k1) (k0)==(k1) 152 : #define MAP_KEY_EQUAL_IS_SLOW 0 153 207240 : #define MAP_KEY_HASH(k) (k) 154 : #define MAP_MEMOIZE 0 155 2876799 : #define MAP_LG_SLOT_CNT FD_SBPF_SYSCALLS_LG_SLOT_CNT 156 : #include "../../util/tmpl/fd_map.c" 157 : 158 : #define FD_SBPF_SYSCALLS_FOOTPRINT (sizeof(fd_sbpf_syscalls_t) * (1UL<<FD_SBPF_SYSCALLS_LG_SLOT_CNT)) 159 : #define FD_SBPF_SYSCALLS_ALIGN alignof(fd_sbpf_syscalls_t) 160 : 161 : /* fd_sbpf_elf_info_t contains basic information extracted from an ELF 162 : binary. Indicates how much scratch memory and buffer size is required 163 : to fully load the program. */ 164 : 165 : struct fd_sbpf_elf_info { 166 : ulong bin_sz; /* size of ELF binary */ 167 : 168 : ulong calldests_max; /* Size of calldests set */ 169 : uint text_off; /* File offset of .text section (overlaps rodata segment) */ 170 : uint text_cnt; /* Instruction count */ 171 : ulong text_sz; /* size of text segment. Guaranteed to be <= bin_sz. */ 172 : 173 : /* Size of the buffer the loader assembles the program into, computed at 174 : peek. This is exactly the buffer the program cache must allocate: 175 : - strict (v3+): rodata + text segments (text_off + text_sz) 176 : - lenient fast path: the assembled rodata image (highest ro section 177 : end) -- exact size, no scratch buffer needed. 178 : Selected only when the read-only sections already 179 : sit at their file offsets (section address == 180 : file offset), so the image needs no section 181 : repositioning, only zeroing of the gaps between 182 : and around the read-only slices. 183 : - lenient legacy path: bin_sz -- the loader relocates in place over the 184 : full ELF image (and may read the unused account 185 : tail), so it needs the whole binary plus scratch 186 : fd_sbpf_loader_is_legacy_lenient() (load_buf_sz==bin_sz) identifies the 187 : last case -- the only one that requires a scratch buffer. */ 188 : ulong load_buf_sz; 189 : 190 : /* Known section indices 191 : In [-1,USHORT_MAX) where -1 means "not found" */ 192 : int shndx_text; 193 : int shndx_symtab; 194 : int shndx_strtab; 195 : int shndx_dyn; 196 : int shndx_dynstr; 197 : int shndx_dynsymtab; /* Section header index of the dynamic symbol table */ 198 : 199 : /* Known program header indices (like shndx_*) */ 200 : int phndx_dyn; 201 : 202 : /* Dynamic relocation table entries */ 203 : uint dt_rel_off; /* File offset of dynamic relocation table */ 204 : uint dt_rel_sz; /* Number of dynamic relocation table entries */ 205 : 206 : /* SBPF version, SIMD-0161 */ 207 : ulong sbpf_version; 208 : }; 209 : typedef struct fd_sbpf_elf_info fd_sbpf_elf_info_t; 210 : 211 : /* fd_sbpf_loader_is_legacy_lenient returns 1 iff the program must be loaded via 212 : the legacy lenient path (relocate in place over the full ELF image using a 213 : scratch buffer). This is exactly the case load_buf_sz==bin_sz: strict (v3+) 214 : and lenient-fast loads assemble a smaller, exact-size buffer with no scratch, 215 : while the legacy path needs the whole binary (load_buf_sz==bin_sz) + scratch. */ 216 : FD_FN_PURE static inline int 217 2577 : fd_sbpf_loader_is_legacy_lenient( fd_sbpf_elf_info_t const * info ) { 218 2577 : return info->load_buf_sz==info->bin_sz; 219 2577 : } 220 : 221 : /* fd_sbpf_program_t describes a loaded program in memory. 222 : 223 : [rodata,rodata+bin_sz) is an externally allocated buffer holding 224 : the read-only segment to be loaded into the VM. WARNING: The rodata 225 : area required doing load (bin_sz) is larger than the area mapped into 226 : the VM (rodata_sz). 227 : 228 : [text,text+8*text_cnt) is a sub-region of the read-only segment 229 : containing executable code. 230 : 231 : We need to maintain a separate value tracking the entrypoint calldest 232 : because we lay out our calldests in a set instead of a map (like 233 : Agave does), which is more performant but comes with a few footguns. 234 : Since we only store the target PC and not a keypair of <hash, target 235 : PC>, we need to make sure we unregister the correct target PC from 236 : the map. For all other cases besides the b"entrypoint" string, we can 237 : simply check for membership within the calldests set because the 238 : 32-bit murmur3 hash function is bijective, implying key collision iff 239 : value collision. However, the b"entrypoint" string is a special case 240 : because the key is the hardcoded hash of the b"entrypoint" string, 241 : but the value can correspond to any target PC. This means that 242 : someone could register several different target PCs with the same 243 : entrypoint PC, and we cannot figure out which target PC we must 244 : unregister. Additionally, we would not be able to check for 245 : collisions for multiple registered b"entrypoint" strings with 246 : different target PCs. 247 : 248 : Once entry_pc is set, any future calls to set the entry_pc within the 249 : loader will error out with FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION. */ 250 : 251 : struct __attribute__((aligned(32UL))) fd_sbpf_program { 252 : fd_sbpf_elf_info_t info; 253 : 254 : /* rodata segment to be mapped into VM memory */ 255 : void * rodata; /* rodata segment data */ 256 : ulong rodata_sz; /* size of read-only data */ 257 : 258 : /* text section within rodata segment */ 259 : ulong * text; 260 : ulong entry_pc; /* entrypoint PC (at text[ entry_pc ]). ULONG_MAX if not set. */ 261 : 262 : /* Bit vector of valid call destinations (bit count is text_cnt). */ 263 : void * calldests_shmem; 264 : /* Local join to bit vector of valid call destinations (target PCs) */ 265 : fd_sbpf_calldests_t * calldests; 266 : }; 267 : typedef struct fd_sbpf_program fd_sbpf_program_t; 268 : 269 : struct fd_sbpf_loader_config { 270 : union { 271 : int elf_deploy_checks; 272 : int reject_broken_elfs; 273 : }; 274 : uint sbpf_min_version; 275 : uint sbpf_max_version; 276 : }; 277 : typedef struct fd_sbpf_loader_config fd_sbpf_loader_config_t; 278 : 279 : /* Prototypes *********************************************************/ 280 : 281 : FD_PROTOTYPES_BEGIN 282 : 283 : /* fd_sbpf_elf_peek partially parses the given ELF file in memory region 284 : [bin,bin+bin_sz) Populates `info`. Returns `info` on success. On 285 : failure, returns NULL. 286 : 287 : elf_deploy_checks: The Agave ELF loader introduced additional checks 288 : that would fail on (certain) existing mainnet programs. Since it is 289 : impossible to retroactively enforce these checks on already deployed programs, 290 : a guard flag is used to enable these checks only when deploying programs. 291 : 292 : sbpf_min_version, sbpf_max_version: determine the min, max SBPF version 293 : allowed, version is retrieved from the ELF header. See SIMD-0161. */ 294 : 295 : int 296 : fd_sbpf_elf_peek( fd_sbpf_elf_info_t * info, 297 : void const * bin, 298 : ulong bin_sz, 299 : fd_sbpf_loader_config_t const * config ); 300 : 301 : /* fd_sbpf_program_{align,footprint} return the alignment and size 302 : requirements of the memory region backing the fd_sbpf_program_t 303 : object. */ 304 : 305 : FD_FN_CONST ulong 306 : fd_sbpf_program_align( void ); 307 : 308 : FD_FN_PURE ulong 309 : fd_sbpf_program_footprint( fd_sbpf_elf_info_t const * info ); 310 : 311 : /* fd_sbpf_program_new formats prog_mem to hold an fd_sbpf_program_t. 312 : prog_mem must match footprint requirements of the given elf_info. 313 : elf_info may be deallocated on return. 314 : 315 : rodata is the read-only segment buffer that the program is configured 316 : against and must be valid for the lifetime of the program object. It 317 : should also meet the alignment requirements of the program object. 318 : */ 319 : 320 : fd_sbpf_program_t * 321 : fd_sbpf_program_new( void * prog_mem, 322 : fd_sbpf_elf_info_t const * elf_info, 323 : void * rodata ); 324 : 325 : /* fd_sbpf_program_load loads an eBPF program for execution. 326 : 327 : prog is a program object allocated with fd_sbpf_program_new and must 328 : match the footprint requirements of this ELF file. 329 : 330 : Initializes and populates the program struct with information about 331 : the program and prepares the read-only segment provided in 332 : fd_sbpf_program_new. This includes performing relocations in the 333 : ELF file and zeroing gaps between rodata sections. 334 : 335 : Memory region [bin,bin+bin_sz) contains the ELF file to be loaded. 336 : 337 : syscalls should be a pointer to a map of registered syscalls and 338 : will be checked against when registering calldests for potential 339 : symbol collisions. 340 : 341 : scratch should be a pointer to a scratch area with size scratch_sz, 342 : used to allocate a temporary buffer for the parsed rodata sections 343 : before copying it back into the rodata. recommended size is bin_sz. 344 : 345 : On success, returns 0. 346 : On error, returns FD_SBPF_ERR_*. 347 : 348 : ### Compliance 349 : 350 : As of writing, this loader is conformant with Solana SBPF v0.12.2, 351 : SBPF versions V0, V1, and V2. 352 : */ 353 : 354 : int 355 : fd_sbpf_program_load( fd_sbpf_program_t * prog, 356 : void const * bin, 357 : ulong bin_sz, 358 : fd_sbpf_syscalls_t * syscalls, 359 : fd_sbpf_loader_config_t const * config, 360 : void * scratch, 361 : ulong scratch_sz ); 362 : 363 : /* fd_sbpf_program_delete destroys the program object and unformats the 364 : memory regions holding it. */ 365 : 366 : void * 367 : fd_sbpf_program_delete( fd_sbpf_program_t * program ); 368 : 369 : /* SBPF versions and features. This should stay in sync with the macro 370 : definitions in fd_vm_private.h until they are removed (once Agave 371 : cleans up the jump table). 372 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L28 */ 373 : 374 12 : #define FD_VM_SBPF_DYNAMIC_STACK_FRAMES_ALIGN (64U) 375 : 376 : /* SIMD-0166 */ 377 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L32-L34 */ 378 315 : static inline int fd_sbpf_manual_stack_frame_bump_enabled ( ulong v ) { return v==FD_SBPF_V1 || v==FD_SBPF_V2; } 379 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L36-L38 */ 380 0 : static inline int fd_sbpf_stack_frame_gaps_enabled ( ulong v ) { return v==FD_SBPF_V0; } 381 : 382 : /* SIMD-0174 */ 383 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L41-L43 */ 384 0 : static inline int fd_sbpf_enable_pqr_enabled ( ulong v ) { return v==FD_SBPF_V2; } 385 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L45-L47 */ 386 0 : static inline int fd_sbpf_explicit_sign_extension_of_results_enabled ( ulong v ) { return v==FD_SBPF_V2; } 387 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L49-L51 */ 388 0 : static inline int fd_sbpf_swap_sub_reg_imm_operands_enabled ( ulong v ) { return v==FD_SBPF_V2; } 389 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L53-L55 */ 390 0 : static inline int fd_sbpf_disable_neg_enabled ( ulong v ) { return v==FD_SBPF_V2; } 391 : 392 : /* SIMD-0173 */ 393 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L58-L60 */ 394 45 : static inline int fd_sbpf_callx_uses_src_reg_enabled ( ulong v ) { return v==FD_SBPF_V2; } 395 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L62-L64 */ 396 0 : static inline int fd_sbpf_disable_lddw_enabled ( ulong v ) { return v==FD_SBPF_V2; } 397 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L66-L68 */ 398 0 : static inline int fd_sbpf_disable_le_enabled ( ulong v ) { return v==FD_SBPF_V2; } 399 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L70-L72 */ 400 0 : static inline int fd_sbpf_move_memory_ix_classes_enabled ( ulong v ) { return v==FD_SBPF_V2; } 401 : 402 : /* SIMD-0178 */ 403 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L75-L77 */ 404 0 : static inline int fd_sbpf_static_syscalls_enabled ( ulong v ) { return v>=FD_SBPF_V3; } 405 : 406 : /* SIMD-0189 */ 407 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L79-L81 */ 408 24096 : static inline int fd_sbpf_enable_stricter_elf_headers_enabled ( ulong v ) { return v>=FD_SBPF_V3; } 409 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L83-L85 */ 410 0 : static inline int fd_sbpf_enable_lower_rodata_vaddr_enabled ( ulong v ) { return v>=FD_SBPF_V3; } 411 : 412 : /* SIMD-0377 */ 413 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L87-L89 */ 414 0 : static inline int fd_sbpf_enable_jmp32_enabled ( ulong v ) { return v>=FD_SBPF_V3; } 415 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/program.rs#L91-L93 */ 416 24 : static inline int fd_sbpf_callx_uses_dst_reg_enabled ( ulong v ) { return v>=FD_SBPF_V3; } 417 : 418 : FD_PROTOTYPES_END 419 : 420 : #endif /* HEADER_fd_src_ballet_sbpf_fd_sbpf_loader_h */