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