Line data Source code
1 : #include "fd_sbpf_loader.h"
2 : #include "fd_sbpf_instr.h"
3 : #include "fd_sbpf_opcodes.h"
4 : #include "../../util/bits/fd_sat.h"
5 : #include "../murmur3/fd_murmur3.h"
6 :
7 : #include <assert.h>
8 : #include <stdio.h>
9 :
10 : /* ELF loader, part 1 **************************************************
11 :
12 : Start with a static piece of scratch memory and do basic validation
13 : of the file content. Walk the section table once and remember
14 : sections of interest.
15 :
16 : ### Terminology
17 :
18 : This source follows common ELF naming practices.
19 :
20 : section: a named data region present in the ELF file
21 : segment: a contiguous memory region containing sections
22 : (not necessarily contiguous in the ELF file)
23 :
24 : physical address (paddr): Byte offset into ELF file (uchar * bin)
25 : virtual address (vaddr): VM memory address */
26 :
27 : /* Provide convenient access to file header and ELF content */
28 :
29 : __extension__ union fd_sbpf_elf {
30 : fd_elf64_ehdr ehdr;
31 : uchar bin[0];
32 : };
33 : typedef union fd_sbpf_elf fd_sbpf_elf_t;
34 :
35 : /* FD_SBPF_MM_{...}_ADDR are hardcoded virtual addresses of segments
36 : in the sBPF virtual machine.
37 :
38 : FIXME: These should be defined elsewhere */
39 :
40 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/ebpf.rs#L42-L43 */
41 36 : #define FD_SBPF_MM_RODATA_START (0x0UL) /* readonly data */
42 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/ebpf.rs#L44-L45 */
43 38613 : #define FD_SBPF_MM_BYTECODE_START (0x100000000UL) /* bytecode / program region */
44 25524 : #define FD_SBPF_MM_PROGRAM_ADDR FD_SBPF_MM_BYTECODE_START
45 : #define FD_SBPF_MM_STACK_ADDR (0x200000000UL) /* stack */
46 : #define FD_SBPF_MM_HEAP_ADDR (0x300000000UL) /* heap */
47 57 : #define FD_SBPF_MM_REGION_SZ (0x100000000UL) /* max region size */
48 :
49 36 : #define FD_SBPF_PF_X (1U) /* executable */
50 : #define FD_SBPF_PF_W (2U) /* writable */
51 48 : #define FD_SBPF_PF_R (4U) /* readable */
52 : #define FD_SBPF_PF_RW (FD_SBPF_PF_R|FD_SBPF_PF_W)
53 :
54 : #define EXPECTED_PHDR_CNT (4U)
55 :
56 : struct fd_sbpf_range {
57 : ulong lo;
58 : ulong hi;
59 : };
60 : typedef struct fd_sbpf_range fd_sbpf_range_t;
61 :
62 : /* fd_sbpf_range_contains returns 1 if x is in the range
63 : [range.lo, range.hi) and 0 otherwise. */
64 : static inline int
65 15492 : fd_sbpf_range_contains( fd_sbpf_range_t const * range, ulong x ) {
66 15492 : return !!(( range->lo<=x ) & ( x<range->hi ));
67 15492 : }
68 :
69 : /* Mimics Elf64Shdr::file_range(). Returns a pointer to range (Some) if
70 : the section header type is not SHT_NOBITS, and sets range.{lo, hi} to
71 : the section header offset and offset + size, respectively. Returns
72 : NULL (None) otherwise, and sets both range.{lo, hi} to 0 (the default
73 : values for a Rust Range type).
74 :
75 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L87-L93 */
76 :
77 : static fd_sbpf_range_t *
78 : fd_shdr_get_file_range( fd_elf64_shdr const * shdr,
79 30924 : fd_sbpf_range_t * range ) {
80 30924 : if( shdr->sh_type==FD_ELF_SHT_NOBITS ) {
81 0 : *range = (fd_sbpf_range_t) { .lo = 0UL, .hi = 0UL };
82 0 : return NULL;
83 30924 : } else {
84 30924 : *range = (fd_sbpf_range_t) { .lo = shdr->sh_offset, .hi = fd_ulong_sat_add( shdr->sh_offset, shdr->sh_size ) };
85 30924 : return range;
86 30924 : }
87 30924 : }
88 :
89 : /* Converts an ElfParserError code to an ElfError code.
90 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L112-L132 */
91 : static int
92 57 : fd_sbpf_elf_parser_err_to_elf_err( int err ) {
93 57 : switch( err ) {
94 21 : case FD_SBPF_ELF_SUCCESS:
95 21 : return err;
96 3 : case FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS:
97 3 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
98 6 : case FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER:
99 6 : return FD_SBPF_ELF_ERR_INVALID_PROGRAM_HEADER;
100 27 : default:
101 27 : return FD_SBPF_ELF_ERR_FAILED_TO_PARSE;
102 57 : }
103 57 : }
104 :
105 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L11-L13 */
106 22218 : #define FD_SBPF_SECTION_NAME_SZ_MAX (16UL)
107 : #define FD_SBPF_SYMBOL_NAME_SZ_MAX (64UL)
108 :
109 : /* ELF loader, part 2 **************************************************
110 :
111 : Prepare a copy of a subrange of the ELF content: The rodata segment.
112 : Mangle the copy by applying dynamic relocations. Then, zero out
113 : parts of the segment that are not interesting to the loader.
114 :
115 : ### Terminology
116 :
117 : Shorthands for relocation handling:
118 :
119 : S: Symbol value (typically an ELF physical address)
120 : A: Implicit addend, i.e. the original value of the field that the
121 : relocation handler is about to write to
122 : V: Virtual address, i.e. the target value that the relocation
123 : handler is about to write into where the implicit addend was
124 : previously stored */
125 :
126 : ulong
127 42 : fd_sbpf_program_align( void ) {
128 42 : return alignof( fd_sbpf_program_t );
129 42 : }
130 :
131 : ulong
132 42 : fd_sbpf_program_footprint( fd_sbpf_elf_info_t const * info ) {
133 42 : FD_COMPILER_UNPREDICTABLE( info ); /* Make this appear as FD_FN_PURE (e.g. footprint might depened on info contents in future) */
134 42 : if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( info->sbpf_version ) ) ) {
135 : /* SBPF v3+ no longer needs calldests bitmap */
136 6 : return FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,
137 6 : alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) ),
138 6 : alignof(fd_sbpf_program_t) );
139 6 : }
140 36 : return FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_INIT,
141 42 : alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) ),
142 42 : fd_sbpf_calldests_align(), fd_sbpf_calldests_footprint( info->calldests_max ) ), /* calldests bitmap */
143 42 : alignof(fd_sbpf_program_t) );
144 42 : }
145 :
146 : fd_sbpf_program_t *
147 : fd_sbpf_program_new( void * prog_mem,
148 : fd_sbpf_elf_info_t const * elf_info,
149 51 : void * rodata ) {
150 :
151 51 : if( FD_UNLIKELY( !prog_mem ) ) {
152 0 : FD_LOG_WARNING(( "NULL prog_mem" ));
153 0 : return NULL;
154 0 : }
155 :
156 51 : if( FD_UNLIKELY( !elf_info ) ) {
157 0 : FD_LOG_WARNING(( "NULL elf_info" ));
158 0 : return NULL;
159 0 : }
160 :
161 51 : if( FD_UNLIKELY( ((elf_info->bin_sz)>0U) & (!rodata)) ) {
162 0 : FD_LOG_WARNING(( "NULL rodata" ));
163 0 : return NULL;
164 0 : }
165 :
166 : /* https://github.com/solana-labs/rbpf/blob/v0.8.0/src/elf_parser/mod.rs#L99 */
167 51 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong) rodata, FD_SBPF_PROG_RODATA_ALIGN ) ) ){
168 0 : FD_LOG_WARNING(( "rodata is not 8-byte aligned" ));
169 0 : return NULL;
170 0 : }
171 :
172 : /* Initialize program struct */
173 :
174 51 : FD_SCRATCH_ALLOC_INIT( laddr, prog_mem );
175 51 : fd_sbpf_program_t * prog = FD_SCRATCH_ALLOC_APPEND( laddr, alignof(fd_sbpf_program_t), sizeof(fd_sbpf_program_t) );
176 :
177 : /* Note that entry_pc and rodata_sz get set during the loading phase. */
178 0 : *prog = (fd_sbpf_program_t) {
179 51 : .info = *elf_info,
180 51 : .rodata = rodata,
181 51 : .rodata_sz = 0UL,
182 51 : .text = (ulong *)((ulong)rodata + elf_info->text_off), /* FIXME: WHAT IF MISALIGNED */
183 51 : .entry_pc = ULONG_MAX,
184 51 : .calldests_shmem = NULL,
185 51 : .calldests = NULL,
186 51 : };
187 :
188 : /* If the text section is empty, or the program is SBPF V3+, then we
189 : do not need a calldests map. */
190 51 : ulong pc_max = elf_info->calldests_max;
191 51 : if( FD_LIKELY( ( !fd_sbpf_enable_stricter_elf_headers_enabled( elf_info->sbpf_version ) ) && pc_max!=0UL ) ) {
192 39 : prog->calldests_shmem = fd_sbpf_calldests_new(
193 39 : FD_SCRATCH_ALLOC_APPEND( laddr, fd_sbpf_calldests_align(),
194 39 : fd_sbpf_calldests_footprint( pc_max ) ),
195 0 : pc_max );
196 0 : prog->calldests = fd_sbpf_calldests_join( prog->calldests_shmem );
197 39 : }
198 :
199 51 : return prog;
200 51 : }
201 :
202 : void *
203 30 : fd_sbpf_program_delete( fd_sbpf_program_t * mem ) {
204 :
205 30 : if( FD_LIKELY( mem->calldests ) ) {
206 30 : fd_sbpf_calldests_delete( fd_sbpf_calldests_leave( mem->calldests ) );
207 30 : }
208 30 : fd_memset( mem, 0, sizeof(fd_sbpf_program_t) );
209 :
210 30 : return (void *)mem;
211 30 : }
212 :
213 : /* fd_sbpf_loader_t contains various temporary state during loading. */
214 :
215 : struct fd_sbpf_loader {
216 : /* External objects */
217 : ulong * calldests; /* owned by program. NULL if calldests_max = 0 or SBPF v3+ */
218 : fd_sbpf_syscalls_t * syscalls; /* owned by caller */
219 : };
220 : typedef struct fd_sbpf_loader fd_sbpf_loader_t;
221 :
222 : /* fd_sbpf_slice_cstr_eq is a helper method for checking equality
223 : between a slice of memory to a null-terminated C-string. Unlike
224 : strcmp, this function does not include the null-terminator in the
225 : comparison. Returns 1 if the first slice_len bytes of the slice and
226 : cstr are equal, and 0 otherwise. */
227 : static inline int
228 : fd_sbpf_slice_cstr_eq( uchar const * slice,
229 : ulong slice_len,
230 90297 : char const * cstr ) {
231 90297 : return !!(slice_len==strlen( cstr ) && fd_memeq( slice, cstr, slice_len ));
232 90297 : }
233 :
234 : /* fd_sbpf_slice_cstr_start_with is a helper method for checking that a
235 : null-terminated C-string is a prefix of a slice of memory. Returns 1
236 : if the first strlen(cstr) bytes of cstr is a prefix of slice, and 0
237 : otherwise. */
238 : static inline int
239 : fd_sbpf_slice_cstr_start_with( uchar const * slice,
240 : ulong slice_len,
241 11460 : char const * cstr ) {
242 11460 : ulong cstr_len = strlen( cstr );
243 11460 : return !!(slice_len>=cstr_len && fd_memeq( slice, cstr, cstr_len ));
244 11460 : }
245 :
246 : /* fd_sbpf_lenient_get_string_in_section queries a single string from a
247 : section which is marked as SHT_STRTAB. Returns an ElfParserError on
248 : failure, and leaves *out_slice and *out_slice_len in an undefined
249 : state. On success, returns 0 and sets *out_slice to a pointer into
250 : elf_bytes corresponding to the beginning of the string within the
251 : section. *out_slice_len is set to the length of the resulting slice.
252 : Note that *out_slice_len does not include the null-terminator of the
253 : resulting string.
254 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L467-L496 */
255 : int
256 : fd_sbpf_lenient_get_string_in_section( uchar const * elf_bytes,
257 : ulong elf_bytes_len,
258 : fd_elf64_shdr const * section_header,
259 : uint offset_in_section,
260 : ulong maximum_length,
261 : uchar const ** out_slice,
262 38280 : ulong * out_slice_len ) {
263 : /* This could be checked only once outside the loop, but to keep the code the same...
264 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L474-L476 */
265 38280 : if( FD_UNLIKELY( section_header->sh_type!=FD_ELF_SHT_STRTAB ) ) {
266 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
267 0 : }
268 :
269 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L477-L482 */
270 38280 : ulong offset_in_file;
271 38280 : if( FD_UNLIKELY( __builtin_uaddl_overflow( section_header->sh_offset, offset_in_section, &offset_in_file ) ) ) {
272 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
273 0 : }
274 :
275 38280 : ulong string_range_start = offset_in_file;
276 38280 : ulong string_range_end = fd_ulong_min( section_header->sh_offset+section_header->sh_size, offset_in_file+maximum_length );
277 38280 : if( FD_UNLIKELY( string_range_end>elf_bytes_len ) ) {
278 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
279 0 : }
280 : /* In rust vec.get([n..n]) returns [], so this is accepted.
281 : vec.get([n..m]) with m<n returns None, so it throws ElfParserError::OutOfBounds. */
282 38280 : if( FD_UNLIKELY( string_range_end<string_range_start ) ) {
283 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
284 0 : }
285 :
286 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L486-L495 */
287 38280 : uchar * null_terminator_ptr = memchr( (uchar const *)elf_bytes+string_range_start, 0, string_range_end-string_range_start );
288 38280 : if( FD_UNLIKELY( null_terminator_ptr==NULL ) ) {
289 3 : return FD_SBPF_ELF_PARSER_ERR_STRING_TOO_LONG;
290 3 : }
291 :
292 38277 : *out_slice = elf_bytes+string_range_start;
293 38277 : *out_slice_len = (ulong)(null_terminator_ptr-*out_slice);
294 :
295 38277 : return FD_SBPF_ELF_SUCCESS;
296 38280 : }
297 :
298 : /* Registers a target PC into the calldests function registry. Returns
299 : 0 on success, inserts the target PC into the calldests, and sets
300 : *opt_out_pc_hash to murmur3_32(target_pc) (if opt_out_pc_hash is
301 : non-NULL). Returns FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION on failure
302 : if the target PC is already in the syscalls registry and leaves
303 : out_pc_hash in an undefined state.
304 :
305 : An important note is that Agave's implementation uses a map to store
306 : key-value pairs of (murmur3_32(target_pc), target_pc) within the
307 : calldests. We optimize this by using a set containing
308 : target_pc (this is our calldests map), and then deriving
309 : the target PC on the fly given murmur3_32(target_pc) (provided as
310 : imm) in the VM by computing the inverse hash (since murmur3_32 is
311 : bijective for uints).
312 :
313 : Another important note is that if a key-value pair already exists in
314 : Agave's calldests map, they will only throw a symbol hash collision
315 : error if the target PC is different from the one already registered.
316 : We can omit this check because of the hash function's bijective
317 : property, since the key-value pairs are deterministically derived
318 : from one another.
319 :
320 : TODO: this function will have to be adapted to hash the target PC
321 : depending on the SBPF version (>= V3). That has not been implemented
322 : yet.
323 :
324 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L142-L178 */
325 : static int
326 : fd_sbpf_register_function_hashed_legacy( fd_sbpf_loader_t * loader,
327 : fd_sbpf_program_t * prog,
328 : uchar const * name,
329 : ulong name_len,
330 : ulong target_pc,
331 14925 : uint * opt_out_pc_hash ) {
332 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L156-L160 */
333 14925 : uint pc_hash;
334 14925 : uchar is_entrypoint = fd_sbpf_slice_cstr_eq( name, name_len, "entrypoint" ) ||
335 14925 : target_pc==FD_SBPF_ENTRYPOINT_PC;
336 14925 : if( FD_UNLIKELY( is_entrypoint ) ) {
337 2586 : if( FD_UNLIKELY( prog->entry_pc!=ULONG_MAX && prog->entry_pc!=target_pc ) ) {
338 : /* We already registered the entrypoint to a different target PC,
339 : so we cannot register it again. */
340 0 : return FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION;
341 0 : }
342 2586 : prog->entry_pc = target_pc;
343 :
344 : /* Optimization for this constant value */
345 2586 : pc_hash = FD_SBPF_ENTRYPOINT_HASH;
346 12339 : } else {
347 12339 : pc_hash = fd_pchash( (uint)target_pc );
348 12339 : }
349 :
350 : /* loader.get_function_registry() is their equivalent of our syscalls
351 : registry. Fail if the target PC is present there.
352 :
353 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/program.rs#L161-L163 */
354 14925 : if( FD_UNLIKELY( fd_sbpf_syscalls_query( loader->syscalls, pc_hash, NULL ) ) ) {
355 0 : return FD_SBPF_ELF_ERR_SYMBOL_HASH_COLLISION;
356 0 : }
357 :
358 : /* Insert the target PC into the calldests set if it's not the
359 : entrypoint. Due to the nature of our calldests, we also want to
360 : make sure that target_pc <= calldests_max, the call destination is
361 : guaranteed not a valid program counter (therefore does not need to
362 : be registered). */
363 14925 : if( FD_LIKELY( !is_entrypoint &&
364 14925 : loader->calldests &&
365 14925 : fd_sbpf_calldests_valid_idx( loader->calldests, target_pc ) ) ) {
366 12339 : fd_sbpf_calldests_insert( loader->calldests, target_pc );
367 12339 : }
368 :
369 14925 : if( opt_out_pc_hash ) *opt_out_pc_hash = pc_hash;
370 14925 : return FD_SBPF_ELF_SUCCESS;
371 14925 : }
372 :
373 : /* ELF Dynamic Relocations *********************************************
374 :
375 : ### Summary
376 :
377 : The sBPF ELF loader provides a limited dynamic relocation mechanism
378 : to fix up Clang-generated shared objects for execution in an sBPF VM.
379 :
380 : The relocation types themselves violate the eBPF and ELF specs in
381 : various ways. In short, the relocation table (via DT_REL) is used to
382 : shift program code from zero-based addressing to the MM_PROGRAM
383 : segment in the VM memory map (at 0x1_0000_0000).
384 :
385 : As part of the Solana VM protocol it abides by strict determinism
386 : requirements. This sadly means that we will have to replicate all
387 : edge cases and bugs in the Solana Labs ELF loader.
388 :
389 : Three relocation types are currently supported:
390 : - R_BPF_64_64: Sets an absolute address of a symbol as the
391 : 64-bit immediate field of an lddw instruction
392 : - R_BPF_64_RELATIVE: Adds MM_PROGRAM_START (0x1_0000_0000) to ...
393 : a) ... the 64-bit imm field of an lddw instruction (if in text)
394 : b) ... a 64-bit integer (if not in text section)
395 : - R_BPF_64_32: Sets the 32-bit immediate field of a call
396 : instruction to ...
397 : a) the ID of a local function (Murmur3 hash of function PC address)
398 : b) the ID of a syscall
399 :
400 : Obviously invalid relocations (e.g. out-of-bounds of ELF file or
401 : unsupported reloc type) raise an error.
402 : Relocations that would corrupt ELF data structures are silently
403 : ignored (using the fd_sbpf_reloc_mask mechanism).
404 :
405 : ### History
406 :
407 : The use of relocations is technically redundant, as the Solana VM
408 : memory map has been hardcoded in program runtime v1 (so far the only
409 : runtime). However, virtually all deployed programs as of April 2023
410 : are position-independent shared objects and make heavy use of such
411 : relocations.
412 :
413 : Relocations in the Solana VM have a complicated history. Over the
414 : course of years, multiple protocol bugs have been added and fixed.
415 : The ELF loader needs to handle all these edge cases to avoid breaking
416 : "userspace". I.e. any deployed programs which might be immutable
417 : must continue to function.
418 :
419 : While this complex logic will probably stick around for the next few
420 : years, the Solana protocol is getting increasingly restrictive for
421 : newly deployed ELFs. Another proposed change is upgrading to
422 : position-dependent binaries without any dynamic relocations. */
423 :
424 : /* R_BPF_64_64 relocates an absolute address into the extended imm field
425 : of an lddw-form instruction. (Two instruction slots, low 32 bits in
426 : first immediate field, high 32 bits in second immediate field)
427 :
428 : Bits 0..32 32..64 64..96 96..128
429 : [ ... ] [ IMM_LO ] [ ... ] [ IMM_HI ]
430 :
431 : Returns 0 on success and writes the imm offset to the rodata.
432 : Returns the error code on failure.
433 :
434 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1069-L1141 */
435 :
436 : static int
437 : fd_sbpf_r_bpf_64_64( fd_sbpf_elf_t const * elf,
438 : ulong elf_sz,
439 : uchar * rodata,
440 : fd_sbpf_elf_info_t const * info,
441 : fd_elf64_rel const * dt_rel,
442 6 : ulong r_offset ) {
443 :
444 6 : fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
445 :
446 : /* Note that the sbpf_version variable is ALWAYS V0 (see Agave's code
447 : to understand why).
448 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1070-L1080 */
449 6 : ulong imm_offset = fd_ulong_sat_add( r_offset, 4UL /* BYTE_OFFSET_IMMEDIATE */ );
450 :
451 : /* Bounds check.
452 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1084-L1086 */
453 6 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
454 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
455 0 : }
456 :
457 : /* Get the symbol entry from the dynamic symbol table.
458 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1089-L1092 */
459 6 : fd_elf64_sym const * symbol = NULL;
460 6 : {
461 : /* Ensure the dynamic symbol table exists. */
462 6 : if( FD_UNLIKELY( info->shndx_dynsymtab<0 ) ) {
463 0 : return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
464 0 : }
465 :
466 : /* Get the dynamic symbol table section header. The section header
467 : was already validated in fd_sbpf_lenient_elf_parse() so we can
468 : directly get the symbol table. */
469 6 : fd_elf64_shdr const * sh_dynsym = &shdrs[ info->shndx_dynsymtab ];
470 6 : fd_elf64_sym const * dynsym_table = (fd_elf64_sym const *)( elf->bin + sh_dynsym->sh_offset );
471 6 : ulong dynsym_cnt = (ulong)(sh_dynsym->sh_size / sizeof(fd_elf64_sym));
472 :
473 : /* The symbol table index is stored in the lower 4 bytes of r_info.
474 : Check the bounds of the symbol table index. */
475 6 : ulong r_sym = FD_ELF64_R_SYM( dt_rel->r_info );
476 6 : if( FD_UNLIKELY( r_sym>=dynsym_cnt ) ) {
477 0 : return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
478 0 : }
479 6 : symbol = &dynsym_table[ r_sym ];
480 6 : }
481 :
482 : /* Use the relative address as an offset to derive the relocated
483 : address.
484 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1094-L1096 */
485 6 : uint refd_addr = FD_LOAD( uint, &rodata[ imm_offset ] );
486 6 : ulong addr = fd_ulong_sat_add( symbol->st_value, refd_addr );
487 :
488 : /* We need to normalize the address into the VM's memory space, which
489 : is rooted at 0x1_0000_0000 (the program ro-data region). If the
490 : linker hasn't normalized the addresses already, we treat addr as
491 : a relative offset into the program ro-data region. */
492 6 : if( addr<FD_SBPF_MM_PROGRAM_ADDR ) {
493 6 : addr = fd_ulong_sat_add( addr, FD_SBPF_MM_PROGRAM_ADDR );
494 6 : }
495 :
496 : /* Again, no need to check the sbpf_version because it's always V0.
497 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1106-L1140 */
498 6 : ulong imm_low_offset = imm_offset;
499 6 : ulong imm_high_offset = fd_ulong_sat_add( imm_low_offset, 8UL /* INSN_SIZE */ );
500 :
501 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1116-L1122 */
502 6 : {
503 : /* Bounds check before writing to the rodata. */
504 6 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_low_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
505 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
506 0 : }
507 :
508 : /* Write back */
509 6 : FD_STORE( uint, rodata+imm_low_offset, (uint)addr );
510 6 : }
511 :
512 : /* Same as above, but for the imm high offset.
513 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1125-L1134 */
514 0 : {
515 : /* Bounds check before writing to the rodata. */
516 6 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_high_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
517 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
518 0 : }
519 :
520 : /* Write back */
521 6 : FD_STORE( uint, rodata+imm_high_offset, (uint)(addr>>32UL) );
522 6 : }
523 :
524 : /* ...rest of this function is a no-op because
525 : enable_symbol_and_section_labels is disabled in production. */
526 :
527 6 : return FD_SBPF_ELF_SUCCESS;
528 6 : }
529 :
530 : /* R_BPF_64_RELATIVE is almost entirely Solana specific. Returns 0 on
531 : success and an ElfError on failure.
532 :
533 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1142-L1247 */
534 :
535 : static int
536 : fd_sbpf_r_bpf_64_relative( fd_sbpf_elf_t const * elf,
537 : ulong elf_sz,
538 : uchar * rodata,
539 : fd_sbpf_elf_info_t const * info,
540 15294 : ulong r_offset ) {
541 :
542 15294 : fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
543 15294 : fd_elf64_shdr const * sh_text = &shdrs[ info->shndx_text ];
544 :
545 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1147-L1148 */
546 15294 : ulong imm_offset = fd_ulong_sat_add( r_offset, 4UL /* BYTE_OFFSET_IMMEDIATE */ );
547 :
548 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1150-L1246 */
549 15294 : fd_sbpf_range_t text_section_range;
550 15294 : if( fd_shdr_get_file_range( sh_text, &text_section_range ) &&
551 15294 : fd_sbpf_range_contains( &text_section_range, r_offset ) ) {
552 :
553 : /* We are relocating a lddw (load double word) instruction which
554 : spans two instruction slots. The address top be relocated is
555 : split in two halves in the two imms of the instruction slots.
556 :
557 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1159-L1162 */
558 10218 : ulong imm_low_offset = imm_offset;
559 10218 : ulong imm_high_offset = fd_ulong_sat_add( r_offset,
560 10218 : 4UL /* BYTE_OFFSET_IMMEDIATE */ + 8UL /* INSN_SIZE */ );
561 :
562 : /* Read the low side of the address. Perform a bounds check first.
563 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1164-L1171 */
564 10218 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_low_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
565 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
566 0 : }
567 10218 : uint va_low = FD_LOAD( uint, rodata+imm_low_offset );
568 :
569 : /* Read the high side of the address. Perform a bounds check first.
570 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1174-L1180 */
571 10218 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_high_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
572 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
573 0 : }
574 10218 : uint va_high = FD_LOAD( uint, rodata+imm_high_offset );
575 :
576 : /* Put the address back together.
577 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1182-L1187 */
578 10218 : ulong refd_addr = ( (ulong)va_high<<32UL ) | va_low;
579 10218 : if( FD_UNLIKELY( refd_addr==0UL ) ) {
580 0 : return FD_SBPF_ELF_ERR_INVALID_VIRTUAL_ADDRESS;
581 0 : }
582 :
583 : /* We need to normalize the address into the VM's memory space, which
584 : is rooted at 0x1_0000_0000 (the program ro-data region). If the
585 : linker hasn't normalized the addresses already, we treat addr as
586 : a relative offset into the program ro-data region.
587 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1189-L1193 */
588 10218 : if( refd_addr<FD_SBPF_MM_PROGRAM_ADDR ) {
589 10218 : refd_addr = fd_ulong_sat_add( refd_addr, FD_SBPF_MM_PROGRAM_ADDR );
590 10218 : }
591 :
592 : /* Write back the low half. Perform a bounds check first.
593 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1195-L1202 */
594 10218 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_low_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
595 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
596 0 : }
597 10218 : FD_STORE( uint, rodata+imm_low_offset, (uint)refd_addr );
598 :
599 : /* Write back the high half. Perform a bounds check first.
600 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1205-L1214 */
601 10218 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_high_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
602 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
603 0 : }
604 10218 : FD_STORE( uint, rodata+imm_high_offset, (uint)(refd_addr>>32UL) );
605 10218 : } else {
606 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1216-L1228 */
607 5076 : ulong refd_addr = 0UL;
608 :
609 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1230-L1239 */
610 5076 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
611 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
612 0 : }
613 5076 : refd_addr = FD_LOAD( uint, rodata+imm_offset );
614 5076 : refd_addr = fd_ulong_sat_add( refd_addr, FD_SBPF_MM_PROGRAM_ADDR );
615 :
616 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1242-L1245 */
617 5076 : if( FD_UNLIKELY( fd_ulong_sat_add( r_offset, sizeof(ulong) )>elf_sz ) ) {
618 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
619 0 : }
620 :
621 5076 : FD_STORE( ulong, rodata+r_offset, refd_addr );
622 5076 : }
623 :
624 15294 : return FD_SBPF_ELF_SUCCESS;
625 15294 : }
626 :
627 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1248-L1301 */
628 : static int
629 : fd_sbpf_r_bpf_64_32( fd_sbpf_loader_t * loader,
630 : fd_sbpf_program_t * prog,
631 : fd_sbpf_elf_t const * elf,
632 : ulong elf_sz,
633 : uchar * rodata,
634 : fd_sbpf_elf_info_t const * info,
635 : fd_elf64_rel const * dt_rel,
636 : ulong r_offset,
637 5070 : fd_sbpf_loader_config_t const * config ) {
638 :
639 5070 : fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
640 5070 : fd_elf64_shdr const * sh_text = &shdrs[ info->shndx_text ];
641 :
642 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1253-L1254 */
643 5070 : ulong imm_offset = fd_ulong_sat_add( r_offset, 4UL /* BYTE_OFFSET_IMMEDIATE */ );
644 :
645 : /* Get the symbol entry from the dynamic symbol table.
646 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1256-L1259 */
647 5070 : fd_elf64_sym const * symbol = NULL;
648 :
649 : /* Ensure the dynamic symbol table exists. */
650 5070 : if( FD_UNLIKELY( info->shndx_dynsymtab<0 ) ) {
651 0 : return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
652 0 : }
653 :
654 : /* Get the dynamic symbol table section header. The section header
655 : was already validated in fd_sbpf_lenient_elf_parse() so we can
656 : directly get the symbol table. */
657 5070 : fd_elf64_shdr const * sh_dynsym = &shdrs[ info->shndx_dynsymtab ];
658 5070 : fd_elf64_sym const * dynsym_table = (fd_elf64_sym const *)( elf->bin + sh_dynsym->sh_offset );
659 5070 : ulong dynsym_cnt = (ulong)(sh_dynsym->sh_size / sizeof(fd_elf64_sym));
660 :
661 : /* The symbol table index is stored in the lower 4 bytes of r_info.
662 : Check the bounds of the symbol table index. */
663 5070 : ulong r_sym = FD_ELF64_R_SYM( dt_rel->r_info );
664 5070 : if( FD_UNLIKELY( r_sym>=dynsym_cnt ) ) {
665 0 : return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
666 0 : }
667 5070 : symbol = &dynsym_table[ r_sym ];
668 :
669 : /* Verify symbol name.
670 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1261-L1263
671 :
672 : First check if the dynamic string table exists:
673 : If the dynamic string table does not exist then dynamic_symbol_name()
674 : will throw an error because
675 :
676 : self.dynamic_symbol_names_section_header
677 : .ok_or(ElfParserError::NoDynamicStringTable)?
678 :
679 : will throw an error which, will be mapped to UnknownSymbol
680 : https://github.com/anza-xyz/sbpf/blob/main/src/elf_parser/mod.rs#L528-L536 */
681 5070 : if( FD_UNLIKELY( info->shndx_dynstr<0 ) ) {
682 0 : return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
683 0 : }
684 :
685 5070 : uchar const * name;
686 5070 : ulong name_len;
687 5070 : fd_elf64_shdr const * dyn_section_names_shdr = &shdrs[ info->shndx_dynstr ];
688 5070 : if( FD_UNLIKELY( fd_sbpf_lenient_get_string_in_section( elf->bin, elf_sz, dyn_section_names_shdr, symbol->st_name, FD_SBPF_SYMBOL_NAME_SZ_MAX, &name, &name_len ) ) ) {
689 0 : return FD_SBPF_ELF_ERR_UNKNOWN_SYMBOL;
690 0 : }
691 :
692 : /* If the symbol is defined, this is a bpf-to-bpf call.
693 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1265-L1295 */
694 5070 : uint key = 0U;
695 5070 : int symbol_is_function = ( FD_ELF64_ST_TYPE( symbol->st_info )==FD_ELF_STT_FUNC );
696 5070 : {
697 5070 : if( symbol_is_function && symbol->st_value!=0UL ) {
698 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1267-L1269 */
699 198 : fd_sbpf_range_t text_section_range = (fd_sbpf_range_t) {
700 198 : .lo = sh_text->sh_addr,
701 198 : .hi = fd_ulong_sat_add( sh_text->sh_addr, sh_text->sh_size ) };
702 198 : if( FD_UNLIKELY( !fd_sbpf_range_contains( &text_section_range, symbol->st_value ) ) ) {
703 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
704 0 : }
705 :
706 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1270-L1279 */
707 198 : ulong target_pc = fd_ulong_sat_sub( symbol->st_value, sh_text->sh_addr ) / 8UL;
708 198 : int err = fd_sbpf_register_function_hashed_legacy( loader, prog, name, name_len, target_pc, &key );
709 198 : if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
710 0 : return err;
711 0 : }
712 4872 : } else {
713 : /* Else, it's a syscall. Ensure that the syscall can be resolved.
714 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1281-L1294 */
715 4872 : key = fd_murmur3_32(name, name_len, 0UL );
716 4872 : if( FD_UNLIKELY( config->reject_broken_elfs &&
717 4872 : fd_sbpf_syscalls_query( loader->syscalls, key, NULL )==NULL ) ) {
718 0 : return FD_SBPF_ELF_ERR_UNRESOLVED_SYMBOL;
719 0 : }
720 4872 : }
721 5070 : }
722 :
723 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1297-L1300 */
724 5070 : if( FD_UNLIKELY( fd_ulong_sat_add( imm_offset, 4UL /* BYTE_LENGTH_IMMEDIATE */ )>elf_sz ) ) {
725 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
726 0 : }
727 :
728 5070 : FD_STORE( uint, rodata+imm_offset, key );
729 :
730 5070 : return FD_SBPF_ELF_SUCCESS;
731 5070 : }
732 :
733 : static int
734 : fd_sbpf_elf_peek_strict( fd_sbpf_elf_info_t * info,
735 : void const * bin,
736 54 : ulong bin_sz ) {
737 :
738 : /* Parse file header */
739 :
740 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L418
741 : (Agave does some extra checks on alignment, but they don't seem necessary) */
742 54 : if( FD_UNLIKELY( bin_sz<sizeof(fd_elf64_ehdr) ) ) {
743 3 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
744 3 : }
745 :
746 51 : fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
747 :
748 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L419-L422 */
749 51 : ulong program_header_table_end = fd_ulong_sat_add( sizeof(fd_elf64_ehdr), fd_ulong_sat_mul( ehdr.e_phnum, sizeof(fd_elf64_phdr) ) );
750 :
751 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L423-L446 */
752 51 : int parse_ehdr_err =
753 51 : ( fd_uint_load_4( ehdr.e_ident ) != FD_ELF_MAG_LE )
754 51 : | ( ehdr.e_ident[ FD_ELF_EI_CLASS ] != FD_ELF_CLASS_64 )
755 51 : | ( ehdr.e_ident[ FD_ELF_EI_DATA ] != FD_ELF_DATA_LE )
756 51 : | ( ehdr.e_ident[ FD_ELF_EI_VERSION ] != 1 )
757 51 : | ( ehdr.e_ident[ FD_ELF_EI_OSABI ] != FD_ELF_OSABI_NONE )
758 : // The 7 padding bytes [9, 16) must be 0. Byte 8 (EI_ABIVERSION) is also 0, so check [8, 16).
759 51 : | ( fd_ulong_load_8( ehdr.e_ident+8 ) != 0UL )
760 : // | ( ehdr.e_type ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L430 */
761 51 : | ( ehdr.e_machine != FD_ELF_EM_BPF ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L431 */
762 51 : | ( ehdr.e_version != 1 )
763 : // | ( ehdr.e_entry )
764 51 : | ( ehdr.e_phoff != sizeof(fd_elf64_ehdr) )
765 : // | ( ehdr.e_shoff )
766 : // | ( ehdr.e_flags )
767 51 : | ( ehdr.e_ehsize != sizeof(fd_elf64_ehdr) )
768 51 : | ( ehdr.e_phentsize != sizeof(fd_elf64_phdr) )
769 51 : | ( ehdr.e_phnum == 0 ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L439 */
770 51 : | ( program_header_table_end > bin_sz ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L440 */
771 : // | ( ehdr.e_shentsize ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L441 */
772 : // | ( ehdr.e_shnum ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L442 */
773 : // | ( ehdr.e_shstrndx ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L443 */
774 51 : ;
775 51 : if( FD_UNLIKELY( parse_ehdr_err ) ) {
776 15 : return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
777 15 : }
778 :
779 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L452-L453
780 : Note: program_header_table_end = sizeof(ehdr) + e_phnum * sizeof(phdr),
781 : all inputs are small so saturating arithmetic is unnecessary.
782 : This means that the modulus is always zero and the code is unreachable.
783 : Commented out so we can reach 100% coverage. */
784 : // if( FD_UNLIKELY( (program_header_table_end-sizeof(fd_elf64_ehdr))%sizeof(fd_elf64_phdr) ) ) {
785 : // return FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE;
786 : // }
787 :
788 : /* Parse program headers (expecting up to 2 segments: rodata + bytecode)
789 : https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L448-L484 */
790 :
791 201 : #define STRICT_EXPECTED_PHDR_CNT (2U)
792 36 : ulong expected_p_vaddr[ STRICT_EXPECTED_PHDR_CNT ] = { FD_SBPF_MM_RODATA_START, FD_SBPF_MM_BYTECODE_START };
793 36 : uint expected_p_flags[ STRICT_EXPECTED_PHDR_CNT ] = { FD_SBPF_PF_R, FD_SBPF_PF_X };
794 :
795 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L455-L463
796 : If the first PH is not marked as readonly, expect the rodata
797 : segment to be skipped. */
798 36 : fd_elf64_phdr phdr0 = FD_LOAD( fd_elf64_phdr, bin + sizeof(fd_elf64_ehdr) );
799 36 : int skip_rodata = ( phdr0.p_flags != expected_p_flags[ 0 ] );
800 36 : uint ph_start = skip_rodata ? 1U : 0U;
801 :
802 36 : if( FD_UNLIKELY( !skip_rodata && ehdr.e_phnum < 2 ) ) {
803 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L461-L463 */
804 3 : return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
805 3 : }
806 :
807 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L464 */
808 33 : ulong expected_offset = program_header_table_end;
809 33 : fd_elf64_phdr bytecode_phdr = {0};
810 :
811 33 : uint ph_count = fd_uint_min( ehdr.e_phnum, STRICT_EXPECTED_PHDR_CNT );
812 84 : for( uint ei=ph_start, pi=0; ei<STRICT_EXPECTED_PHDR_CNT && pi<ph_count; ei++, pi++ ) {
813 57 : fd_elf64_phdr phdr_i = FD_LOAD( fd_elf64_phdr, bin + sizeof(fd_elf64_ehdr) + pi*sizeof(fd_elf64_phdr) );
814 :
815 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L468-L479 */
816 57 : int parse_phdr_err =
817 57 : ( phdr_i.p_type != FD_ELF_PT_LOAD )
818 57 : | ( phdr_i.p_flags != expected_p_flags[ ei ] )
819 57 : | ( phdr_i.p_offset != expected_offset ) /* exact sequential: https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L470 */
820 57 : | ( phdr_i.p_offset >= bin_sz )
821 57 : | ( phdr_i.p_offset % 8UL != 0UL )
822 57 : | ( phdr_i.p_vaddr != expected_p_vaddr[ ei ] )
823 57 : | ( phdr_i.p_paddr != expected_p_vaddr[ ei ] )
824 57 : | ( phdr_i.p_filesz != phdr_i.p_memsz ) /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L475 */
825 57 : | ( phdr_i.p_filesz > bin_sz - phdr_i.p_offset )
826 57 : | ( phdr_i.p_filesz % 8UL != 0UL )
827 57 : | ( phdr_i.p_memsz >= FD_SBPF_MM_REGION_SZ )
828 57 : ;
829 57 : if( FD_UNLIKELY( parse_phdr_err ) ) {
830 6 : return FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER;
831 6 : }
832 :
833 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L483 */
834 51 : expected_offset = fd_ulong_sat_add( expected_offset, phdr_i.p_filesz );
835 51 : if( ei == 1 ) { bytecode_phdr = phdr_i; }
836 51 : }
837 :
838 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L486-L496
839 : Determine bytecode_header based on skip_rodata */
840 27 : if( skip_rodata ) {
841 6 : bytecode_phdr = phdr0;
842 6 : }
843 27 : #undef STRICT_EXPECTED_PHDR_CNT
844 :
845 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L501-L508 */
846 27 : ulong vm_range_start = bytecode_phdr.p_vaddr;
847 27 : ulong vm_range_end = bytecode_phdr.p_vaddr + bytecode_phdr.p_memsz;
848 27 : ulong entry_chk = ehdr.e_entry + 7UL;
849 27 : int parse_e_entry_err =
850 27 : !( vm_range_start <= entry_chk && entry_chk < vm_range_end ) /* rust contains includes min, excludes max*/
851 27 : | ( ehdr.e_entry % 8UL != 0UL )
852 27 : ;
853 27 : if( FD_UNLIKELY( parse_e_entry_err ) ) {
854 6 : return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
855 6 : }
856 :
857 : /* entry_pc is computed later in fd_sbpf_program_load.
858 : https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L510-L514 */
859 :
860 : /* config.enable_symbol_and_section_labels is false in production,
861 : so there's nothing else to do.
862 : https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L516-L518 */
863 :
864 21 : info->bin_sz = bin_sz;
865 21 : info->text_off = skip_rodata ? 0U : (uint)phdr0.p_memsz;
866 21 : info->text_sz = (uint)bytecode_phdr.p_memsz;
867 21 : info->text_cnt = (uint)( bytecode_phdr.p_memsz / 8UL );
868 :
869 21 : return FD_SBPF_ELF_SUCCESS;
870 27 : }
871 :
872 : static inline int
873 41184 : fd_sbpf_check_overlap( ulong a_start, ulong a_end, ulong b_start, ulong b_end ) {
874 41184 : return !( ( a_end <= b_start || b_end <= a_start ) );
875 41184 : }
876 :
877 : /* Mirrors Elf64::parse() in Agave. Returns an ElfParserError code on
878 : failure and 0 on success.
879 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L148 */
880 : int
881 : fd_sbpf_lenient_elf_parse( fd_sbpf_elf_info_t * info,
882 : void const * bin,
883 2592 : ulong bin_sz ) {
884 :
885 : /* This documents the values that will be set in this function */
886 2592 : info->bin_sz = bin_sz;
887 2592 : info->phndx_dyn = -1;
888 2592 : info->shndx_dyn = -1;
889 2592 : info->shndx_symtab = -1;
890 2592 : info->shndx_strtab = -1;
891 2592 : info->shndx_dynstr = -1;
892 2592 : info->shndx_dynsymtab = -1;
893 :
894 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L149 */
895 2592 : if( FD_UNLIKELY( bin_sz<sizeof(fd_elf64_ehdr) ) ) {
896 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
897 0 : }
898 :
899 2592 : fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
900 2592 : ulong ehdr_start = 0;
901 2592 : ulong ehdr_end = sizeof(fd_elf64_ehdr);
902 :
903 : /* ELF header
904 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L151-L162 */
905 2592 : int parse_ehdr_err =
906 2592 : ( fd_uint_load_4( ehdr.e_ident ) != FD_ELF_MAG_LE )
907 2592 : | ( ehdr.e_ident[ FD_ELF_EI_CLASS ] != FD_ELF_CLASS_64 )
908 2592 : | ( ehdr.e_ident[ FD_ELF_EI_DATA ] != FD_ELF_DATA_LE )
909 2592 : | ( ehdr.e_ident[ FD_ELF_EI_VERSION ] != 1 )
910 2592 : | ( ehdr.e_version != 1 )
911 2592 : | ( ehdr.e_ehsize != sizeof(fd_elf64_ehdr) )
912 2592 : | ( ehdr.e_phentsize != sizeof(fd_elf64_phdr) )
913 2592 : | ( ehdr.e_shentsize != sizeof(fd_elf64_shdr) )
914 2592 : | ( ehdr.e_shstrndx >= ehdr.e_shnum )
915 2592 : ;
916 2592 : if( FD_UNLIKELY( parse_ehdr_err ) ) {
917 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_FILE_HEADER;
918 0 : }
919 :
920 : /* Program headers
921 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L164-L165 */
922 2592 : ulong phdr_start = ehdr.e_phoff;
923 2592 : ulong phdr_end, phdr_sz;
924 : /* Elf64::parse_program_header_table() */
925 2592 : {
926 2592 : if( FD_UNLIKELY( __builtin_umull_overflow( ehdr.e_phnum, sizeof(fd_elf64_phdr), &phdr_sz ) ) ) {
927 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
928 0 : }
929 :
930 2592 : if( FD_UNLIKELY( __builtin_uaddl_overflow( ehdr.e_phoff, phdr_sz, &phdr_end ) ) ) {
931 : /* ArithmeticOverflow -> ElfParserError::OutOfBounds
932 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L671-L675 */
933 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
934 0 : }
935 :
936 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L301 */
937 2592 : if( FD_UNLIKELY( fd_sbpf_check_overlap( ehdr_start, ehdr_end, phdr_start, phdr_end ) ) ) {
938 0 : return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
939 0 : }
940 :
941 : /* Ensure program header table range lies within the file, like
942 : slice_from_bytes. Unfortunately the checks have to be split up
943 : because Agave throws different error codes depending on which
944 : condition fails...
945 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L302-L303 */
946 2592 : if( FD_UNLIKELY( phdr_sz%sizeof(fd_elf64_phdr)!=0UL ) ) {
947 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE;
948 0 : }
949 :
950 2592 : if( FD_UNLIKELY( phdr_end>bin_sz ) ) {
951 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
952 0 : }
953 :
954 2592 : if( FD_UNLIKELY( !fd_ulong_is_aligned( phdr_start, 8UL ) ) ) {
955 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_ALIGNMENT;
956 0 : }
957 2592 : }
958 :
959 : /* Section headers
960 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L167-L172 */
961 :
962 2592 : ulong shdr_start = ehdr.e_shoff;
963 2592 : ulong shdr_end, shdr_sz;
964 : /* Elf64::parse_section_header_table() */
965 2592 : {
966 2592 : if( FD_UNLIKELY( __builtin_umull_overflow( ehdr.e_shnum, sizeof(fd_elf64_shdr), &shdr_sz ) ) ) {
967 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
968 0 : }
969 :
970 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L314-L317 */
971 2592 : if( FD_UNLIKELY( __builtin_uaddl_overflow( ehdr.e_shoff, shdr_sz, &shdr_end ) ) ) {
972 : /* ArithmeticOverflow -> ElfParserError::OutOfBounds
973 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L671-L675 */
974 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
975 0 : }
976 :
977 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L318 */
978 2592 : if( FD_UNLIKELY( fd_sbpf_check_overlap( ehdr_start, ehdr_end, shdr_start, shdr_end ) ) ) {
979 0 : return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
980 0 : }
981 :
982 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L319 */
983 2592 : if( FD_UNLIKELY( fd_sbpf_check_overlap( phdr_start, phdr_end, shdr_start, shdr_end ) ) ) {
984 0 : return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
985 0 : }
986 :
987 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L321 */
988 2592 : if( FD_UNLIKELY( (shdr_end-ehdr.e_shoff)%sizeof(fd_elf64_shdr) ) ) {
989 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE;
990 0 : }
991 :
992 : /* Ensure section header table range lies within the file, like slice_from_bytes */
993 2592 : if( FD_UNLIKELY( shdr_end > bin_sz ) ) {
994 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
995 0 : }
996 :
997 2592 : if( FD_UNLIKELY( !fd_ulong_is_aligned( ehdr.e_shoff, 8UL ) ) ) {
998 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_ALIGNMENT;
999 0 : }
1000 2592 : }
1001 :
1002 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L174-L177 */
1003 2592 : fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + ehdr.e_shoff );
1004 2592 : if( FD_UNLIKELY( shdr.sh_type != FD_ELF_SHT_NULL ) ) {
1005 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
1006 0 : }
1007 :
1008 : /* Parse each program header
1009 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L179-L196 */
1010 2592 : ulong vaddr = 0UL;
1011 5580 : for( ulong i=0; i<ehdr.e_phnum; i++ ) {
1012 2988 : fd_elf64_phdr phdr = FD_LOAD( fd_elf64_phdr, bin + phdr_start + i*sizeof(fd_elf64_phdr) );
1013 2988 : if( FD_UNLIKELY( phdr.p_type != FD_ELF_PT_LOAD ) ) {
1014 : /* Remember first PT_DYNAMIC program header for dynamic parsing */
1015 141 : if( phdr.p_type==FD_ELF_PT_DYNAMIC && info->phndx_dyn == -1 ) {
1016 141 : info->phndx_dyn = (int)i;
1017 141 : }
1018 141 : continue;
1019 141 : }
1020 2847 : if( FD_UNLIKELY( phdr.p_vaddr<vaddr ) ) {
1021 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_PROGRAM_HEADER;
1022 0 : }
1023 2847 : ulong _offset_plus_size;
1024 2847 : if( FD_UNLIKELY( __builtin_uaddl_overflow( phdr.p_offset, phdr.p_filesz, &_offset_plus_size ) ) ) {
1025 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1026 0 : }
1027 2847 : if( FD_UNLIKELY( phdr.p_offset + phdr.p_filesz > bin_sz ) ) {
1028 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1029 0 : }
1030 2847 : vaddr = phdr.p_vaddr;
1031 2847 : }
1032 :
1033 : /* Parse each section header
1034 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L198-L216 */
1035 2592 : ulong offset = 0UL;
1036 13731 : for( ulong i=0; i<ehdr.e_shnum; i++ ) {
1037 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L200-L205 */
1038 11139 : fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
1039 11139 : if( FD_UNLIKELY( shdr.sh_type==FD_ELF_SHT_NOBITS ) ) {
1040 3 : continue;
1041 3 : }
1042 :
1043 : /* Remember first SHT_DYNAMIC section header for dynamic parsing */
1044 11136 : if( shdr.sh_type==FD_ELF_SHT_DYNAMIC && info->shndx_dyn == -1 ) {
1045 141 : info->shndx_dyn = (int)i;
1046 141 : }
1047 :
1048 11136 : ulong sh_start = shdr.sh_offset;
1049 11136 : ulong sh_end;
1050 11136 : if( FD_UNLIKELY( __builtin_uaddl_overflow( shdr.sh_offset, shdr.sh_size, &sh_end ) ) ) {
1051 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1052 0 : }
1053 :
1054 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L206-L208 */
1055 11136 : if( FD_UNLIKELY( fd_sbpf_check_overlap( sh_start, sh_end, ehdr_start, ehdr_end ) ) ) {
1056 0 : return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
1057 0 : }
1058 11136 : if( FD_UNLIKELY( fd_sbpf_check_overlap( sh_start, sh_end, phdr_start, phdr_end ) ) ) {
1059 0 : return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
1060 0 : }
1061 11136 : if( FD_UNLIKELY( fd_sbpf_check_overlap( sh_start, sh_end, shdr_start, shdr_end ) ) ) {
1062 0 : return FD_SBPF_ELF_PARSER_ERR_OVERLAP;
1063 0 : }
1064 :
1065 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L209-L215 */
1066 11136 : if( FD_UNLIKELY( sh_start < offset ) ) {
1067 0 : return FD_SBPF_ELF_PARSER_ERR_SECTION_NOT_IN_ORDER;
1068 0 : }
1069 11136 : offset = sh_end;
1070 11136 : if( FD_UNLIKELY( sh_end > bin_sz ) ) {
1071 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1072 0 : }
1073 11136 : }
1074 :
1075 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L218-L224
1076 : section_header_table.get() returning ok is equivalent to ehdr.e_shstrndx < ehdr.e_shnum,
1077 : and this is already checked above. So, nothing to do here. */
1078 :
1079 : /* Parse sections
1080 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L240 */
1081 2592 : {
1082 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L340-L342 */
1083 2592 : if( FD_UNLIKELY( ehdr.e_shstrndx == 0 ) ) {
1084 0 : return FD_SBPF_ELF_PARSER_ERR_NO_SECTION_NAME_STRING_TABLE;
1085 0 : }
1086 :
1087 : /* Use section name string table to identify well-known sections */
1088 2592 : ulong section_names_shdr_idx = ehdr.e_shstrndx;
1089 2592 : fd_elf64_shdr section_names_shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + section_names_shdr_idx*sizeof(fd_elf64_shdr) );
1090 : /* Agave repeats the following validation all the times, we can do it once here
1091 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L474-L476 */
1092 2592 : if( FD_UNLIKELY( section_names_shdr.sh_type != FD_ELF_SHT_STRTAB ) ) {
1093 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
1094 0 : }
1095 :
1096 : /* Iterate sections and record indices for .text, .symtab, .strtab, .dyn, .dynstr */
1097 13707 : for( ulong i=0; i<ehdr.e_shnum; i++ ) {
1098 : /* Again... */
1099 11118 : fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
1100 :
1101 11118 : uchar const * name;
1102 11118 : ulong name_len;
1103 11118 : int res = fd_sbpf_lenient_get_string_in_section( bin, bin_sz, §ion_names_shdr, shdr.sh_name, FD_SBPF_SECTION_NAME_SZ_MAX, &name, &name_len );
1104 11118 : if( FD_UNLIKELY( res < 0 ) ) {
1105 3 : return res;
1106 3 : }
1107 :
1108 : /* Store the first section by name:
1109 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L350-L355
1110 : The rust code expands in:
1111 : match section_name {
1112 : b".symtab" => {
1113 : if self.symbol_section_header.is_some() {
1114 : return Err(ElfParserError::InvalidSectionHeader);
1115 : }
1116 : self.symbol_section_header = Some(section_header);
1117 : }
1118 : ...
1119 : _ => {}
1120 : }
1121 : Note that the number of bytes compared should not include the
1122 : null-terminator.
1123 : */
1124 11115 : if( fd_sbpf_slice_cstr_eq( name, name_len, ".symtab" ) ) {
1125 33 : if( FD_UNLIKELY( info->shndx_symtab != -1 ) ) {
1126 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
1127 0 : }
1128 33 : info->shndx_symtab = (int)i;
1129 11082 : } else if( fd_sbpf_slice_cstr_eq( name, name_len, ".strtab" ) ) {
1130 33 : if( FD_UNLIKELY( info->shndx_strtab != -1 ) ) {
1131 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
1132 0 : }
1133 33 : info->shndx_strtab = (int)i;
1134 11049 : } else if( fd_sbpf_slice_cstr_eq( name, name_len, ".dynstr" ) ) {
1135 138 : if( FD_UNLIKELY( info->shndx_dynstr != -1 ) ) {
1136 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
1137 0 : }
1138 138 : info->shndx_dynstr = (int)i;
1139 138 : }
1140 11115 : }
1141 2592 : }
1142 :
1143 : /* Parse dynamic
1144 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L241 */
1145 2589 : {
1146 : /* Try PT_DYNAMIC first; if invalid or absent, fall back to SHT_DYNAMIC.
1147 : Note that only the first PT_DYNAMIC and SHT_DYNAMIC are used because of Rust iter().find().
1148 : Mirrors Rust logic:
1149 : - Try PT_DYNAMIC: https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L364-L372
1150 : - Fallback to SHT_DYNAMIC if PT missing/invalid: https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L374-L387
1151 : If neither exists, return OK (static file). If SHT_DYNAMIC exists but is invalid, error. */
1152 :
1153 2589 : ulong dynamic_table_start = ULONG_MAX;
1154 2589 : ulong dynamic_table_end = ULONG_MAX;
1155 :
1156 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L364-L372 */
1157 2589 : if( info->phndx_dyn >= 0 ) {
1158 138 : fd_elf64_phdr dyn_ph = FD_LOAD( fd_elf64_phdr, bin + phdr_start + (ulong)info->phndx_dyn*sizeof(fd_elf64_phdr) );
1159 138 : dynamic_table_start = dyn_ph.p_offset;
1160 138 : dynamic_table_end = dyn_ph.p_offset + dyn_ph.p_filesz;
1161 :
1162 : /* slice_from_program_header also checks that the size of the
1163 : slice is a multiple of the type size and that the alignment is
1164 : correct. */
1165 138 : if( FD_UNLIKELY( dynamic_table_end<dynamic_table_start ||
1166 138 : dynamic_table_end>bin_sz ||
1167 138 : dyn_ph.p_filesz%sizeof(fd_elf64_dyn)!=0UL ||
1168 138 : !fd_ulong_is_aligned( dynamic_table_start, 8UL ) ) ) {
1169 : /* skip - try SHT_DYNAMIC instead */
1170 0 : dynamic_table_start = ULONG_MAX;
1171 0 : dynamic_table_end = ULONG_MAX;
1172 0 : }
1173 138 : }
1174 :
1175 : /* If PT_DYNAMIC did not validate, try SHT_DYNAMIC
1176 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L376-L387 */
1177 2589 : if( dynamic_table_start==ULONG_MAX && info->shndx_dyn >= 0 ) {
1178 0 : fd_elf64_shdr dyn_sh = FD_LOAD( fd_elf64_shdr, bin + shdr_start + (ulong)info->shndx_dyn*sizeof(fd_elf64_shdr) );
1179 0 : dynamic_table_start = dyn_sh.sh_offset;
1180 0 : if( FD_UNLIKELY( ( __builtin_uaddl_overflow( dyn_sh.sh_offset, dyn_sh.sh_size, &dynamic_table_end ) ) || /* checked_add */
1181 0 : ( dyn_sh.sh_size % sizeof(fd_elf64_dyn) != 0UL ) || /* slice_from_bytes InvalidSize */
1182 0 : ( dynamic_table_end > bin_sz ) || /* slice_from_bytes OutOfBounds */
1183 0 : !fd_ulong_is_aligned( dynamic_table_start, 8UL ) /* slice_from_bytes InvalidAlignment */ ) ) {
1184 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L382-L385 */
1185 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
1186 0 : }
1187 0 : }
1188 :
1189 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L393 */
1190 2589 : if( dynamic_table_start==ULONG_MAX ) {
1191 2451 : return FD_SBPF_ELF_SUCCESS;
1192 2451 : }
1193 :
1194 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L396-L407 */
1195 138 : ulong dynamic_table[ FD_ELF_DT_NUM ] = { 0UL };
1196 138 : ulong dyn_cnt = (dynamic_table_end - dynamic_table_start) / (ulong)sizeof(fd_elf64_dyn);
1197 1473 : for( ulong i = 0UL; i<dyn_cnt; i++ ) {
1198 1473 : fd_elf64_dyn dyn = FD_LOAD( fd_elf64_dyn, bin + dynamic_table_start + i*sizeof(fd_elf64_dyn) );
1199 :
1200 1473 : if( FD_UNLIKELY( dyn.d_tag==FD_ELF_DT_NULL ) ) {
1201 138 : break;
1202 138 : }
1203 1335 : if( FD_UNLIKELY( dyn.d_tag>=FD_ELF_DT_NUM ) ) {
1204 129 : continue;
1205 129 : }
1206 :
1207 1206 : dynamic_table[ dyn.d_tag ] = dyn.d_un.d_val;
1208 1206 : }
1209 :
1210 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L409
1211 : solana_sbpf::elf_parser::Elf64::parse_dynamic_relocations */
1212 138 : do {
1213 138 : ulong vaddr = dynamic_table[ FD_ELF_DT_REL ];
1214 138 : if( FD_UNLIKELY( vaddr==0UL ) ) {
1215 15 : break; /* from this do-while */
1216 15 : }
1217 :
1218 123 : if ( FD_UNLIKELY( dynamic_table[ FD_ELF_DT_RELENT ] != sizeof(fd_elf64_rel) ) ) {
1219 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
1220 0 : }
1221 :
1222 123 : ulong size = dynamic_table[ FD_ELF_DT_RELSZ ];
1223 123 : if( FD_UNLIKELY( size==0UL ) ) {
1224 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
1225 0 : }
1226 :
1227 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L430-L444 */
1228 123 : _Bool offset_found = 0;
1229 123 : ulong offset;
1230 123 : fd_elf64_phdr phdr;
1231 360 : for( ulong i=0; i<ehdr.e_phnum; i++ ) { /* program_header_for_vaddr */
1232 360 : phdr = FD_LOAD( fd_elf64_phdr, bin + phdr_start + i*sizeof(fd_elf64_phdr) );
1233 360 : ulong p_vaddr0 = phdr.p_vaddr;
1234 360 : ulong p_memsz = phdr.p_memsz;
1235 360 : ulong p_vaddr1;
1236 360 : if( FD_UNLIKELY( __builtin_uaddl_overflow( p_vaddr0, p_memsz, &p_vaddr1 ) ) ) {
1237 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1238 0 : }
1239 360 : if( p_vaddr0 <= vaddr && vaddr < p_vaddr1 ) {
1240 123 : offset_found = 1;
1241 123 : break;
1242 123 : }
1243 360 : }
1244 123 : if( offset_found ) {
1245 123 : if( FD_UNLIKELY( __builtin_usubl_overflow( vaddr, phdr.p_vaddr, &offset ) ) ) {
1246 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1247 0 : }
1248 123 : if( FD_UNLIKELY( __builtin_uaddl_overflow( offset, phdr.p_offset, &offset ) ) ) {
1249 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1250 0 : }
1251 123 : } else {
1252 0 : for( ulong i=0; i<ehdr.e_shnum; i++ ) { /* section_header_table.iter().find(...) */
1253 0 : fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
1254 0 : if( shdr.sh_addr == vaddr ) {
1255 0 : offset = shdr.sh_offset;
1256 0 : offset_found = 1;
1257 0 : break;
1258 0 : }
1259 0 : }
1260 0 : if( FD_UNLIKELY( !offset_found ) ) {
1261 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
1262 0 : }
1263 0 : }
1264 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L446-L448 */
1265 123 : ulong offset_plus_size;
1266 123 : if( FD_UNLIKELY( __builtin_uaddl_overflow( offset, size, &offset_plus_size ) ) ) {
1267 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1268 0 : }
1269 :
1270 : /* slice_from_bytes checks that size is a multiple of the type
1271 : size and that the alignment of the bytes + offset is correct. */
1272 123 : if( FD_UNLIKELY( ( size%sizeof(fd_elf64_rel)!=0UL ) ||
1273 123 : ( offset_plus_size>bin_sz ) ||
1274 123 : ( !fd_ulong_is_aligned( offset, 8UL ) ) ) ) {
1275 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
1276 0 : }
1277 :
1278 : /* Save the dynamic relocation table info */
1279 123 : info->dt_rel_off = (uint)offset;
1280 123 : info->dt_rel_sz = (uint)size;
1281 123 : } while( 0 ); /* so we can break out */
1282 :
1283 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L410 */
1284 138 : do {
1285 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L452-L455 */
1286 138 : ulong vaddr = dynamic_table[ FD_ELF_DT_SYMTAB ];
1287 138 : if( FD_UNLIKELY( vaddr==0UL ) ) {
1288 0 : break; /* from this do-while */
1289 0 : }
1290 :
1291 138 : fd_elf64_shdr shdr_sym = { 0 };
1292 768 : for( ulong i=0; i<ehdr.e_shnum; i++ ) {
1293 : /* Again... */
1294 768 : shdr_sym = FD_LOAD( fd_elf64_shdr, bin + shdr_start + i*sizeof(fd_elf64_shdr) );
1295 768 : if( shdr_sym.sh_addr == vaddr ) {
1296 138 : info->shndx_dynsymtab = (int)i;
1297 138 : break;
1298 138 : }
1299 768 : }
1300 :
1301 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L457-L461 */
1302 138 : if( FD_UNLIKELY( info->shndx_dynsymtab==-1 ) ) {
1303 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_DYNAMIC_SECTION_TABLE;
1304 0 : }
1305 :
1306 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L463-L464 */
1307 138 : {
1308 138 : if( FD_UNLIKELY( shdr_sym.sh_type != FD_ELF_SHT_SYMTAB && shdr_sym.sh_type != FD_ELF_SHT_DYNSYM ) ) {
1309 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SECTION_HEADER;
1310 0 : }
1311 138 : ulong shdr_sym_start = shdr_sym.sh_offset;
1312 138 : ulong shdr_sym_end;
1313 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L574
1314 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L671 */
1315 138 : if( FD_UNLIKELY( __builtin_uaddl_overflow( shdr_sym.sh_offset, shdr_sym.sh_size, &shdr_sym_end ) ) ) {
1316 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1317 0 : }
1318 : /* slice_from_bytes InvalidSize */
1319 138 : if( FD_UNLIKELY( shdr_sym.sh_size%sizeof(fd_elf64_sym) ) ) {
1320 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_SIZE;
1321 0 : }
1322 : /* slice_from_bytes OutOfBounds */
1323 138 : if( FD_UNLIKELY( shdr_sym_end>bin_sz ) ) {
1324 0 : return FD_SBPF_ELF_PARSER_ERR_OUT_OF_BOUNDS;
1325 0 : }
1326 : /* slice_from_bytes InvalidAlignment */
1327 138 : if( FD_UNLIKELY( !fd_ulong_is_aligned( shdr_sym_start, 8UL ) ) ) {
1328 0 : return FD_SBPF_ELF_PARSER_ERR_INVALID_ALIGNMENT;
1329 0 : }
1330 138 : }
1331 138 : } while( 0 ); /* so we can break out */
1332 138 : }
1333 :
1334 138 : return FD_SBPF_ELF_SUCCESS;
1335 138 : }
1336 :
1337 : /* Performs validation checks on the ELF. Returns an ElfError on failure
1338 : and 0 on success.
1339 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L719-L809 */
1340 : static int
1341 : fd_sbpf_lenient_elf_validate( fd_sbpf_elf_info_t * info,
1342 : void const * bin,
1343 : ulong bin_sz,
1344 2589 : fd_elf64_shdr * text_shdr ) {
1345 :
1346 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L721-L736 */
1347 2589 : fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
1348 2589 : if( FD_UNLIKELY( ehdr.e_ident[ FD_ELF_EI_CLASS ] != FD_ELF_CLASS_64 ) ) {
1349 0 : return FD_SBPF_ELF_ERR_WRONG_CLASS;
1350 0 : }
1351 2589 : if( FD_UNLIKELY( ehdr.e_ident[ FD_ELF_EI_DATA ] != FD_ELF_DATA_LE ) ) {
1352 0 : return FD_SBPF_ELF_ERR_WRONG_ENDIANNESS;
1353 0 : }
1354 2589 : if( FD_UNLIKELY( ehdr.e_ident[ FD_ELF_EI_OSABI ] != FD_ELF_OSABI_NONE ) ) {
1355 0 : return FD_SBPF_ELF_ERR_WRONG_ABI;
1356 0 : }
1357 2589 : if( FD_UNLIKELY( ehdr.e_machine != FD_ELF_EM_BPF && ehdr.e_machine != FD_ELF_EM_SBPF ) ) {
1358 0 : return FD_SBPF_ELF_ERR_WRONG_MACHINE;
1359 0 : }
1360 2589 : if( FD_UNLIKELY( ehdr.e_type != FD_ELF_ET_DYN ) ) {
1361 0 : return FD_SBPF_ELF_ERR_WRONG_TYPE;
1362 0 : }
1363 :
1364 : /* This code doesn't do anything:
1365 : 1. version is already checked at the very beginning of elf_peek
1366 : 2. the if condition is never true because sbpf_version is always v0
1367 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L738-L763 */
1368 :
1369 2589 : ulong shdr_start = ehdr.e_shoff;
1370 2589 : ulong section_names_shdr_idx = ehdr.e_shstrndx;
1371 2589 : fd_elf64_shdr section_names_shdr = FD_LOAD( fd_elf64_shdr, bin + shdr_start + section_names_shdr_idx*sizeof(fd_elf64_shdr) );
1372 :
1373 : /* We do a single iteration over the section header table, collect all info
1374 : we need and return the errors later to match Agave. */
1375 :
1376 2589 : int shndx_text = -1;
1377 2589 : int writeable_err = 0;
1378 2589 : int oob_err = 0;
1379 13689 : for( ulong i=0UL; i<ehdr.e_shnum; i++ ) {
1380 : /* Again... */
1381 11100 : fd_elf64_shdr shdr = FD_LOAD( fd_elf64_shdr, bin + ehdr.e_shoff + i*sizeof(fd_elf64_shdr) );
1382 :
1383 11100 : uchar const * name;
1384 11100 : ulong name_len;
1385 11100 : int res = fd_sbpf_lenient_get_string_in_section( bin, bin_sz, §ion_names_shdr, shdr.sh_name, FD_SBPF_SECTION_NAME_SZ_MAX, &name, &name_len );
1386 11100 : if( FD_UNLIKELY( res ) ) {
1387 : /* this can never fail because it was checked above, but safer to keep it */
1388 0 : return fd_sbpf_elf_parser_err_to_elf_err( res );
1389 0 : }
1390 :
1391 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L765-L775 */
1392 11100 : if( FD_UNLIKELY( fd_sbpf_slice_cstr_eq( name, name_len, ".text" ) ) ) {
1393 2589 : if( FD_LIKELY( shndx_text==-1 ) ) {
1394 2589 : *text_shdr = shdr; /* Store the text section header */
1395 2589 : shndx_text = (int)i;
1396 2589 : } else {
1397 0 : return FD_SBPF_ELF_ERR_NOT_ONE_TEXT_SECTION;
1398 0 : }
1399 2589 : }
1400 :
1401 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L780-L791 */
1402 11100 : if( FD_UNLIKELY( fd_sbpf_slice_cstr_start_with( name, name_len, ".bss" ) ||
1403 11100 : ( ( ( shdr.sh_flags & (FD_ELF_SHF_ALLOC | FD_ELF_SHF_WRITE) ) == (FD_ELF_SHF_ALLOC | FD_ELF_SHF_WRITE) ) &&
1404 11100 : fd_sbpf_slice_cstr_start_with( name, name_len, ".data" ) &&
1405 11100 : !fd_sbpf_slice_cstr_start_with( name, name_len, ".data.rel" ) ) ) ) {
1406 : /* to match Agave return error we can't fail here */
1407 0 : writeable_err = 1;
1408 0 : }
1409 :
1410 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L793-L802 */
1411 11100 : ulong shdr_end;
1412 11100 : if( FD_UNLIKELY( __builtin_uaddl_overflow( shdr.sh_offset, shdr.sh_size, &shdr_end ) ||
1413 11100 : shdr_end>bin_sz ) ) {
1414 0 : oob_err = 1;
1415 0 : }
1416 11100 : }
1417 :
1418 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L776-L778 */
1419 2589 : if( FD_UNLIKELY( shndx_text==-1 ) ) {
1420 0 : return FD_SBPF_ELF_ERR_NOT_ONE_TEXT_SECTION;
1421 0 : }
1422 :
1423 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L786-L788 */
1424 2589 : if( FD_UNLIKELY( writeable_err ) ) {
1425 0 : return FD_SBPF_ELF_ERR_WRITABLE_SECTION_NOT_SUPPORTED;
1426 0 : }
1427 :
1428 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L798 */
1429 2589 : if( FD_UNLIKELY( oob_err ) ) {
1430 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1431 0 : }
1432 :
1433 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L804-L806 */
1434 2589 : if( FD_UNLIKELY( !(
1435 2589 : text_shdr->sh_addr <= ehdr.e_entry && ehdr.e_entry < fd_ulong_sat_add( text_shdr->sh_addr, text_shdr->sh_size )
1436 2589 : ) ) ) {
1437 0 : return FD_SBPF_ELF_ERR_ENTRYPOINT_OUT_OF_BOUNDS;
1438 0 : }
1439 :
1440 : /* Get text section file ranges to calculate the size. */
1441 2589 : fd_sbpf_range_t text_section_range;
1442 2589 : fd_shdr_get_file_range( text_shdr, &text_section_range );
1443 :
1444 2589 : info->text_off = (uint)text_shdr->sh_addr;
1445 2589 : info->text_sz = text_section_range.hi-text_section_range.lo;
1446 2589 : info->text_cnt = (uint)( info->text_sz/8UL );
1447 2589 : info->shndx_text = shndx_text;
1448 2589 : info->calldests_max = (fd_ulong_min( text_shdr->sh_size, bin_sz )+7UL)/8UL;
1449 :
1450 2589 : return FD_SBPF_ELF_SUCCESS;
1451 2589 : }
1452 :
1453 : /* First part of Agave's load_with_lenient_parser(). We split up this
1454 : function into two parts so we know how much memory we need to
1455 : allocate for the loading step. Returns an ElfError on failure and 0
1456 : on success.
1457 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L593-L638 */
1458 : static int
1459 : fd_sbpf_elf_peek_lenient( fd_sbpf_elf_info_t * info,
1460 : void const * bin,
1461 : ulong bin_sz,
1462 2592 : fd_sbpf_loader_config_t const * config ) {
1463 :
1464 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L607 */
1465 2592 : int res = fd_sbpf_lenient_elf_parse( info, bin, bin_sz );
1466 2592 : if( FD_UNLIKELY( res<0 ) ) {
1467 3 : return fd_sbpf_elf_parser_err_to_elf_err( res );
1468 3 : }
1469 :
1470 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L617 */
1471 2589 : fd_elf64_shdr text_shdr = { 0 };
1472 2589 : res = fd_sbpf_lenient_elf_validate( info, bin, bin_sz, &text_shdr );
1473 2589 : if( FD_UNLIKELY( res<0 ) ) {
1474 0 : return res;
1475 0 : }
1476 :
1477 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L620-L638 */
1478 2589 : {
1479 2589 : ulong text_section_vaddr = fd_ulong_sat_add( text_shdr.sh_addr, FD_SBPF_MM_BYTECODE_START );
1480 2589 : ulong vaddr_end = text_section_vaddr;
1481 :
1482 : /* Validate bounds and text section addrs / offsets.
1483 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L632-L638 */
1484 2589 : if( FD_UNLIKELY( ( config->reject_broken_elfs && text_shdr.sh_addr!=text_shdr.sh_offset ) ||
1485 2589 : vaddr_end>FD_SBPF_MM_STACK_ADDR ) ) {
1486 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1487 0 : }
1488 2589 : }
1489 :
1490 : /* Peek (vs load) stops here
1491 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L638 */
1492 :
1493 2589 : return FD_SBPF_ELF_SUCCESS;
1494 2589 : }
1495 :
1496 : static int
1497 : fd_sbpf_program_get_sbpf_version_or_err( void const * bin,
1498 : ulong bin_sz,
1499 2697 : fd_sbpf_loader_config_t const * config ) {
1500 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L376-L381 */
1501 2697 : const ulong E_FLAGS_OFFSET = 48UL;
1502 :
1503 2697 : if( FD_UNLIKELY( bin_sz<E_FLAGS_OFFSET+sizeof(uint) ) ) {
1504 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1505 0 : }
1506 2697 : uint e_flags = FD_LOAD( uint, bin+E_FLAGS_OFFSET );
1507 :
1508 : /* https://github.com/anza-xyz/sbpf/blob/v0.13.0/src/elf.rs#L382-L390 */
1509 2697 : uint sbpf_version = ( e_flags < FD_SBPF_VERSION_COUNT ) ? e_flags : FD_SBPF_RESERVED;
1510 :
1511 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L399-L401 */
1512 2697 : if( FD_UNLIKELY( !( config->sbpf_min_version <= sbpf_version && sbpf_version <= config->sbpf_max_version ) ) ) {
1513 51 : return FD_SBPF_ELF_ERR_UNSUPPORTED_SBPF_VERSION;
1514 51 : }
1515 :
1516 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L403-L407 */
1517 2646 : return (int)sbpf_version;
1518 2697 : }
1519 :
1520 : int
1521 : fd_sbpf_elf_peek( fd_sbpf_elf_info_t * info,
1522 : void const * bin,
1523 : ulong bin_sz,
1524 2697 : fd_sbpf_loader_config_t const * config ) {
1525 : /* Extract sbpf_version (or error)
1526 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L376-L401 */
1527 2697 : int maybe_sbpf_version = fd_sbpf_program_get_sbpf_version_or_err( bin, bin_sz, config );
1528 2697 : if( FD_UNLIKELY( maybe_sbpf_version<0 ) ) {
1529 51 : return maybe_sbpf_version;
1530 51 : }
1531 :
1532 : /* Initialize info struct */
1533 2646 : *info = (fd_sbpf_elf_info_t) {
1534 2646 : .bin_sz = 0U,
1535 2646 : .text_off = 0U,
1536 2646 : .text_cnt = 0U,
1537 2646 : .text_sz = 0UL,
1538 2646 : .shndx_text = -1,
1539 2646 : .shndx_symtab = -1,
1540 2646 : .shndx_strtab = -1,
1541 2646 : .shndx_dyn = -1,
1542 2646 : .shndx_dynstr = -1,
1543 2646 : .shndx_dynsymtab = -1,
1544 2646 : .phndx_dyn = -1,
1545 2646 : .dt_rel_off = 0UL,
1546 2646 : .dt_rel_sz = 0UL,
1547 2646 : .sbpf_version = (uint)maybe_sbpf_version,
1548 : /* !!! Keep this in sync with -Werror=missing-field-initializers */
1549 2646 : };
1550 :
1551 : /* Invoke strict vs lenient parser. The strict parser is used for
1552 : SBPF version >= 3. The strict parser also returns an ElfParserError
1553 : while the lenient parser returns an ElfError, so we have to map
1554 : the strict parser's error code.
1555 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L403-L407 */
1556 2646 : if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( info->sbpf_version ) ) ) {
1557 54 : return fd_sbpf_elf_parser_err_to_elf_err( fd_sbpf_elf_peek_strict( info, bin, bin_sz ) );
1558 54 : }
1559 2592 : return fd_sbpf_elf_peek_lenient( info, bin, bin_sz, config );
1560 2646 : }
1561 :
1562 : /* Parses and concatenates the readonly data sections. This function
1563 : also computes and sets the rodata_sz field inside the SBPF program
1564 : struct. scratch is a pointer to a scratch area with size scratch_sz,
1565 : used to allocate a temporary buffer for the parsed rodata sections
1566 : before copying it back into the rodata (recommended size is bin_sz).
1567 : Returns 0 on success and an ElfError error code on failure. On
1568 : success, the rodata and rodata_sz fields in the sbpf program struct
1569 : are updated.
1570 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L812-L987 */
1571 : static int
1572 : fd_sbpf_parse_ro_sections( fd_sbpf_program_t * prog,
1573 : void const * bin,
1574 : ulong bin_sz,
1575 : fd_sbpf_loader_config_t const * config,
1576 : void * scratch,
1577 2577 : ulong scratch_sz ) {
1578 :
1579 2577 : fd_sbpf_elf_t const * elf = (fd_sbpf_elf_t const *)bin;
1580 2577 : fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
1581 2577 : fd_elf64_shdr const * section_names_shdr = &shdrs[ elf->ehdr.e_shstrndx ];
1582 2577 : uchar * rodata = prog->rodata;
1583 :
1584 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L818-L834 */
1585 2577 : ulong lowest_addr = ULONG_MAX; /* Lowest section address */
1586 2577 : ulong highest_addr = 0UL; /* Highest section address */
1587 2577 : ulong ro_fill_length = 0UL; /* Aggregated section length, excluding gaps between sections */
1588 2577 : uchar invalid_offsets = 0; /* Whether the section has invalid offsets */
1589 :
1590 : /* Store the section header indices of ro slices to fill later. */
1591 2577 : ulong ro_slices_shidxs[ elf->ehdr.e_shnum ];
1592 2577 : ulong ro_slices_cnt = 0UL;
1593 :
1594 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L837-L909 */
1595 13569 : for( uint i=0U; i<elf->ehdr.e_shnum; i++ ) {
1596 10992 : fd_elf64_shdr const * section_header = &shdrs[ i ];
1597 :
1598 : /* Match the section name.
1599 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L838-L845 */
1600 10992 : uchar const * name;
1601 10992 : ulong name_len;
1602 10992 : if( FD_UNLIKELY( fd_sbpf_lenient_get_string_in_section( bin, bin_sz, section_names_shdr, section_header->sh_name, FD_SBPF_SECTION_NAME_SZ_MAX, &name, &name_len ) ) ) {
1603 0 : continue;
1604 0 : }
1605 :
1606 10992 : if( FD_UNLIKELY( !fd_sbpf_slice_cstr_eq( name, name_len, ".text" ) &&
1607 10992 : !fd_sbpf_slice_cstr_eq( name, name_len, ".rodata" ) &&
1608 10992 : !fd_sbpf_slice_cstr_eq( name, name_len, ".data.rel.ro" ) &&
1609 10992 : !fd_sbpf_slice_cstr_eq( name, name_len, ".eh_frame" ) ) ) {
1610 5760 : continue;
1611 5760 : }
1612 :
1613 5232 : ulong section_addr = section_header->sh_addr;
1614 :
1615 : /* Handling for the section header offsets. If ELF vaddrs are
1616 : enabled, the section header addresses are allowed to be > the
1617 : section header offsets, as long as address - offset is constant
1618 : across all sections. Otherwise, the section header addresses
1619 : and offsets must match.
1620 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L865-L884 */
1621 5232 : if( FD_LIKELY( !invalid_offsets ) ) {
1622 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L866-L880 */
1623 5232 : if( FD_UNLIKELY( section_addr!=section_header->sh_offset ) ) {
1624 3 : invalid_offsets = 1;
1625 3 : }
1626 5232 : }
1627 :
1628 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L886-L897 */
1629 5232 : ulong vaddr_end = section_addr;
1630 5232 : if( section_addr<FD_SBPF_MM_BYTECODE_START ) {
1631 5232 : vaddr_end = fd_ulong_sat_add( section_addr, FD_SBPF_MM_BYTECODE_START );
1632 5232 : }
1633 :
1634 5232 : if( FD_UNLIKELY( ( config->reject_broken_elfs && invalid_offsets ) ||
1635 5232 : vaddr_end>FD_SBPF_MM_STACK_ADDR ) ) {
1636 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1637 0 : }
1638 :
1639 : /* Append the ro slices vector and update the lowest / highest addr
1640 : and ro_fill_length variables. Agave stores three fields in the
1641 : ro slices array that can all be derived from the section header,
1642 : so we just need to store the indices.
1643 :
1644 : The call to fd_shdr_get_file_range() is allowed to fail (Agave's
1645 : unwrap_or_default() call returns a range of 0..0 in this case).
1646 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L899-L908 */
1647 5232 : fd_sbpf_range_t section_header_range;
1648 5232 : fd_shdr_get_file_range( section_header, §ion_header_range );
1649 5232 : if( FD_UNLIKELY( section_header_range.hi>bin_sz ) ) {
1650 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1651 0 : }
1652 5232 : ulong section_data_len = section_header_range.hi-section_header_range.lo;
1653 :
1654 5232 : lowest_addr = fd_ulong_min( lowest_addr, section_addr );
1655 5232 : highest_addr = fd_ulong_max( highest_addr, fd_ulong_sat_add( section_addr, section_data_len ) );
1656 5232 : ro_fill_length = fd_ulong_sat_add( ro_fill_length, section_data_len );
1657 5232 : ro_slices_shidxs[ ro_slices_cnt++ ] = i;
1658 5232 : }
1659 :
1660 : /* This checks that the ro sections are not overlapping. This check
1661 : is incomplete, however, because it does not account for the
1662 : existence of gaps between sections in calculations.
1663 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L910-L913 */
1664 2577 : if( FD_UNLIKELY( config->reject_broken_elfs &&
1665 2577 : fd_ulong_sat_add( lowest_addr, ro_fill_length )>highest_addr ) ) {
1666 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1667 0 : }
1668 :
1669 : /* Note that optimize_rodata is always false.
1670 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L923-L984 */
1671 2577 : {
1672 : /* Readonly / non-readonly sections are mixed, so non-readonly
1673 : sections must be zeroed and the readonly sections must be copied
1674 : at their respective offsets.
1675 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L950-L983 */
1676 2577 : lowest_addr = 0UL;
1677 :
1678 : /* Bounds check. */
1679 2577 : ulong buf_len = highest_addr;
1680 2577 : if( FD_UNLIKELY( buf_len>bin_sz ) ) {
1681 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1682 0 : }
1683 :
1684 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L971-L976 */
1685 2577 : if( FD_UNLIKELY( buf_len>scratch_sz ) ) {
1686 0 : FD_LOG_CRIT(( "scratch_sz is too small: %lu, required: %lu", scratch_sz, buf_len ));
1687 0 : }
1688 2577 : uchar * ro_section = scratch;
1689 2577 : fd_memset( ro_section, 0, buf_len );
1690 :
1691 7809 : for( ulong i=0UL; i<ro_slices_cnt; i++ ) {
1692 5232 : ulong sh_idx = ro_slices_shidxs[ i ];
1693 5232 : fd_elf64_shdr const * shdr = &shdrs[ sh_idx ];
1694 5232 : ulong section_addr = shdr->sh_addr;
1695 :
1696 : /* This was checked above and should never fail. */
1697 5232 : fd_sbpf_range_t slice_range;
1698 5232 : fd_shdr_get_file_range( shdr, &slice_range );
1699 5232 : if( FD_UNLIKELY( slice_range.hi>bin_sz ) ) {
1700 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1701 0 : }
1702 :
1703 5232 : ulong buf_offset_start = fd_ulong_sat_sub( section_addr, lowest_addr );
1704 5232 : ulong slice_len = slice_range.hi-slice_range.lo;
1705 5232 : if( FD_UNLIKELY( slice_len>buf_len ) ) {
1706 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1707 0 : }
1708 :
1709 5232 : fd_memcpy( ro_section+buf_offset_start, rodata+slice_range.lo, slice_len );
1710 5232 : }
1711 :
1712 : /* Copy the rodata section back in. */
1713 2577 : prog->rodata_sz = buf_len;
1714 2577 : fd_memcpy( rodata, ro_section, buf_len );
1715 2577 : }
1716 :
1717 2577 : return FD_SBPF_ELF_SUCCESS;
1718 2577 : }
1719 :
1720 : /* Applies ELF relocations in-place. Returns 0 on success and an
1721 : ElfError error code on failure.
1722 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L990-L1331 */
1723 : static int
1724 : fd_sbpf_program_relocate( fd_sbpf_program_t * prog,
1725 : void const * bin,
1726 : ulong bin_sz,
1727 : fd_sbpf_loader_config_t const * config,
1728 2577 : fd_sbpf_loader_t * loader ) {
1729 2577 : fd_sbpf_elf_info_t const * elf_info = &prog->info;
1730 2577 : fd_sbpf_elf_t const * elf = (fd_sbpf_elf_t const *)bin;
1731 2577 : uchar * rodata = prog->rodata;
1732 2577 : fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
1733 2577 : fd_elf64_shdr const * shtext = &shdrs[ elf_info->shndx_text ];
1734 :
1735 : /* Copy rodata segment */
1736 2577 : fd_memcpy( rodata, elf->bin, elf_info->bin_sz );
1737 :
1738 : /* Fixup all program counter relative call instructions
1739 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1005-L1041 */
1740 2577 : {
1741 : /* Validate the bytes range of the text section.
1742 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1006-L1008 */
1743 2577 : fd_sbpf_range_t text_section_range;
1744 2577 : fd_shdr_get_file_range( shtext, &text_section_range );
1745 :
1746 2577 : ulong insn_cnt = (text_section_range.hi-text_section_range.lo)/8UL;
1747 2577 : if( FD_UNLIKELY( shtext->sh_size+shtext->sh_offset>bin_sz ) ) {
1748 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1749 0 : }
1750 :
1751 2577 : uchar * ptr = rodata + shtext->sh_offset;
1752 :
1753 365454 : for( ulong i=0UL; i<insn_cnt; i++, ptr+=8UL ) {
1754 362877 : ulong insn = FD_LOAD( ulong, ptr );
1755 :
1756 : /* Check for call instruction. If immediate is UINT_MAX, assume
1757 : that compiler generated a relocation instead.
1758 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1015 */
1759 362877 : ulong opc = insn & 0xFF;
1760 362877 : int imm = (int)(insn >> 32UL);
1761 362877 : if( (opc!=FD_SBPF_OP_CALL_IMM) || (imm==-1) ) continue;
1762 :
1763 : /* Calculate and check the target PC
1764 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1016-L1021 */
1765 12150 : long target_pc = fd_long_sat_add( fd_long_sat_add( (long)i, 1L ), imm);
1766 12150 : if( FD_UNLIKELY( target_pc<0L || target_pc>=(long)insn_cnt ) ) {
1767 0 : return FD_SBPF_ELF_ERR_RELATIVE_JUMP_OUT_OF_BOUNDS;
1768 0 : }
1769 :
1770 : /* Update the calldests
1771 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1027-L1032 */
1772 12150 : uint pc_hash;
1773 12150 : int err = fd_sbpf_register_function_hashed_legacy( loader, prog, NULL, 0UL, (ulong)target_pc, &pc_hash );
1774 12150 : if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
1775 0 : return err;
1776 0 : }
1777 :
1778 : /* Store PC hash in text section. Check for writes outside the
1779 : text section.
1780 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1034-L1038 */
1781 12150 : ulong offset = fd_ulong_sat_add( fd_ulong_sat_mul( i, 8UL ), 4UL ); // offset in text section
1782 12150 : if( FD_UNLIKELY( offset+4UL>shtext->sh_size ) ) {
1783 0 : return FD_SBPF_ELF_ERR_VALUE_OUT_OF_BOUNDS;
1784 0 : }
1785 :
1786 12150 : FD_STORE( uint, ptr+4UL, pc_hash );
1787 12150 : }
1788 2577 : }
1789 :
1790 : /* Fixup all the relocations in the relocation section if exists. The
1791 : dynamic relocations table was already parsed and validated in
1792 : fd_sbpf_lenient_elf_parse().
1793 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1046-L1304 */
1794 2577 : {
1795 2577 : fd_elf64_rel const * dt_rels = (fd_elf64_rel const *)( elf->bin + elf_info->dt_rel_off );
1796 2577 : uint dt_rel_cnt = elf_info->dt_rel_sz / sizeof(fd_elf64_rel);
1797 :
1798 22947 : for( uint i=0U; i<dt_rel_cnt; i++ ) {
1799 20370 : fd_elf64_rel const * dt_rel = &dt_rels[ i ];
1800 20370 : ulong r_offset = dt_rel->r_offset;
1801 :
1802 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L1068-L1303 */
1803 20370 : int err;
1804 20370 : switch( FD_ELF64_R_TYPE( dt_rel->r_info ) ) {
1805 6 : case FD_ELF_R_BPF_64_64:
1806 6 : err = fd_sbpf_r_bpf_64_64( elf, bin_sz, rodata, elf_info, dt_rel, r_offset );
1807 6 : break;
1808 15294 : case FD_ELF_R_BPF_64_RELATIVE:
1809 15294 : err = fd_sbpf_r_bpf_64_relative(elf, bin_sz, rodata, elf_info, r_offset );
1810 15294 : break;
1811 5070 : case FD_ELF_R_BPF_64_32:
1812 5070 : err = fd_sbpf_r_bpf_64_32( loader, prog, elf, bin_sz, rodata, elf_info, dt_rel, r_offset, config );
1813 5070 : break;
1814 0 : default:
1815 0 : return FD_SBPF_ELF_ERR_UNKNOWN_RELOCATION;
1816 20370 : }
1817 :
1818 20370 : if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
1819 0 : return err;
1820 0 : }
1821 20370 : }
1822 2577 : }
1823 :
1824 : /* ...rest of this function is a no-op because
1825 : enable_symbol_and_section_labels is disabled in production. */
1826 :
1827 2577 : return FD_SBPF_ELF_SUCCESS;
1828 2577 : }
1829 :
1830 : /* Second part of load_with_lenient_parser().
1831 :
1832 : This function is responsible for "loading" an sBPF program. This
1833 : means...
1834 : 1. Applies any relocations in-place to the rodata section.
1835 : 2. Registers the program entrypoint and other valid calldests.
1836 : 3. Parses and validates the rodata sections, zeroing out any gaps
1837 : between sections.
1838 :
1839 : Returns 0 on success and an ElfError error code on failure.
1840 :
1841 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L640-L689
1842 : */
1843 : static int
1844 : fd_sbpf_program_load_lenient( fd_sbpf_program_t * prog,
1845 : void const * bin,
1846 : ulong bin_sz,
1847 : fd_sbpf_loader_t * loader,
1848 : fd_sbpf_loader_config_t const * config,
1849 : void * scratch,
1850 2577 : ulong scratch_sz ) {
1851 :
1852 : /* Load (vs peek) starts here
1853 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L641 */
1854 :
1855 2577 : fd_sbpf_elf_t const * elf = (fd_sbpf_elf_t const *)bin;
1856 2577 : fd_sbpf_elf_info_t * elf_info = &prog->info;
1857 2577 : fd_elf64_shdr const * shdrs = (fd_elf64_shdr const *)( elf->bin + elf->ehdr.e_shoff );
1858 2577 : fd_elf64_shdr const * sh_text = &shdrs[ elf_info->shndx_text ];
1859 :
1860 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L642-L647 */
1861 2577 : int err = fd_sbpf_program_relocate( prog, bin, bin_sz, config, loader );
1862 2577 : if( FD_UNLIKELY( err ) ) return err;
1863 :
1864 : /* https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L649-L653 */
1865 2577 : ulong offset = fd_ulong_sat_sub( elf->ehdr.e_entry, sh_text->sh_addr );
1866 2577 : if( FD_UNLIKELY( offset&0x7UL ) ) { /* offset % 8 != 0 */
1867 0 : return FD_SBPF_ELF_ERR_INVALID_ENTRYPOINT;
1868 0 : }
1869 :
1870 : /* Unregister the entrypoint from the calldests, and register the
1871 : entry_pc. Our behavior slightly diverges from Agave's because we
1872 : rely on an explicit entry_pc field within the elf_info struct
1873 : to handle the b"entrypoint" symbol, and rely on PC hash inverses
1874 : for any other CALL_IMM targets.
1875 :
1876 : Note that even though we won't use the calldests value for the
1877 : entry pc, we still need to "register" it to check for any potential
1878 : symbol collisions and report errors accordingly. We unregister it
1879 : first by setting it to ULONG_MAX.
1880 :
1881 : TODO: Add special casing for static syscalls enabled. For now, it
1882 : is not implemented.
1883 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L654-L667 */
1884 2577 : prog->entry_pc = ULONG_MAX;
1885 2577 : ulong entry_pc = offset/8UL;
1886 2577 : err = fd_sbpf_register_function_hashed_legacy(
1887 2577 : loader,
1888 2577 : prog,
1889 2577 : (uchar const *)"entrypoint",
1890 2577 : strlen( "entrypoint" ),
1891 2577 : entry_pc,
1892 2577 : NULL );
1893 2577 : if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
1894 0 : return err;
1895 0 : }
1896 :
1897 : /* Parse the ro sections.
1898 : https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L669-L676 */
1899 2577 : err = fd_sbpf_parse_ro_sections( prog, bin, bin_sz, config, scratch, scratch_sz );
1900 2577 : if( FD_UNLIKELY( err!=FD_SBPF_ELF_SUCCESS ) ) {
1901 0 : return err;
1902 0 : }
1903 :
1904 2577 : return FD_SBPF_ELF_SUCCESS;
1905 2577 : }
1906 :
1907 : /* Strict ELF loading (for SBPF V3+ programs).
1908 :
1909 : SBPF V3+ programs do not require relocations or calldests, so this
1910 : function is much cheaper than fd_sbpf_program_load_lenient.
1911 :
1912 : https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L406-L590 */
1913 : static int
1914 : fd_sbpf_program_load_strict( fd_sbpf_program_t * prog,
1915 12 : void const * bin ) {
1916 12 : fd_elf64_ehdr ehdr = FD_LOAD( fd_elf64_ehdr, bin );
1917 12 : fd_elf64_phdr phdr_0 = FD_LOAD( fd_elf64_phdr, bin+sizeof(fd_elf64_ehdr) );
1918 12 : int skip_rodata = phdr_0.p_flags != FD_SBPF_PF_R;
1919 :
1920 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L486-L496 */
1921 12 : fd_elf64_phdr bytecode_phdr;
1922 12 : if( FD_UNLIKELY( skip_rodata ) ) {
1923 3 : prog->rodata_sz = 0UL;
1924 3 : bytecode_phdr = phdr_0;
1925 9 : } else {
1926 9 : prog->rodata_sz = phdr_0.p_memsz;
1927 9 : bytecode_phdr = FD_LOAD( fd_elf64_phdr, bin+sizeof(fd_elf64_ehdr)+sizeof(fd_elf64_phdr) );
1928 :
1929 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L493
1930 : https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L497 */
1931 9 : fd_memcpy( prog->rodata, (uchar const *)bin + phdr_0.p_offset, phdr_0.p_filesz );
1932 9 : }
1933 :
1934 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L498-L499 */
1935 12 : prog->text = (ulong *)( (uchar *)prog->rodata + prog->rodata_sz );
1936 12 : fd_memcpy( (uchar *)prog->text, (uchar const *)bin + bytecode_phdr.p_offset, bytecode_phdr.p_filesz );
1937 :
1938 : /* https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L510-L514 */
1939 12 : prog->entry_pc = fd_ulong_sat_sub( ehdr.e_entry, bytecode_phdr.p_vaddr ) / 8UL;
1940 12 : return FD_SBPF_ELF_SUCCESS;
1941 12 : }
1942 :
1943 : int
1944 : fd_sbpf_program_load( fd_sbpf_program_t * prog,
1945 : void const * bin,
1946 : ulong bin_sz,
1947 : fd_sbpf_syscalls_t * syscalls,
1948 : fd_sbpf_loader_config_t const * config,
1949 : void * scratch,
1950 2589 : ulong scratch_sz ) {
1951 2589 : fd_sbpf_loader_t loader = {
1952 2589 : .calldests = prog->calldests,
1953 2589 : .syscalls = syscalls,
1954 2589 : };
1955 :
1956 : /* Invoke strict vs lenient loader
1957 : Note: info.sbpf_version is already set by fd_sbpf_program_parse()
1958 : https://github.com/anza-xyz/sbpf/blob/v0.14.4/src/elf.rs#L396-L402 */
1959 2589 : if( FD_UNLIKELY( fd_sbpf_enable_stricter_elf_headers_enabled( prog->info.sbpf_version ) ) ) {
1960 12 : return fd_sbpf_program_load_strict( prog, bin );
1961 12 : }
1962 2577 : return fd_sbpf_program_load_lenient( prog, bin, bin_sz, &loader, config, scratch, scratch_sz );
1963 2589 : }
1964 :
1965 : #undef ERR
1966 : #undef FAIL
1967 : #undef REQUIRE
|