162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 - Google LLC
462306a36Sopenharmony_ci * Author: David Brazdil <dbrazdil@google.com>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Generates relocation information used by the kernel to convert
762306a36Sopenharmony_ci * absolute addresses in hyp data from kernel VAs to hyp VAs.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This is necessary because hyp code is linked into the same binary
1062306a36Sopenharmony_ci * as the kernel but executes under different memory mappings.
1162306a36Sopenharmony_ci * If the compiler used absolute addressing, those addresses need to
1262306a36Sopenharmony_ci * be converted before they are used by hyp code.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * The input of this program is the relocatable ELF object containing
1562306a36Sopenharmony_ci * all hyp code/data, not yet linked into vmlinux. Hyp section names
1662306a36Sopenharmony_ci * should have been prefixed with `.hyp` at this point.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * The output (printed to stdout) is an assembly file containing
1962306a36Sopenharmony_ci * an array of 32-bit integers and static relocations that instruct
2062306a36Sopenharmony_ci * the linker of `vmlinux` to populate the array entries with offsets
2162306a36Sopenharmony_ci * to positions in the kernel binary containing VAs used by hyp code.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Note that dynamic relocations could be used for the same purpose.
2462306a36Sopenharmony_ci * However, those are only generated if CONFIG_RELOCATABLE=y.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <elf.h>
2862306a36Sopenharmony_ci#include <endian.h>
2962306a36Sopenharmony_ci#include <errno.h>
3062306a36Sopenharmony_ci#include <fcntl.h>
3162306a36Sopenharmony_ci#include <stdbool.h>
3262306a36Sopenharmony_ci#include <stdio.h>
3362306a36Sopenharmony_ci#include <stdlib.h>
3462306a36Sopenharmony_ci#include <string.h>
3562306a36Sopenharmony_ci#include <sys/mman.h>
3662306a36Sopenharmony_ci#include <sys/types.h>
3762306a36Sopenharmony_ci#include <sys/stat.h>
3862306a36Sopenharmony_ci#include <unistd.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <generated/autoconf.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define HYP_SECTION_PREFIX		".hyp"
4362306a36Sopenharmony_ci#define HYP_RELOC_SECTION		".hyp.reloc"
4462306a36Sopenharmony_ci#define HYP_SECTION_SYMBOL_PREFIX	"__hyp_section_"
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * AArch64 relocation type constants.
4862306a36Sopenharmony_ci * Included in case these are not defined in the host toolchain.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#ifndef R_AARCH64_ABS64
5162306a36Sopenharmony_ci#define R_AARCH64_ABS64			257
5262306a36Sopenharmony_ci#endif
5362306a36Sopenharmony_ci#ifndef R_AARCH64_PREL64
5462306a36Sopenharmony_ci#define R_AARCH64_PREL64		260
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci#ifndef R_AARCH64_PREL32
5762306a36Sopenharmony_ci#define R_AARCH64_PREL32		261
5862306a36Sopenharmony_ci#endif
5962306a36Sopenharmony_ci#ifndef R_AARCH64_PREL16
6062306a36Sopenharmony_ci#define R_AARCH64_PREL16		262
6162306a36Sopenharmony_ci#endif
6262306a36Sopenharmony_ci#ifndef R_AARCH64_PLT32
6362306a36Sopenharmony_ci#define R_AARCH64_PLT32			314
6462306a36Sopenharmony_ci#endif
6562306a36Sopenharmony_ci#ifndef R_AARCH64_LD_PREL_LO19
6662306a36Sopenharmony_ci#define R_AARCH64_LD_PREL_LO19		273
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ci#ifndef R_AARCH64_ADR_PREL_LO21
6962306a36Sopenharmony_ci#define R_AARCH64_ADR_PREL_LO21		274
7062306a36Sopenharmony_ci#endif
7162306a36Sopenharmony_ci#ifndef R_AARCH64_ADR_PREL_PG_HI21
7262306a36Sopenharmony_ci#define R_AARCH64_ADR_PREL_PG_HI21	275
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_ci#ifndef R_AARCH64_ADR_PREL_PG_HI21_NC
7562306a36Sopenharmony_ci#define R_AARCH64_ADR_PREL_PG_HI21_NC	276
7662306a36Sopenharmony_ci#endif
7762306a36Sopenharmony_ci#ifndef R_AARCH64_ADD_ABS_LO12_NC
7862306a36Sopenharmony_ci#define R_AARCH64_ADD_ABS_LO12_NC	277
7962306a36Sopenharmony_ci#endif
8062306a36Sopenharmony_ci#ifndef R_AARCH64_LDST8_ABS_LO12_NC
8162306a36Sopenharmony_ci#define R_AARCH64_LDST8_ABS_LO12_NC	278
8262306a36Sopenharmony_ci#endif
8362306a36Sopenharmony_ci#ifndef R_AARCH64_TSTBR14
8462306a36Sopenharmony_ci#define R_AARCH64_TSTBR14		279
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci#ifndef R_AARCH64_CONDBR19
8762306a36Sopenharmony_ci#define R_AARCH64_CONDBR19		280
8862306a36Sopenharmony_ci#endif
8962306a36Sopenharmony_ci#ifndef R_AARCH64_JUMP26
9062306a36Sopenharmony_ci#define R_AARCH64_JUMP26		282
9162306a36Sopenharmony_ci#endif
9262306a36Sopenharmony_ci#ifndef R_AARCH64_CALL26
9362306a36Sopenharmony_ci#define R_AARCH64_CALL26		283
9462306a36Sopenharmony_ci#endif
9562306a36Sopenharmony_ci#ifndef R_AARCH64_LDST16_ABS_LO12_NC
9662306a36Sopenharmony_ci#define R_AARCH64_LDST16_ABS_LO12_NC	284
9762306a36Sopenharmony_ci#endif
9862306a36Sopenharmony_ci#ifndef R_AARCH64_LDST32_ABS_LO12_NC
9962306a36Sopenharmony_ci#define R_AARCH64_LDST32_ABS_LO12_NC	285
10062306a36Sopenharmony_ci#endif
10162306a36Sopenharmony_ci#ifndef R_AARCH64_LDST64_ABS_LO12_NC
10262306a36Sopenharmony_ci#define R_AARCH64_LDST64_ABS_LO12_NC	286
10362306a36Sopenharmony_ci#endif
10462306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G0
10562306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G0		287
10662306a36Sopenharmony_ci#endif
10762306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G0_NC
10862306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G0_NC	288
10962306a36Sopenharmony_ci#endif
11062306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G1
11162306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G1		289
11262306a36Sopenharmony_ci#endif
11362306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G1_NC
11462306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G1_NC	290
11562306a36Sopenharmony_ci#endif
11662306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G2
11762306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G2		291
11862306a36Sopenharmony_ci#endif
11962306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G2_NC
12062306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G2_NC	292
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci#ifndef R_AARCH64_MOVW_PREL_G3
12362306a36Sopenharmony_ci#define R_AARCH64_MOVW_PREL_G3		293
12462306a36Sopenharmony_ci#endif
12562306a36Sopenharmony_ci#ifndef R_AARCH64_LDST128_ABS_LO12_NC
12662306a36Sopenharmony_ci#define R_AARCH64_LDST128_ABS_LO12_NC	299
12762306a36Sopenharmony_ci#endif
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* Global state of the processed ELF. */
13062306a36Sopenharmony_cistatic struct {
13162306a36Sopenharmony_ci	const char	*path;
13262306a36Sopenharmony_ci	char		*begin;
13362306a36Sopenharmony_ci	size_t		size;
13462306a36Sopenharmony_ci	Elf64_Ehdr	*ehdr;
13562306a36Sopenharmony_ci	Elf64_Shdr	*sh_table;
13662306a36Sopenharmony_ci	const char	*sh_string;
13762306a36Sopenharmony_ci} elf;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#if defined(CONFIG_CPU_LITTLE_ENDIAN)
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define elf16toh(x)	le16toh(x)
14262306a36Sopenharmony_ci#define elf32toh(x)	le32toh(x)
14362306a36Sopenharmony_ci#define elf64toh(x)	le64toh(x)
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#define ELFENDIAN	ELFDATA2LSB
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#elif defined(CONFIG_CPU_BIG_ENDIAN)
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci#define elf16toh(x)	be16toh(x)
15062306a36Sopenharmony_ci#define elf32toh(x)	be32toh(x)
15162306a36Sopenharmony_ci#define elf64toh(x)	be64toh(x)
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#define ELFENDIAN	ELFDATA2MSB
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#else
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#error PDP-endian sadly unsupported...
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#endif
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#define fatal_error(fmt, ...)						\
16262306a36Sopenharmony_ci	({								\
16362306a36Sopenharmony_ci		fprintf(stderr, "error: %s: " fmt "\n",			\
16462306a36Sopenharmony_ci			elf.path, ## __VA_ARGS__);			\
16562306a36Sopenharmony_ci		exit(EXIT_FAILURE);					\
16662306a36Sopenharmony_ci		__builtin_unreachable();				\
16762306a36Sopenharmony_ci	})
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci#define fatal_perror(msg)						\
17062306a36Sopenharmony_ci	({								\
17162306a36Sopenharmony_ci		fprintf(stderr, "error: %s: " msg ": %s\n",		\
17262306a36Sopenharmony_ci			elf.path, strerror(errno));			\
17362306a36Sopenharmony_ci		exit(EXIT_FAILURE);					\
17462306a36Sopenharmony_ci		__builtin_unreachable();				\
17562306a36Sopenharmony_ci	})
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci#define assert_op(lhs, rhs, fmt, op)					\
17862306a36Sopenharmony_ci	({								\
17962306a36Sopenharmony_ci		typeof(lhs) _lhs = (lhs);				\
18062306a36Sopenharmony_ci		typeof(rhs) _rhs = (rhs);				\
18162306a36Sopenharmony_ci									\
18262306a36Sopenharmony_ci		if (!(_lhs op _rhs)) {					\
18362306a36Sopenharmony_ci			fatal_error("assertion " #lhs " " #op " " #rhs	\
18462306a36Sopenharmony_ci				" failed (lhs=" fmt ", rhs=" fmt	\
18562306a36Sopenharmony_ci				", line=%d)", _lhs, _rhs, __LINE__);	\
18662306a36Sopenharmony_ci		}							\
18762306a36Sopenharmony_ci	})
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#define assert_eq(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, ==)
19062306a36Sopenharmony_ci#define assert_ne(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, !=)
19162306a36Sopenharmony_ci#define assert_lt(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, <)
19262306a36Sopenharmony_ci#define assert_ge(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, >=)
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * Return a pointer of a given type at a given offset from
19662306a36Sopenharmony_ci * the beginning of the ELF file.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_ci#define elf_ptr(type, off) ((type *)(elf.begin + (off)))
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/* Iterate over all sections in the ELF. */
20162306a36Sopenharmony_ci#define for_each_section(var) \
20262306a36Sopenharmony_ci	for (var = elf.sh_table; var < elf.sh_table + elf16toh(elf.ehdr->e_shnum); ++var)
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* Iterate over all Elf64_Rela relocations in a given section. */
20562306a36Sopenharmony_ci#define for_each_rela(shdr, var)					\
20662306a36Sopenharmony_ci	for (var = elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset));	\
20762306a36Sopenharmony_ci	     var < elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset) + elf64toh(shdr->sh_size)); var++)
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/* True if a string starts with a given prefix. */
21062306a36Sopenharmony_cistatic inline bool starts_with(const char *str, const char *prefix)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	return memcmp(str, prefix, strlen(prefix)) == 0;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/* Returns a string containing the name of a given section. */
21662306a36Sopenharmony_cistatic inline const char *section_name(Elf64_Shdr *shdr)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	return elf.sh_string + elf32toh(shdr->sh_name);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/* Returns a pointer to the first byte of section data. */
22262306a36Sopenharmony_cistatic inline const char *section_begin(Elf64_Shdr *shdr)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	return elf_ptr(char, elf64toh(shdr->sh_offset));
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/* Find a section by its offset from the beginning of the file. */
22862306a36Sopenharmony_cistatic inline Elf64_Shdr *section_by_off(Elf64_Off off)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	assert_ne(off, 0UL, "%lu");
23162306a36Sopenharmony_ci	return elf_ptr(Elf64_Shdr, off);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/* Find a section by its index. */
23562306a36Sopenharmony_cistatic inline Elf64_Shdr *section_by_idx(uint16_t idx)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	assert_ne(idx, SHN_UNDEF, "%u");
23862306a36Sopenharmony_ci	return &elf.sh_table[idx];
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci * Memory-map the given ELF file, perform sanity checks, and
24362306a36Sopenharmony_ci * populate global state.
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic void init_elf(const char *path)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	int fd, ret;
24862306a36Sopenharmony_ci	struct stat stat;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Store path in the global struct for error printing. */
25162306a36Sopenharmony_ci	elf.path = path;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Open the ELF file. */
25462306a36Sopenharmony_ci	fd = open(path, O_RDONLY);
25562306a36Sopenharmony_ci	if (fd < 0)
25662306a36Sopenharmony_ci		fatal_perror("Could not open ELF file");
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Get status of ELF file to obtain its size. */
25962306a36Sopenharmony_ci	ret = fstat(fd, &stat);
26062306a36Sopenharmony_ci	if (ret < 0) {
26162306a36Sopenharmony_ci		close(fd);
26262306a36Sopenharmony_ci		fatal_perror("Could not get status of ELF file");
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* mmap() the entire ELF file read-only at an arbitrary address. */
26662306a36Sopenharmony_ci	elf.begin = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
26762306a36Sopenharmony_ci	if (elf.begin == MAP_FAILED) {
26862306a36Sopenharmony_ci		close(fd);
26962306a36Sopenharmony_ci		fatal_perror("Could not mmap ELF file");
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* mmap() was successful, close the FD. */
27362306a36Sopenharmony_ci	close(fd);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Get pointer to the ELF header. */
27662306a36Sopenharmony_ci	assert_ge(stat.st_size, sizeof(*elf.ehdr), "%lu");
27762306a36Sopenharmony_ci	elf.ehdr = elf_ptr(Elf64_Ehdr, 0);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Check the ELF magic. */
28062306a36Sopenharmony_ci	assert_eq(elf.ehdr->e_ident[EI_MAG0], ELFMAG0, "0x%x");
28162306a36Sopenharmony_ci	assert_eq(elf.ehdr->e_ident[EI_MAG1], ELFMAG1, "0x%x");
28262306a36Sopenharmony_ci	assert_eq(elf.ehdr->e_ident[EI_MAG2], ELFMAG2, "0x%x");
28362306a36Sopenharmony_ci	assert_eq(elf.ehdr->e_ident[EI_MAG3], ELFMAG3, "0x%x");
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Sanity check that this is an ELF64 relocatable object for AArch64. */
28662306a36Sopenharmony_ci	assert_eq(elf.ehdr->e_ident[EI_CLASS], ELFCLASS64, "%u");
28762306a36Sopenharmony_ci	assert_eq(elf.ehdr->e_ident[EI_DATA], ELFENDIAN, "%u");
28862306a36Sopenharmony_ci	assert_eq(elf16toh(elf.ehdr->e_type), ET_REL, "%u");
28962306a36Sopenharmony_ci	assert_eq(elf16toh(elf.ehdr->e_machine), EM_AARCH64, "%u");
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* Populate fields of the global struct. */
29262306a36Sopenharmony_ci	elf.sh_table = section_by_off(elf64toh(elf.ehdr->e_shoff));
29362306a36Sopenharmony_ci	elf.sh_string = section_begin(section_by_idx(elf16toh(elf.ehdr->e_shstrndx)));
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/* Print the prologue of the output ASM file. */
29762306a36Sopenharmony_cistatic void emit_prologue(void)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	printf(".data\n"
30062306a36Sopenharmony_ci	       ".pushsection " HYP_RELOC_SECTION ", \"a\"\n");
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/* Print ASM statements needed as a prologue to a processed hyp section. */
30462306a36Sopenharmony_cistatic void emit_section_prologue(const char *sh_orig_name)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	/* Declare the hyp section symbol. */
30762306a36Sopenharmony_ci	printf(".global %s%s\n", HYP_SECTION_SYMBOL_PREFIX, sh_orig_name);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/*
31162306a36Sopenharmony_ci * Print ASM statements to create a hyp relocation entry for a given
31262306a36Sopenharmony_ci * R_AARCH64_ABS64 relocation.
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * The linker of vmlinux will populate the position given by `rela` with
31562306a36Sopenharmony_ci * an absolute 64-bit kernel VA. If the kernel is relocatable, it will
31662306a36Sopenharmony_ci * also generate a dynamic relocation entry so that the kernel can shift
31762306a36Sopenharmony_ci * the address at runtime for KASLR.
31862306a36Sopenharmony_ci *
31962306a36Sopenharmony_ci * Emit a 32-bit offset from the current address to the position given
32062306a36Sopenharmony_ci * by `rela`. This way the kernel can iterate over all kernel VAs used
32162306a36Sopenharmony_ci * by hyp at runtime and convert them to hyp VAs. However, that offset
32262306a36Sopenharmony_ci * will not be known until linking of `vmlinux`, so emit a PREL32
32362306a36Sopenharmony_ci * relocation referencing a symbol that the hyp linker script put at
32462306a36Sopenharmony_ci * the beginning of the relocated section + the offset from `rela`.
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_cistatic void emit_rela_abs64(Elf64_Rela *rela, const char *sh_orig_name)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	/* Offset of this reloc from the beginning of HYP_RELOC_SECTION. */
32962306a36Sopenharmony_ci	static size_t reloc_offset;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* Create storage for the 32-bit offset. */
33262306a36Sopenharmony_ci	printf(".word 0\n");
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/*
33562306a36Sopenharmony_ci	 * Create a PREL32 relocation which instructs the linker of `vmlinux`
33662306a36Sopenharmony_ci	 * to insert offset to position <base> + <offset>, where <base> is
33762306a36Sopenharmony_ci	 * a symbol at the beginning of the relocated section, and <offset>
33862306a36Sopenharmony_ci	 * is `rela->r_offset`.
33962306a36Sopenharmony_ci	 */
34062306a36Sopenharmony_ci	printf(".reloc %lu, R_AARCH64_PREL32, %s%s + 0x%lx\n",
34162306a36Sopenharmony_ci	       reloc_offset, HYP_SECTION_SYMBOL_PREFIX, sh_orig_name,
34262306a36Sopenharmony_ci	       elf64toh(rela->r_offset));
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	reloc_offset += 4;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* Print the epilogue of the output ASM file. */
34862306a36Sopenharmony_cistatic void emit_epilogue(void)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	printf(".popsection\n");
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/*
35462306a36Sopenharmony_ci * Iterate over all RELA relocations in a given section and emit
35562306a36Sopenharmony_ci * hyp relocation data for all absolute addresses in hyp code/data.
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * Static relocations that generate PC-relative-addressing are ignored.
35862306a36Sopenharmony_ci * Failure is reported for unexpected relocation types.
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_cistatic void emit_rela_section(Elf64_Shdr *sh_rela)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	Elf64_Shdr *sh_orig = &elf.sh_table[elf32toh(sh_rela->sh_info)];
36362306a36Sopenharmony_ci	const char *sh_orig_name = section_name(sh_orig);
36462306a36Sopenharmony_ci	Elf64_Rela *rela;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Skip all non-hyp sections. */
36762306a36Sopenharmony_ci	if (!starts_with(sh_orig_name, HYP_SECTION_PREFIX))
36862306a36Sopenharmony_ci		return;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	emit_section_prologue(sh_orig_name);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	for_each_rela(sh_rela, rela) {
37362306a36Sopenharmony_ci		uint32_t type = (uint32_t)elf64toh(rela->r_info);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		/* Check that rela points inside the relocated section. */
37662306a36Sopenharmony_ci		assert_lt(elf64toh(rela->r_offset), elf64toh(sh_orig->sh_size), "0x%lx");
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		switch (type) {
37962306a36Sopenharmony_ci		/*
38062306a36Sopenharmony_ci		 * Data relocations to generate absolute addressing.
38162306a36Sopenharmony_ci		 * Emit a hyp relocation.
38262306a36Sopenharmony_ci		 */
38362306a36Sopenharmony_ci		case R_AARCH64_ABS64:
38462306a36Sopenharmony_ci			emit_rela_abs64(rela, sh_orig_name);
38562306a36Sopenharmony_ci			break;
38662306a36Sopenharmony_ci		/* Allow position-relative data relocations. */
38762306a36Sopenharmony_ci		case R_AARCH64_PREL64:
38862306a36Sopenharmony_ci		case R_AARCH64_PREL32:
38962306a36Sopenharmony_ci		case R_AARCH64_PREL16:
39062306a36Sopenharmony_ci		case R_AARCH64_PLT32:
39162306a36Sopenharmony_ci			break;
39262306a36Sopenharmony_ci		/* Allow relocations to generate PC-relative addressing. */
39362306a36Sopenharmony_ci		case R_AARCH64_LD_PREL_LO19:
39462306a36Sopenharmony_ci		case R_AARCH64_ADR_PREL_LO21:
39562306a36Sopenharmony_ci		case R_AARCH64_ADR_PREL_PG_HI21:
39662306a36Sopenharmony_ci		case R_AARCH64_ADR_PREL_PG_HI21_NC:
39762306a36Sopenharmony_ci		case R_AARCH64_ADD_ABS_LO12_NC:
39862306a36Sopenharmony_ci		case R_AARCH64_LDST8_ABS_LO12_NC:
39962306a36Sopenharmony_ci		case R_AARCH64_LDST16_ABS_LO12_NC:
40062306a36Sopenharmony_ci		case R_AARCH64_LDST32_ABS_LO12_NC:
40162306a36Sopenharmony_ci		case R_AARCH64_LDST64_ABS_LO12_NC:
40262306a36Sopenharmony_ci		case R_AARCH64_LDST128_ABS_LO12_NC:
40362306a36Sopenharmony_ci			break;
40462306a36Sopenharmony_ci		/* Allow relative relocations for control-flow instructions. */
40562306a36Sopenharmony_ci		case R_AARCH64_TSTBR14:
40662306a36Sopenharmony_ci		case R_AARCH64_CONDBR19:
40762306a36Sopenharmony_ci		case R_AARCH64_JUMP26:
40862306a36Sopenharmony_ci		case R_AARCH64_CALL26:
40962306a36Sopenharmony_ci			break;
41062306a36Sopenharmony_ci		/* Allow group relocations to create PC-relative offset inline. */
41162306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G0:
41262306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G0_NC:
41362306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G1:
41462306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G1_NC:
41562306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G2:
41662306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G2_NC:
41762306a36Sopenharmony_ci		case R_AARCH64_MOVW_PREL_G3:
41862306a36Sopenharmony_ci			break;
41962306a36Sopenharmony_ci		default:
42062306a36Sopenharmony_ci			fatal_error("Unexpected RELA type %u", type);
42162306a36Sopenharmony_ci		}
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/* Iterate over all sections and emit hyp relocation data for RELA sections. */
42662306a36Sopenharmony_cistatic void emit_all_relocs(void)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	Elf64_Shdr *shdr;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	for_each_section(shdr) {
43162306a36Sopenharmony_ci		switch (elf32toh(shdr->sh_type)) {
43262306a36Sopenharmony_ci		case SHT_REL:
43362306a36Sopenharmony_ci			fatal_error("Unexpected SHT_REL section \"%s\"",
43462306a36Sopenharmony_ci				section_name(shdr));
43562306a36Sopenharmony_ci		case SHT_RELA:
43662306a36Sopenharmony_ci			emit_rela_section(shdr);
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciint main(int argc, const char **argv)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	if (argc != 2) {
44562306a36Sopenharmony_ci		fprintf(stderr, "Usage: %s <elf_input>\n", argv[0]);
44662306a36Sopenharmony_ci		return EXIT_FAILURE;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	init_elf(argv[1]);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	emit_prologue();
45262306a36Sopenharmony_ci	emit_all_relocs();
45362306a36Sopenharmony_ci	emit_epilogue();
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return EXIT_SUCCESS;
45662306a36Sopenharmony_ci}
457