Chromium Code Reviews| Index: src/untrusted/irt/tls_edit.c |
| diff --git a/src/untrusted/irt/tls_edit.c b/src/untrusted/irt/tls_edit.c |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..39ea334b46b2bf32c5e938930a890e663bf24fe0 |
| --- /dev/null |
| +++ b/src/untrusted/irt/tls_edit.c |
| @@ -0,0 +1,342 @@ |
| +#include <errno.h> |
|
Roland McGrath
2014/01/24 18:50:49
Needs copyright header at top.
|
| +#include <fcntl.h> |
| +#include <stdio.h> |
| +#include <string.h> |
| +#include <sys/stat.h> |
| + |
| +#include "native_client/src/include/elf32.h" |
| +#include "native_client/src/include/elf64.h" |
| +#include "native_client/src/include/arm_sandbox.h" |
| +#include "native_client/src/include/portability.h" |
| +#include "native_client/src/trusted/validator_ragel/validator.h" |
| + |
| +#ifndef O_BINARY |
| +# define O_BINARY 0 |
| +#endif |
| + |
| +static void *g_contents; |
| +static size_t g_contents_size; |
| +static uint8_t *g_code_start; |
| +static Elf32_Addr g_code_addr; |
| +static int g_errors; |
| +static int g_changes; |
| + |
| +static void ReportError(const void *insn, const char *message) { |
| + Elf32_Addr insn_addr = (Elf32_Addr) ((uint8_t *) insn - g_code_start) + |
| + g_code_addr; |
| + |
| + fprintf(stderr, "%#x: %s\n", (unsigned int) insn_addr, message); |
| + ++g_errors; |
| +} |
| + |
| +static void EditArmCode(void *code, size_t code_length) { |
| + Elf32_Word *const words = code; |
| + Elf32_Word *const end = (void *) ((uint8_t *) code + code_length); |
| + Elf32_Word *insn; |
| + |
| + for (insn = words; insn < end; ++insn) { |
| + if (*insn == NACL_INSTR_ARM_LITERAL_POOL_HEAD) { |
| + if ((insn - words) % 4 != 0) { |
| + ReportError(insn, "Roadblock not at start of bundle"); |
| + } else { |
| + /* |
| + * Ignore the rest of this bundle. |
| + */ |
| + insn += 3; |
| + } |
| + } else if ((*insn & 0x0FFF0FFF) == 0x05990000) { |
| + /* |
| + * This is 'ldr REG, [r9, #0]'. |
| + * Turn it into 'ldr REG, [r9, #4]'. |
| + */ |
| + *insn |= 4; |
| + ++g_changes; |
| + } |
| + } |
| +} |
| + |
| +static Bool ConsiderOneInsn(const uint8_t *insn_begin, const uint8_t *insn_end, |
| + uint32_t validation_info, void *data) { |
| + UNREFERENCED_PARAMETER(data); |
| + if (insn_begin[0] == 0x65) { /* GS prefix */ |
| + if (insn_end - insn_begin < 6) { |
| + ReportError(insn_begin, "Unexpected GS prefix"); |
| + } else if (insn_end[-1] != 0 || insn_end[-2] != 0 || |
| + insn_end[-3] != 0 || insn_end[-4] != 0) { |
| + ReportError(insn_begin, "Unexpected %gs address"); |
| + } else { |
| + /* |
| + * This is 'something %gs:0'. |
| + * Turn it into 'something %gs:4'. |
| + */ |
| + ((uint8_t *) insn_end)[-4] = 4; |
| + ++g_changes; |
| + } |
| + } |
| + |
| + return (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET)) == 0; |
| +} |
| + |
| +static void EditX86_32Code(void *code, size_t code_length) { |
| + const NaClCPUFeaturesX86* cpu_features = &kFullCPUIDFeatures; |
| + if (!ValidateChunkIA32(code, code_length, |
| + CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, |
| + cpu_features, &ConsiderOneInsn, NULL)) |
| + ReportError(code, "Validation failed"); |
| +} |
| + |
| +static void ReadInput(const char *filename) { |
| + struct stat st; |
| + FILE *fp = fopen(filename, "rb"); |
| + if (fp == NULL) { |
| + 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
|
| + filename, strerror(errno)); |
| + exit(1); |
| + } |
| + |
| + if (fstat(fileno(fp), &st) < 0) { |
| + fprintf(stderr, "fstat: %s: %s\n", |
| + filename, strerror(errno)); |
| + exit(1); |
| + } |
| + |
| + g_contents_size = st.st_size; |
| + g_contents = malloc(g_contents_size); |
| + if (g_contents == NULL) { |
| + fprintf(stderr, "Cannot allocate %u bytes: %s\n", |
| + (unsigned int) st.st_size, strerror(errno)); |
| + exit(1); |
| + } |
| + |
| + { |
| + size_t read_bytes = fread(g_contents, 1, g_contents_size, fp); |
| + if (read_bytes != g_contents_size) { |
| + int error_num = ferror(fp); |
|
Roland McGrath
2014/01/24 18:50:49
ferror and feof return a Boolean value, not an err
|
| + int eof_error = feof(fp); |
| + if (error_num) { |
| + 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
|
| + filename, strerror(error_num)); |
| + } else if (eof_error) { |
| + fprintf(stderr, "read: %s: premature EOF: %s\n", |
| + filename, strerror(eof_error)); |
| + } else { |
| + fprintf(stderr, "read: %s: unknown error\n", |
| + filename); |
| + } |
| + exit(1); |
| + } |
| + } |
| + |
| + fclose(fp); |
| +} |
| + |
| +static void WriteOutput(const char *filename) { |
| + FILE *fp = fopen(filename, "wb+"); |
| + if (fp == NULL) { |
| + fprintf(stderr, "open: %s: %s\n", filename, strerror(errno)); |
| + exit(1); |
| + } |
| + |
| + { |
| + size_t nwrote = fwrite(g_contents, 1, g_contents_size, fp); |
| + if (nwrote != g_contents_size) { |
| + fprintf(stderr, "write: %s: %s\n", filename, strerror(errno)); |
| + exit(1); |
| + } |
| + } |
| + |
| + fclose(fp); |
| +} |
| + |
| +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++.
|
| +{ |
| + const Elf32_Ehdr *ehdr; |
| + const Elf32_Phdr *phdr; |
| + size_t code_length; |
| + int i; |
| + |
| + ehdr = g_contents; |
| + if (ehdr->e_phoff > g_contents_size) { |
| + fprintf(stderr, "%s: bogus e_phoff\n", |
| + infile); |
| + return FALSE; |
| + } |
| + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) { |
| + fprintf(stderr, "%s: not an ELFCLASS32 file\n", |
| + infile); |
| + return FALSE; |
| + } |
| + if (ehdr->e_phentsize != sizeof(Elf32_Phdr)) { |
| + fprintf(stderr, "%s: wrong e_phentsize: %u\n", |
| + infile, ehdr->e_phentsize); |
| + return FALSE; |
| + } |
| + if (g_contents_size - ehdr->e_phoff < ehdr->e_phnum * sizeof(Elf32_Phdr)) { |
| + fprintf(stderr, "%s: bogus elf32 e_phnum\n", |
| + infile); |
| + return FALSE; |
| + } |
| + |
| + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { |
| + fprintf(stderr, "%s: not an ELFDATA2LSB file\n", |
| + infile); |
| + return FALSE; |
| + } |
| + |
| + phdr = (const void *) ((uint8_t *) g_contents + ehdr->e_phoff); |
| + for (i = 0; i < ehdr->e_phnum; ++i) { |
| + if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X) != 0) |
| + break; |
| + } |
| + if (i == ehdr->e_phnum) { |
| + fprintf(stderr, "%s: Could not find executable load segment!\n", |
| + infile); |
| + return FALSE; |
| + } |
| + |
| + 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.
|
| + g_contents_size - phdr[i].p_offset < phdr[i].p_filesz) { |
| + fprintf(stderr, "%s: Program header %d has invalid offset or size!\n", |
| + infile, i); |
| + return FALSE; |
| + } |
| + |
| + g_code_addr = phdr[i].p_vaddr; |
| + g_code_start = (uint8_t *) g_contents + phdr[i].p_offset; |
| + code_length = phdr[i].p_filesz; |
| + |
| + switch (ehdr->e_machine) { |
| + case EM_ARM: |
| + EditArmCode(g_code_start, code_length); |
| + break; |
| + case EM_386: |
| + EditX86_32Code(g_code_start, code_length); |
| + break; |
| + default: |
| + fprintf(stderr, "%s: Unsupported e_machine %d\n", |
| + infile, ehdr->e_machine); |
| + return FALSE; |
| + } |
| + |
| + if (g_changes == 0) { |
| + fprintf(stderr, "%s: Found no changes to make!\n", |
| + infile); |
| + return FALSE; |
| + } else { |
| + printf("%s: %d instructions changed\n", |
| + infile, g_changes); |
| + } |
| + |
| + return TRUE; |
| +} |
| + |
| +static Bool Process64BitFile(const char* infile) |
|
Roland McGrath
2014/01/24 18:50:49
brace at end of decl line
|
| +{ |
| + const Elf64_Ehdr *ehdr; |
| + |
| + ehdr = g_contents; |
| + if (ehdr->e_phoff > g_contents_size) { |
| + fprintf(stderr, "%s: bogus e_phoff\n", |
| + infile); |
| + return FALSE; |
| + } |
| + if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) { |
| + fprintf(stderr, "%s: not an ELFCLASS64 file\n", |
| + infile); |
| + return FALSE; |
| + } |
| + if (ehdr->e_phentsize != sizeof(Elf64_Phdr)) { |
| + fprintf(stderr, "%s: wrong e_phentsize: %u\n", |
| + infile, ehdr->e_phentsize); |
| + return FALSE; |
| + } |
| + if (g_contents_size - ehdr->e_phoff < ehdr->e_phnum * sizeof(Elf64_Phdr)) { |
| + fprintf(stderr, "%s: bogus elf64 e_phnum\n", |
| + infile); |
| + return FALSE; |
| + } |
| + |
| + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { |
| + fprintf(stderr, "%s: not an ELFDATA2LSB file\n", |
| + infile); |
| + return FALSE; |
| + } |
| + |
| + switch (ehdr->e_machine) { |
| + case EM_X86_64: |
| + printf("%s: x86-64 ELF detected, no instructions changed\n", |
| + infile); |
| + return TRUE; |
| + default: |
| + fprintf(stderr, "%s: Unsupported e_machine %d\n", |
| + infile, ehdr->e_machine); |
| + return FALSE; |
| + } |
| + |
| + if (g_changes == 0) { |
| + fprintf(stderr, "%s: Found no changes to make!\n", |
| + infile); |
| + return FALSE; |
| + } else { |
| + printf("%s: %d instructions changed\n", |
| + infile, g_changes); |
| + } |
| + |
| + return TRUE; |
| +} |
| + |
| +int main(int argc, char **argv) { |
| + const char *infile; |
| + const char *outfile; |
| + |
| + if (argc != 3) { |
| + fprintf(stderr, "Usage: %s INFILE OUTFILE\n", argv[0]); |
| + return 1; |
| + } |
| + |
| + infile = argv[1]; |
| + outfile = argv[2]; |
| + |
| + ReadInput(infile); |
| + |
| + if (g_contents_size < SELFMAG) { |
| + fprintf(stderr, "%s: too short to be an ELF file\n", |
| + infile); |
| + return 1; |
| + } |
| + if (memcmp(g_contents, ELFMAG, SELFMAG) != 0) { |
| + fprintf(stderr, "%s: not an ELF file\n", |
| + infile); |
| + return 1; |
| + } |
| + |
| + /* |
| + * We will examine the header to figure out whether or not we are dealing |
| + * with a 32 bit or 64 bit executable file. |
| + */ |
| + if (g_contents_size >= sizeof(Elf32_Ehdr) && |
| + ((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
|
| + if (!Process32BitFile(infile)) { |
| + fprintf(stderr, "%s: Could not process 32 bit ELF file\n", |
| + infile); |
| + return 1; |
| + } |
| + } else if (g_contents_size >= sizeof(Elf64_Ehdr) && |
| + ((Elf64_Ehdr*)g_contents)->e_ident[EI_CLASS] == ELFCLASS64) { |
| + if (!Process64BitFile(infile)) { |
| + fprintf(stderr, "%s: Could not process 64 bit ELF file\n", |
| + infile); |
| + return 1; |
| + } |
| + } else { |
| + fprintf(stderr, "%s: Invalid ELF file!\n", |
| + infile); |
| + return 1; |
| + } |
| + |
| + if (g_errors != 0) |
| + return 1; |
| + |
| + WriteOutput(outfile); |
| + return 0; |
| +} |