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