Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2020 - Google LLC
0004  * Author: David Brazdil <dbrazdil@google.com>
0005  *
0006  * Generates relocation information used by the kernel to convert
0007  * absolute addresses in hyp data from kernel VAs to hyp VAs.
0008  *
0009  * This is necessary because hyp code is linked into the same binary
0010  * as the kernel but executes under different memory mappings.
0011  * If the compiler used absolute addressing, those addresses need to
0012  * be converted before they are used by hyp code.
0013  *
0014  * The input of this program is the relocatable ELF object containing
0015  * all hyp code/data, not yet linked into vmlinux. Hyp section names
0016  * should have been prefixed with `.hyp` at this point.
0017  *
0018  * The output (printed to stdout) is an assembly file containing
0019  * an array of 32-bit integers and static relocations that instruct
0020  * the linker of `vmlinux` to populate the array entries with offsets
0021  * to positions in the kernel binary containing VAs used by hyp code.
0022  *
0023  * Note that dynamic relocations could be used for the same purpose.
0024  * However, those are only generated if CONFIG_RELOCATABLE=y.
0025  */
0026 
0027 #include <elf.h>
0028 #include <endian.h>
0029 #include <errno.h>
0030 #include <fcntl.h>
0031 #include <stdbool.h>
0032 #include <stdio.h>
0033 #include <stdlib.h>
0034 #include <string.h>
0035 #include <sys/mman.h>
0036 #include <sys/types.h>
0037 #include <sys/stat.h>
0038 #include <unistd.h>
0039 
0040 #include <generated/autoconf.h>
0041 
0042 #define HYP_SECTION_PREFIX      ".hyp"
0043 #define HYP_RELOC_SECTION       ".hyp.reloc"
0044 #define HYP_SECTION_SYMBOL_PREFIX   "__hyp_section_"
0045 
0046 /*
0047  * AArch64 relocation type constants.
0048  * Included in case these are not defined in the host toolchain.
0049  */
0050 #ifndef R_AARCH64_ABS64
0051 #define R_AARCH64_ABS64         257
0052 #endif
0053 #ifndef R_AARCH64_PREL64
0054 #define R_AARCH64_PREL64        260
0055 #endif
0056 #ifndef R_AARCH64_PREL32
0057 #define R_AARCH64_PREL32        261
0058 #endif
0059 #ifndef R_AARCH64_PREL16
0060 #define R_AARCH64_PREL16        262
0061 #endif
0062 #ifndef R_AARCH64_PLT32
0063 #define R_AARCH64_PLT32         314
0064 #endif
0065 #ifndef R_AARCH64_LD_PREL_LO19
0066 #define R_AARCH64_LD_PREL_LO19      273
0067 #endif
0068 #ifndef R_AARCH64_ADR_PREL_LO21
0069 #define R_AARCH64_ADR_PREL_LO21     274
0070 #endif
0071 #ifndef R_AARCH64_ADR_PREL_PG_HI21
0072 #define R_AARCH64_ADR_PREL_PG_HI21  275
0073 #endif
0074 #ifndef R_AARCH64_ADR_PREL_PG_HI21_NC
0075 #define R_AARCH64_ADR_PREL_PG_HI21_NC   276
0076 #endif
0077 #ifndef R_AARCH64_ADD_ABS_LO12_NC
0078 #define R_AARCH64_ADD_ABS_LO12_NC   277
0079 #endif
0080 #ifndef R_AARCH64_LDST8_ABS_LO12_NC
0081 #define R_AARCH64_LDST8_ABS_LO12_NC 278
0082 #endif
0083 #ifndef R_AARCH64_TSTBR14
0084 #define R_AARCH64_TSTBR14       279
0085 #endif
0086 #ifndef R_AARCH64_CONDBR19
0087 #define R_AARCH64_CONDBR19      280
0088 #endif
0089 #ifndef R_AARCH64_JUMP26
0090 #define R_AARCH64_JUMP26        282
0091 #endif
0092 #ifndef R_AARCH64_CALL26
0093 #define R_AARCH64_CALL26        283
0094 #endif
0095 #ifndef R_AARCH64_LDST16_ABS_LO12_NC
0096 #define R_AARCH64_LDST16_ABS_LO12_NC    284
0097 #endif
0098 #ifndef R_AARCH64_LDST32_ABS_LO12_NC
0099 #define R_AARCH64_LDST32_ABS_LO12_NC    285
0100 #endif
0101 #ifndef R_AARCH64_LDST64_ABS_LO12_NC
0102 #define R_AARCH64_LDST64_ABS_LO12_NC    286
0103 #endif
0104 #ifndef R_AARCH64_MOVW_PREL_G0
0105 #define R_AARCH64_MOVW_PREL_G0      287
0106 #endif
0107 #ifndef R_AARCH64_MOVW_PREL_G0_NC
0108 #define R_AARCH64_MOVW_PREL_G0_NC   288
0109 #endif
0110 #ifndef R_AARCH64_MOVW_PREL_G1
0111 #define R_AARCH64_MOVW_PREL_G1      289
0112 #endif
0113 #ifndef R_AARCH64_MOVW_PREL_G1_NC
0114 #define R_AARCH64_MOVW_PREL_G1_NC   290
0115 #endif
0116 #ifndef R_AARCH64_MOVW_PREL_G2
0117 #define R_AARCH64_MOVW_PREL_G2      291
0118 #endif
0119 #ifndef R_AARCH64_MOVW_PREL_G2_NC
0120 #define R_AARCH64_MOVW_PREL_G2_NC   292
0121 #endif
0122 #ifndef R_AARCH64_MOVW_PREL_G3
0123 #define R_AARCH64_MOVW_PREL_G3      293
0124 #endif
0125 #ifndef R_AARCH64_LDST128_ABS_LO12_NC
0126 #define R_AARCH64_LDST128_ABS_LO12_NC   299
0127 #endif
0128 
0129 /* Global state of the processed ELF. */
0130 static struct {
0131     const char  *path;
0132     char        *begin;
0133     size_t      size;
0134     Elf64_Ehdr  *ehdr;
0135     Elf64_Shdr  *sh_table;
0136     const char  *sh_string;
0137 } elf;
0138 
0139 #if defined(CONFIG_CPU_LITTLE_ENDIAN)
0140 
0141 #define elf16toh(x) le16toh(x)
0142 #define elf32toh(x) le32toh(x)
0143 #define elf64toh(x) le64toh(x)
0144 
0145 #define ELFENDIAN   ELFDATA2LSB
0146 
0147 #elif defined(CONFIG_CPU_BIG_ENDIAN)
0148 
0149 #define elf16toh(x) be16toh(x)
0150 #define elf32toh(x) be32toh(x)
0151 #define elf64toh(x) be64toh(x)
0152 
0153 #define ELFENDIAN   ELFDATA2MSB
0154 
0155 #else
0156 
0157 #error PDP-endian sadly unsupported...
0158 
0159 #endif
0160 
0161 #define fatal_error(fmt, ...)                       \
0162     ({                              \
0163         fprintf(stderr, "error: %s: " fmt "\n",         \
0164             elf.path, ## __VA_ARGS__);          \
0165         exit(EXIT_FAILURE);                 \
0166         __builtin_unreachable();                \
0167     })
0168 
0169 #define fatal_perror(msg)                       \
0170     ({                              \
0171         fprintf(stderr, "error: %s: " msg ": %s\n",     \
0172             elf.path, strerror(errno));         \
0173         exit(EXIT_FAILURE);                 \
0174         __builtin_unreachable();                \
0175     })
0176 
0177 #define assert_op(lhs, rhs, fmt, op)                    \
0178     ({                              \
0179         typeof(lhs) _lhs = (lhs);               \
0180         typeof(rhs) _rhs = (rhs);               \
0181                                     \
0182         if (!(_lhs op _rhs)) {                  \
0183             fatal_error("assertion " #lhs " " #op " " #rhs  \
0184                 " failed (lhs=" fmt ", rhs=" fmt    \
0185                 ", line=%d)", _lhs, _rhs, __LINE__);    \
0186         }                           \
0187     })
0188 
0189 #define assert_eq(lhs, rhs, fmt)    assert_op(lhs, rhs, fmt, ==)
0190 #define assert_ne(lhs, rhs, fmt)    assert_op(lhs, rhs, fmt, !=)
0191 #define assert_lt(lhs, rhs, fmt)    assert_op(lhs, rhs, fmt, <)
0192 #define assert_ge(lhs, rhs, fmt)    assert_op(lhs, rhs, fmt, >=)
0193 
0194 /*
0195  * Return a pointer of a given type at a given offset from
0196  * the beginning of the ELF file.
0197  */
0198 #define elf_ptr(type, off) ((type *)(elf.begin + (off)))
0199 
0200 /* Iterate over all sections in the ELF. */
0201 #define for_each_section(var) \
0202     for (var = elf.sh_table; var < elf.sh_table + elf16toh(elf.ehdr->e_shnum); ++var)
0203 
0204 /* Iterate over all Elf64_Rela relocations in a given section. */
0205 #define for_each_rela(shdr, var)                    \
0206     for (var = elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset));  \
0207          var < elf_ptr(Elf64_Rela, elf64toh(shdr->sh_offset) + elf64toh(shdr->sh_size)); var++)
0208 
0209 /* True if a string starts with a given prefix. */
0210 static inline bool starts_with(const char *str, const char *prefix)
0211 {
0212     return memcmp(str, prefix, strlen(prefix)) == 0;
0213 }
0214 
0215 /* Returns a string containing the name of a given section. */
0216 static inline const char *section_name(Elf64_Shdr *shdr)
0217 {
0218     return elf.sh_string + elf32toh(shdr->sh_name);
0219 }
0220 
0221 /* Returns a pointer to the first byte of section data. */
0222 static inline const char *section_begin(Elf64_Shdr *shdr)
0223 {
0224     return elf_ptr(char, elf64toh(shdr->sh_offset));
0225 }
0226 
0227 /* Find a section by its offset from the beginning of the file. */
0228 static inline Elf64_Shdr *section_by_off(Elf64_Off off)
0229 {
0230     assert_ne(off, 0UL, "%lu");
0231     return elf_ptr(Elf64_Shdr, off);
0232 }
0233 
0234 /* Find a section by its index. */
0235 static inline Elf64_Shdr *section_by_idx(uint16_t idx)
0236 {
0237     assert_ne(idx, SHN_UNDEF, "%u");
0238     return &elf.sh_table[idx];
0239 }
0240 
0241 /*
0242  * Memory-map the given ELF file, perform sanity checks, and
0243  * populate global state.
0244  */
0245 static void init_elf(const char *path)
0246 {
0247     int fd, ret;
0248     struct stat stat;
0249 
0250     /* Store path in the global struct for error printing. */
0251     elf.path = path;
0252 
0253     /* Open the ELF file. */
0254     fd = open(path, O_RDONLY);
0255     if (fd < 0)
0256         fatal_perror("Could not open ELF file");
0257 
0258     /* Get status of ELF file to obtain its size. */
0259     ret = fstat(fd, &stat);
0260     if (ret < 0) {
0261         close(fd);
0262         fatal_perror("Could not get status of ELF file");
0263     }
0264 
0265     /* mmap() the entire ELF file read-only at an arbitrary address. */
0266     elf.begin = mmap(0, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
0267     if (elf.begin == MAP_FAILED) {
0268         close(fd);
0269         fatal_perror("Could not mmap ELF file");
0270     }
0271 
0272     /* mmap() was successful, close the FD. */
0273     close(fd);
0274 
0275     /* Get pointer to the ELF header. */
0276     assert_ge(stat.st_size, sizeof(*elf.ehdr), "%lu");
0277     elf.ehdr = elf_ptr(Elf64_Ehdr, 0);
0278 
0279     /* Check the ELF magic. */
0280     assert_eq(elf.ehdr->e_ident[EI_MAG0], ELFMAG0, "0x%x");
0281     assert_eq(elf.ehdr->e_ident[EI_MAG1], ELFMAG1, "0x%x");
0282     assert_eq(elf.ehdr->e_ident[EI_MAG2], ELFMAG2, "0x%x");
0283     assert_eq(elf.ehdr->e_ident[EI_MAG3], ELFMAG3, "0x%x");
0284 
0285     /* Sanity check that this is an ELF64 relocatable object for AArch64. */
0286     assert_eq(elf.ehdr->e_ident[EI_CLASS], ELFCLASS64, "%u");
0287     assert_eq(elf.ehdr->e_ident[EI_DATA], ELFENDIAN, "%u");
0288     assert_eq(elf16toh(elf.ehdr->e_type), ET_REL, "%u");
0289     assert_eq(elf16toh(elf.ehdr->e_machine), EM_AARCH64, "%u");
0290 
0291     /* Populate fields of the global struct. */
0292     elf.sh_table = section_by_off(elf64toh(elf.ehdr->e_shoff));
0293     elf.sh_string = section_begin(section_by_idx(elf16toh(elf.ehdr->e_shstrndx)));
0294 }
0295 
0296 /* Print the prologue of the output ASM file. */
0297 static void emit_prologue(void)
0298 {
0299     printf(".data\n"
0300            ".pushsection " HYP_RELOC_SECTION ", \"a\"\n");
0301 }
0302 
0303 /* Print ASM statements needed as a prologue to a processed hyp section. */
0304 static void emit_section_prologue(const char *sh_orig_name)
0305 {
0306     /* Declare the hyp section symbol. */
0307     printf(".global %s%s\n", HYP_SECTION_SYMBOL_PREFIX, sh_orig_name);
0308 }
0309 
0310 /*
0311  * Print ASM statements to create a hyp relocation entry for a given
0312  * R_AARCH64_ABS64 relocation.
0313  *
0314  * The linker of vmlinux will populate the position given by `rela` with
0315  * an absolute 64-bit kernel VA. If the kernel is relocatable, it will
0316  * also generate a dynamic relocation entry so that the kernel can shift
0317  * the address at runtime for KASLR.
0318  *
0319  * Emit a 32-bit offset from the current address to the position given
0320  * by `rela`. This way the kernel can iterate over all kernel VAs used
0321  * by hyp at runtime and convert them to hyp VAs. However, that offset
0322  * will not be known until linking of `vmlinux`, so emit a PREL32
0323  * relocation referencing a symbol that the hyp linker script put at
0324  * the beginning of the relocated section + the offset from `rela`.
0325  */
0326 static void emit_rela_abs64(Elf64_Rela *rela, const char *sh_orig_name)
0327 {
0328     /* Offset of this reloc from the beginning of HYP_RELOC_SECTION. */
0329     static size_t reloc_offset;
0330 
0331     /* Create storage for the 32-bit offset. */
0332     printf(".word 0\n");
0333 
0334     /*
0335      * Create a PREL32 relocation which instructs the linker of `vmlinux`
0336      * to insert offset to position <base> + <offset>, where <base> is
0337      * a symbol at the beginning of the relocated section, and <offset>
0338      * is `rela->r_offset`.
0339      */
0340     printf(".reloc %lu, R_AARCH64_PREL32, %s%s + 0x%lx\n",
0341            reloc_offset, HYP_SECTION_SYMBOL_PREFIX, sh_orig_name,
0342            elf64toh(rela->r_offset));
0343 
0344     reloc_offset += 4;
0345 }
0346 
0347 /* Print the epilogue of the output ASM file. */
0348 static void emit_epilogue(void)
0349 {
0350     printf(".popsection\n");
0351 }
0352 
0353 /*
0354  * Iterate over all RELA relocations in a given section and emit
0355  * hyp relocation data for all absolute addresses in hyp code/data.
0356  *
0357  * Static relocations that generate PC-relative-addressing are ignored.
0358  * Failure is reported for unexpected relocation types.
0359  */
0360 static void emit_rela_section(Elf64_Shdr *sh_rela)
0361 {
0362     Elf64_Shdr *sh_orig = &elf.sh_table[elf32toh(sh_rela->sh_info)];
0363     const char *sh_orig_name = section_name(sh_orig);
0364     Elf64_Rela *rela;
0365 
0366     /* Skip all non-hyp sections. */
0367     if (!starts_with(sh_orig_name, HYP_SECTION_PREFIX))
0368         return;
0369 
0370     emit_section_prologue(sh_orig_name);
0371 
0372     for_each_rela(sh_rela, rela) {
0373         uint32_t type = (uint32_t)elf64toh(rela->r_info);
0374 
0375         /* Check that rela points inside the relocated section. */
0376         assert_lt(elf64toh(rela->r_offset), elf64toh(sh_orig->sh_size), "0x%lx");
0377 
0378         switch (type) {
0379         /*
0380          * Data relocations to generate absolute addressing.
0381          * Emit a hyp relocation.
0382          */
0383         case R_AARCH64_ABS64:
0384             emit_rela_abs64(rela, sh_orig_name);
0385             break;
0386         /* Allow position-relative data relocations. */
0387         case R_AARCH64_PREL64:
0388         case R_AARCH64_PREL32:
0389         case R_AARCH64_PREL16:
0390         case R_AARCH64_PLT32:
0391             break;
0392         /* Allow relocations to generate PC-relative addressing. */
0393         case R_AARCH64_LD_PREL_LO19:
0394         case R_AARCH64_ADR_PREL_LO21:
0395         case R_AARCH64_ADR_PREL_PG_HI21:
0396         case R_AARCH64_ADR_PREL_PG_HI21_NC:
0397         case R_AARCH64_ADD_ABS_LO12_NC:
0398         case R_AARCH64_LDST8_ABS_LO12_NC:
0399         case R_AARCH64_LDST16_ABS_LO12_NC:
0400         case R_AARCH64_LDST32_ABS_LO12_NC:
0401         case R_AARCH64_LDST64_ABS_LO12_NC:
0402         case R_AARCH64_LDST128_ABS_LO12_NC:
0403             break;
0404         /* Allow relative relocations for control-flow instructions. */
0405         case R_AARCH64_TSTBR14:
0406         case R_AARCH64_CONDBR19:
0407         case R_AARCH64_JUMP26:
0408         case R_AARCH64_CALL26:
0409             break;
0410         /* Allow group relocations to create PC-relative offset inline. */
0411         case R_AARCH64_MOVW_PREL_G0:
0412         case R_AARCH64_MOVW_PREL_G0_NC:
0413         case R_AARCH64_MOVW_PREL_G1:
0414         case R_AARCH64_MOVW_PREL_G1_NC:
0415         case R_AARCH64_MOVW_PREL_G2:
0416         case R_AARCH64_MOVW_PREL_G2_NC:
0417         case R_AARCH64_MOVW_PREL_G3:
0418             break;
0419         default:
0420             fatal_error("Unexpected RELA type %u", type);
0421         }
0422     }
0423 }
0424 
0425 /* Iterate over all sections and emit hyp relocation data for RELA sections. */
0426 static void emit_all_relocs(void)
0427 {
0428     Elf64_Shdr *shdr;
0429 
0430     for_each_section(shdr) {
0431         switch (elf32toh(shdr->sh_type)) {
0432         case SHT_REL:
0433             fatal_error("Unexpected SHT_REL section \"%s\"",
0434                 section_name(shdr));
0435         case SHT_RELA:
0436             emit_rela_section(shdr);
0437             break;
0438         }
0439     }
0440 }
0441 
0442 int main(int argc, const char **argv)
0443 {
0444     if (argc != 2) {
0445         fprintf(stderr, "Usage: %s <elf_input>\n", argv[0]);
0446         return EXIT_FAILURE;
0447     }
0448 
0449     init_elf(argv[1]);
0450 
0451     emit_prologue();
0452     emit_all_relocs();
0453     emit_epilogue();
0454 
0455     return EXIT_SUCCESS;
0456 }