12fa536d1SRichard Henderson /*
22fa536d1SRichard Henderson * Post-process a vdso elf image for inclusion into qemu.
32fa536d1SRichard Henderson *
42fa536d1SRichard Henderson * Copyright 2023 Linaro, Ltd.
52fa536d1SRichard Henderson *
62fa536d1SRichard Henderson * SPDX-License-Identifier: GPL-2.0-or-later
72fa536d1SRichard Henderson */
82fa536d1SRichard Henderson
92fa536d1SRichard Henderson #include <stdlib.h>
102fa536d1SRichard Henderson #include <stdbool.h>
112fa536d1SRichard Henderson #include <stdint.h>
122fa536d1SRichard Henderson #include <stdio.h>
132fa536d1SRichard Henderson #include <string.h>
142fa536d1SRichard Henderson #include <errno.h>
152fa536d1SRichard Henderson #include <endian.h>
162fa536d1SRichard Henderson #include <unistd.h>
172fa536d1SRichard Henderson #include "elf.h"
182fa536d1SRichard Henderson
192fa536d1SRichard Henderson
202fa536d1SRichard Henderson #define bswap_(p) _Generic(*(p), \
212fa536d1SRichard Henderson uint16_t: __builtin_bswap16, \
222fa536d1SRichard Henderson uint32_t: __builtin_bswap32, \
232fa536d1SRichard Henderson uint64_t: __builtin_bswap64, \
242fa536d1SRichard Henderson int16_t: __builtin_bswap16, \
252fa536d1SRichard Henderson int32_t: __builtin_bswap32, \
262fa536d1SRichard Henderson int64_t: __builtin_bswap64)
272fa536d1SRichard Henderson #define bswaps(p) (*(p) = bswap_(p)(*(p)))
282fa536d1SRichard Henderson
output_reloc(FILE * outf,void * buf,void * loc)292fa536d1SRichard Henderson static void output_reloc(FILE *outf, void *buf, void *loc)
302fa536d1SRichard Henderson {
312fa536d1SRichard Henderson fprintf(outf, " 0x%08tx,\n", loc - buf);
322fa536d1SRichard Henderson }
332fa536d1SRichard Henderson
342fa536d1SRichard Henderson static const char *sigreturn_sym;
352fa536d1SRichard Henderson static const char *rt_sigreturn_sym;
362fa536d1SRichard Henderson
372fa536d1SRichard Henderson static unsigned sigreturn_addr;
382fa536d1SRichard Henderson static unsigned rt_sigreturn_addr;
392fa536d1SRichard Henderson
402fa536d1SRichard Henderson #define N 32
412fa536d1SRichard Henderson #define elfN(x) elf32_##x
422fa536d1SRichard Henderson #define ElfN(x) Elf32_##x
432fa536d1SRichard Henderson #include "gen-vdso-elfn.c.inc"
442fa536d1SRichard Henderson #undef N
452fa536d1SRichard Henderson #undef elfN
462fa536d1SRichard Henderson #undef ElfN
472fa536d1SRichard Henderson
482fa536d1SRichard Henderson #define N 64
492fa536d1SRichard Henderson #define elfN(x) elf64_##x
502fa536d1SRichard Henderson #define ElfN(x) Elf64_##x
512fa536d1SRichard Henderson #include "gen-vdso-elfn.c.inc"
522fa536d1SRichard Henderson #undef N
532fa536d1SRichard Henderson #undef elfN
542fa536d1SRichard Henderson #undef ElfN
552fa536d1SRichard Henderson
562fa536d1SRichard Henderson
main(int argc,char ** argv)572fa536d1SRichard Henderson int main(int argc, char **argv)
582fa536d1SRichard Henderson {
592fa536d1SRichard Henderson FILE *inf, *outf;
602fa536d1SRichard Henderson long total_len;
612fa536d1SRichard Henderson const char *prefix = "vdso";
622fa536d1SRichard Henderson const char *inf_name;
632fa536d1SRichard Henderson const char *outf_name = NULL;
642fa536d1SRichard Henderson unsigned char *buf;
652fa536d1SRichard Henderson bool need_bswap;
662fa536d1SRichard Henderson
672fa536d1SRichard Henderson while (1) {
682fa536d1SRichard Henderson int opt = getopt(argc, argv, "o:p:r:s:");
692fa536d1SRichard Henderson if (opt < 0) {
702fa536d1SRichard Henderson break;
712fa536d1SRichard Henderson }
722fa536d1SRichard Henderson switch (opt) {
732fa536d1SRichard Henderson case 'o':
742fa536d1SRichard Henderson outf_name = optarg;
752fa536d1SRichard Henderson break;
762fa536d1SRichard Henderson case 'p':
772fa536d1SRichard Henderson prefix = optarg;
782fa536d1SRichard Henderson break;
792fa536d1SRichard Henderson case 'r':
802fa536d1SRichard Henderson rt_sigreturn_sym = optarg;
812fa536d1SRichard Henderson break;
822fa536d1SRichard Henderson case 's':
832fa536d1SRichard Henderson sigreturn_sym = optarg;
842fa536d1SRichard Henderson break;
852fa536d1SRichard Henderson default:
862fa536d1SRichard Henderson usage:
872fa536d1SRichard Henderson fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] "
882fa536d1SRichard Henderson "[-s sigreturn-name] -o output-file input-file\n");
892fa536d1SRichard Henderson return EXIT_FAILURE;
902fa536d1SRichard Henderson }
912fa536d1SRichard Henderson }
922fa536d1SRichard Henderson
932fa536d1SRichard Henderson if (optind >= argc || outf_name == NULL) {
942fa536d1SRichard Henderson goto usage;
952fa536d1SRichard Henderson }
962fa536d1SRichard Henderson inf_name = argv[optind];
972fa536d1SRichard Henderson
982fa536d1SRichard Henderson /*
992fa536d1SRichard Henderson * Open the input and output files.
1002fa536d1SRichard Henderson */
1012fa536d1SRichard Henderson inf = fopen(inf_name, "rb");
1022fa536d1SRichard Henderson if (inf == NULL) {
1032fa536d1SRichard Henderson goto perror_inf;
1042fa536d1SRichard Henderson }
1052fa536d1SRichard Henderson outf = fopen(outf_name, "w");
1062fa536d1SRichard Henderson if (outf == NULL) {
1072fa536d1SRichard Henderson goto perror_outf;
1082fa536d1SRichard Henderson }
1092fa536d1SRichard Henderson
1102fa536d1SRichard Henderson /*
1112fa536d1SRichard Henderson * Read the input file into a buffer.
1122fa536d1SRichard Henderson * We expect the vdso to be small, on the order of one page,
1132fa536d1SRichard Henderson * therefore we do not expect a partial read.
1142fa536d1SRichard Henderson */
1152fa536d1SRichard Henderson fseek(inf, 0, SEEK_END);
1162fa536d1SRichard Henderson total_len = ftell(inf);
1172fa536d1SRichard Henderson fseek(inf, 0, SEEK_SET);
1182fa536d1SRichard Henderson
1192fa536d1SRichard Henderson buf = malloc(total_len);
1202fa536d1SRichard Henderson if (buf == NULL) {
1212fa536d1SRichard Henderson goto perror_inf;
1222fa536d1SRichard Henderson }
1232fa536d1SRichard Henderson
1242fa536d1SRichard Henderson errno = 0;
1252fa536d1SRichard Henderson if (fread(buf, 1, total_len, inf) != total_len) {
1262fa536d1SRichard Henderson if (errno) {
1272fa536d1SRichard Henderson goto perror_inf;
1282fa536d1SRichard Henderson }
1292fa536d1SRichard Henderson fprintf(stderr, "%s: incomplete read\n", inf_name);
1302fa536d1SRichard Henderson return EXIT_FAILURE;
1312fa536d1SRichard Henderson }
1322fa536d1SRichard Henderson fclose(inf);
1332fa536d1SRichard Henderson
1342fa536d1SRichard Henderson /*
1352fa536d1SRichard Henderson * Identify which elf flavor we're processing.
1362fa536d1SRichard Henderson * The first 16 bytes of the file are e_ident.
1372fa536d1SRichard Henderson */
1382fa536d1SRichard Henderson
1392fa536d1SRichard Henderson if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 ||
1402fa536d1SRichard Henderson buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) {
1412fa536d1SRichard Henderson fprintf(stderr, "%s: not an elf file\n", inf_name);
1422fa536d1SRichard Henderson return EXIT_FAILURE;
1432fa536d1SRichard Henderson }
1442fa536d1SRichard Henderson switch (buf[EI_DATA]) {
1452fa536d1SRichard Henderson case ELFDATA2LSB:
1462fa536d1SRichard Henderson need_bswap = BYTE_ORDER != LITTLE_ENDIAN;
1472fa536d1SRichard Henderson break;
1482fa536d1SRichard Henderson case ELFDATA2MSB:
1492fa536d1SRichard Henderson need_bswap = BYTE_ORDER != BIG_ENDIAN;
1502fa536d1SRichard Henderson break;
1512fa536d1SRichard Henderson default:
1522fa536d1SRichard Henderson fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n",
1532fa536d1SRichard Henderson inf_name, buf[EI_DATA]);
1542fa536d1SRichard Henderson return EXIT_FAILURE;
1552fa536d1SRichard Henderson }
1562fa536d1SRichard Henderson
1572fa536d1SRichard Henderson /*
1582fa536d1SRichard Henderson * We need to relocate the VDSO image. The one built into the kernel
1592fa536d1SRichard Henderson * is built for a fixed address. The one we built for QEMU is not,
1602fa536d1SRichard Henderson * since that requires close control of the guest address space.
1612fa536d1SRichard Henderson *
1622fa536d1SRichard Henderson * Output relocation addresses as we go.
1632fa536d1SRichard Henderson */
1642fa536d1SRichard Henderson
165*6e9dcfb9SIlya Leoshkevich fprintf(outf,
166*6e9dcfb9SIlya Leoshkevich "/* Automatically generated by linux-user/gen-vdso.c. */\n"
167*6e9dcfb9SIlya Leoshkevich "\n"
168*6e9dcfb9SIlya Leoshkevich "static const unsigned %s_relocs[] = {\n", prefix);
1692fa536d1SRichard Henderson
1702fa536d1SRichard Henderson switch (buf[EI_CLASS]) {
1712fa536d1SRichard Henderson case ELFCLASS32:
172*6e9dcfb9SIlya Leoshkevich elf32_process(outf, buf, total_len, need_bswap);
1732fa536d1SRichard Henderson break;
1742fa536d1SRichard Henderson case ELFCLASS64:
175*6e9dcfb9SIlya Leoshkevich elf64_process(outf, buf, total_len, need_bswap);
1762fa536d1SRichard Henderson break;
1772fa536d1SRichard Henderson default:
1782fa536d1SRichard Henderson fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n",
1792fa536d1SRichard Henderson inf_name, buf[EI_CLASS]);
1802fa536d1SRichard Henderson return EXIT_FAILURE;
1812fa536d1SRichard Henderson }
1822fa536d1SRichard Henderson
1832fa536d1SRichard Henderson fprintf(outf, "};\n\n"); /* end vdso_relocs. */
1842fa536d1SRichard Henderson
185*6e9dcfb9SIlya Leoshkevich /*
186*6e9dcfb9SIlya Leoshkevich * Write out the vdso image now, after we made local changes.
187*6e9dcfb9SIlya Leoshkevich */
188*6e9dcfb9SIlya Leoshkevich fprintf(outf,
189*6e9dcfb9SIlya Leoshkevich "static const uint8_t %s_image[] = {",
190*6e9dcfb9SIlya Leoshkevich prefix);
191*6e9dcfb9SIlya Leoshkevich for (long i = 0; i < total_len; ++i) {
192*6e9dcfb9SIlya Leoshkevich if (i % 12 == 0) {
193*6e9dcfb9SIlya Leoshkevich fputs("\n ", outf);
194*6e9dcfb9SIlya Leoshkevich }
195*6e9dcfb9SIlya Leoshkevich fprintf(outf, " 0x%02x,", buf[i]);
196*6e9dcfb9SIlya Leoshkevich }
197*6e9dcfb9SIlya Leoshkevich fprintf(outf, "\n};\n\n");
198*6e9dcfb9SIlya Leoshkevich
1992fa536d1SRichard Henderson fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix);
2002fa536d1SRichard Henderson fprintf(outf, " .image = %s_image,\n", prefix);
2012fa536d1SRichard Henderson fprintf(outf, " .relocs = %s_relocs,\n", prefix);
2022fa536d1SRichard Henderson fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix);
2032fa536d1SRichard Henderson fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix);
2042fa536d1SRichard Henderson fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr);
2052fa536d1SRichard Henderson fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr);
2062fa536d1SRichard Henderson fprintf(outf, "};\n");
2072fa536d1SRichard Henderson
2082fa536d1SRichard Henderson /*
2092fa536d1SRichard Henderson * Everything should have gone well.
2102fa536d1SRichard Henderson */
2112fa536d1SRichard Henderson if (fclose(outf)) {
2122fa536d1SRichard Henderson goto perror_outf;
2132fa536d1SRichard Henderson }
2142fa536d1SRichard Henderson return EXIT_SUCCESS;
2152fa536d1SRichard Henderson
2162fa536d1SRichard Henderson perror_inf:
2172fa536d1SRichard Henderson perror(inf_name);
2182fa536d1SRichard Henderson return EXIT_FAILURE;
2192fa536d1SRichard Henderson
2202fa536d1SRichard Henderson perror_outf:
2212fa536d1SRichard Henderson perror(outf_name);
2222fa536d1SRichard Henderson return EXIT_FAILURE;
2232fa536d1SRichard Henderson }
224