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 fseek(inf, 0, SEEK_END); 117 total_len = ftell(inf); 118 fseek(inf, 0, SEEK_SET); 119 120 buf = malloc(total_len); 121 if (buf == NULL) { 122 goto perror_inf; 123 } 124 125 errno = 0; 126 if (fread(buf, 1, total_len, inf) != total_len) { 127 if (errno) { 128 goto perror_inf; 129 } 130 fprintf(stderr, "%s: incomplete read\n", inf_name); 131 return EXIT_FAILURE; 132 } 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 ret = EXIT_SUCCESS; 209 210 cleanup: 211 free(buf); 212 213 if (outf && fclose(outf) != 0) { 214 ret = EXIT_FAILURE; 215 } 216 if (inf && fclose(inf) != 0) { 217 ret = EXIT_FAILURE; 218 } 219 return ret; 220 221 perror_inf: 222 perror(inf_name); 223 goto cleanup; 224 225 perror_outf: 226 perror(outf_name); 227 goto cleanup; 228 } 229