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