OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2014 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 /* | |
8 * This alters instructions for the IRT so that access to the TLS | |
9 * point to the IRT's TLS. | |
10 */ | |
11 | |
12 | |
13 #include <errno.h> | |
14 #include <stdio.h> | |
15 #include <string.h> | |
16 #include <sys/stat.h> | |
17 | |
18 #include "native_client/src/include/elf32.h" | |
19 #include "native_client/src/include/elf64.h" | |
20 #include "native_client/src/include/arm_sandbox.h" | |
21 #include "native_client/src/include/portability.h" | |
22 #include "native_client/src/trusted/validator_ragel/validator.h" | |
23 | |
24 static void *g_contents; | |
25 static size_t g_contents_size; | |
26 static uint8_t *g_code_start; | |
27 static uint32_t g_code_addr; | |
28 static int g_errors; | |
29 static int g_changes; | |
30 | |
31 static uint32_t GetInsnAddr(const void *insn) { | |
32 return (uint32_t) ((uint8_t *) insn - g_code_start) + g_code_addr; | |
33 } | |
34 | |
35 static void ReportError(const void *insn, const char *message) { | |
36 uint32_t insn_addr = GetInsnAddr(insn); | |
37 | |
38 fprintf(stderr, "%#x: %s\n", (unsigned int) insn_addr, message); | |
39 ++g_errors; | |
40 } | |
41 | |
42 static void EditArmCode(void *code, size_t code_length) { | |
43 Elf32_Word *const words = code; | |
44 Elf32_Word *const end = (void *) ((uint8_t *) code + code_length); | |
45 Elf32_Word *insn; | |
46 | |
47 for (insn = words; insn < end; ++insn) { | |
48 if (*insn == NACL_INSTR_ARM_LITERAL_POOL_HEAD) { | |
49 if ((insn - words) % 4 != 0) { | |
50 ReportError(insn, "Roadblock not at start of bundle"); | |
51 } else { | |
52 /* | |
53 * Ignore the rest of this bundle. | |
54 */ | |
55 insn += 3; | |
56 } | |
57 } else if ((*insn & 0x0FFF0FFF) == 0x05990000) { | |
58 /* | |
59 * This is 'ldr REG, [r9, #0]'. | |
60 * Turn it into 'ldr REG, [r9, #4]'. | |
61 */ | |
62 *insn |= 4; | |
63 ++g_changes; | |
64 } | |
65 } | |
66 } | |
67 | |
68 static Bool ConsiderOneInsn(const uint8_t *insn_begin, const uint8_t *insn_end, | |
69 uint32_t validation_info, void *data) { | |
70 UNREFERENCED_PARAMETER(data); | |
71 if (insn_begin[0] == 0x65) { /* GS prefix */ | |
72 if (insn_end - insn_begin < 6) { | |
73 ReportError(insn_begin, "Unexpected GS prefix"); | |
74 } else if (insn_end[-1] == 0 && insn_end[-2] == 0 && | |
75 insn_end[-3] == 0 && insn_end[-4] == 0) { | |
76 /* | |
77 * This is 'something %gs:0'. | |
78 * Turn it into 'something %gs:4'. | |
79 */ | |
80 ((uint8_t *) insn_end)[-4] = 4; | |
81 ++g_changes; | |
82 } else if (insn_end[-1] == 0 && insn_end[-2] == 0 && | |
83 insn_end[-3] == 0 && insn_end[-4] == 4) { | |
84 /* | |
85 * The Instruction is already offset to the right location. | |
86 * TODO(dyen): This case should be removed eventually once the IRT | |
87 * is being properly compiled without any special TLS flags. | |
88 */ | |
89 uint32_t insn_addr = GetInsnAddr(insn_begin); | |
90 printf("%#x: %%gs address already pointing to correct offset (4)\n", | |
91 insn_addr); | |
92 } else { | |
93 ReportError(insn_begin, "Unexpected %gs address"); | |
94 } | |
95 } | |
96 return (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET)) == 0; | |
97 } | |
98 | |
99 static void EditX86_32Code(void *code, size_t code_length) { | |
100 const NaClCPUFeaturesX86 *cpu_features = &kFullCPUIDFeatures; | |
101 if (!ValidateChunkIA32(code, code_length, | |
102 CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, | |
103 cpu_features, &ConsiderOneInsn, NULL)) | |
104 ReportError(code, "Validation failed"); | |
105 } | |
106 | |
107 static void ReadInput(const char *filename) { | |
108 struct stat st; | |
109 FILE *fp = fopen(filename, "rb"); | |
110 if (fp == NULL) { | |
111 fprintf(stderr, "fopen: %s: %s\n", | |
112 filename, strerror(errno)); | |
113 exit(1); | |
114 } | |
115 | |
116 if (fstat(fileno(fp), &st) < 0) { | |
117 fprintf(stderr, "fstat: %s: %s\n", | |
118 filename, strerror(errno)); | |
119 exit(1); | |
120 } | |
121 | |
122 g_contents_size = st.st_size; | |
123 g_contents = malloc(g_contents_size); | |
124 if (g_contents == NULL) { | |
125 fprintf(stderr, "Cannot allocate %u bytes: %s\n", | |
126 (unsigned int) st.st_size, strerror(errno)); | |
127 exit(1); | |
128 } | |
129 | |
130 { | |
131 size_t read_bytes = fread(g_contents, 1, g_contents_size, fp); | |
132 if (read_bytes != g_contents_size) { | |
133 if (ferror(fp)) { | |
134 fprintf(stderr, "fread: %s: %s\n", | |
135 filename, strerror(errno)); | |
136 } else if (feof(fp)) { | |
137 fprintf(stderr, "fread: %s: premature EOF\n", | |
138 filename); | |
139 } else { | |
140 fprintf(stderr, "fread: %s: unexpected read count - %u != %u\n", | |
141 filename, (unsigned int) read_bytes, | |
142 (unsigned int) g_contents_size); | |
143 } | |
144 exit(1); | |
145 } | |
146 } | |
147 | |
148 fclose(fp); | |
149 } | |
150 | |
151 static void WriteOutput(const char *filename) { | |
152 FILE *fp = fopen(filename, "wb+"); | |
153 if (fp == NULL) { | |
154 fprintf(stderr, "fopen: %s: %s\n", filename, strerror(errno)); | |
155 exit(1); | |
156 } | |
157 | |
158 { | |
159 size_t nwrote = fwrite(g_contents, 1, g_contents_size, fp); | |
160 if (nwrote != g_contents_size) { | |
161 fprintf(stderr, "fwrite: %s: %s\n", filename, strerror(errno)); | |
162 exit(1); | |
163 } | |
164 } | |
165 | |
166 fclose(fp); | |
167 } | |
168 | |
169 static Bool EditTLSCode(const char *infile, uint32_t code_addr, | |
170 uint8_t *code_start, size_t code_length, | |
171 uint16_t e_machine) { | |
172 g_code_addr = code_addr; | |
173 g_code_start = code_start; | |
174 | |
175 switch (e_machine) { | |
176 case EM_ARM: | |
177 EditArmCode(g_code_start, code_length); | |
178 break; | |
179 case EM_386: | |
180 EditX86_32Code(g_code_start, code_length); | |
181 break; | |
182 case EM_X86_64: | |
183 printf("%s: x86-64 ELF detected, no instructions changed\n", | |
184 infile); | |
185 return TRUE; | |
186 case EM_MIPS: | |
187 printf("%s: MIP ELF detected, no instructions changed\n", | |
Roland McGrath
2014/02/04 17:49:31
typo: MIPS
| |
188 infile); | |
189 return TRUE; | |
190 default: | |
191 fprintf(stderr, "%s: Unsupported e_machine %d\n", | |
192 infile, e_machine); | |
193 return FALSE; | |
194 } | |
195 | |
196 if (g_changes == 0) { | |
197 printf("%s: Found no changes to make\n", | |
198 infile); | |
199 return TRUE; | |
200 } else { | |
201 printf("%s: %d instructions changed\n", | |
202 infile, g_changes); | |
203 } | |
204 | |
205 return TRUE; | |
206 } | |
207 | |
208 static Bool Process32BitFile(const char *infile) { | |
209 const Elf32_Ehdr *ehdr; | |
210 const Elf32_Phdr *phdr; | |
211 int i; | |
212 | |
213 ehdr = g_contents; | |
214 if (ehdr->e_phoff > g_contents_size) { | |
215 fprintf(stderr, "%s: bogus e_phoff\n", | |
216 infile); | |
217 return FALSE; | |
218 } | |
219 if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) { | |
220 fprintf(stderr, "%s: not an ELFCLASS32 file\n", | |
221 infile); | |
222 return FALSE; | |
223 } | |
224 if (ehdr->e_phentsize != sizeof(Elf32_Phdr)) { | |
225 fprintf(stderr, "%s: wrong e_phentsize: %u\n", | |
226 infile, ehdr->e_phentsize); | |
227 return FALSE; | |
228 } | |
229 if (g_contents_size - ehdr->e_phoff < ehdr->e_phnum * sizeof(Elf32_Phdr)) { | |
230 fprintf(stderr, "%s: bogus elf32 e_phnum\n", | |
231 infile); | |
232 return FALSE; | |
233 } | |
234 | |
235 if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { | |
236 fprintf(stderr, "%s: not an ELFDATA2LSB file\n", | |
237 infile); | |
238 return FALSE; | |
239 } | |
240 | |
241 phdr = (const void *) ((uint8_t *) g_contents + ehdr->e_phoff); | |
242 for (i = 0; i < ehdr->e_phnum; ++i) { | |
243 if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X) != 0) | |
244 break; | |
245 } | |
246 if (i == ehdr->e_phnum) { | |
247 fprintf(stderr, "%s: Could not find executable load segment!\n", | |
248 infile); | |
249 return FALSE; | |
250 } | |
251 | |
252 if (phdr[i].p_offset > g_contents_size || | |
253 g_contents_size - phdr[i].p_offset < phdr[i].p_filesz) { | |
254 fprintf(stderr, "%s: Program header %d has invalid offset or size!\n", | |
255 infile, i); | |
256 return FALSE; | |
257 } | |
258 | |
259 return EditTLSCode(infile, phdr[i].p_vaddr, | |
260 (uint8_t *) g_contents + phdr[i].p_offset, | |
261 phdr[i].p_filesz, ehdr->e_machine); | |
262 } | |
263 | |
264 static Bool Process64BitFile(const char *infile) { | |
265 const Elf64_Ehdr *ehdr; | |
266 const Elf64_Phdr *phdr; | |
267 int i; | |
268 | |
269 ehdr = g_contents; | |
270 if (ehdr->e_phoff > g_contents_size) { | |
271 fprintf(stderr, "%s: bogus e_phoff\n", | |
272 infile); | |
273 return FALSE; | |
274 } | |
275 if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) { | |
276 fprintf(stderr, "%s: not an ELFCLASS64 file\n", | |
277 infile); | |
278 return FALSE; | |
279 } | |
280 if (ehdr->e_phentsize != sizeof(Elf64_Phdr)) { | |
281 fprintf(stderr, "%s: wrong e_phentsize: %u\n", | |
282 infile, ehdr->e_phentsize); | |
283 return FALSE; | |
284 } | |
285 if (g_contents_size - ehdr->e_phoff < ehdr->e_phnum * sizeof(Elf64_Phdr)) { | |
286 fprintf(stderr, "%s: bogus elf64 e_phnum\n", | |
287 infile); | |
288 return FALSE; | |
289 } | |
290 | |
291 if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { | |
292 fprintf(stderr, "%s: not an ELFDATA2LSB file\n", | |
293 infile); | |
294 return FALSE; | |
295 } | |
296 | |
297 phdr = (const void *) ((uint8_t *) g_contents + ehdr->e_phoff); | |
298 for (i = 0; i < ehdr->e_phnum; ++i) { | |
299 if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X) != 0) | |
300 break; | |
301 } | |
302 if (i == ehdr->e_phnum) { | |
303 fprintf(stderr, "%s: Could not find executable load segment!\n", | |
304 infile); | |
305 return FALSE; | |
306 } | |
307 | |
308 if (phdr[i].p_offset > g_contents_size || | |
309 g_contents_size - phdr[i].p_offset < phdr[i].p_filesz) { | |
310 fprintf(stderr, "%s: Program header %d has invalid offset or size!\n", | |
311 infile, i); | |
312 return FALSE; | |
313 } | |
314 | |
315 return EditTLSCode(infile, (uint32_t) phdr[i].p_vaddr, | |
316 (uint8_t *) g_contents + phdr[i].p_offset, | |
317 (size_t) phdr[i].p_filesz, ehdr->e_machine); | |
318 } | |
319 | |
320 int main(int argc, char **argv) { | |
321 const char *infile; | |
322 const char *outfile; | |
323 | |
324 if (argc != 3) { | |
325 fprintf(stderr, "Usage: %s INFILE OUTFILE\n", argv[0]); | |
326 return 1; | |
327 } | |
328 | |
329 infile = argv[1]; | |
330 outfile = argv[2]; | |
331 | |
332 ReadInput(infile); | |
333 | |
334 if (g_contents_size < SELFMAG) { | |
335 fprintf(stderr, "%s: too short to be an ELF file\n", | |
336 infile); | |
337 return 1; | |
338 } | |
339 if (memcmp(g_contents, ELFMAG, SELFMAG) != 0) { | |
340 fprintf(stderr, "%s: not an ELF file\n", | |
341 infile); | |
342 return 1; | |
343 } | |
344 | |
345 /* | |
346 * We will examine the header to figure out whether we are dealing | |
347 * with a 32 bit or 64 bit executable file. | |
348 */ | |
349 if (g_contents_size >= sizeof(Elf32_Ehdr) && | |
350 ((Elf32_Ehdr*) g_contents)->e_ident[EI_CLASS] == ELFCLASS32) { | |
351 if (!Process32BitFile(infile)) { | |
352 fprintf(stderr, "%s: Could not process 32 bit ELF file\n", | |
353 infile); | |
354 return 1; | |
355 } | |
356 } else if (g_contents_size >= sizeof(Elf64_Ehdr) && | |
357 ((Elf64_Ehdr*) g_contents)->e_ident[EI_CLASS] == ELFCLASS64) { | |
358 if (!Process64BitFile(infile)) { | |
359 fprintf(stderr, "%s: Could not process 64 bit ELF file\n", | |
360 infile); | |
361 return 1; | |
362 } | |
363 } else { | |
364 fprintf(stderr, "%s: Invalid ELF file!\n", | |
365 infile); | |
366 return 1; | |
367 } | |
368 | |
369 if (g_errors != 0) | |
370 return 1; | |
371 | |
372 WriteOutput(outfile); | |
373 return 0; | |
374 } | |
OLD | NEW |