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