|  | /* | 
|  | * Post-process a vdso elf image for inclusion into qemu. | 
|  | * | 
|  | * Copyright 2023 Linaro, Ltd. | 
|  | * | 
|  | * SPDX-License-Identifier: GPL-2.0-or-later | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdbool.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <endian.h> | 
|  | #include <unistd.h> | 
|  | #include "elf.h" | 
|  |  | 
|  |  | 
|  | #define bswap_(p)  _Generic(*(p), \ | 
|  | uint16_t: __builtin_bswap16,       \ | 
|  | uint32_t: __builtin_bswap32,       \ | 
|  | uint64_t: __builtin_bswap64,       \ | 
|  | int16_t: __builtin_bswap16,        \ | 
|  | int32_t: __builtin_bswap32,        \ | 
|  | int64_t: __builtin_bswap64) | 
|  | #define bswaps(p) (*(p) = bswap_(p)(*(p))) | 
|  |  | 
|  | static void output_reloc(FILE *outf, void *buf, void *loc) | 
|  | { | 
|  | fprintf(outf, "    0x%08tx,\n", loc - buf); | 
|  | } | 
|  |  | 
|  | static const char *sigreturn_sym; | 
|  | static const char *rt_sigreturn_sym; | 
|  |  | 
|  | static unsigned sigreturn_addr; | 
|  | static unsigned rt_sigreturn_addr; | 
|  |  | 
|  | #define N 32 | 
|  | #define elfN(x)  elf32_##x | 
|  | #define ElfN(x)  Elf32_##x | 
|  | #include "gen-vdso-elfn.c.inc" | 
|  | #undef N | 
|  | #undef elfN | 
|  | #undef ElfN | 
|  |  | 
|  | #define N 64 | 
|  | #define elfN(x)  elf64_##x | 
|  | #define ElfN(x)  Elf64_##x | 
|  | #include "gen-vdso-elfn.c.inc" | 
|  | #undef N | 
|  | #undef elfN | 
|  | #undef ElfN | 
|  |  | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | FILE *inf, *outf; | 
|  | long total_len; | 
|  | const char *prefix = "vdso"; | 
|  | const char *inf_name; | 
|  | const char *outf_name = NULL; | 
|  | unsigned char *buf; | 
|  | bool need_bswap; | 
|  |  | 
|  | while (1) { | 
|  | int opt = getopt(argc, argv, "o:p:r:s:"); | 
|  | if (opt < 0) { | 
|  | break; | 
|  | } | 
|  | switch (opt) { | 
|  | case 'o': | 
|  | outf_name = optarg; | 
|  | break; | 
|  | case 'p': | 
|  | prefix = optarg; | 
|  | break; | 
|  | case 'r': | 
|  | rt_sigreturn_sym = optarg; | 
|  | break; | 
|  | case 's': | 
|  | sigreturn_sym = optarg; | 
|  | break; | 
|  | default: | 
|  | usage: | 
|  | fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] " | 
|  | "[-s sigreturn-name] -o output-file input-file\n"); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (optind >= argc || outf_name == NULL) { | 
|  | goto usage; | 
|  | } | 
|  | inf_name = argv[optind]; | 
|  |  | 
|  | /* | 
|  | * Open the input and output files. | 
|  | */ | 
|  | inf = fopen(inf_name, "rb"); | 
|  | if (inf == NULL) { | 
|  | goto perror_inf; | 
|  | } | 
|  | outf = fopen(outf_name, "w"); | 
|  | if (outf == NULL) { | 
|  | goto perror_outf; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read the input file into a buffer. | 
|  | * We expect the vdso to be small, on the order of one page, | 
|  | * therefore we do not expect a partial read. | 
|  | */ | 
|  | fseek(inf, 0, SEEK_END); | 
|  | total_len = ftell(inf); | 
|  | fseek(inf, 0, SEEK_SET); | 
|  |  | 
|  | buf = malloc(total_len); | 
|  | if (buf == NULL) { | 
|  | goto perror_inf; | 
|  | } | 
|  |  | 
|  | errno = 0; | 
|  | if (fread(buf, 1, total_len, inf) != total_len) { | 
|  | if (errno) { | 
|  | goto perror_inf; | 
|  | } | 
|  | fprintf(stderr, "%s: incomplete read\n", inf_name); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  | fclose(inf); | 
|  |  | 
|  | /* | 
|  | * Identify which elf flavor we're processing. | 
|  | * The first 16 bytes of the file are e_ident. | 
|  | */ | 
|  |  | 
|  | if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 || | 
|  | buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) { | 
|  | fprintf(stderr, "%s: not an elf file\n", inf_name); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  | switch (buf[EI_DATA]) { | 
|  | case ELFDATA2LSB: | 
|  | need_bswap = BYTE_ORDER != LITTLE_ENDIAN; | 
|  | break; | 
|  | case ELFDATA2MSB: | 
|  | need_bswap = BYTE_ORDER != BIG_ENDIAN; | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n", | 
|  | inf_name, buf[EI_DATA]); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * We need to relocate the VDSO image.  The one built into the kernel | 
|  | * is built for a fixed address.  The one we built for QEMU is not, | 
|  | * since that requires close control of the guest address space. | 
|  | * | 
|  | * Output relocation addresses as we go. | 
|  | */ | 
|  |  | 
|  | fprintf(outf, | 
|  | "/* Automatically generated by linux-user/gen-vdso.c. */\n" | 
|  | "\n" | 
|  | "static const unsigned %s_relocs[] = {\n", prefix); | 
|  |  | 
|  | switch (buf[EI_CLASS]) { | 
|  | case ELFCLASS32: | 
|  | elf32_process(outf, buf, total_len, need_bswap); | 
|  | break; | 
|  | case ELFCLASS64: | 
|  | elf64_process(outf, buf, total_len, need_bswap); | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n", | 
|  | inf_name, buf[EI_CLASS]); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  |  | 
|  | fprintf(outf, "};\n\n");   /* end vdso_relocs. */ | 
|  |  | 
|  | /* | 
|  | * Write out the vdso image now, after we made local changes. | 
|  | */ | 
|  | fprintf(outf, | 
|  | "static const uint8_t %s_image[] = {", | 
|  | prefix); | 
|  | for (long i = 0; i < total_len; ++i) { | 
|  | if (i % 12 == 0) { | 
|  | fputs("\n   ", outf); | 
|  | } | 
|  | fprintf(outf, " 0x%02x,", buf[i]); | 
|  | } | 
|  | fprintf(outf, "\n};\n\n"); | 
|  |  | 
|  | fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix); | 
|  | fprintf(outf, "    .image = %s_image,\n", prefix); | 
|  | fprintf(outf, "    .relocs = %s_relocs,\n", prefix); | 
|  | fprintf(outf, "    .image_size = sizeof(%s_image),\n", prefix); | 
|  | fprintf(outf, "    .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix); | 
|  | fprintf(outf, "    .sigreturn_ofs = 0x%x,\n", sigreturn_addr); | 
|  | fprintf(outf, "    .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); | 
|  | fprintf(outf, "};\n"); | 
|  |  | 
|  | /* | 
|  | * Everything should have gone well. | 
|  | */ | 
|  | if (fclose(outf)) { | 
|  | goto perror_outf; | 
|  | } | 
|  | return EXIT_SUCCESS; | 
|  |  | 
|  | perror_inf: | 
|  | perror(inf_name); | 
|  | return EXIT_FAILURE; | 
|  |  | 
|  | perror_outf: | 
|  | perror(outf_name); | 
|  | return EXIT_FAILURE; | 
|  | } |