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

Generated by: LCOV version 2.0-1