LCOV - code coverage report
Current view: top level - waltz/ebpf - fd_ebpf.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 147 150 98.0 %
Date: 2024-11-13 11:58:15 Functions: 1 1 100.0 %

          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 : }

Generated by: LCOV version 1.14