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