]>
Commit | Line | Data |
---|---|---|
2fa536d1 RH |
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 | * Write out the vdso image now, before we make local changes. | |
136 | */ | |
137 | ||
138 | fprintf(outf, | |
139 | "/* Automatically generated from linux-user/gen-vdso.c. */\n" | |
140 | "\n" | |
141 | "static const uint8_t %s_image[] = {", | |
142 | prefix); | |
143 | for (long i = 0; i < total_len; ++i) { | |
144 | if (i % 12 == 0) { | |
145 | fputs("\n ", outf); | |
146 | } | |
147 | fprintf(outf, " 0x%02x,", buf[i]); | |
148 | } | |
149 | fprintf(outf, "\n};\n\n"); | |
150 | ||
151 | /* | |
152 | * Identify which elf flavor we're processing. | |
153 | * The first 16 bytes of the file are e_ident. | |
154 | */ | |
155 | ||
156 | if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1 || | |
157 | buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3) { | |
158 | fprintf(stderr, "%s: not an elf file\n", inf_name); | |
159 | return EXIT_FAILURE; | |
160 | } | |
161 | switch (buf[EI_DATA]) { | |
162 | case ELFDATA2LSB: | |
163 | need_bswap = BYTE_ORDER != LITTLE_ENDIAN; | |
164 | break; | |
165 | case ELFDATA2MSB: | |
166 | need_bswap = BYTE_ORDER != BIG_ENDIAN; | |
167 | break; | |
168 | default: | |
169 | fprintf(stderr, "%s: invalid elf EI_DATA (%u)\n", | |
170 | inf_name, buf[EI_DATA]); | |
171 | return EXIT_FAILURE; | |
172 | } | |
173 | ||
174 | /* | |
175 | * We need to relocate the VDSO image. The one built into the kernel | |
176 | * is built for a fixed address. The one we built for QEMU is not, | |
177 | * since that requires close control of the guest address space. | |
178 | * | |
179 | * Output relocation addresses as we go. | |
180 | */ | |
181 | ||
182 | fprintf(outf, "static const unsigned %s_relocs[] = {\n", prefix); | |
183 | ||
184 | switch (buf[EI_CLASS]) { | |
185 | case ELFCLASS32: | |
186 | elf32_process(outf, buf, need_bswap); | |
187 | break; | |
188 | case ELFCLASS64: | |
189 | elf64_process(outf, buf, need_bswap); | |
190 | break; | |
191 | default: | |
192 | fprintf(stderr, "%s: invalid elf EI_CLASS (%u)\n", | |
193 | inf_name, buf[EI_CLASS]); | |
194 | return EXIT_FAILURE; | |
195 | } | |
196 | ||
197 | fprintf(outf, "};\n\n"); /* end vdso_relocs. */ | |
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 | } |