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 size_t read_bytes; |
| 110 |
| 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 read_bytes = fread(g_contents, 1, g_contents_size, fp); |
| 133 if (read_bytes != g_contents_size) { |
| 134 if (ferror(fp)) { |
| 135 fprintf(stderr, "fread: %s: %s\n", |
| 136 filename, strerror(errno)); |
| 137 } else if (feof(fp)) { |
| 138 fprintf(stderr, "fread: %s: premature EOF\n", |
| 139 filename); |
| 140 } else { |
| 141 fprintf(stderr, "fread: %s: unexpected read count - %u != %u\n", |
| 142 filename, (unsigned int) read_bytes, |
| 143 (unsigned int) g_contents_size); |
| 144 } |
| 145 exit(1); |
| 146 } |
| 147 |
| 148 fclose(fp); |
| 149 } |
| 150 |
| 151 static void WriteOutput(const char *filename) { |
| 152 size_t nwrote; |
| 153 |
| 154 FILE *fp = fopen(filename, "wb+"); |
| 155 if (fp == NULL) { |
| 156 fprintf(stderr, "fopen: %s: %s\n", filename, strerror(errno)); |
| 157 exit(1); |
| 158 } |
| 159 |
| 160 nwrote = fwrite(g_contents, 1, g_contents_size, fp); |
| 161 if (nwrote != g_contents_size) { |
| 162 fprintf(stderr, "fwrite: %s: %s\n", filename, strerror(errno)); |
| 163 exit(1); |
| 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: MIPS ELF detected, no instructions changed\n", |
| 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 |