cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gen-hyprel.c (12909B)


      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}