2 * Copyright (c) 2012 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.
8 * This tool rewrites ELF files to replace instructions that will be
9 * rejected by the validator with safe HLT instructions. This is
10 * useful if you have a large library in which many functions do not
11 * validate but are not immediately required to work. Replacing the
12 * forbidden instructions with HLTs makes it easier to find the
13 * instructions that are needed first, and fix and test them.
20 #include "native_client/src/include/elf.h"
21 #include "native_client/src/shared/gio/gio.h"
22 #include "native_client/src/shared/platform/nacl_check.h"
23 #include "native_client/src/shared/utils/types.h"
24 #include "native_client/src/trusted/validator/ncvalidate.h"
26 static Bool FixUpSectionCheckStatus(NaClValidationStatus status) {
28 case NaClValidationSucceeded:
31 case NaClValidationFailed:
32 fprintf(stderr, "Errors still exist after attempting to stubout code\n");
34 case NaClValidationFailedOutOfMemory:
35 fprintf(stderr, "Unable to stubout code, not enough memory\n");
37 case NaClValidationFailedNotImplemented:
38 fprintf(stderr, "Unable to stubout code, not implemented\n");
40 case NaClValidationFailedCpuNotSupported:
41 /* This shouldn't happen, but if it does, report the problem. */
42 fprintf(stderr, "Unable to stubout code, cpu not supported\n");
44 case NaClValidationFailedSegmentationIssue:
45 fprintf(stderr, "Unable to stubout code, segmentation issues found\n");
51 static Bool FixUpSection(const struct NaClValidatorInterface *validator,
52 uintptr_t load_address,
56 NaClValidationStatus status;
57 NaClCPUFeatures *cpu_features = malloc(validator->CPUFeatureSize);
58 if (cpu_features == NULL) {
59 fprintf(stderr, "Unable to create memory for CPU features\n");
62 /* Pretend that the CPU supports every feature so that we will only stub out
63 * instructions that NaCl will never allow under any condition.
65 validator->SetAllCPUFeatures(cpu_features);
67 status = validator->Validate(
68 load_address, code, code_size,
69 /* stubout_mode= */ TRUE,
70 /* readonly_text= */ FALSE,
74 if (status == NaClValidationSucceeded) {
75 /* Now run the validator again, so that we report any errors
76 * that were not fixed by stubbing out. This is done so that
77 * the user knows that stubout doesn't fix all errors.
79 status = NACL_SUBARCH_NAME(ApplyValidatorVerbosely,
82 (load_address, code, code_size, cpu_features);
85 result = FixUpSectionCheckStatus(status);
90 static void CheckBounds(unsigned char *data, size_t data_size,
91 void *ptr, size_t inside_size) {
92 CHECK(data <= (unsigned char *) ptr);
93 CHECK((unsigned char *) ptr + inside_size <= data + data_size);
96 static Bool FixUpELF32(const struct NaClValidatorInterface *validator,
101 Bool fixed = TRUE; /* until proven otherwise. */
103 header = (Elf32_Ehdr *) data;
104 CheckBounds(data, data_size, header, sizeof(*header));
105 CHECK(memcmp(header->e_ident, ELFMAG, strlen(ELFMAG)) == 0);
107 for (index = 0; index < header->e_shnum; index++) {
108 Elf32_Shdr *section = (Elf32_Shdr *) (data + header->e_shoff +
109 header->e_shentsize * index);
110 CheckBounds(data, data_size, section, sizeof(*section));
112 if ((section->sh_flags & SHF_EXECINSTR) != 0) {
113 CheckBounds(data, data_size,
114 data + section->sh_offset, section->sh_size);
115 if (!FixUpSection(validator,
117 data + section->sh_offset,
126 #if NACL_TARGET_SUBARCH == 64
127 static Bool FixUpELF64(const struct NaClValidatorInterface *validator,
132 Bool fixed = TRUE; /* until proven otherwise. */
134 header = (Elf64_Ehdr *) data;
135 CheckBounds(data, data_size, header, sizeof(*header));
136 CHECK(memcmp(header->e_ident, ELFMAG, strlen(ELFMAG)) == 0);
138 for (index = 0; index < header->e_shnum; index++) {
139 Elf64_Shdr *section = (Elf64_Shdr *) (data + header->e_shoff +
140 header->e_shentsize * index);
141 CheckBounds(data, data_size, section, sizeof(*section));
143 if ((section->sh_flags & SHF_EXECINSTR) != 0) {
144 CheckBounds(data, data_size,
145 data + section->sh_offset, section->sh_size);
146 if (!FixUpSection(validator,
148 data + section->sh_offset,
158 static Bool FixUpELF(const struct NaClValidatorInterface *validator,
161 #if NACL_TARGET_SUBARCH == 64
162 if (data_size > EI_CLASS && data[EI_CLASS] == ELFCLASS64)
163 return FixUpELF64(validator, data, data_size);
165 return FixUpELF32(validator, data, data_size);
168 static Bool FixUpELFFile(const struct NaClValidatorInterface *validator,
169 const char *input_file,
170 const char *output_file) {
177 /* Read whole ELF file and write it back with modifications. */
178 fp = fopen(input_file, "rb");
180 fprintf(stderr, "Failed to open input file: %s\n", input_file);
183 /* Find the file size. */
184 fseek(fp, 0, SEEK_END);
185 file_size = ftell(fp);
186 data = malloc(file_size);
188 fprintf(stderr, "Unable to create memory imate of input file: %s\n",
192 fseek(fp, 0, SEEK_SET);
193 got = fread(data, 1, file_size, fp);
194 if (got != file_size) {
195 fprintf(stderr, "Unable to read data from input file: %s\n",
201 if (!FixUpELF(validator, data, file_size)) return FALSE;
203 fp = fopen(output_file, "wb");
205 fprintf(stderr, "Failed to open output file: %s\n", output_file);
208 written = fwrite(data, 1, file_size, fp);
209 if (written != file_size) {
210 fprintf(stderr, "Unable to write data to output file: %s\n",
218 int main(int argc, const char *argv[]) {
219 /* Be sure to redirect validator error messages to stderr. */
220 const struct NaClValidatorInterface *validator;
222 validator = NaClCreateValidator();
223 if (!validator->stubout_mode_implemented) {
225 "This platform does not support stubout mode.");
228 if (argc != 4 || strcmp(argv[2], "-o") != 0) {
229 fprintf(stderr, "Usage: %s <input-file> -o <output-file>\n\n", argv[0]);
231 "This tool rewrites ELF objects to replace instructions that are\n"
232 "rejected by the NaCl validator with safe HLT instructions.\n");
235 return FixUpELFFile(validator, argv[1], argv[3]) ? 0 : 1;