LCOV - code coverage report
Current view: top level - bpfilter/cgen - elfstub.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 85.4 % 96 82
Test Date: 2025-06-20 13:16:12 Functions: 100.0 % 5 5

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

Generated by: LCOV version 2.0-1