Line data Source code
1 : #define _DEFAULT_SOURCE 2 : #include "fd_ebpf.h" 3 : 4 : #include "../../ballet/elf/fd_elf.h" 5 : #include "../../util/fd_util.h" 6 : 7 : #define FD_EBPF_MAX_SYM_CNT (32UL) 8 : 9 : struct __attribute__((aligned(16UL))) fd_ebpf_known_sym { 10 : ulong value; 11 : uchar known; 12 : }; 13 : typedef struct fd_ebpf_known_sym fd_ebpf_known_sym_t; 14 : 15 : fd_ebpf_link_opts_t * 16 : fd_ebpf_static_link( fd_ebpf_link_opts_t * const opts, 17 : void * const elf, 18 6 : ulong const elf_sz ) { 19 : 20 330 : # define FD_ELF_REQUIRE(c) do { if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return NULL; } } while(0) 21 : 22 6 : FD_ELF_REQUIRE( opts->section ); 23 6 : FD_ELF_REQUIRE( opts->sym_cnt==0 || opts->sym ); 24 6 : FD_ELF_REQUIRE( opts ); 25 6 : FD_ELF_REQUIRE( elf ); 26 6 : FD_ELF_REQUIRE( fd_ulong_is_aligned( (ulong)elf, 8UL ) ); 27 : 28 : /* Load file header */ 29 : 30 6 : FD_ELF_REQUIRE( elf_sz>=sizeof(fd_elf64_ehdr) ); 31 6 : fd_elf64_ehdr eh[1]; memcpy( eh, elf, sizeof(fd_elf64_ehdr) ); 32 : 33 6 : FD_ELF_REQUIRE( eh->e_type == FD_ELF_ET_REL ); 34 : 35 : /* Check file type */ 36 : 37 6 : FD_ELF_REQUIRE( fd_uint_load_4( eh->e_ident ) == 0x464c457fU ); 38 : 39 6 : FD_ELF_REQUIRE( eh->e_ident[ FD_ELF_EI_CLASS ] == FD_ELF_CLASS_64 ); 40 6 : FD_ELF_REQUIRE( eh->e_ident[ FD_ELF_EI_DATA ] == FD_ELF_DATA_LE ); 41 6 : FD_ELF_REQUIRE( eh->e_ident[ FD_ELF_EI_VERSION ] == 1 ); 42 : 43 6 : FD_ELF_REQUIRE( eh->e_type == FD_ELF_ET_REL ); 44 6 : FD_ELF_REQUIRE( eh->e_machine == FD_ELF_EM_BPF ); 45 : 46 : /* Load section header table */ 47 : 48 6 : FD_ELF_REQUIRE( eh->e_shentsize == sizeof(fd_elf64_shdr) ); 49 : 50 6 : FD_ELF_REQUIRE( eh->e_shoff < elf_sz ); 51 6 : FD_ELF_REQUIRE( eh->e_shnum < 128 ); 52 6 : ulong shoff_end = eh->e_shoff + eh->e_shnum*sizeof(fd_elf64_shdr); 53 6 : FD_ELF_REQUIRE( shoff_end <= elf_sz ); 54 : 55 6 : fd_elf64_shdr const * shdr = (fd_elf64_shdr *)( (ulong)elf + eh->e_shoff ); 56 : 57 : /* Load section header string table */ 58 : 59 6 : FD_ELF_REQUIRE( eh->e_shstrndx < eh->e_shnum ); 60 6 : fd_elf64_shdr const * shstrtab = &shdr[ eh->e_shstrndx ]; 61 : 62 : /* Walk section header table */ 63 : 64 6 : fd_elf64_shdr prog [1] = {0}; long prog_shndx = -1L; 65 6 : fd_elf64_shdr rel_prog[1] = {0}; long rel_prog_shndx = -1L; 66 6 : fd_elf64_shdr symtab [1] = {0}; long symtab_shndx = -1L; 67 6 : fd_elf64_shdr strtab [1] = {0}; long strtab_shndx = -1L; 68 : 69 54 : for( uint i=0; i < eh->e_shnum; i++ ) { 70 48 : ulong sh_name_off = shstrtab->sh_offset + shdr[ i ].sh_name; 71 48 : char const * sh_name = fd_elf_read_cstr( elf, elf_sz, sh_name_off, 128UL ); 72 48 : if( !sh_name ) continue; 73 : 74 48 : switch( shdr[ i ].sh_type ) { 75 18 : case FD_ELF_SHT_PROGBITS: 76 18 : if( 0==strcmp( sh_name, opts->section ) ) { 77 6 : prog_shndx = i; 78 6 : *prog = shdr[ i ]; 79 6 : } 80 18 : break; 81 6 : case FD_ELF_SHT_REL: 82 6 : if( 0==strncmp( sh_name, ".rel", 4UL ) && 83 6 : 0== strcmp( sh_name+4, opts->section ) ) { 84 6 : rel_prog_shndx = i; 85 6 : *rel_prog = shdr[ i ]; 86 6 : } 87 6 : break; 88 6 : case FD_ELF_SHT_SYMTAB: 89 6 : if( 0==strcmp( sh_name, ".symtab" ) ) { 90 6 : symtab_shndx = i; 91 6 : *symtab = shdr[ i ]; 92 6 : } 93 6 : break; 94 6 : case FD_ELF_SHT_STRTAB: 95 6 : if( 0==strcmp( sh_name, ".strtab" ) ) { 96 6 : strtab_shndx = i; 97 6 : *strtab = shdr[ i ]; 98 6 : } 99 6 : break; 100 12 : default: 101 12 : continue; 102 48 : } 103 48 : } 104 : 105 6 : FD_ELF_REQUIRE( prog_shndx >=0 ); 106 6 : FD_ELF_REQUIRE( rel_prog_shndx>=0 ); 107 6 : FD_ELF_REQUIRE( symtab_shndx >=0 ); 108 6 : FD_ELF_REQUIRE( strtab_shndx >=0 ); 109 : 110 : /* Load bytecode */ 111 : 112 6 : FD_ELF_REQUIRE( prog->sh_offset <=elf_sz ); 113 6 : FD_ELF_REQUIRE( prog->sh_size <=elf_sz ); 114 6 : FD_ELF_REQUIRE( prog->sh_offset + prog->sh_size <=elf_sz ); 115 6 : ulong * code = (ulong *)( (ulong)elf + prog->sh_offset ); 116 6 : FD_ELF_REQUIRE( fd_ulong_is_aligned( (ulong)code, 8UL ) ); 117 : 118 : /* Load symbol table */ 119 : 120 6 : FD_ELF_REQUIRE( symtab->sh_entsize == sizeof(fd_elf64_sym) ); 121 6 : FD_ELF_REQUIRE( symtab->sh_offset <=elf_sz ); 122 6 : FD_ELF_REQUIRE( symtab->sh_size <=elf_sz ); 123 6 : FD_ELF_REQUIRE( symtab->sh_offset + symtab->sh_size <=elf_sz ); 124 : 125 6 : ulong sym_cnt = symtab->sh_size / sizeof(fd_elf64_sym); 126 6 : fd_elf64_sym const * sym = (fd_elf64_sym *)( (ulong)elf + symtab->sh_offset ); 127 6 : FD_ELF_REQUIRE( sym_cnt <= FD_EBPF_MAX_SYM_CNT ); 128 : 129 : /* Load string table */ 130 : 131 6 : FD_ELF_REQUIRE( strtab->sh_offset <=elf_sz ); 132 6 : FD_ELF_REQUIRE( strtab->sh_size <=elf_sz ); 133 6 : FD_ELF_REQUIRE( strtab->sh_offset + strtab->sh_size <=elf_sz ); 134 6 : FD_ELF_REQUIRE( symtab->sh_link == strtab_shndx ); 135 : 136 : /* Load relocation table */ 137 : 138 6 : FD_ELF_REQUIRE( rel_prog->sh_entsize == sizeof(fd_elf64_rel) ); 139 6 : FD_ELF_REQUIRE( rel_prog->sh_offset <=elf_sz ); 140 6 : FD_ELF_REQUIRE( rel_prog->sh_size <=elf_sz ); 141 6 : FD_ELF_REQUIRE( rel_prog->sh_offset + rel_prog->sh_size <=elf_sz ); 142 6 : FD_ELF_REQUIRE( rel_prog->sh_link == symtab_shndx ); 143 6 : FD_ELF_REQUIRE( rel_prog->sh_info == prog_shndx ); 144 : 145 6 : ulong rel_cnt = rel_prog->sh_size / sizeof(fd_elf64_rel); 146 6 : fd_elf64_rel const * rel = (fd_elf64_rel *)( (ulong)elf + rel_prog->sh_offset ); 147 : 148 : /* Create symbol mapping table */ 149 : 150 6 : fd_ebpf_known_sym_t sym_mapping[ FD_EBPF_MAX_SYM_CNT ]; 151 : 152 : /* Walk symbol table */ 153 : 154 48 : for( ulong i=0; i<sym_cnt; i++ ) { 155 42 : sym_mapping[ i ] = (fd_ebpf_known_sym_t){ .value = 0UL, .known = 0U }; 156 : 157 42 : char const * sym_name = fd_elf_read_cstr( elf, elf_sz, strtab->sh_offset + sym[ i ].st_name, 128UL ); 158 42 : if( !sym_name ) continue; 159 : 160 : /* TODO: O(n^2) complexity -- fine for now as factors are small */ 161 : 162 108 : for( ulong j=0; j<opts->sym_cnt; j++ ) { 163 78 : if( 0==strcmp( sym_name, opts->sym[ j ].name ) ) { 164 12 : sym_mapping[ i ] = (fd_ebpf_known_sym_t) { 165 12 : .known = 1, 166 12 : .value = (ulong)(uint)opts->sym[ j ].value 167 12 : }; 168 12 : break; /* next symbol */ 169 12 : } 170 78 : } 171 42 : } 172 : 173 : /* Apply relocations */ 174 : 175 18 : for( ulong i=0; i<rel_cnt; i++ ) { 176 12 : FD_ELF_REQUIRE( rel[ i ].r_offset < prog->sh_size ); 177 12 : FD_ELF_REQUIRE( rel[ i ].r_offset+8UL <=prog->sh_size ); 178 : 179 12 : ulong r_sym = FD_ELF64_R_SYM( rel[ i ].r_info ); 180 12 : ulong r_type = FD_ELF64_R_TYPE( rel[ i ].r_info ); 181 12 : FD_ELF_REQUIRE( r_sym < sym_cnt ); 182 : 183 12 : FD_ELF_REQUIRE( sym_mapping[ r_sym ].known ); 184 12 : ulong S = sym_mapping[ r_sym ].value; 185 : 186 : /* TODO another bounds check? */ 187 : 188 12 : switch( r_type ) { 189 12 : case FD_ELF_R_BPF_64_64: { 190 12 : ulong r_lo_off = prog->sh_offset + rel[ i ].r_offset + 4UL; 191 12 : ulong r_hi_off = prog->sh_offset + rel[ i ].r_offset + 12UL; 192 : 193 12 : FD_ELF_REQUIRE( fd_ulong_is_aligned( r_lo_off, 4UL ) ); 194 12 : FD_ELF_REQUIRE( fd_ulong_is_aligned( r_hi_off, 4UL ) ); 195 12 : FD_ELF_REQUIRE( r_hi_off+4UL <= elf_sz ); 196 12 : uchar * insn = (uchar *)elf + prog->sh_offset + rel[ i ].r_offset; 197 : 198 12 : ulong insn0_pre = FD_LOAD( ulong, insn+0 ); 199 12 : ulong insn1_pre = FD_LOAD( ulong, insn+8 ); 200 : 201 12 : ulong A = insn0_pre>>32; /* implicit addend */ 202 12 : ulong value = S + A; 203 : 204 12 : ulong insn0_post = ( (insn0_pre&0xFFFFFFFF) | (value<<32 ) ); 205 12 : ulong insn1_post = ( (insn1_pre&0xFFFFFFFF) | (value&0xFFFFFFFF00000000) ); 206 : 207 : /* FIXME: clang bug? relocations against eBPF map require 208 : src_reg==1 (BPF_PSEUDO_MAP_FD). This is obviously 209 : not the intended behavior of R_BPF_64_64, which just 210 : relocates the imm, not src_reg field. However, the 211 : eBPF code generated by clang has src_reg==0. */ 212 12 : insn0_post |= 0x1000; 213 : 214 12 : FD_STORE( ulong, insn+0, insn0_post ); 215 12 : FD_STORE( ulong, insn+8, insn1_post ); 216 : 217 12 : FD_LOG_DEBUG(( "reloc %lu at insn %lu\n" 218 12 : " S: %#lx\tA: %#lx\n" 219 12 : " pre: %016lx %016lx\n" 220 12 : " post: %016lx %016lx", 221 12 : i, rel[ i ].r_offset / 8UL, 222 12 : S, A, 223 12 : insn0_pre, insn1_pre, 224 12 : insn0_post, insn1_post )); 225 12 : break; 226 12 : } 227 0 : default: 228 0 : FD_LOG_WARNING(( "reloc %lu: Unsupported relocation type %#lx", i, r_type )); 229 0 : return NULL; 230 12 : } 231 12 : } 232 : 233 : /* Save bytecode slice */ 234 : 235 6 : opts->bpf = (void *)( (ulong)elf + prog->sh_offset ); 236 6 : opts->bpf_sz = fd_ulong_align_dn( prog->sh_size, 8UL ); 237 : 238 6 : return opts; 239 : 240 6 : # undef FD_ELF_REQUIRE 241 6 : }