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