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> 28*bc93763fSMarc 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 40*bc93763fSMarc Zyngier #include <generated/autoconf.h> 41*bc93763fSMarc 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 538c49b5d4SDavid Brazdil #ifndef R_AARCH64_LD_PREL_LO19 548c49b5d4SDavid Brazdil #define R_AARCH64_LD_PREL_LO19 273 558c49b5d4SDavid Brazdil #endif 568c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADR_PREL_LO21 578c49b5d4SDavid Brazdil #define R_AARCH64_ADR_PREL_LO21 274 588c49b5d4SDavid Brazdil #endif 598c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADR_PREL_PG_HI21 608c49b5d4SDavid Brazdil #define R_AARCH64_ADR_PREL_PG_HI21 275 618c49b5d4SDavid Brazdil #endif 628c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADR_PREL_PG_HI21_NC 638c49b5d4SDavid Brazdil #define R_AARCH64_ADR_PREL_PG_HI21_NC 276 648c49b5d4SDavid Brazdil #endif 658c49b5d4SDavid Brazdil #ifndef R_AARCH64_ADD_ABS_LO12_NC 668c49b5d4SDavid Brazdil #define R_AARCH64_ADD_ABS_LO12_NC 277 678c49b5d4SDavid Brazdil #endif 688c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST8_ABS_LO12_NC 698c49b5d4SDavid Brazdil #define R_AARCH64_LDST8_ABS_LO12_NC 278 708c49b5d4SDavid Brazdil #endif 718c49b5d4SDavid Brazdil #ifndef R_AARCH64_TSTBR14 728c49b5d4SDavid Brazdil #define R_AARCH64_TSTBR14 279 738c49b5d4SDavid Brazdil #endif 748c49b5d4SDavid Brazdil #ifndef R_AARCH64_CONDBR19 758c49b5d4SDavid Brazdil #define R_AARCH64_CONDBR19 280 768c49b5d4SDavid Brazdil #endif 778c49b5d4SDavid Brazdil #ifndef R_AARCH64_JUMP26 788c49b5d4SDavid Brazdil #define R_AARCH64_JUMP26 282 798c49b5d4SDavid Brazdil #endif 808c49b5d4SDavid Brazdil #ifndef R_AARCH64_CALL26 818c49b5d4SDavid Brazdil #define R_AARCH64_CALL26 283 828c49b5d4SDavid Brazdil #endif 838c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST16_ABS_LO12_NC 848c49b5d4SDavid Brazdil #define R_AARCH64_LDST16_ABS_LO12_NC 284 858c49b5d4SDavid Brazdil #endif 868c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST32_ABS_LO12_NC 878c49b5d4SDavid Brazdil #define R_AARCH64_LDST32_ABS_LO12_NC 285 888c49b5d4SDavid Brazdil #endif 898c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST64_ABS_LO12_NC 908c49b5d4SDavid Brazdil #define R_AARCH64_LDST64_ABS_LO12_NC 286 918c49b5d4SDavid Brazdil #endif 928c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G0 938c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G0 287 948c49b5d4SDavid Brazdil #endif 958c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G0_NC 968c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G0_NC 288 978c49b5d4SDavid Brazdil #endif 988c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G1 998c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G1 289 1008c49b5d4SDavid Brazdil #endif 1018c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G1_NC 1028c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G1_NC 290 1038c49b5d4SDavid Brazdil #endif 1048c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G2 1058c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G2 291 1068c49b5d4SDavid Brazdil #endif 1078c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G2_NC 1088c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G2_NC 292 1098c49b5d4SDavid Brazdil #endif 1108c49b5d4SDavid Brazdil #ifndef R_AARCH64_MOVW_PREL_G3 1118c49b5d4SDavid Brazdil #define R_AARCH64_MOVW_PREL_G3 293 1128c49b5d4SDavid Brazdil #endif 1138c49b5d4SDavid Brazdil #ifndef R_AARCH64_LDST128_ABS_LO12_NC 1148c49b5d4SDavid Brazdil #define R_AARCH64_LDST128_ABS_LO12_NC 299 1158c49b5d4SDavid Brazdil #endif 1168c49b5d4SDavid Brazdil 1178c49b5d4SDavid Brazdil /* Global state of the processed ELF. */ 1188c49b5d4SDavid Brazdil static struct { 1198c49b5d4SDavid Brazdil const char *path; 1208c49b5d4SDavid Brazdil char *begin; 1218c49b5d4SDavid Brazdil size_t size; 1228c49b5d4SDavid Brazdil Elf64_Ehdr *ehdr; 1238c49b5d4SDavid Brazdil Elf64_Shdr *sh_table; 1248c49b5d4SDavid Brazdil const char *sh_string; 1258c49b5d4SDavid Brazdil } elf; 1268c49b5d4SDavid Brazdil 127*bc93763fSMarc Zyngier #if defined(CONFIG_CPU_LITTLE_ENDIAN) 128*bc93763fSMarc Zyngier 129*bc93763fSMarc Zyngier #define elf16toh(x) le16toh(x) 130*bc93763fSMarc Zyngier #define elf32toh(x) le32toh(x) 131*bc93763fSMarc Zyngier #define elf64toh(x) le64toh(x) 132*bc93763fSMarc Zyngier 133*bc93763fSMarc Zyngier #define ELFENDIAN ELFDATA2LSB 134*bc93763fSMarc Zyngier 135*bc93763fSMarc Zyngier #elif defined(CONFIG_CPU_BIG_ENDIAN) 136*bc93763fSMarc Zyngier 137*bc93763fSMarc Zyngier #define elf16toh(x) be16toh(x) 138*bc93763fSMarc Zyngier #define elf32toh(x) be32toh(x) 139*bc93763fSMarc Zyngier #define elf64toh(x) be64toh(x) 140*bc93763fSMarc Zyngier 141*bc93763fSMarc Zyngier #define ELFENDIAN ELFDATA2MSB 142*bc93763fSMarc Zyngier 143*bc93763fSMarc Zyngier #else 144*bc93763fSMarc Zyngier 145*bc93763fSMarc Zyngier #error PDP-endian sadly unsupported... 146*bc93763fSMarc Zyngier 147*bc93763fSMarc Zyngier #endif 148*bc93763fSMarc Zyngier 1498c49b5d4SDavid Brazdil #define fatal_error(fmt, ...) \ 1508c49b5d4SDavid Brazdil ({ \ 1518c49b5d4SDavid Brazdil fprintf(stderr, "error: %s: " fmt "\n", \ 1528c49b5d4SDavid Brazdil elf.path, ## __VA_ARGS__); \ 1538c49b5d4SDavid Brazdil exit(EXIT_FAILURE); \ 1548c49b5d4SDavid Brazdil __builtin_unreachable(); \ 1558c49b5d4SDavid Brazdil }) 1568c49b5d4SDavid Brazdil 1578c49b5d4SDavid Brazdil #define fatal_perror(msg) \ 1588c49b5d4SDavid Brazdil ({ \ 1598c49b5d4SDavid Brazdil fprintf(stderr, "error: %s: " msg ": %s\n", \ 1608c49b5d4SDavid Brazdil elf.path, strerror(errno)); \ 1618c49b5d4SDavid Brazdil exit(EXIT_FAILURE); \ 1628c49b5d4SDavid Brazdil __builtin_unreachable(); \ 1638c49b5d4SDavid Brazdil }) 1648c49b5d4SDavid Brazdil 1658c49b5d4SDavid Brazdil #define assert_op(lhs, rhs, fmt, op) \ 1668c49b5d4SDavid Brazdil ({ \ 1678c49b5d4SDavid Brazdil typeof(lhs) _lhs = (lhs); \ 1688c49b5d4SDavid Brazdil typeof(rhs) _rhs = (rhs); \ 1698c49b5d4SDavid Brazdil \ 1708c49b5d4SDavid Brazdil if (!(_lhs op _rhs)) { \ 1718c49b5d4SDavid Brazdil fatal_error("assertion " #lhs " " #op " " #rhs \ 1728c49b5d4SDavid Brazdil " failed (lhs=" fmt ", rhs=" fmt \ 1738c49b5d4SDavid Brazdil ", line=%d)", _lhs, _rhs, __LINE__); \ 1748c49b5d4SDavid Brazdil } \ 1758c49b5d4SDavid Brazdil }) 1768c49b5d4SDavid Brazdil 1778c49b5d4SDavid Brazdil #define assert_eq(lhs, rhs, fmt) assert_op(lhs, rhs, fmt, ==) 1788c49b5d4SDavid Brazdil #define assert_ne(lhs, rhs, fmt) assert_op(lhs, rhs, fmt, !=) 1798c49b5d4SDavid Brazdil #define assert_lt(lhs, rhs, fmt) assert_op(lhs, rhs, fmt, <) 1808c49b5d4SDavid Brazdil #define assert_ge(lhs, rhs, fmt) assert_op(lhs, rhs, fmt, >=) 1818c49b5d4SDavid Brazdil 1828c49b5d4SDavid Brazdil /* 1838c49b5d4SDavid Brazdil * Return a pointer of a given type at a given offset from 1848c49b5d4SDavid Brazdil * the beginning of the ELF file. 1858c49b5d4SDavid Brazdil */ 1868c49b5d4SDavid Brazdil #define elf_ptr(type, off) ((type *)(elf.begin + (off))) 1878c49b5d4SDavid Brazdil 1888c49b5d4SDavid Brazdil /* Iterate over all sections in the ELF. */ 1898c49b5d4SDavid Brazdil #define for_each_section(var) \ 190*bc93763fSMarc Zyngier for (var = elf.sh_table; var < elf.sh_table + elf16toh(elf.ehdr->e_shnum); ++var) 1918c49b5d4SDavid Brazdil 1928c49b5d4SDavid Brazdil /* Iterate over all Elf64_Rela relocations in a given section. */ 1938c49b5d4SDavid Brazdil #define for_each_rela(shdr, var) \ 194*bc93763fSMarc Zyngier for (var = elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset)); \ 195*bc93763fSMarc Zyngier var < elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset) + elf64toh(shdr->sh_size)); var++) 1968c49b5d4SDavid Brazdil 1978c49b5d4SDavid Brazdil /* True if a string starts with a given prefix. */ 1988c49b5d4SDavid Brazdil static inline bool starts_with(const char *str, const char *prefix) 1998c49b5d4SDavid Brazdil { 2008c49b5d4SDavid Brazdil return memcmp(str, prefix, strlen(prefix)) == 0; 2018c49b5d4SDavid Brazdil } 2028c49b5d4SDavid Brazdil 2038c49b5d4SDavid Brazdil /* Returns a string containing the name of a given section. */ 2048c49b5d4SDavid Brazdil static inline const char *section_name(Elf64_Shdr *shdr) 2058c49b5d4SDavid Brazdil { 206*bc93763fSMarc Zyngier return elf.sh_string + elf32toh(shdr->sh_name); 2078c49b5d4SDavid Brazdil } 2088c49b5d4SDavid Brazdil 2098c49b5d4SDavid Brazdil /* Returns a pointer to the first byte of section data. */ 2108c49b5d4SDavid Brazdil static inline const char *section_begin(Elf64_Shdr *shdr) 2118c49b5d4SDavid Brazdil { 212*bc93763fSMarc Zyngier return elf_ptr(char, elf64toh(shdr->sh_offset)); 2138c49b5d4SDavid Brazdil } 2148c49b5d4SDavid Brazdil 2158c49b5d4SDavid Brazdil /* Find a section by its offset from the beginning of the file. */ 2168c49b5d4SDavid Brazdil static inline Elf64_Shdr *section_by_off(Elf64_Off off) 2178c49b5d4SDavid Brazdil { 2188c49b5d4SDavid Brazdil assert_ne(off, 0UL, "%lu"); 2198c49b5d4SDavid Brazdil return elf_ptr(Elf64_Shdr, off); 2208c49b5d4SDavid Brazdil } 2218c49b5d4SDavid Brazdil 2228c49b5d4SDavid Brazdil /* Find a section by its index. */ 2238c49b5d4SDavid Brazdil static inline Elf64_Shdr *section_by_idx(uint16_t idx) 2248c49b5d4SDavid Brazdil { 2258c49b5d4SDavid Brazdil assert_ne(idx, SHN_UNDEF, "%u"); 2268c49b5d4SDavid Brazdil return &elf.sh_table[idx]; 2278c49b5d4SDavid Brazdil } 2288c49b5d4SDavid Brazdil 2298c49b5d4SDavid Brazdil /* 2308c49b5d4SDavid Brazdil * Memory-map the given ELF file, perform sanity checks, and 2318c49b5d4SDavid Brazdil * populate global state. 2328c49b5d4SDavid Brazdil */ 2338c49b5d4SDavid Brazdil static void init_elf(const char *path) 2348c49b5d4SDavid Brazdil { 2358c49b5d4SDavid Brazdil int fd, ret; 2368c49b5d4SDavid Brazdil struct stat stat; 2378c49b5d4SDavid Brazdil 2388c49b5d4SDavid Brazdil /* Store path in the global struct for error printing. */ 2398c49b5d4SDavid Brazdil elf.path = path; 2408c49b5d4SDavid Brazdil 2418c49b5d4SDavid Brazdil /* Open the ELF file. */ 2428c49b5d4SDavid Brazdil fd = open(path, O_RDONLY); 2438c49b5d4SDavid Brazdil if (fd < 0) 2448c49b5d4SDavid Brazdil fatal_perror("Could not open ELF file"); 2458c49b5d4SDavid Brazdil 2468c49b5d4SDavid Brazdil /* Get status of ELF file to obtain its size. */ 2478c49b5d4SDavid Brazdil ret = fstat(fd, &stat); 2488c49b5d4SDavid Brazdil if (ret < 0) { 2498c49b5d4SDavid Brazdil close(fd); 2508c49b5d4SDavid Brazdil fatal_perror("Could not get status of ELF file"); 2518c49b5d4SDavid Brazdil } 2528c49b5d4SDavid Brazdil 2538c49b5d4SDavid Brazdil /* mmap() the entire ELF file read-only at an arbitrary address. */ 2548c49b5d4SDavid Brazdil elf.begin = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 2558c49b5d4SDavid Brazdil if (elf.begin == MAP_FAILED) { 2568c49b5d4SDavid Brazdil close(fd); 2578c49b5d4SDavid Brazdil fatal_perror("Could not mmap ELF file"); 2588c49b5d4SDavid Brazdil } 2598c49b5d4SDavid Brazdil 2608c49b5d4SDavid Brazdil /* mmap() was successful, close the FD. */ 2618c49b5d4SDavid Brazdil close(fd); 2628c49b5d4SDavid Brazdil 2638c49b5d4SDavid Brazdil /* Get pointer to the ELF header. */ 2648c49b5d4SDavid Brazdil assert_ge(stat.st_size, sizeof(*elf.ehdr), "%lu"); 2658c49b5d4SDavid Brazdil elf.ehdr = elf_ptr(Elf64_Ehdr, 0); 2668c49b5d4SDavid Brazdil 2678c49b5d4SDavid Brazdil /* Check the ELF magic. */ 2688c49b5d4SDavid Brazdil assert_eq(elf.ehdr->e_ident[EI_MAG0], ELFMAG0, "0x%x"); 2698c49b5d4SDavid Brazdil assert_eq(elf.ehdr->e_ident[EI_MAG1], ELFMAG1, "0x%x"); 2708c49b5d4SDavid Brazdil assert_eq(elf.ehdr->e_ident[EI_MAG2], ELFMAG2, "0x%x"); 2718c49b5d4SDavid Brazdil assert_eq(elf.ehdr->e_ident[EI_MAG3], ELFMAG3, "0x%x"); 2728c49b5d4SDavid Brazdil 2738c49b5d4SDavid Brazdil /* Sanity check that this is an ELF64 relocatable object for AArch64. */ 2748c49b5d4SDavid Brazdil assert_eq(elf.ehdr->e_ident[EI_CLASS], ELFCLASS64, "%u"); 275*bc93763fSMarc Zyngier assert_eq(elf.ehdr->e_ident[EI_DATA], ELFENDIAN, "%u"); 276*bc93763fSMarc Zyngier assert_eq(elf16toh(elf.ehdr->e_type), ET_REL, "%u"); 277*bc93763fSMarc Zyngier assert_eq(elf16toh(elf.ehdr->e_machine), EM_AARCH64, "%u"); 2788c49b5d4SDavid Brazdil 2798c49b5d4SDavid Brazdil /* Populate fields of the global struct. */ 280*bc93763fSMarc Zyngier elf.sh_table = section_by_off(elf64toh(elf.ehdr->e_shoff)); 281*bc93763fSMarc Zyngier elf.sh_string = section_begin(section_by_idx(elf16toh(elf.ehdr->e_shstrndx))); 2828c49b5d4SDavid Brazdil } 2838c49b5d4SDavid Brazdil 2848c49b5d4SDavid Brazdil /* Print the prologue of the output ASM file. */ 2858c49b5d4SDavid Brazdil static void emit_prologue(void) 2868c49b5d4SDavid Brazdil { 2878c49b5d4SDavid Brazdil printf(".data\n" 2888c49b5d4SDavid Brazdil ".pushsection " HYP_RELOC_SECTION ", \"a\"\n"); 2898c49b5d4SDavid Brazdil } 2908c49b5d4SDavid Brazdil 2918c49b5d4SDavid Brazdil /* Print ASM statements needed as a prologue to a processed hyp section. */ 2928c49b5d4SDavid Brazdil static void emit_section_prologue(const char *sh_orig_name) 2938c49b5d4SDavid Brazdil { 2948c49b5d4SDavid Brazdil /* Declare the hyp section symbol. */ 2958c49b5d4SDavid Brazdil printf(".global %s%s\n", HYP_SECTION_SYMBOL_PREFIX, sh_orig_name); 2968c49b5d4SDavid Brazdil } 2978c49b5d4SDavid Brazdil 2988c49b5d4SDavid Brazdil /* 2998c49b5d4SDavid Brazdil * Print ASM statements to create a hyp relocation entry for a given 3008c49b5d4SDavid Brazdil * R_AARCH64_ABS64 relocation. 3018c49b5d4SDavid Brazdil * 3028c49b5d4SDavid Brazdil * The linker of vmlinux will populate the position given by `rela` with 3038c49b5d4SDavid Brazdil * an absolute 64-bit kernel VA. If the kernel is relocatable, it will 3048c49b5d4SDavid Brazdil * also generate a dynamic relocation entry so that the kernel can shift 3058c49b5d4SDavid Brazdil * the address at runtime for KASLR. 3068c49b5d4SDavid Brazdil * 3078c49b5d4SDavid Brazdil * Emit a 32-bit offset from the current address to the position given 3088c49b5d4SDavid Brazdil * by `rela`. This way the kernel can iterate over all kernel VAs used 3098c49b5d4SDavid Brazdil * by hyp at runtime and convert them to hyp VAs. However, that offset 3108c49b5d4SDavid Brazdil * will not be known until linking of `vmlinux`, so emit a PREL32 3118c49b5d4SDavid Brazdil * relocation referencing a symbol that the hyp linker script put at 3128c49b5d4SDavid Brazdil * the beginning of the relocated section + the offset from `rela`. 3138c49b5d4SDavid Brazdil */ 3148c49b5d4SDavid Brazdil static void emit_rela_abs64(Elf64_Rela *rela, const char *sh_orig_name) 3158c49b5d4SDavid Brazdil { 3168c49b5d4SDavid Brazdil /* Offset of this reloc from the beginning of HYP_RELOC_SECTION. */ 3178c49b5d4SDavid Brazdil static size_t reloc_offset; 3188c49b5d4SDavid Brazdil 3198c49b5d4SDavid Brazdil /* Create storage for the 32-bit offset. */ 3208c49b5d4SDavid Brazdil printf(".word 0\n"); 3218c49b5d4SDavid Brazdil 3228c49b5d4SDavid Brazdil /* 3238c49b5d4SDavid Brazdil * Create a PREL32 relocation which instructs the linker of `vmlinux` 3248c49b5d4SDavid Brazdil * to insert offset to position <base> + <offset>, where <base> is 3258c49b5d4SDavid Brazdil * a symbol at the beginning of the relocated section, and <offset> 3268c49b5d4SDavid Brazdil * is `rela->r_offset`. 3278c49b5d4SDavid Brazdil */ 3288c49b5d4SDavid Brazdil printf(".reloc %lu, R_AARCH64_PREL32, %s%s + 0x%lx\n", 3298c49b5d4SDavid Brazdil reloc_offset, HYP_SECTION_SYMBOL_PREFIX, sh_orig_name, 330*bc93763fSMarc Zyngier elf64toh(rela->r_offset)); 3318c49b5d4SDavid Brazdil 3328c49b5d4SDavid Brazdil reloc_offset += 4; 3338c49b5d4SDavid Brazdil } 3348c49b5d4SDavid Brazdil 3358c49b5d4SDavid Brazdil /* Print the epilogue of the output ASM file. */ 3368c49b5d4SDavid Brazdil static void emit_epilogue(void) 3378c49b5d4SDavid Brazdil { 3388c49b5d4SDavid Brazdil printf(".popsection\n"); 3398c49b5d4SDavid Brazdil } 3408c49b5d4SDavid Brazdil 3418c49b5d4SDavid Brazdil /* 3428c49b5d4SDavid Brazdil * Iterate over all RELA relocations in a given section and emit 3438c49b5d4SDavid Brazdil * hyp relocation data for all absolute addresses in hyp code/data. 3448c49b5d4SDavid Brazdil * 3458c49b5d4SDavid Brazdil * Static relocations that generate PC-relative-addressing are ignored. 3468c49b5d4SDavid Brazdil * Failure is reported for unexpected relocation types. 3478c49b5d4SDavid Brazdil */ 3488c49b5d4SDavid Brazdil static void emit_rela_section(Elf64_Shdr *sh_rela) 3498c49b5d4SDavid Brazdil { 350*bc93763fSMarc Zyngier Elf64_Shdr *sh_orig = &elf.sh_table[elf32toh(sh_rela->sh_info)]; 3518c49b5d4SDavid Brazdil const char *sh_orig_name = section_name(sh_orig); 3528c49b5d4SDavid Brazdil Elf64_Rela *rela; 3538c49b5d4SDavid Brazdil 3548c49b5d4SDavid Brazdil /* Skip all non-hyp sections. */ 3558c49b5d4SDavid Brazdil if (!starts_with(sh_orig_name, HYP_SECTION_PREFIX)) 3568c49b5d4SDavid Brazdil return; 3578c49b5d4SDavid Brazdil 3588c49b5d4SDavid Brazdil emit_section_prologue(sh_orig_name); 3598c49b5d4SDavid Brazdil 3608c49b5d4SDavid Brazdil for_each_rela(sh_rela, rela) { 361*bc93763fSMarc Zyngier uint32_t type = (uint32_t)elf64toh(rela->r_info); 3628c49b5d4SDavid Brazdil 3638c49b5d4SDavid Brazdil /* Check that rela points inside the relocated section. */ 364*bc93763fSMarc Zyngier assert_lt(elf64toh(rela->r_offset), elf64toh(sh_orig->sh_size), "0x%lx"); 3658c49b5d4SDavid Brazdil 3668c49b5d4SDavid Brazdil switch (type) { 3678c49b5d4SDavid Brazdil /* 3688c49b5d4SDavid Brazdil * Data relocations to generate absolute addressing. 3698c49b5d4SDavid Brazdil * Emit a hyp relocation. 3708c49b5d4SDavid Brazdil */ 3718c49b5d4SDavid Brazdil case R_AARCH64_ABS64: 3728c49b5d4SDavid Brazdil emit_rela_abs64(rela, sh_orig_name); 3738c49b5d4SDavid Brazdil break; 3748c49b5d4SDavid Brazdil /* Allow relocations to generate PC-relative addressing. */ 3758c49b5d4SDavid Brazdil case R_AARCH64_LD_PREL_LO19: 3768c49b5d4SDavid Brazdil case R_AARCH64_ADR_PREL_LO21: 3778c49b5d4SDavid Brazdil case R_AARCH64_ADR_PREL_PG_HI21: 3788c49b5d4SDavid Brazdil case R_AARCH64_ADR_PREL_PG_HI21_NC: 3798c49b5d4SDavid Brazdil case R_AARCH64_ADD_ABS_LO12_NC: 3808c49b5d4SDavid Brazdil case R_AARCH64_LDST8_ABS_LO12_NC: 3818c49b5d4SDavid Brazdil case R_AARCH64_LDST16_ABS_LO12_NC: 3828c49b5d4SDavid Brazdil case R_AARCH64_LDST32_ABS_LO12_NC: 3838c49b5d4SDavid Brazdil case R_AARCH64_LDST64_ABS_LO12_NC: 3848c49b5d4SDavid Brazdil case R_AARCH64_LDST128_ABS_LO12_NC: 3858c49b5d4SDavid Brazdil break; 3868c49b5d4SDavid Brazdil /* Allow relative relocations for control-flow instructions. */ 3878c49b5d4SDavid Brazdil case R_AARCH64_TSTBR14: 3888c49b5d4SDavid Brazdil case R_AARCH64_CONDBR19: 3898c49b5d4SDavid Brazdil case R_AARCH64_JUMP26: 3908c49b5d4SDavid Brazdil case R_AARCH64_CALL26: 3918c49b5d4SDavid Brazdil break; 3928c49b5d4SDavid Brazdil /* Allow group relocations to create PC-relative offset inline. */ 3938c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G0: 3948c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G0_NC: 3958c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G1: 3968c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G1_NC: 3978c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G2: 3988c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G2_NC: 3998c49b5d4SDavid Brazdil case R_AARCH64_MOVW_PREL_G3: 4008c49b5d4SDavid Brazdil break; 4018c49b5d4SDavid Brazdil default: 4028c49b5d4SDavid Brazdil fatal_error("Unexpected RELA type %u", type); 4038c49b5d4SDavid Brazdil } 4048c49b5d4SDavid Brazdil } 4058c49b5d4SDavid Brazdil } 4068c49b5d4SDavid Brazdil 4078c49b5d4SDavid Brazdil /* Iterate over all sections and emit hyp relocation data for RELA sections. */ 4088c49b5d4SDavid Brazdil static void emit_all_relocs(void) 4098c49b5d4SDavid Brazdil { 4108c49b5d4SDavid Brazdil Elf64_Shdr *shdr; 4118c49b5d4SDavid Brazdil 4128c49b5d4SDavid Brazdil for_each_section(shdr) { 413*bc93763fSMarc Zyngier switch (elf32toh(shdr->sh_type)) { 4148c49b5d4SDavid Brazdil case SHT_REL: 4158c49b5d4SDavid Brazdil fatal_error("Unexpected SHT_REL section \"%s\"", 4168c49b5d4SDavid Brazdil section_name(shdr)); 4178c49b5d4SDavid Brazdil case SHT_RELA: 4188c49b5d4SDavid Brazdil emit_rela_section(shdr); 4198c49b5d4SDavid Brazdil break; 4208c49b5d4SDavid Brazdil } 4218c49b5d4SDavid Brazdil } 4228c49b5d4SDavid Brazdil } 4238c49b5d4SDavid Brazdil 4248c49b5d4SDavid Brazdil int main(int argc, const char **argv) 4258c49b5d4SDavid Brazdil { 4268c49b5d4SDavid Brazdil if (argc != 2) { 4278c49b5d4SDavid Brazdil fprintf(stderr, "Usage: %s <elf_input>\n", argv[0]); 4288c49b5d4SDavid Brazdil return EXIT_FAILURE; 4298c49b5d4SDavid Brazdil } 4308c49b5d4SDavid Brazdil 4318c49b5d4SDavid Brazdil init_elf(argv[1]); 4328c49b5d4SDavid Brazdil 4338c49b5d4SDavid Brazdil emit_prologue(); 4348c49b5d4SDavid Brazdil emit_all_relocs(); 4358c49b5d4SDavid Brazdil emit_epilogue(); 4368c49b5d4SDavid Brazdil 4378c49b5d4SDavid Brazdil return EXIT_SUCCESS; 4388c49b5d4SDavid Brazdil } 439