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 = NULL, *outf = NULL; 60 long total_len; 61 const char *prefix = "vdso"; 62 const char *inf_name; 63 const char *outf_name = NULL; 64 unsigned char *buf = NULL; 65 bool need_bswap; 66 int ret = EXIT_FAILURE; 67 68 while (1) { 69 int opt = getopt(argc, argv, "o:p:r:s:"); 70 if (opt < 0) { 71 break; 72 } 73 switch (opt) { 74 case 'o': 75 outf_name = optarg; 76 break; 77 case 'p': 78 prefix = optarg; 79 break; 80 case 'r': 81 rt_sigreturn_sym = optarg; 82 break; 83 case 's': 84 sigreturn_sym = optarg; 85 break; 86 default: 87 usage: 88 fprintf(stderr, "usage: [-p prefix] [-r rt-sigreturn-name] " 89 "[-s sigreturn-name] -o output-file input-file\n"); 90 return EXIT_FAILURE; 91 } 92 } 93 94 if (optind >= argc || outf_name == NULL) { 95 goto usage; 96 } 97 inf_name = argv[optind]; 98 99 /* 100 * Open the input and output files. 101 */ 102 inf = fopen(inf_name, "rb"); 103 if (inf == NULL) { 104 goto perror_inf; 105 } 106 outf = fopen(outf_name, "w"); 107 if (outf == NULL) { 108 goto perror_outf; 109 } 110 111 /* 112 * Read the input file into a buffer. 113 * We expect the vdso to be small, on the order of one page, 114 * therefore we do not expect a partial read. 115 */ 116 if (fseek(inf, 0, SEEK_END) < 0) { 117 goto perror_inf; 118 } 119 total_len = ftell(inf); 120 if (total_len < 0) { 121 goto perror_inf; 122 } 123 if (fseek(inf, 0, SEEK_SET) < 0) { 124 goto perror_inf; 125 } 126 127 if (total_len < EI_NIDENT) { 128 fprintf(stderr, "%s: file too small (truncated?)\n", inf_name); 129 return EXIT_FAILURE; 130 } 131 132 buf = malloc(total_len); 133 if (buf == NULL) { 134 goto perror_inf; 135 } 136 137 errno = 0; 138 if (fread(buf, 1, total_len, inf) != total_len) { 139 if (errno) { 140 goto perror_inf; 141 } 142 fprintf(stderr, "%s: incomplete read\n", inf_name); 143 return EXIT_FAILURE; 144 } 145 146 /* 147 * Identify which elf flavor we're processing. 148 * The first 16 bytes of the file are e_ident. 149 */ 150 151 if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 || 152 buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) { 153 fprintf(stderr, "%s: not an elf file\n", inf_name); 154 return EXIT_FAILURE; 155 } 156 switch (buf[EI_DATA]) { 157 case ELFDATA2LSB: 158 need_bswap = BYTE_ORDER != LITTLE_ENDIAN; 159 break; 160 case ELFDATA2MSB: 161 need_bswap = BYTE_ORDER != BIG_ENDIAN; 162 break; 163 default: 164 fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n", 165 inf_name, buf[EI_DATA]); 166 return EXIT_FAILURE; 167 } 168 169 /* 170 * We need to relocate the VDSO image. The one built into the kernel 171 * is built for a fixed address. The one we built for QEMU is not, 172 * since that requires close control of the guest address space. 173 * 174 * Output relocation addresses as we go. 175 */ 176 177 fprintf(outf, 178 "/* Automatically generated by linux-user/gen-vdso.c. */\n" 179 "\n" 180 "static const unsigned %s_relocs[] = {\n", prefix); 181 182 switch (buf[EI_CLASS]) { 183 case ELFCLASS32: 184 elf32_process(outf, buf, total_len, need_bswap); 185 break; 186 case ELFCLASS64: 187 elf64_process(outf, buf, total_len, need_bswap); 188 break; 189 default: 190 fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n", 191 inf_name, buf[EI_CLASS]); 192 return EXIT_FAILURE; 193 } 194 195 fprintf(outf, "};\n\n"); /* end vdso_relocs. */ 196 197 /* 198 * Write out the vdso image now, after we made local changes. 199 */ 200 fprintf(outf, 201 "static const uint8_t %s_image[] = {", 202 prefix); 203 for (long i = 0; i < total_len; ++i) { 204 if (i % 12 == 0) { 205 fputs("\n ", outf); 206 } 207 fprintf(outf, " 0x%02x,", buf[i]); 208 } 209 fprintf(outf, "\n};\n\n"); 210 211 fprintf(outf, "static const VdsoImageInfo %s_image_info = {\n", prefix); 212 fprintf(outf, " .image = %s_image,\n", prefix); 213 fprintf(outf, " .relocs = %s_relocs,\n", prefix); 214 fprintf(outf, " .image_size = sizeof(%s_image),\n", prefix); 215 fprintf(outf, " .reloc_count = ARRAY_SIZE(%s_relocs),\n", prefix); 216 fprintf(outf, " .sigreturn_ofs = 0x%x,\n", sigreturn_addr); 217 fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); 218 fprintf(outf, "};\n"); 219 220 ret = EXIT_SUCCESS; 221 222 cleanup: 223 free(buf); 224 225 if (outf && fclose(outf) != 0) { 226 ret = EXIT_FAILURE; 227 } 228 if (inf && fclose(inf) != 0) { 229 ret = EXIT_FAILURE; 230 } 231 return ret; 232 233 perror_inf: 234 perror(inf_name); 235 goto cleanup; 236 237 perror_outf: 238 perror(outf_name); 239 goto cleanup; 240 } 241