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