~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm64/kvm/hyp/nvhe/gen-hyprel.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright (C) 2020 - Google LLC
  4  * Author: David Brazdil <dbrazdil@google.com>
  5  *
  6  * Generates relocation information used by the kernel to convert
  7  * absolute addresses in hyp data from kernel VAs to hyp VAs.
  8  *
  9  * This is necessary because hyp code is linked into the same binary
 10  * as the kernel but executes under different memory mappings.
 11  * If the compiler used absolute addressing, those addresses need to
 12  * be converted before they are used by hyp code.
 13  *
 14  * The input of this program is the relocatable ELF object containing
 15  * all hyp code/data, not yet linked into vmlinux. Hyp section names
 16  * should have been prefixed with `.hyp` at this point.
 17  *
 18  * The output (printed to stdout) is an assembly file containing
 19  * an array of 32-bit integers and static relocations that instruct
 20  * the linker of `vmlinux` to populate the array entries with offsets
 21  * to positions in the kernel binary containing VAs used by hyp code.
 22  *
 23  * Note that dynamic relocations could be used for the same purpose.
 24  * However, those are only generated if CONFIG_RELOCATABLE=y.
 25  */
 26 
 27 #include <elf.h>
 28 #include <endian.h>
 29 #include <errno.h>
 30 #include <fcntl.h>
 31 #include <stdbool.h>
 32 #include <stdio.h>
 33 #include <stdlib.h>
 34 #include <string.h>
 35 #include <sys/mman.h>
 36 #include <sys/types.h>
 37 #include <sys/stat.h>
 38 #include <unistd.h>
 39 
 40 #include <generated/autoconf.h>
 41 
 42 #define HYP_SECTION_PREFIX              ".hyp"
 43 #define HYP_RELOC_SECTION               ".hyp.reloc"
 44 #define HYP_SECTION_SYMBOL_PREFIX       "__hyp_section_"
 45 
 46 /*
 47  * AArch64 relocation type constants.
 48  * Included in case these are not defined in the host toolchain.
 49  */
 50 #ifndef R_AARCH64_ABS64
 51 #define R_AARCH64_ABS64                 257
 52 #endif
 53 #ifndef R_AARCH64_ABS32
 54 #define R_AARCH64_ABS32                 258
 55 #endif
 56 #ifndef R_AARCH64_PREL64
 57 #define R_AARCH64_PREL64                260
 58 #endif
 59 #ifndef R_AARCH64_PREL32
 60 #define R_AARCH64_PREL32                261
 61 #endif
 62 #ifndef R_AARCH64_PREL16
 63 #define R_AARCH64_PREL16                262
 64 #endif
 65 #ifndef R_AARCH64_PLT32
 66 #define R_AARCH64_PLT32                 314
 67 #endif
 68 #ifndef R_AARCH64_LD_PREL_LO19
 69 #define R_AARCH64_LD_PREL_LO19          273
 70 #endif
 71 #ifndef R_AARCH64_ADR_PREL_LO21
 72 #define R_AARCH64_ADR_PREL_LO21         274
 73 #endif
 74 #ifndef R_AARCH64_ADR_PREL_PG_HI21
 75 #define R_AARCH64_ADR_PREL_PG_HI21      275
 76 #endif
 77 #ifndef R_AARCH64_ADR_PREL_PG_HI21_NC
 78 #define R_AARCH64_ADR_PREL_PG_HI21_NC   276
 79 #endif
 80 #ifndef R_AARCH64_ADD_ABS_LO12_NC
 81 #define R_AARCH64_ADD_ABS_LO12_NC       277
 82 #endif
 83 #ifndef R_AARCH64_LDST8_ABS_LO12_NC
 84 #define R_AARCH64_LDST8_ABS_LO12_NC     278
 85 #endif
 86 #ifndef R_AARCH64_TSTBR14
 87 #define R_AARCH64_TSTBR14               279
 88 #endif
 89 #ifndef R_AARCH64_CONDBR19
 90 #define R_AARCH64_CONDBR19              280
 91 #endif
 92 #ifndef R_AARCH64_JUMP26
 93 #define R_AARCH64_JUMP26                282
 94 #endif
 95 #ifndef R_AARCH64_CALL26
 96 #define R_AARCH64_CALL26                283
 97 #endif
 98 #ifndef R_AARCH64_LDST16_ABS_LO12_NC
 99 #define R_AARCH64_LDST16_ABS_LO12_NC    284
100 #endif
101 #ifndef R_AARCH64_LDST32_ABS_LO12_NC
102 #define R_AARCH64_LDST32_ABS_LO12_NC    285
103 #endif
104 #ifndef R_AARCH64_LDST64_ABS_LO12_NC
105 #define R_AARCH64_LDST64_ABS_LO12_NC    286
106 #endif
107 #ifndef R_AARCH64_MOVW_PREL_G0
108 #define R_AARCH64_MOVW_PREL_G0          287
109 #endif
110 #ifndef R_AARCH64_MOVW_PREL_G0_NC
111 #define R_AARCH64_MOVW_PREL_G0_NC       288
112 #endif
113 #ifndef R_AARCH64_MOVW_PREL_G1
114 #define R_AARCH64_MOVW_PREL_G1          289
115 #endif
116 #ifndef R_AARCH64_MOVW_PREL_G1_NC
117 #define R_AARCH64_MOVW_PREL_G1_NC       290
118 #endif
119 #ifndef R_AARCH64_MOVW_PREL_G2
120 #define R_AARCH64_MOVW_PREL_G2          291
121 #endif
122 #ifndef R_AARCH64_MOVW_PREL_G2_NC
123 #define R_AARCH64_MOVW_PREL_G2_NC       292
124 #endif
125 #ifndef R_AARCH64_MOVW_PREL_G3
126 #define R_AARCH64_MOVW_PREL_G3          293
127 #endif
128 #ifndef R_AARCH64_LDST128_ABS_LO12_NC
129 #define R_AARCH64_LDST128_ABS_LO12_NC   299
130 #endif
131 
132 /* Global state of the processed ELF. */
133 static struct {
134         const char      *path;
135         char            *begin;
136         size_t          size;
137         Elf64_Ehdr      *ehdr;
138         Elf64_Shdr      *sh_table;
139         const char      *sh_string;
140 } elf;
141 
142 #if defined(CONFIG_CPU_LITTLE_ENDIAN)
143 
144 #define elf16toh(x)     le16toh(x)
145 #define elf32toh(x)     le32toh(x)
146 #define elf64toh(x)     le64toh(x)
147 
148 #define ELFENDIAN       ELFDATA2LSB
149 
150 #elif defined(CONFIG_CPU_BIG_ENDIAN)
151 
152 #define elf16toh(x)     be16toh(x)
153 #define elf32toh(x)     be32toh(x)
154 #define elf64toh(x)     be64toh(x)
155 
156 #define ELFENDIAN       ELFDATA2MSB
157 
158 #else
159 
160 #error PDP-endian sadly unsupported...
161 
162 #endif
163 
164 #define fatal_error(fmt, ...)                                           \
165         ({                                                              \
166                 fprintf(stderr, "error: %s: " fmt "\n",                 \
167                         elf.path, ## __VA_ARGS__);                      \
168                 exit(EXIT_FAILURE);                                     \
169                 __builtin_unreachable();                                \
170         })
171 
172 #define fatal_perror(msg)                                               \
173         ({                                                              \
174                 fprintf(stderr, "error: %s: " msg ": %s\n",             \
175                         elf.path, strerror(errno));                     \
176                 exit(EXIT_FAILURE);                                     \
177                 __builtin_unreachable();                                \
178         })
179 
180 #define assert_op(lhs, rhs, fmt, op)                                    \
181         ({                                                              \
182                 typeof(lhs) _lhs = (lhs);                               \
183                 typeof(rhs) _rhs = (rhs);                               \
184                                                                         \
185                 if (!(_lhs op _rhs)) {                                  \
186                         fatal_error("assertion " #lhs " " #op " " #rhs  \
187                                 " failed (lhs=" fmt ", rhs=" fmt        \
188                                 ", line=%d)", _lhs, _rhs, __LINE__);    \
189                 }                                                       \
190         })
191 
192 #define assert_eq(lhs, rhs, fmt)        assert_op(lhs, rhs, fmt, ==)
193 #define assert_ne(lhs, rhs, fmt)        assert_op(lhs, rhs, fmt, !=)
194 #define assert_lt(lhs, rhs, fmt)        assert_op(lhs, rhs, fmt, <)
195 #define assert_ge(lhs, rhs, fmt)        assert_op(lhs, rhs, fmt, >=)
196 
197 /*
198  * Return a pointer of a given type at a given offset from
199  * the beginning of the ELF file.
200  */
201 #define elf_ptr(type, off) ((type *)(elf.begin + (off)))
202 
203 /* Iterate over all sections in the ELF. */
204 #define for_each_section(var) \
205         for (var = elf.sh_table; var < elf.sh_table + elf16toh(elf.ehdr->e_shnum); ++var)
206 
207 /* Iterate over all Elf64_Rela relocations in a given section. */
208 #define for_each_rela(shdr, var)                                        \
209         for (var = elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset));      \
210              var < elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset) + elf64toh(shdr->sh_size)); var++)
211 
212 /* True if a string starts with a given prefix. */
213 static inline bool starts_with(const char *str, const char *prefix)
214 {
215         return memcmp(str, prefix, strlen(prefix)) == 0;
216 }
217 
218 /* Returns a string containing the name of a given section. */
219 static inline const char *section_name(Elf64_Shdr *shdr)
220 {
221         return elf.sh_string + elf32toh(shdr->sh_name);
222 }
223 
224 /* Returns a pointer to the first byte of section data. */
225 static inline const char *section_begin(Elf64_Shdr *shdr)
226 {
227         return elf_ptr(char, elf64toh(shdr->sh_offset));
228 }
229 
230 /* Find a section by its offset from the beginning of the file. */
231 static inline Elf64_Shdr *section_by_off(Elf64_Off off)
232 {
233         assert_ne(off, 0UL, "%lu");
234         return elf_ptr(Elf64_Shdr, off);
235 }
236 
237 /* Find a section by its index. */
238 static inline Elf64_Shdr *section_by_idx(uint16_t idx)
239 {
240         assert_ne(idx, SHN_UNDEF, "%u");
241         return &elf.sh_table[idx];
242 }
243 
244 /*
245  * Memory-map the given ELF file, perform sanity checks, and
246  * populate global state.
247  */
248 static void init_elf(const char *path)
249 {
250         int fd, ret;
251         struct stat stat;
252 
253         /* Store path in the global struct for error printing. */
254         elf.path = path;
255 
256         /* Open the ELF file. */
257         fd = open(path, O_RDONLY);
258         if (fd < 0)
259                 fatal_perror("Could not open ELF file");
260 
261         /* Get status of ELF file to obtain its size. */
262         ret = fstat(fd, &stat);
263         if (ret < 0) {
264                 close(fd);
265                 fatal_perror("Could not get status of ELF file");
266         }
267 
268         /* mmap() the entire ELF file read-only at an arbitrary address. */
269         elf.begin = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
270         if (elf.begin == MAP_FAILED) {
271                 close(fd);
272                 fatal_perror("Could not mmap ELF file");
273         }
274 
275         /* mmap() was successful, close the FD. */
276         close(fd);
277 
278         /* Get pointer to the ELF header. */
279         assert_ge(stat.st_size, sizeof(*elf.ehdr), "%lu");
280         elf.ehdr = elf_ptr(Elf64_Ehdr, 0);
281 
282         /* Check the ELF magic. */
283         assert_eq(elf.ehdr->e_ident[EI_MAG0], ELFMAG0, "0x%x");
284         assert_eq(elf.ehdr->e_ident[EI_MAG1], ELFMAG1, "0x%x");
285         assert_eq(elf.ehdr->e_ident[EI_MAG2], ELFMAG2, "0x%x");
286         assert_eq(elf.ehdr->e_ident[EI_MAG3], ELFMAG3, "0x%x");
287 
288         /* Sanity check that this is an ELF64 relocatable object for AArch64. */
289         assert_eq(elf.ehdr->e_ident[EI_CLASS], ELFCLASS64, "%u");
290         assert_eq(elf.ehdr->e_ident[EI_DATA], ELFENDIAN, "%u");
291         assert_eq(elf16toh(elf.ehdr->e_type), ET_REL, "%u");
292         assert_eq(elf16toh(elf.ehdr->e_machine), EM_AARCH64, "%u");
293 
294         /* Populate fields of the global struct. */
295         elf.sh_table = section_by_off(elf64toh(elf.ehdr->e_shoff));
296         elf.sh_string = section_begin(section_by_idx(elf16toh(elf.ehdr->e_shstrndx)));
297 }
298 
299 /* Print the prologue of the output ASM file. */
300 static void emit_prologue(void)
301 {
302         printf(".data\n"
303                ".pushsection " HYP_RELOC_SECTION ", \"a\"\n");
304 }
305 
306 /* Print ASM statements needed as a prologue to a processed hyp section. */
307 static void emit_section_prologue(const char *sh_orig_name)
308 {
309         /* Declare the hyp section symbol. */
310         printf(".global %s%s\n", HYP_SECTION_SYMBOL_PREFIX, sh_orig_name);
311 }
312 
313 /*
314  * Print ASM statements to create a hyp relocation entry for a given
315  * R_AARCH64_ABS64 relocation.
316  *
317  * The linker of vmlinux will populate the position given by `rela` with
318  * an absolute 64-bit kernel VA. If the kernel is relocatable, it will
319  * also generate a dynamic relocation entry so that the kernel can shift
320  * the address at runtime for KASLR.
321  *
322  * Emit a 32-bit offset from the current address to the position given
323  * by `rela`. This way the kernel can iterate over all kernel VAs used
324  * by hyp at runtime and convert them to hyp VAs. However, that offset
325  * will not be known until linking of `vmlinux`, so emit a PREL32
326  * relocation referencing a symbol that the hyp linker script put at
327  * the beginning of the relocated section + the offset from `rela`.
328  */
329 static void emit_rela_abs64(Elf64_Rela *rela, const char *sh_orig_name)
330 {
331         /* Offset of this reloc from the beginning of HYP_RELOC_SECTION. */
332         static size_t reloc_offset;
333 
334         /* Create storage for the 32-bit offset. */
335         printf(".word 0\n");
336 
337         /*
338          * Create a PREL32 relocation which instructs the linker of `vmlinux`
339          * to insert offset to position <base> + <offset>, where <base> is
340          * a symbol at the beginning of the relocated section, and <offset>
341          * is `rela->r_offset`.
342          */
343         printf(".reloc %lu, R_AARCH64_PREL32, %s%s + 0x%lx\n",
344                reloc_offset, HYP_SECTION_SYMBOL_PREFIX, sh_orig_name,
345                elf64toh(rela->r_offset));
346 
347         reloc_offset += 4;
348 }
349 
350 /* Print the epilogue of the output ASM file. */
351 static void emit_epilogue(void)
352 {
353         printf(".popsection\n");
354 }
355 
356 /*
357  * Iterate over all RELA relocations in a given section and emit
358  * hyp relocation data for all absolute addresses in hyp code/data.
359  *
360  * Static relocations that generate PC-relative-addressing are ignored.
361  * Failure is reported for unexpected relocation types.
362  */
363 static void emit_rela_section(Elf64_Shdr *sh_rela)
364 {
365         Elf64_Shdr *sh_orig = &elf.sh_table[elf32toh(sh_rela->sh_info)];
366         const char *sh_orig_name = section_name(sh_orig);
367         Elf64_Rela *rela;
368 
369         /* Skip all non-hyp sections. */
370         if (!starts_with(sh_orig_name, HYP_SECTION_PREFIX))
371                 return;
372 
373         emit_section_prologue(sh_orig_name);
374 
375         for_each_rela(sh_rela, rela) {
376                 uint32_t type = (uint32_t)elf64toh(rela->r_info);
377 
378                 /* Check that rela points inside the relocated section. */
379                 assert_lt(elf64toh(rela->r_offset), elf64toh(sh_orig->sh_size), "0x%lx");
380 
381                 switch (type) {
382                 /*
383                  * Data relocations to generate absolute addressing.
384                  * Emit a hyp relocation.
385                  */
386                 case R_AARCH64_ABS64:
387                         emit_rela_abs64(rela, sh_orig_name);
388                         break;
389                 /* Allow 32-bit absolute relocation, for kCFI type hashes. */
390                 case R_AARCH64_ABS32:
391                         break;
392                 /* Allow position-relative data relocations. */
393                 case R_AARCH64_PREL64:
394                 case R_AARCH64_PREL32:
395                 case R_AARCH64_PREL16:
396                 case R_AARCH64_PLT32:
397                         break;
398                 /* Allow relocations to generate PC-relative addressing. */
399                 case R_AARCH64_LD_PREL_LO19:
400                 case R_AARCH64_ADR_PREL_LO21:
401                 case R_AARCH64_ADR_PREL_PG_HI21:
402                 case R_AARCH64_ADR_PREL_PG_HI21_NC:
403                 case R_AARCH64_ADD_ABS_LO12_NC:
404                 case R_AARCH64_LDST8_ABS_LO12_NC:
405                 case R_AARCH64_LDST16_ABS_LO12_NC:
406                 case R_AARCH64_LDST32_ABS_LO12_NC:
407                 case R_AARCH64_LDST64_ABS_LO12_NC:
408                 case R_AARCH64_LDST128_ABS_LO12_NC:
409                         break;
410                 /* Allow relative relocations for control-flow instructions. */
411                 case R_AARCH64_TSTBR14:
412                 case R_AARCH64_CONDBR19:
413                 case R_AARCH64_JUMP26:
414                 case R_AARCH64_CALL26:
415                         break;
416                 /* Allow group relocations to create PC-relative offset inline. */
417                 case R_AARCH64_MOVW_PREL_G0:
418                 case R_AARCH64_MOVW_PREL_G0_NC:
419                 case R_AARCH64_MOVW_PREL_G1:
420                 case R_AARCH64_MOVW_PREL_G1_NC:
421                 case R_AARCH64_MOVW_PREL_G2:
422                 case R_AARCH64_MOVW_PREL_G2_NC:
423                 case R_AARCH64_MOVW_PREL_G3:
424                         break;
425                 default:
426                         fatal_error("Unexpected RELA type %u", type);
427                 }
428         }
429 }
430 
431 /* Iterate over all sections and emit hyp relocation data for RELA sections. */
432 static void emit_all_relocs(void)
433 {
434         Elf64_Shdr *shdr;
435 
436         for_each_section(shdr) {
437                 switch (elf32toh(shdr->sh_type)) {
438                 case SHT_REL:
439                         fatal_error("Unexpected SHT_REL section \"%s\"",
440                                 section_name(shdr));
441                 case SHT_RELA:
442                         emit_rela_section(shdr);
443                         break;
444                 }
445         }
446 }
447 
448 int main(int argc, const char **argv)
449 {
450         if (argc != 2) {
451                 fprintf(stderr, "Usage: %s <elf_input>\n", argv[0]);
452                 return EXIT_FAILURE;
453         }
454 
455         init_elf(argv[1]);
456 
457         emit_prologue();
458         emit_all_relocs();
459         emit_epilogue();
460 
461         return EXIT_SUCCESS;
462 }
463 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php