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. */
130static 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. */
210static 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. */
216static 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. */
222static 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. */
228static 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. */
235static 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 */
245static 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. */
297static 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. */
304static 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 */
326static 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. */
348static 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 */
360static 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. */
393		case R_AARCH64_LD_PREL_LO19:
394		case R_AARCH64_ADR_PREL_LO21:
395		case R_AARCH64_ADR_PREL_PG_HI21:
396		case R_AARCH64_ADR_PREL_PG_HI21_NC:
397		case R_AARCH64_ADD_ABS_LO12_NC:
398		case R_AARCH64_LDST8_ABS_LO12_NC:
399		case R_AARCH64_LDST16_ABS_LO12_NC:
400		case R_AARCH64_LDST32_ABS_LO12_NC:
401		case R_AARCH64_LDST64_ABS_LO12_NC:
402		case R_AARCH64_LDST128_ABS_LO12_NC:
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. */
411		case R_AARCH64_MOVW_PREL_G0:
412		case R_AARCH64_MOVW_PREL_G0_NC:
413		case R_AARCH64_MOVW_PREL_G1:
414		case R_AARCH64_MOVW_PREL_G1_NC:
415		case R_AARCH64_MOVW_PREL_G2:
416		case R_AARCH64_MOVW_PREL_G2_NC:
417		case R_AARCH64_MOVW_PREL_G3:
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. */
426static 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
442int 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}
457