xref: /openbmc/linux/arch/arm64/kvm/hyp/nvhe/gen-hyprel.c (revision 77e06b30)
18c49b5d4SDavid Brazdil // SPDX-License-Identifier: GPL-2.0-only
28c49b5d4SDavid Brazdil /*
38c49b5d4SDavid Brazdil  * Copyright (C) 2020 - Google LLC
48c49b5d4SDavid Brazdil  * Author: David Brazdil <dbrazdil@google.com>
58c49b5d4SDavid Brazdil  *
68c49b5d4SDavid Brazdil  * Generates relocation information used by the kernel to convert
78c49b5d4SDavid Brazdil  * absolute addresses in hyp data from kernel VAs to hyp VAs.
88c49b5d4SDavid Brazdil  *
98c49b5d4SDavid Brazdil  * This is necessary because hyp code is linked into the same binary
108c49b5d4SDavid Brazdil  * as the kernel but executes under different memory mappings.
118c49b5d4SDavid Brazdil  * If the compiler used absolute addressing, those addresses need to
128c49b5d4SDavid Brazdil  * be converted before they are used by hyp code.
138c49b5d4SDavid Brazdil  *
148c49b5d4SDavid Brazdil  * The input of this program is the relocatable ELF object containing
158c49b5d4SDavid Brazdil  * all hyp code/data, not yet linked into vmlinux. Hyp section names
168c49b5d4SDavid Brazdil  * should have been prefixed with `.hyp` at this point.
178c49b5d4SDavid Brazdil  *
188c49b5d4SDavid Brazdil  * The output (printed to stdout) is an assembly file containing
198c49b5d4SDavid Brazdil  * an array of 32-bit integers and static relocations that instruct
208c49b5d4SDavid Brazdil  * the linker of `vmlinux` to populate the array entries with offsets
218c49b5d4SDavid Brazdil  * to positions in the kernel binary containing VAs used by hyp code.
228c49b5d4SDavid Brazdil  *
238c49b5d4SDavid Brazdil  * Note that dynamic relocations could be used for the same purpose.
248c49b5d4SDavid Brazdil  * However, those are only generated if CONFIG_RELOCATABLE=y.
258c49b5d4SDavid Brazdil  */
268c49b5d4SDavid Brazdil 
278c49b5d4SDavid Brazdil #include <elf.h>
28bc93763fSMarc Zyngier #include <endian.h>
298c49b5d4SDavid Brazdil #include <errno.h>
308c49b5d4SDavid Brazdil #include <fcntl.h>
318c49b5d4SDavid Brazdil #include <stdbool.h>
328c49b5d4SDavid Brazdil #include <stdio.h>
338c49b5d4SDavid Brazdil #include <stdlib.h>
348c49b5d4SDavid Brazdil #include <string.h>
358c49b5d4SDavid Brazdil #include <sys/mman.h>
368c49b5d4SDavid Brazdil #include <sys/types.h>
378c49b5d4SDavid Brazdil #include <sys/stat.h>
388c49b5d4SDavid Brazdil #include <unistd.h>
398c49b5d4SDavid Brazdil 
40bc93763fSMarc Zyngier #include <generated/autoconf.h>
41bc93763fSMarc Zyngier 
428c49b5d4SDavid Brazdil #define HYP_SECTION_PREFIX		".hyp"
438c49b5d4SDavid Brazdil #define HYP_RELOC_SECTION		".hyp.reloc"
448c49b5d4SDavid Brazdil #define HYP_SECTION_SYMBOL_PREFIX	"__hyp_section_"
458c49b5d4SDavid Brazdil 
468c49b5d4SDavid Brazdil /*
478c49b5d4SDavid Brazdil  * AArch64 relocation type constants.
488c49b5d4SDavid Brazdil  * Included in case these are not defined in the host toolchain.
498c49b5d4SDavid Brazdil  */
508c49b5d4SDavid Brazdil #ifndef R_AARCH64_ABS64
518c49b5d4SDavid Brazdil #define R_AARCH64_ABS64			257
528c49b5d4SDavid Brazdil #endif
53*77e06b30SDavid Brazdil #ifndef R_AARCH64_PREL64
54*77e06b30SDavid Brazdil #define R_AARCH64_PREL64		260
55*77e06b30SDavid Brazdil #endif
56*77e06b30SDavid Brazdil #ifndef R_AARCH64_PREL32
57*77e06b30SDavid Brazdil #define R_AARCH64_PREL32		261
58*77e06b30SDavid Brazdil #endif
59*77e06b30SDavid Brazdil #ifndef R_AARCH64_PREL16
60*77e06b30SDavid Brazdil #define R_AARCH64_PREL16		262
61*77e06b30SDavid Brazdil #endif
62*77e06b30SDavid Brazdil #ifndef R_AARCH64_PLT32
63*77e06b30SDavid Brazdil #define R_AARCH64_PLT32			314
64*77e06b30SDavid Brazdil #endif
658c49b5d4SDavid Brazdil #ifndef R_AARCH64_LD_PREL_LO19
668c49b5d4SDavid Brazdil #define R_AARCH64_LD_PREL_LO19		273
678c49b5d4SDavid Brazdil #endif
688c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADR_PREL_LO21
698c49b5d4SDavid Brazdil #define R_AARCH64_ADR_PREL_LO21		274
708c49b5d4SDavid Brazdil #endif
718c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADR_PREL_PG_HI21
728c49b5d4SDavid Brazdil #define R_AARCH64_ADR_PREL_PG_HI21	275
738c49b5d4SDavid Brazdil #endif
748c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADR_PREL_PG_HI21_NC
758c49b5d4SDavid Brazdil #define R_AARCH64_ADR_PREL_PG_HI21_NC	276
768c49b5d4SDavid Brazdil #endif
778c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADD_ABS_LO12_NC
788c49b5d4SDavid Brazdil #define R_AARCH64_ADD_ABS_LO12_NC	277
798c49b5d4SDavid Brazdil #endif
808c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST8_ABS_LO12_NC
818c49b5d4SDavid Brazdil #define R_AARCH64_LDST8_ABS_LO12_NC	278
828c49b5d4SDavid Brazdil #endif
838c49b5d4SDavid Brazdil #ifndef R_AARCH64_TSTBR14
848c49b5d4SDavid Brazdil #define R_AARCH64_TSTBR14		279
858c49b5d4SDavid Brazdil #endif
868c49b5d4SDavid Brazdil #ifndef R_AARCH64_CONDBR19
878c49b5d4SDavid Brazdil #define R_AARCH64_CONDBR19		280
888c49b5d4SDavid Brazdil #endif
898c49b5d4SDavid Brazdil #ifndef R_AARCH64_JUMP26
908c49b5d4SDavid Brazdil #define R_AARCH64_JUMP26		282
918c49b5d4SDavid Brazdil #endif
928c49b5d4SDavid Brazdil #ifndef R_AARCH64_CALL26
938c49b5d4SDavid Brazdil #define R_AARCH64_CALL26		283
948c49b5d4SDavid Brazdil #endif
958c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST16_ABS_LO12_NC
968c49b5d4SDavid Brazdil #define R_AARCH64_LDST16_ABS_LO12_NC	284
978c49b5d4SDavid Brazdil #endif
988c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST32_ABS_LO12_NC
998c49b5d4SDavid Brazdil #define R_AARCH64_LDST32_ABS_LO12_NC	285
1008c49b5d4SDavid Brazdil #endif
1018c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST64_ABS_LO12_NC
1028c49b5d4SDavid Brazdil #define R_AARCH64_LDST64_ABS_LO12_NC	286
1038c49b5d4SDavid Brazdil #endif
1048c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G0
1058c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G0		287
1068c49b5d4SDavid Brazdil #endif
1078c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G0_NC
1088c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G0_NC	288
1098c49b5d4SDavid Brazdil #endif
1108c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G1
1118c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G1		289
1128c49b5d4SDavid Brazdil #endif
1138c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G1_NC
1148c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G1_NC	290
1158c49b5d4SDavid Brazdil #endif
1168c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G2
1178c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G2		291
1188c49b5d4SDavid Brazdil #endif
1198c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G2_NC
1208c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G2_NC	292
1218c49b5d4SDavid Brazdil #endif
1228c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G3
1238c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G3		293
1248c49b5d4SDavid Brazdil #endif
1258c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST128_ABS_LO12_NC
1268c49b5d4SDavid Brazdil #define R_AARCH64_LDST128_ABS_LO12_NC	299
1278c49b5d4SDavid Brazdil #endif
1288c49b5d4SDavid Brazdil 
1298c49b5d4SDavid Brazdil /* Global state of the processed ELF. */
1308c49b5d4SDavid Brazdil static struct {
1318c49b5d4SDavid Brazdil 	const char	*path;
1328c49b5d4SDavid Brazdil 	char		*begin;
1338c49b5d4SDavid Brazdil 	size_t		size;
1348c49b5d4SDavid Brazdil 	Elf64_Ehdr	*ehdr;
1358c49b5d4SDavid Brazdil 	Elf64_Shdr	*sh_table;
1368c49b5d4SDavid Brazdil 	const char	*sh_string;
1378c49b5d4SDavid Brazdil } elf;
1388c49b5d4SDavid Brazdil 
139bc93763fSMarc Zyngier #if defined(CONFIG_CPU_LITTLE_ENDIAN)
140bc93763fSMarc Zyngier 
141bc93763fSMarc Zyngier #define elf16toh(x)	le16toh(x)
142bc93763fSMarc Zyngier #define elf32toh(x)	le32toh(x)
143bc93763fSMarc Zyngier #define elf64toh(x)	le64toh(x)
144bc93763fSMarc Zyngier 
145bc93763fSMarc Zyngier #define ELFENDIAN	ELFDATA2LSB
146bc93763fSMarc Zyngier 
147bc93763fSMarc Zyngier #elif defined(CONFIG_CPU_BIG_ENDIAN)
148bc93763fSMarc Zyngier 
149bc93763fSMarc Zyngier #define elf16toh(x)	be16toh(x)
150bc93763fSMarc Zyngier #define elf32toh(x)	be32toh(x)
151bc93763fSMarc Zyngier #define elf64toh(x)	be64toh(x)
152bc93763fSMarc Zyngier 
153bc93763fSMarc Zyngier #define ELFENDIAN	ELFDATA2MSB
154bc93763fSMarc Zyngier 
155bc93763fSMarc Zyngier #else
156bc93763fSMarc Zyngier 
157bc93763fSMarc Zyngier #error PDP-endian sadly unsupported...
158bc93763fSMarc Zyngier 
159bc93763fSMarc Zyngier #endif
160bc93763fSMarc Zyngier 
1618c49b5d4SDavid Brazdil #define fatal_error(fmt, ...)						\
1628c49b5d4SDavid Brazdil 	({								\
1638c49b5d4SDavid Brazdil 		fprintf(stderr, "error: %s: " fmt "\n",			\
1648c49b5d4SDavid Brazdil 			elf.path, ## __VA_ARGS__);			\
1658c49b5d4SDavid Brazdil 		exit(EXIT_FAILURE);					\
1668c49b5d4SDavid Brazdil 		__builtin_unreachable();				\
1678c49b5d4SDavid Brazdil 	})
1688c49b5d4SDavid Brazdil 
1698c49b5d4SDavid Brazdil #define fatal_perror(msg)						\
1708c49b5d4SDavid Brazdil 	({								\
1718c49b5d4SDavid Brazdil 		fprintf(stderr, "error: %s: " msg ": %s\n",		\
1728c49b5d4SDavid Brazdil 			elf.path, strerror(errno));			\
1738c49b5d4SDavid Brazdil 		exit(EXIT_FAILURE);					\
1748c49b5d4SDavid Brazdil 		__builtin_unreachable();				\
1758c49b5d4SDavid Brazdil 	})
1768c49b5d4SDavid Brazdil 
1778c49b5d4SDavid Brazdil #define assert_op(lhs, rhs, fmt, op)					\
1788c49b5d4SDavid Brazdil 	({								\
1798c49b5d4SDavid Brazdil 		typeof(lhs) _lhs = (lhs);				\
1808c49b5d4SDavid Brazdil 		typeof(rhs) _rhs = (rhs);				\
1818c49b5d4SDavid Brazdil 									\
1828c49b5d4SDavid Brazdil 		if (!(_lhs op _rhs)) {					\
1838c49b5d4SDavid Brazdil 			fatal_error("assertion " #lhs " " #op " " #rhs	\
1848c49b5d4SDavid Brazdil 				" failed (lhs=" fmt ", rhs=" fmt	\
1858c49b5d4SDavid Brazdil 				", line=%d)", _lhs, _rhs, __LINE__);	\
1868c49b5d4SDavid Brazdil 		}							\
1878c49b5d4SDavid Brazdil 	})
1888c49b5d4SDavid Brazdil 
1898c49b5d4SDavid Brazdil #define assert_eq(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, ==)
1908c49b5d4SDavid Brazdil #define assert_ne(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, !=)
1918c49b5d4SDavid Brazdil #define assert_lt(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, <)
1928c49b5d4SDavid Brazdil #define assert_ge(lhs, rhs, fmt)	assert_op(lhs, rhs, fmt, >=)
1938c49b5d4SDavid Brazdil 
1948c49b5d4SDavid Brazdil /*
1958c49b5d4SDavid Brazdil  * Return a pointer of a given type at a given offset from
1968c49b5d4SDavid Brazdil  * the beginning of the ELF file.
1978c49b5d4SDavid Brazdil  */
1988c49b5d4SDavid Brazdil #define elf_ptr(type, off) ((type *)(elf.begin + (off)))
1998c49b5d4SDavid Brazdil 
2008c49b5d4SDavid Brazdil /* Iterate over all sections in the ELF. */
2018c49b5d4SDavid Brazdil #define for_each_section(var) \
202bc93763fSMarc Zyngier 	for (var = elf.sh_table; var < elf.sh_table + elf16toh(elf.ehdr->e_shnum); ++var)
2038c49b5d4SDavid Brazdil 
2048c49b5d4SDavid Brazdil /* Iterate over all Elf64_Rela relocations in a given section. */
2058c49b5d4SDavid Brazdil #define for_each_rela(shdr, var)					\
206bc93763fSMarc Zyngier 	for (var = elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset));	\
207bc93763fSMarc Zyngier 	     var < elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset) + elf64toh(shdr->sh_size)); var++)
2088c49b5d4SDavid Brazdil 
2098c49b5d4SDavid Brazdil /* True if a string starts with a given prefix. */
starts_with(const char * str,const char * prefix)2108c49b5d4SDavid Brazdil static inline bool starts_with(const char *str, const char *prefix)
2118c49b5d4SDavid Brazdil {
2128c49b5d4SDavid Brazdil 	return memcmp(str, prefix, strlen(prefix)) == 0;
2138c49b5d4SDavid Brazdil }
2148c49b5d4SDavid Brazdil 
2158c49b5d4SDavid Brazdil /* Returns a string containing the name of a given section. */
section_name(Elf64_Shdr * shdr)2168c49b5d4SDavid Brazdil static inline const char *section_name(Elf64_Shdr *shdr)
2178c49b5d4SDavid Brazdil {
218bc93763fSMarc Zyngier 	return elf.sh_string + elf32toh(shdr->sh_name);
2198c49b5d4SDavid Brazdil }
2208c49b5d4SDavid Brazdil 
2218c49b5d4SDavid Brazdil /* Returns a pointer to the first byte of section data. */
section_begin(Elf64_Shdr * shdr)2228c49b5d4SDavid Brazdil static inline const char *section_begin(Elf64_Shdr *shdr)
2238c49b5d4SDavid Brazdil {
224bc93763fSMarc Zyngier 	return elf_ptr(char, elf64toh(shdr->sh_offset));
2258c49b5d4SDavid Brazdil }
2268c49b5d4SDavid Brazdil 
2278c49b5d4SDavid Brazdil /* Find a section by its offset from the beginning of the file. */
section_by_off(Elf64_Off off)2288c49b5d4SDavid Brazdil static inline Elf64_Shdr *section_by_off(Elf64_Off off)
2298c49b5d4SDavid Brazdil {
2308c49b5d4SDavid Brazdil 	assert_ne(off, 0UL, "%lu");
2318c49b5d4SDavid Brazdil 	return elf_ptr(Elf64_Shdr, off);
2328c49b5d4SDavid Brazdil }
2338c49b5d4SDavid Brazdil 
2348c49b5d4SDavid Brazdil /* Find a section by its index. */
section_by_idx(uint16_t idx)2358c49b5d4SDavid Brazdil static inline Elf64_Shdr *section_by_idx(uint16_t idx)
2368c49b5d4SDavid Brazdil {
2378c49b5d4SDavid Brazdil 	assert_ne(idx, SHN_UNDEF, "%u");
2388c49b5d4SDavid Brazdil 	return &elf.sh_table[idx];
2398c49b5d4SDavid Brazdil }
2408c49b5d4SDavid Brazdil 
2418c49b5d4SDavid Brazdil /*
2428c49b5d4SDavid Brazdil  * Memory-map the given ELF file, perform sanity checks, and
2438c49b5d4SDavid Brazdil  * populate global state.
2448c49b5d4SDavid Brazdil  */
init_elf(const char * path)2458c49b5d4SDavid Brazdil static void init_elf(const char *path)
2468c49b5d4SDavid Brazdil {
2478c49b5d4SDavid Brazdil 	int fd, ret;
2488c49b5d4SDavid Brazdil 	struct stat stat;
2498c49b5d4SDavid Brazdil 
2508c49b5d4SDavid Brazdil 	/* Store path in the global struct for error printing. */
2518c49b5d4SDavid Brazdil 	elf.path = path;
2528c49b5d4SDavid Brazdil 
2538c49b5d4SDavid Brazdil 	/* Open the ELF file. */
2548c49b5d4SDavid Brazdil 	fd = open(path, O_RDONLY);
2558c49b5d4SDavid Brazdil 	if (fd < 0)
2568c49b5d4SDavid Brazdil 		fatal_perror("Could not open ELF file");
2578c49b5d4SDavid Brazdil 
2588c49b5d4SDavid Brazdil 	/* Get status of ELF file to obtain its size. */
2598c49b5d4SDavid Brazdil 	ret = fstat(fd, &stat);
2608c49b5d4SDavid Brazdil 	if (ret < 0) {
2618c49b5d4SDavid Brazdil 		close(fd);
2628c49b5d4SDavid Brazdil 		fatal_perror("Could not get status of ELF file");
2638c49b5d4SDavid Brazdil 	}
2648c49b5d4SDavid Brazdil 
2658c49b5d4SDavid Brazdil 	/* mmap() the entire ELF file read-only at an arbitrary address. */
2668c49b5d4SDavid Brazdil 	elf.begin = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
2678c49b5d4SDavid Brazdil 	if (elf.begin == MAP_FAILED) {
2688c49b5d4SDavid Brazdil 		close(fd);
2698c49b5d4SDavid Brazdil 		fatal_perror("Could not mmap ELF file");
2708c49b5d4SDavid Brazdil 	}
2718c49b5d4SDavid Brazdil 
2728c49b5d4SDavid Brazdil 	/* mmap() was successful, close the FD. */
2738c49b5d4SDavid Brazdil 	close(fd);
2748c49b5d4SDavid Brazdil 
2758c49b5d4SDavid Brazdil 	/* Get pointer to the ELF header. */
2768c49b5d4SDavid Brazdil 	assert_ge(stat.st_size, sizeof(*elf.ehdr), "%lu");
2778c49b5d4SDavid Brazdil 	elf.ehdr = elf_ptr(Elf64_Ehdr, 0);
2788c49b5d4SDavid Brazdil 
2798c49b5d4SDavid Brazdil 	/* Check the ELF magic. */
2808c49b5d4SDavid Brazdil 	assert_eq(elf.ehdr->e_ident[EI_MAG0], ELFMAG0, "0x%x");
2818c49b5d4SDavid Brazdil 	assert_eq(elf.ehdr->e_ident[EI_MAG1], ELFMAG1, "0x%x");
2828c49b5d4SDavid Brazdil 	assert_eq(elf.ehdr->e_ident[EI_MAG2], ELFMAG2, "0x%x");
2838c49b5d4SDavid Brazdil 	assert_eq(elf.ehdr->e_ident[EI_MAG3], ELFMAG3, "0x%x");
2848c49b5d4SDavid Brazdil 
2858c49b5d4SDavid Brazdil 	/* Sanity check that this is an ELF64 relocatable object for AArch64. */
2868c49b5d4SDavid Brazdil 	assert_eq(elf.ehdr->e_ident[EI_CLASS], ELFCLASS64, "%u");
287bc93763fSMarc Zyngier 	assert_eq(elf.ehdr->e_ident[EI_DATA], ELFENDIAN, "%u");
288bc93763fSMarc Zyngier 	assert_eq(elf16toh(elf.ehdr->e_type), ET_REL, "%u");
289bc93763fSMarc Zyngier 	assert_eq(elf16toh(elf.ehdr->e_machine), EM_AARCH64, "%u");
2908c49b5d4SDavid Brazdil 
2918c49b5d4SDavid Brazdil 	/* Populate fields of the global struct. */
292bc93763fSMarc Zyngier 	elf.sh_table = section_by_off(elf64toh(elf.ehdr->e_shoff));
293bc93763fSMarc Zyngier 	elf.sh_string = section_begin(section_by_idx(elf16toh(elf.ehdr->e_shstrndx)));
2948c49b5d4SDavid Brazdil }
2958c49b5d4SDavid Brazdil 
2968c49b5d4SDavid Brazdil /* Print the prologue of the output ASM file. */
emit_prologue(void)2978c49b5d4SDavid Brazdil static void emit_prologue(void)
2988c49b5d4SDavid Brazdil {
2998c49b5d4SDavid Brazdil 	printf(".data\n"
3008c49b5d4SDavid Brazdil 	       ".pushsection " HYP_RELOC_SECTION ", \"a\"\n");
3018c49b5d4SDavid Brazdil }
3028c49b5d4SDavid Brazdil 
3038c49b5d4SDavid Brazdil /* Print ASM statements needed as a prologue to a processed hyp section. */
emit_section_prologue(const char * sh_orig_name)3048c49b5d4SDavid Brazdil static void emit_section_prologue(const char *sh_orig_name)
3058c49b5d4SDavid Brazdil {
3068c49b5d4SDavid Brazdil 	/* Declare the hyp section symbol. */
3078c49b5d4SDavid Brazdil 	printf(".global %s%s\n", HYP_SECTION_SYMBOL_PREFIX, sh_orig_name);
3088c49b5d4SDavid Brazdil }
3098c49b5d4SDavid Brazdil 
3108c49b5d4SDavid Brazdil /*
3118c49b5d4SDavid Brazdil  * Print ASM statements to create a hyp relocation entry for a given
3128c49b5d4SDavid Brazdil  * R_AARCH64_ABS64 relocation.
3138c49b5d4SDavid Brazdil  *
3148c49b5d4SDavid Brazdil  * The linker of vmlinux will populate the position given by `rela` with
3158c49b5d4SDavid Brazdil  * an absolute 64-bit kernel VA. If the kernel is relocatable, it will
3168c49b5d4SDavid Brazdil  * also generate a dynamic relocation entry so that the kernel can shift
3178c49b5d4SDavid Brazdil  * the address at runtime for KASLR.
3188c49b5d4SDavid Brazdil  *
3198c49b5d4SDavid Brazdil  * Emit a 32-bit offset from the current address to the position given
3208c49b5d4SDavid Brazdil  * by `rela`. This way the kernel can iterate over all kernel VAs used
3218c49b5d4SDavid Brazdil  * by hyp at runtime and convert them to hyp VAs. However, that offset
3228c49b5d4SDavid Brazdil  * will not be known until linking of `vmlinux`, so emit a PREL32
3238c49b5d4SDavid Brazdil  * relocation referencing a symbol that the hyp linker script put at
3248c49b5d4SDavid Brazdil  * the beginning of the relocated section + the offset from `rela`.
3258c49b5d4SDavid Brazdil  */
emit_rela_abs64(Elf64_Rela * rela,const char * sh_orig_name)3268c49b5d4SDavid Brazdil static void emit_rela_abs64(Elf64_Rela *rela, const char *sh_orig_name)
3278c49b5d4SDavid Brazdil {
3288c49b5d4SDavid Brazdil 	/* Offset of this reloc from the beginning of HYP_RELOC_SECTION. */
3298c49b5d4SDavid Brazdil 	static size_t reloc_offset;
3308c49b5d4SDavid Brazdil 
3318c49b5d4SDavid Brazdil 	/* Create storage for the 32-bit offset. */
3328c49b5d4SDavid Brazdil 	printf(".word 0\n");
3338c49b5d4SDavid Brazdil 
3348c49b5d4SDavid Brazdil 	/*
3358c49b5d4SDavid Brazdil 	 * Create a PREL32 relocation which instructs the linker of `vmlinux`
3368c49b5d4SDavid Brazdil 	 * to insert offset to position <base> + <offset>, where <base> is
3378c49b5d4SDavid Brazdil 	 * a symbol at the beginning of the relocated section, and <offset>
3388c49b5d4SDavid Brazdil 	 * is `rela->r_offset`.
3398c49b5d4SDavid Brazdil 	 */
3408c49b5d4SDavid Brazdil 	printf(".reloc %lu, R_AARCH64_PREL32, %s%s + 0x%lx\n",
3418c49b5d4SDavid Brazdil 	       reloc_offset, HYP_SECTION_SYMBOL_PREFIX, sh_orig_name,
342bc93763fSMarc Zyngier 	       elf64toh(rela->r_offset));
3438c49b5d4SDavid Brazdil 
3448c49b5d4SDavid Brazdil 	reloc_offset += 4;
3458c49b5d4SDavid Brazdil }
3468c49b5d4SDavid Brazdil 
3478c49b5d4SDavid Brazdil /* Print the epilogue of the output ASM file. */
emit_epilogue(void)3488c49b5d4SDavid Brazdil static void emit_epilogue(void)
3498c49b5d4SDavid Brazdil {
3508c49b5d4SDavid Brazdil 	printf(".popsection\n");
3518c49b5d4SDavid Brazdil }
3528c49b5d4SDavid Brazdil 
3538c49b5d4SDavid Brazdil /*
3548c49b5d4SDavid Brazdil  * Iterate over all RELA relocations in a given section and emit
3558c49b5d4SDavid Brazdil  * hyp relocation data for all absolute addresses in hyp code/data.
3568c49b5d4SDavid Brazdil  *
3578c49b5d4SDavid Brazdil  * Static relocations that generate PC-relative-addressing are ignored.
3588c49b5d4SDavid Brazdil  * Failure is reported for unexpected relocation types.
3598c49b5d4SDavid Brazdil  */
emit_rela_section(Elf64_Shdr * sh_rela)3608c49b5d4SDavid Brazdil static void emit_rela_section(Elf64_Shdr *sh_rela)
3618c49b5d4SDavid Brazdil {
362bc93763fSMarc Zyngier 	Elf64_Shdr *sh_orig = &elf.sh_table[elf32toh(sh_rela->sh_info)];
3638c49b5d4SDavid Brazdil 	const char *sh_orig_name = section_name(sh_orig);
3648c49b5d4SDavid Brazdil 	Elf64_Rela *rela;
3658c49b5d4SDavid Brazdil 
3668c49b5d4SDavid Brazdil 	/* Skip all non-hyp sections. */
3678c49b5d4SDavid Brazdil 	if (!starts_with(sh_orig_name, HYP_SECTION_PREFIX))
3688c49b5d4SDavid Brazdil 		return;
3698c49b5d4SDavid Brazdil 
3708c49b5d4SDavid Brazdil 	emit_section_prologue(sh_orig_name);
3718c49b5d4SDavid Brazdil 
3728c49b5d4SDavid Brazdil 	for_each_rela(sh_rela, rela) {
373bc93763fSMarc Zyngier 		uint32_t type = (uint32_t)elf64toh(rela->r_info);
3748c49b5d4SDavid Brazdil 
3758c49b5d4SDavid Brazdil 		/* Check that rela points inside the relocated section. */
376bc93763fSMarc Zyngier 		assert_lt(elf64toh(rela->r_offset), elf64toh(sh_orig->sh_size), "0x%lx");
3778c49b5d4SDavid Brazdil 
3788c49b5d4SDavid Brazdil 		switch (type) {
3798c49b5d4SDavid Brazdil 		/*
3808c49b5d4SDavid Brazdil 		 * Data relocations to generate absolute addressing.
3818c49b5d4SDavid Brazdil 		 * Emit a hyp relocation.
3828c49b5d4SDavid Brazdil 		 */
3838c49b5d4SDavid Brazdil 		case R_AARCH64_ABS64:
3848c49b5d4SDavid Brazdil 			emit_rela_abs64(rela, sh_orig_name);
3858c49b5d4SDavid Brazdil 			break;
386*77e06b30SDavid Brazdil 		/* Allow position-relative data relocations. */
387*77e06b30SDavid Brazdil 		case R_AARCH64_PREL64:
388*77e06b30SDavid Brazdil 		case R_AARCH64_PREL32:
389*77e06b30SDavid Brazdil 		case R_AARCH64_PREL16:
390*77e06b30SDavid Brazdil 		case R_AARCH64_PLT32:
391*77e06b30SDavid Brazdil 			break;
3928c49b5d4SDavid Brazdil 		/* Allow relocations to generate PC-relative addressing. */
3938c49b5d4SDavid Brazdil 		case R_AARCH64_LD_PREL_LO19:
3948c49b5d4SDavid Brazdil 		case R_AARCH64_ADR_PREL_LO21:
3958c49b5d4SDavid Brazdil 		case R_AARCH64_ADR_PREL_PG_HI21:
3968c49b5d4SDavid Brazdil 		case R_AARCH64_ADR_PREL_PG_HI21_NC:
3978c49b5d4SDavid Brazdil 		case R_AARCH64_ADD_ABS_LO12_NC:
3988c49b5d4SDavid Brazdil 		case R_AARCH64_LDST8_ABS_LO12_NC:
3998c49b5d4SDavid Brazdil 		case R_AARCH64_LDST16_ABS_LO12_NC:
4008c49b5d4SDavid Brazdil 		case R_AARCH64_LDST32_ABS_LO12_NC:
4018c49b5d4SDavid Brazdil 		case R_AARCH64_LDST64_ABS_LO12_NC:
4028c49b5d4SDavid Brazdil 		case R_AARCH64_LDST128_ABS_LO12_NC:
4038c49b5d4SDavid Brazdil 			break;
4048c49b5d4SDavid Brazdil 		/* Allow relative relocations for control-flow instructions. */
4058c49b5d4SDavid Brazdil 		case R_AARCH64_TSTBR14:
4068c49b5d4SDavid Brazdil 		case R_AARCH64_CONDBR19:
4078c49b5d4SDavid Brazdil 		case R_AARCH64_JUMP26:
4088c49b5d4SDavid Brazdil 		case R_AARCH64_CALL26:
4098c49b5d4SDavid Brazdil 			break;
4108c49b5d4SDavid Brazdil 		/* Allow group relocations to create PC-relative offset inline. */
4118c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G0:
4128c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G0_NC:
4138c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G1:
4148c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G1_NC:
4158c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G2:
4168c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G2_NC:
4178c49b5d4SDavid Brazdil 		case R_AARCH64_MOVW_PREL_G3:
4188c49b5d4SDavid Brazdil 			break;
4198c49b5d4SDavid Brazdil 		default:
4208c49b5d4SDavid Brazdil 			fatal_error("Unexpected RELA type %u", type);
4218c49b5d4SDavid Brazdil 		}
4228c49b5d4SDavid Brazdil 	}
4238c49b5d4SDavid Brazdil }
4248c49b5d4SDavid Brazdil 
4258c49b5d4SDavid Brazdil /* Iterate over all sections and emit hyp relocation data for RELA sections. */
emit_all_relocs(void)4268c49b5d4SDavid Brazdil static void emit_all_relocs(void)
4278c49b5d4SDavid Brazdil {
4288c49b5d4SDavid Brazdil 	Elf64_Shdr *shdr;
4298c49b5d4SDavid Brazdil 
4308c49b5d4SDavid Brazdil 	for_each_section(shdr) {
431bc93763fSMarc Zyngier 		switch (elf32toh(shdr->sh_type)) {
4328c49b5d4SDavid Brazdil 		case SHT_REL:
4338c49b5d4SDavid Brazdil 			fatal_error("Unexpected SHT_REL section \"%s\"",
4348c49b5d4SDavid Brazdil 				section_name(shdr));
4358c49b5d4SDavid Brazdil 		case SHT_RELA:
4368c49b5d4SDavid Brazdil 			emit_rela_section(shdr);
4378c49b5d4SDavid Brazdil 			break;
4388c49b5d4SDavid Brazdil 		}
4398c49b5d4SDavid Brazdil 	}
4408c49b5d4SDavid Brazdil }
4418c49b5d4SDavid Brazdil 
main(int argc,const char ** argv)4428c49b5d4SDavid Brazdil int main(int argc, const char **argv)
4438c49b5d4SDavid Brazdil {
4448c49b5d4SDavid Brazdil 	if (argc != 2) {
4458c49b5d4SDavid Brazdil 		fprintf(stderr, "Usage: %s <elf_input>\n", argv[0]);
4468c49b5d4SDavid Brazdil 		return EXIT_FAILURE;
4478c49b5d4SDavid Brazdil 	}
4488c49b5d4SDavid Brazdil 
4498c49b5d4SDavid Brazdil 	init_elf(argv[1]);
4508c49b5d4SDavid Brazdil 
4518c49b5d4SDavid Brazdil 	emit_prologue();
4528c49b5d4SDavid Brazdil 	emit_all_relocs();
4538c49b5d4SDavid Brazdil 	emit_epilogue();
4548c49b5d4SDavid Brazdil 
4558c49b5d4SDavid Brazdil 	return EXIT_SUCCESS;
4568c49b5d4SDavid Brazdil }
457