linux/arch/arm64/kvm/hyp/nvhe/gen-hyprel.c
<<
>>
Prefs
   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}
 457
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.