1 /* 2 * Post-process a vdso elf image for inclusion into qemu. 3 * 4 * Copyright 2023 Linaro, Ltd. 5 * 6 * SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 9 #include <stdlib.h> 10 #include <stdbool.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <string.h> 14 #include <errno.h> 15 #include <endian.h> 16 #include <unistd.h> 17 #include "elf.h" 18 19 20 #define bswap_(p) _Generic(*(p), \ 21 uint16_t: __builtin_bswap16, \ 22 uint32_t: __builtin_bswap32, \ 23 uint64_t: __builtin_bswap64, \ 24 int16_t: __builtin_bswap16, \ 25 int32_t: __builtin_bswap32, \ 26 int64_t: __builtin_bswap64) 27 #define bswaps(p) (*(p) = bswap_(p)(*(p))) 28 29 static void output_reloc(FILE *outf, void *buf, void *loc) 30 { 31 fprintf(outf, " 0x%08tx,\n", loc - buf); 32 } 33 34 static const char *sigreturn_sym; 35 static const char *rt_sigreturn_sym; 36 37 static unsigned sigreturn_addr; 38 static unsigned rt_sigreturn_addr; 39 40 #define N 32 41 #define elfN(x) elf32_##x 42 #define ElfN(x) Elf32_##x 43 #include "gen-vdso-elfn.c.inc" 44 #undef N 45 #undef elfN 46 #undef ElfN 47 48 #define N 64 49 #define elfN(x) elf64_##x 50 #define ElfN(x) Elf64_##x 51 #include "gen-vdso-elfn.c.inc" 52 #undef N 53 #undef elfN 54 #undef ElfN 55 56 57 int main(int argc, char **argv) 58 { 59 FILE *inf, *outf; 60 long total_len; 61 const char *prefix = "vdso"; 62 const char *inf_name; 63 const char *outf_name = NULL; 64 unsigned char *buf; 65 bool need_bswap; 66 67 while (1) { 68 int opt = getopt(argc, argv, "o:p:r:s:"); 69 if (opt < 0) { 70 break; 71 } 72 switch (opt) { 73 case 'o': 74 outf_name = optarg; 75 break; 76 case 'p': 77 prefix = optarg; 78 break; 79 case 'r': 80 rt_sigreturn_sym = optarg; 81 break; 82 case 's': 83 sigreturn_sym = optarg; 84 break; 85 default: 86 usage: 87 fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] " 88 "[-s sigreturn-name] -o output-file input-file\n"); 89 return EXIT_FAILURE; 90 } 91 } 92 93 if (optind >= argc || outf_name == NULL) { 94 goto usage; 95 } 96 inf_name = argv[optind]; 97 98 /* 99 * Open the input and output files. 100 */ 101 inf = fopen(inf_name, "rb"); 102 if (inf == NULL) { 103 goto perror_inf; 104 } 105 outf = fopen(outf_name, "w"); 106 if (outf == NULL) { 107 goto perror_outf; 108 } 109 110 /* 111 * Read the input file into a buffer. 112 * We expect the vdso to be small, on the order of one page, 113 * therefore we do not expect a partial read. 114 */ 115 fseek(inf, 0, SEEK_END); 116 total_len = ftell(inf); 117 fseek(inf, 0, SEEK_SET); 118 119 buf = malloc(total_len); 120 if (buf == NULL) { 121 goto perror_inf; 122 } 123 124 errno = 0; 125 if (fread(buf, 1, total_len, inf) != total_len) { 126 if (errno) { 127 goto perror_inf; 128 } 129 fprintf(stderr, "%s: incomplete read\n", inf_name); 130 return EXIT_FAILURE; 131 } 132 fclose(inf); 133 134 /* 135 * Identify which elf flavor we're processing. 136 * The first 16 bytes of the file are e_ident. 137 */ 138 139 if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 || 140 buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) { 141 fprintf(stderr, "%s: not an elf file\n", inf_name); 142 return EXIT_FAILURE; 143 } 144 switch (buf[EI_DATA]) { 145 case ELFDATA2LSB: 146 need_bswap = BYTE_ORDER != LITTLE_ENDIAN; 147 break; 148 case ELFDATA2MSB: 149 need_bswap = BYTE_ORDER != BIG_ENDIAN; 150 break; 151 default: 152 fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n", 153 inf_name, buf[EI_DATA]); 154 return EXIT_FAILURE; 155 } 156 157 /* 158 * We need to relocate the VDSO image. The one built into the kernel 159 * is built for a fixed address. The one we built for QEMU is not, 160 * since that requires close control of the guest address space. 161 * 162 * Output relocation addresses as we go. 163 */ 164 165 fprintf(outf, 166 "/* Automatically generated by linux-user/gen-vdso.c. */\n" 167 "\n" 168 "static const unsigned %s_relocs[] = {\n", prefix); 169 170 switch (buf[EI_CLASS]) { 171 case ELFCLASS32: 172 elf32_process(outf, buf, total_len, need_bswap); 173 break; 174 case ELFCLASS64: 175 elf64_process(outf, buf, total_len, need_bswap); 176 break; 177 default: 178 fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n", 179 inf_name, buf[EI_CLASS]); 180 return EXIT_FAILURE; 181 } 182 183 fprintf(outf, "};\n\n"); /* end vdso_relocs. */ 184 185 /* 186 * Write out the vdso image now, after we made local changes. 187 */ 188 fprintf(outf, 189 "static const uint8_t %s_image[] = {", 190 prefix); 191 for (long i = 0; i < total_len; ++i) { 192 if (i % 12 == 0) { 193 fputs("\n ", outf); 194 } 195 fprintf(outf, " 0x%02x,", buf[i]); 196 } 197 fprintf(outf, "\n};\n\n"); 198 199 fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix); 200 fprintf(outf, " .image = %s_image,\n", prefix); 201 fprintf(outf, " .relocs = %s_relocs,\n", prefix); 202 fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix); 203 fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix); 204 fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr); 205 fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); 206 fprintf(outf, "};\n"); 207 208 /* 209 * Everything should have gone well. 210 */ 211 if (fclose(outf)) { 212 goto perror_outf; 213 } 214 return EXIT_SUCCESS; 215 216 perror_inf: 217 perror(inf_name); 218 return EXIT_FAILURE; 219 220 perror_outf: 221 perror(outf_name); 222 return EXIT_FAILURE; 223 } 224