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