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