xref: /openbmc/qemu/linux-user/gen-vdso.c (revision 851ed57d7a24ddf234a90b5bb196a143c84c10bc)
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