]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/config/bpf/coreout.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / config / bpf / coreout.cc
1 /* BPF Compile Once - Run Everywhere (CO-RE) support.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 GCC is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3. If not see
18 <http://www.gnu.org/licenses/>. */
19
20 #define IN_TARGET_CODE 1
21
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "target.h"
26 #include "memmodel.h"
27 #include "tm_p.h"
28 #include "output.h"
29 #include "dwarf2asm.h"
30 #include "ctfc.h"
31 #include "btf.h"
32 #include "rtl.h"
33 #include "tree-pretty-print.h"
34
35 #include "coreout.h"
36
37 /* This file contains data structures and routines for construction and output
38 of BPF Compile Once - Run Everywhere (BPF CO-RE) information.
39
40 eBPF programs written in C usually include Linux kernel headers, so that
41 they may interact with kernel data structures in a useful way. This
42 intrudces two major portability issues:
43
44 1. Kernel data structures regularly change, with fields added, moved or
45 deleted between versions. An eBPF program cannot in general be expected
46 to run on any systems which does not share an identical kernel version to
47 the system on which it was compiled.
48
49 2. Included kernel headers (and used data structures) may be internal, not
50 exposed in an userspace API, and therefore target-specific. An eBPF
51 program compiled on an x86_64 machine will include x86_64 kernel headers.
52 The resulting program may not run well (or at all) in machines of
53 another architecture.
54
55 BPF CO-RE is designed to solve the first issue by leveraging the BPF loader
56 to adjust references to kernel data structures made by the program as-needed
57 according to versions of structures actually present on the host kernel.
58
59 To achieve this, additional information is placed in a ".BTF.ext" section.
60 This information tells the loader which references will require adjusting,
61 and how to perform each necessary adjustment.
62
63 For any access to a data structure which may require load-time adjustment,
64 the following information is recorded (making up a CO-RE relocation record):
65 - The BTF type ID of the outermost structure which is accessed.
66 - An access string encoding the accessed member via a series of member and
67 array indexes. These indexes are used to look up detailed BTF information
68 about the member.
69 - The offset of the appropriate instruction to patch in the BPF program.
70 - An integer specifying what kind of relocation to perform.
71
72 A CO-RE-capable BPF loader reads this information together with the BTF
73 information of the program, compares it against BTF information of the host
74 kernel, and determines the appropriate way to patch the specified
75 instruction.
76
77 Once all CO-RE relocations are resolved, the program is loaded and verified
78 as usual. The process can be summarized with the following diagram:
79
80 +------------+
81 | C compiler |
82 +-----+------+
83 | BPF + BTF + CO-RE relocations
84 v
85 +------------+
86 +--->| BPF loader |
87 | +-----+------+
88 | | BPF (adapted)
89 BTF | v
90 | +------------+
91 +----+ Kernel |
92 +------------+
93
94 Note that a single ELF object may contain multiple eBPF programs. As a
95 result, a single .BTF.ext section can contain CO-RE relocations for multiple
96 programs in distinct sections. */
97
98 /* Internal representation of a BPF CO-RE relocation record. */
99
100 typedef struct GTY (()) bpf_core_reloc {
101 unsigned int bpfcr_type; /* BTF type ID of container. */
102 unsigned int bpfcr_astr_off; /* Offset of access string in .BTF
103 string table. */
104 rtx_code_label * bpfcr_insn_label; /* RTX label attached to instruction
105 to patch. */
106 enum btf_core_reloc_kind bpfcr_kind; /* Kind of relocation to perform. */
107 } bpf_core_reloc_t;
108
109 typedef bpf_core_reloc_t * bpf_core_reloc_ref;
110
111 /* Internal representation of a CO-RE relocation (sub)section of the
112 .BTF.ext information. One such section is generated for each ELF section
113 in the output object having relocations that a BPF loader must resolve. */
114
115 typedef struct GTY (()) bpf_core_section {
116 /* Name of ELF section to which these CO-RE relocations apply. */
117 const char * name;
118
119 /* Offset of section name in .BTF string table. */
120 uint32_t name_offset;
121
122 /* Relocations in the section. */
123 vec <bpf_core_reloc_ref, va_gc> * GTY (()) relocs;
124 } bpf_core_section_t;
125
126 typedef bpf_core_section_t * bpf_core_section_ref;
127
128 /* BTF.ext debug info section. */
129
130 static GTY (()) section * btf_ext_info_section;
131
132 static int btf_ext_label_num;
133
134 #ifndef BTF_EXT_INFO_SECTION_NAME
135 #define BTF_EXT_INFO_SECTION_NAME ".BTF.ext"
136 #endif
137
138 #define BTF_EXT_INFO_SECTION_FLAGS (SECTION_DEBUG)
139
140 #define MAX_BTF_EXT_LABEL_BYTES 40
141
142 static char btf_ext_info_section_label[MAX_BTF_EXT_LABEL_BYTES];
143
144 #ifndef BTF_EXT_INFO_SECTION_LABEL
145 #define BTF_EXT_INFO_SECTION_LABEL "Lbtfext"
146 #endif
147
148 static GTY (()) vec<bpf_core_section_ref, va_gc> *bpf_core_sections;
149
150 struct GTY(()) bpf_core_extra {
151 const char *accessor_str;
152 tree type;
153 };
154 typedef struct bpf_core_extra *bpf_core_extra_ref;
155 static GTY(()) hash_map<bpf_core_reloc_ref, bpf_core_extra_ref> *bpf_comment_info;
156
157 /* Create a new BPF CO-RE relocation record, and add it to the appropriate
158 CO-RE section. */
159 void
160 bpf_core_reloc_add (const tree type, const char * section_name,
161 const char *accessor,
162 rtx_code_label *label,
163 enum btf_core_reloc_kind kind)
164 {
165 bpf_core_reloc_ref bpfcr = ggc_cleared_alloc<bpf_core_reloc_t> ();
166 bpf_core_extra_ref info = ggc_cleared_alloc<struct bpf_core_extra> ();
167 ctf_container_ref ctfc = ctf_get_tu_ctfc ();
168
169 /* Buffer the access string in the auxiliary strtab. */
170 ctf_add_string (ctfc, accessor, &(bpfcr->bpfcr_astr_off), CTF_AUX_STRTAB);
171 bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type));
172 bpfcr->bpfcr_insn_label = label;
173 bpfcr->bpfcr_kind = kind;
174
175 info->accessor_str = accessor;
176 info->type = type;
177 bpf_comment_info->put (bpfcr, info);
178
179 /* Add the CO-RE reloc to the appropriate section. */
180 bpf_core_section_ref sec;
181 int i;
182 FOR_EACH_VEC_ELT (*bpf_core_sections, i, sec)
183 if (strcmp (sec->name, section_name) == 0)
184 {
185 vec_safe_push (sec->relocs, bpfcr);
186 return;
187 }
188
189 /* If the CO-RE section does not yet exist, create it. */
190 sec = ggc_cleared_alloc<bpf_core_section_t> ();
191
192 ctf_add_string (ctfc, section_name, &sec->name_offset, CTF_AUX_STRTAB);
193 if (strcmp (section_name, ""))
194 ctfc->ctfc_aux_strlen += strlen (section_name) + 1;
195
196 sec->name = section_name;
197 vec_alloc (sec->relocs, 1);
198 vec_safe_push (sec->relocs, bpfcr);
199
200 vec_safe_push (bpf_core_sections, sec);
201 }
202
203 /* Return the 0-based index of the field NODE in its containing struct or union
204 type. */
205
206 int
207 bpf_core_get_sou_member_index (ctf_container_ref ctfc, const tree node)
208 {
209 if (TREE_CODE (node) == FIELD_DECL)
210 {
211 const tree container = DECL_CONTEXT (node);
212
213 /* Lookup the CTF type info for the containing type. */
214 dw_die_ref die = lookup_type_die (container);
215 if (die == NULL)
216 return -1;
217
218 ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
219 if (dtd == NULL)
220 return -1;
221
222 unsigned int kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
223 if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
224 return -1;
225
226 tree field = TYPE_FIELDS (container);
227 int i = 0;
228 ctf_dmdef_t * dmd;
229 for (dmd = dtd->dtd_u.dtu_members;
230 dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
231 {
232 bool field_has_btf = get_btf_id (dmd->dmd_type) <= BTF_MAX_TYPE;
233
234 if (field == node)
235 return field_has_btf ? i : -1;
236
237 if (field_has_btf)
238 i++;
239
240 field = DECL_CHAIN (field);
241 }
242 }
243 return -1;
244 }
245
246 /* Compute and output the header of a .BTF.ext debug info section. */
247
248 static void
249 output_btfext_header (void)
250 {
251 switch_to_section (btf_ext_info_section);
252 ASM_OUTPUT_LABEL (asm_out_file, btf_ext_info_section_label);
253
254 dw2_asm_output_data (2, BTF_MAGIC, "btf_magic");
255 dw2_asm_output_data (1, BTF_VERSION, "btfext_version");
256 dw2_asm_output_data (1, 0, "btfext_flags");
257 dw2_asm_output_data (4, sizeof (struct btf_ext_header), "btfext_hdr_len");
258
259 uint32_t func_info_off = 0, func_info_len = 0;
260 uint32_t line_info_off = 0, line_info_len = 0;
261 uint32_t core_relo_off = 0, core_relo_len = 0;
262
263 /* Header core_relo_len is the sum total length in bytes of all CO-RE
264 relocation sections, plus the 4 byte record size. */
265 size_t i;
266 bpf_core_section_ref sec;
267 core_relo_len += vec_safe_length (bpf_core_sections)
268 * sizeof (struct btf_ext_section_header);
269
270 FOR_EACH_VEC_ELT (*bpf_core_sections, i, sec)
271 core_relo_len +=
272 vec_safe_length (sec->relocs) * sizeof (struct btf_ext_reloc);
273
274 if (core_relo_len)
275 core_relo_len += sizeof (uint32_t);
276
277 dw2_asm_output_data (4, func_info_off, "func_info_offset");
278 dw2_asm_output_data (4, func_info_len, "func_info_len");
279
280 dw2_asm_output_data (4, line_info_off, "line_info_offset");
281 dw2_asm_output_data (4, line_info_len, "line_info_len");
282
283 dw2_asm_output_data (4, core_relo_off, "core_relo_offset");
284 dw2_asm_output_data (4, core_relo_len, "core_relo_len");
285 }
286
287 /* Output a single CO-RE relocation record. */
288
289 static void
290 output_asm_btfext_core_reloc (bpf_core_reloc_ref bpfcr)
291 {
292 bpf_core_extra_ref *info = bpf_comment_info->get (bpfcr);
293 gcc_assert (info != NULL);
294
295 bpfcr->bpfcr_astr_off += ctfc_get_strtab_len (ctf_get_tu_ctfc (),
296 CTF_STRTAB);
297
298 dw2_assemble_integer (4, gen_rtx_LABEL_REF (Pmode, bpfcr->bpfcr_insn_label));
299 fprintf (asm_out_file, "\t%s%s\n",
300 flag_debug_asm ? ASM_COMMENT_START : "",
301 (flag_debug_asm ? " bpfcr_insn" : ""));
302
303 /* Extract the pretty print for the type expression. */
304 pretty_printer pp;
305 dump_generic_node (&pp, (*info)->type, 0, TDF_VOPS|TDF_MEMSYMS|TDF_SLIM,
306 false);
307 char *str = xstrdup (pp_formatted_text (&pp));
308
309 dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type (%s)", str);
310 dw2_asm_output_data (4, bpfcr->bpfcr_astr_off, "bpfcr_astr_off (\"%s\")",
311 (*info)->accessor_str);
312 dw2_asm_output_data (4, bpfcr->bpfcr_kind, "bpfcr_kind");
313 }
314
315 /* Output all CO-RE relocation records for a section. */
316
317 static void
318 output_btfext_core_relocs (bpf_core_section_ref sec)
319 {
320 size_t i;
321 bpf_core_reloc_ref bpfcr;
322 FOR_EACH_VEC_ELT (*(sec->relocs), i, bpfcr)
323 output_asm_btfext_core_reloc (bpfcr);
324 }
325
326 /* Output all CO-RE relocation sections. */
327
328 static void
329 output_btfext_core_sections (void)
330 {
331 size_t i;
332 bpf_core_section_ref sec;
333
334 /* BTF Ext section info. */
335 dw2_asm_output_data (4, sizeof (struct btf_ext_reloc),
336 "btfext_core_info_rec_size");
337
338 FOR_EACH_VEC_ELT (*bpf_core_sections, i, sec)
339 {
340 /* Section name offset, refers to the offset of a string with the name of
341 the section to which these CORE relocations refer, e.g. '.text'.
342 The string is buffered in the BTF strings table. */
343
344 /* BTF specific strings are in CTF_AUX_STRTAB, which is concatenated
345 after CTF_STRTAB. Add the length of STRTAB to the final offset. */
346 sec->name_offset += ctfc_get_strtab_len (ctf_get_tu_ctfc (), CTF_STRTAB);
347
348 dw2_asm_output_data (4, sec->name_offset, "btfext_secinfo_sec_name_off");
349 dw2_asm_output_data (4, vec_safe_length (sec->relocs),
350 "btfext_secinfo_num_recs");
351
352 output_btfext_core_relocs (sec);
353 }
354 }
355
356 /* Initialize sections, labels, and data structures for BTF.ext output. */
357
358 void
359 btf_ext_init (void)
360 {
361 btf_ext_info_section = get_section (BTF_EXT_INFO_SECTION_NAME,
362 BTF_EXT_INFO_SECTION_FLAGS, NULL);
363
364 ASM_GENERATE_INTERNAL_LABEL (btf_ext_info_section_label,
365 BTF_EXT_INFO_SECTION_LABEL,
366 btf_ext_label_num++);
367
368 vec_alloc (bpf_core_sections, 1);
369 bpf_comment_info = hash_map<bpf_core_reloc_ref, bpf_core_extra_ref>::create_ggc ();
370 }
371
372 /* Output the entire .BTF.ext section. */
373
374 void
375 btf_ext_output (void)
376 {
377 output_btfext_header ();
378 output_btfext_core_sections ();
379
380 bpf_core_sections = NULL;
381 }
382
383 #include "gt-coreout.h"