LCOV - code coverage report
Current view: top level - bpfilter/cgen - elfstub.c (source / functions) Coverage Total Hit
Test: coverage.lcov Lines: 86.2 % 87 75
Test Date: 2025-11-24 12:34:34 Functions: 100.0 % 4 4
Branches: 46.8 % 94 44

             Branch data     Line data    Source code
       1                 :             : /* SPDX-License-Identifier: GPL-2.0-only */
       2                 :             : /*
       3                 :             :  * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "cgen/elfstub.h"
       7                 :             : 
       8                 :             : #include <linux/bpf.h>
       9                 :             : 
      10                 :             : #include <elf.h>
      11                 :             : #include <errno.h>
      12                 :             : 
      13                 :             : #include <bpfilter/btf.h>
      14                 :             : #include <bpfilter/helper.h>
      15                 :             : #include <bpfilter/logger.h>
      16                 :             : 
      17                 :             : #include "cgen/rawstubs.h"
      18                 :             : 
      19                 :             : static_assert(ARRAY_SIZE(_bf_rawstubs) == _BF_ELFSTUB_MAX,
      20                 :             :               "_bf_rawstubs doesn't contain as many entries as bf_elfstub_id");
      21                 :             : 
      22                 :             : #define _free_bf_printk_str_ __attribute__((cleanup(_bf_printk_str_free)))
      23                 :             : 
      24                 :             : static int _bf_printk_str_new(struct bf_printk_str **pstr, size_t insn_idx,
      25                 :             :                               const char *str)
      26                 :             : {
      27                 :             :     bf_assert(pstr && str);
      28                 :             : 
      29                 :          40 :     *pstr = malloc(sizeof(struct bf_printk_str));
      30                 :          40 :     if (!*pstr)
      31                 :             :         return -ENOMEM;
      32                 :             : 
      33                 :          40 :     (*pstr)->insn_idx = insn_idx;
      34                 :          40 :     (*pstr)->str = str;
      35                 :             : 
      36                 :             :     return 0;
      37                 :             : }
      38                 :             : 
      39                 :          38 : static void _bf_printk_str_free(struct bf_printk_str **pstr)
      40                 :             : {
      41   [ -  -  +  - ]:          38 :     if (!*pstr)
      42                 :             :         return;
      43                 :             : 
      44                 :             :     freep((void *)pstr);
      45                 :             : }
      46                 :             : 
      47                 :          80 : static int _bf_elfstub_prepare(struct bf_elfstub *stub,
      48                 :             :                                const struct bf_rawstub *raw)
      49                 :             : {
      50                 :          80 :     const Elf64_Ehdr *ehdr = raw->elf;
      51                 :             :     Elf64_Shdr *shstrtab;
      52                 :             :     Elf64_Shdr *shdrs;
      53                 :             :     Elf64_Shdr *symtab = NULL;
      54                 :             :     Elf64_Shdr *symstrtab = NULL;
      55                 :             :     Elf64_Shdr *rodata_shdr = NULL;
      56                 :             :     Elf64_Sym *symbols;
      57                 :             :     char *sym_strtab;
      58                 :             :     size_t sym_count;
      59                 :             :     char *strtab;
      60                 :             :     int r;
      61                 :             : 
      62         [ -  + ]:          80 :     if (raw->len < sizeof(Elf64_Ehdr))
      63         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "invalid ELF header (wrong header size)");
      64                 :             : 
      65                 :             :     // Check ELF magic
      66         [ -  + ]:          80 :     if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
      67         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "invalid ELF header (wrong magic number)");
      68                 :             : 
      69                 :             :     // Ensure 64-bit ELF
      70         [ -  + ]:          80 :     if (ehdr->e_ident[EI_CLASS] != ELFCLASS64)
      71         [ #  # ]:           0 :         return bf_err_r(-ENOTSUP, "only 64-bit ELF is supported");
      72                 :             : 
      73                 :             :     // Get section header table
      74   [ +  -  -  + ]:          80 :     if (ehdr->e_shoff >= raw->len || ehdr->e_shentsize != sizeof(Elf64_Shdr))
      75         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "invalid section header table");
      76                 :             : 
      77         [ -  + ]:          80 :     if (ehdr->e_shstrndx >= ehdr->e_shnum)
      78         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "invalid string table index");
      79                 :             : 
      80                 :          80 :     shdrs = (Elf64_Shdr *)(raw->elf + ehdr->e_shoff);
      81                 :          80 :     shstrtab = &shdrs[ehdr->e_shstrndx];
      82         [ -  + ]:          80 :     if (shstrtab->sh_offset >= raw->len)
      83         [ #  # ]:           0 :         return bf_err_r(-EINVAL, "invalid string table offset");
      84                 :             : 
      85                 :          80 :     strtab = (char *)(raw->elf + shstrtab->sh_offset);
      86                 :             : 
      87                 :             :     // Find .text section
      88         [ +  - ]:         240 :     for (int i = 0; i < ehdr->e_shnum; i++) {
      89         [ +  + ]:         240 :         if (!bf_streq(&strtab[shdrs[i].sh_name], ".text"))
      90                 :             :             continue;
      91                 :             : 
      92         [ -  + ]:          80 :         if (shdrs[i].sh_offset + shdrs[i].sh_size > raw->len)
      93         [ #  # ]:           0 :             return bf_err_r(-EINVAL, "invalid .text section");
      94                 :             : 
      95                 :          80 :         stub->insns = malloc(shdrs[i].sh_size);
      96         [ +  - ]:          80 :         if (!stub->insns)
      97                 :             :             return -ENOMEM;
      98                 :             : 
      99                 :          80 :         memcpy(stub->insns, raw->elf + shdrs[i].sh_offset, shdrs[i].sh_size);
     100                 :          80 :         stub->ninsns = shdrs[i].sh_size / sizeof(struct bpf_insn);
     101                 :          80 :         break;
     102                 :             :     }
     103                 :             : 
     104         [ -  + ]:          80 :     if (!stub->insns)
     105         [ #  # ]:           0 :         return bf_err_r(-ENOENT, ".text section not found");
     106                 :             : 
     107                 :             :     // Find symbol table and its string table
     108         [ +  + ]:        2040 :     for (int i = 0; i < ehdr->e_shnum; i++) {
     109         [ +  + ]:        1960 :         if (shdrs[i].sh_type == SHT_SYMTAB) {
     110                 :             :             symtab = &shdrs[i];
     111         [ +  - ]:          80 :             if (shdrs[i].sh_link < ehdr->e_shnum)
     112                 :          80 :                 symstrtab = &shdrs[shdrs[i].sh_link];
     113         [ +  + ]:        1880 :         } else if (bf_streq(&strtab[shdrs[i].sh_name], ".rodata")) {
     114                 :             :             rodata_shdr = &shdrs[i];
     115                 :             :         }
     116                 :             :     }
     117                 :             : 
     118         [ -  + ]:          80 :     if (!symtab || !symstrtab)
     119         [ #  # ]:           0 :         return bf_err_r(-ENOENT, "symbol table not found");
     120                 :             : 
     121                 :          80 :     symbols = (Elf64_Sym *)(raw->elf + symtab->sh_offset);
     122                 :          80 :     sym_strtab = (char *)(raw->elf + symstrtab->sh_offset);
     123                 :          80 :     sym_count = symtab->sh_size / sizeof(Elf64_Sym);
     124                 :             : 
     125                 :             :     // Process REL relocation sections
     126         [ +  + ]:        2040 :     for (int i = 0; i < ehdr->e_shnum; i++) {
     127         [ +  + ]:        1960 :         if (shdrs[i].sh_type != SHT_REL)
     128                 :        1360 :             continue;
     129                 :             : 
     130                 :             :         // Check if this relocation section applies to .text
     131         [ +  - ]:         600 :         if (shdrs[i].sh_info != 0) {
     132                 :             :             // sh_info contains the section index this relocation applies to
     133         [ +  + ]:         600 :             if (!bf_streq(&strtab[shdrs[shdrs[i].sh_info].sh_name], ".text"))
     134                 :         520 :                 continue;
     135                 :             :         }
     136                 :             : 
     137                 :             :         // REL relocations (no addend)
     138                 :          80 :         Elf64_Rel *rels = (Elf64_Rel *)(raw->elf + shdrs[i].sh_offset);
     139                 :          80 :         size_t rel_count = shdrs[i].sh_size / sizeof(Elf64_Rel);
     140                 :             : 
     141         [ +  + ]:         160 :         for (size_t j = 0; j < rel_count; j++) {
     142                 :          80 :             uint32_t type = ELF64_R_TYPE(rels[j].r_info);
     143                 :          80 :             uint32_t sym_idx = ELF64_R_SYM(rels[j].r_info);
     144                 :             : 
     145   [ +  +  +  - ]:          80 :             if (type == R_BPF_64_32 && sym_idx < sym_count) {
     146                 :          40 :                 uint32_t name_idx = symbols[sym_idx].st_name;
     147         [ +  - ]:          40 :                 if (name_idx < symstrtab->sh_size) {
     148                 :          40 :                     const char *name = &sym_strtab[name_idx];
     149                 :          40 :                     int id = bf_btf_get_id(name);
     150         [ -  + ]:          40 :                     if (id < 0)
     151         [ #  # ]:           0 :                         return bf_err_r(id, "function %s not found", name);
     152                 :             : 
     153                 :          40 :                     size_t idx = rels[j].r_offset / 8;
     154                 :          40 :                     stub->insns[idx] =
     155                 :             :                         ((struct bpf_insn) {.code = BPF_JMP | BPF_CALL,
     156                 :             :                                             .dst_reg = 0,
     157                 :             :                                             .src_reg = BPF_PSEUDO_KFUNC_CALL,
     158                 :             :                                             .off = 0,
     159                 :             :                                             .imm = id});
     160                 :             : 
     161         [ +  - ]:          40 :                     bf_dbg("updated stub to call '%s' from instruction %lu",
     162                 :             :                            name, idx);
     163                 :             :                 }
     164         [ +  - ]:          40 :             } else if (type == R_BPF_64_64 && rodata_shdr) {
     165                 :             :                 _free_bf_printk_str_ struct bf_printk_str *pstr = NULL;
     166                 :          40 :                 size_t insn_idx = rels[j].r_offset / 8;
     167                 :          40 :                 size_t str_offset = stub->insns[insn_idx].imm;
     168                 :          40 :                 const char *str =
     169         [ -  + ]:          40 :                     raw->elf + rodata_shdr->sh_offset + str_offset;
     170                 :             : 
     171                 :             :                 r = _bf_printk_str_new(&pstr, insn_idx, str);
     172                 :             :                 if (r)
     173         [ #  # ]:           0 :                     return bf_err_r(r, "failed to create printk_str");
     174                 :             : 
     175                 :          40 :                 r = bf_list_add_tail(&stub->strs, pstr);
     176         [ -  + ]:          40 :                 if (r)
     177         [ #  # ]:           0 :                     return bf_err_r(r, "failed to add printk_str to elfstub");
     178                 :             : 
     179                 :             :                 TAKE_PTR(pstr);
     180                 :             :             }
     181                 :             :         }
     182                 :             :     }
     183                 :             : 
     184                 :             :     return 0;
     185                 :             : }
     186                 :             : 
     187                 :          80 : int bf_elfstub_new(struct bf_elfstub **stub, enum bf_elfstub_id id)
     188                 :             : {
     189                 :          80 :     _free_bf_elfstub_ struct bf_elfstub *_stub = NULL;
     190                 :             :     int r;
     191                 :             : 
     192                 :             :     bf_assert(stub);
     193                 :             : 
     194                 :          80 :     _stub = calloc(1, sizeof(*_stub));
     195         [ +  - ]:          80 :     if (!_stub)
     196                 :             :         return -ENOMEM;
     197                 :             : 
     198                 :          80 :     _stub->strs = bf_list_default(_bf_printk_str_free, NULL);
     199                 :             : 
     200                 :          80 :     r = _bf_elfstub_prepare(_stub, &_bf_rawstubs[id]);
     201         [ +  - ]:          80 :     if (r)
     202                 :             :         return r;
     203                 :             : 
     204                 :          80 :     *stub = TAKE_PTR(_stub);
     205                 :             : 
     206                 :          80 :     return 0;
     207                 :             : }
     208                 :             : 
     209                 :         156 : void bf_elfstub_free(struct bf_elfstub **stub)
     210                 :             : {
     211                 :             :     bf_assert(stub);
     212                 :             : 
     213         [ +  + ]:         156 :     if (!*stub)
     214                 :             :         return;
     215                 :             : 
     216                 :          76 :     bf_list_clean(&(*stub)->strs);
     217                 :          76 :     freep((void *)&(*stub)->insns);
     218                 :             :     freep((void *)stub);
     219                 :             : }
        

Generated by: LCOV version 2.0-1