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