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