1 : /*
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.
5 : */
6 :
7 : /*
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.
14 : */
15 :
16 : #include <assert.h>
17 : #include <stdio.h>
18 : #include <string.h>
19 :
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/cpufeatures.h"
25 : #include "native_client/src/trusted/validator/ncvalidate.h"
26 :
27 : static Bool FixUpSection(uintptr_t load_address,
28 : unsigned char *code,
29 3 : size_t code_size) {
30 3 : int bundle_size = 32;
31 3 : enum NaClSBKind sb_kind = NACL_SB_DEFAULT;
32 : NaClValidationStatus status;
33 : NaClCPUFeatures cpu_features;
34 : /* Pretend that the CPU supports every feature so that we will only stub out
35 : * instructions that NaCl will never allow under any condition.
36 : */
37 3 : NaClSetAllCPUFeatures(&cpu_features);
38 :
39 3 : status = NACL_SUBARCH_NAME(ApplyValidator,
40 : NACL_TARGET_ARCH,
41 : NACL_TARGET_SUBARCH)
42 : (sb_kind, NaClApplyValidationDoStubout, load_address, code,
43 : code_size, bundle_size, &cpu_features);
44 3 : if (status == NaClValidationSucceeded) {
45 : /* Now run the validator again, so that we report any errors
46 : * that were not fixed by stubbing out. This is done so that
47 : * the user knows that stubout doesn't fix all errors.
48 : */
49 3 : status = NACL_SUBARCH_NAME(ApplyValidatorVerbosely,
50 : NACL_TARGET_ARCH,
51 : NACL_TARGET_SUBARCH)
52 : (sb_kind, NaClApplyCodeValidation, load_address, code, code_size,
53 : bundle_size, &cpu_features);
54 : }
55 :
56 3 : switch (status) {
57 : case NaClValidationSucceeded:
58 3 : return TRUE;
59 : default:
60 : case NaClValidationFailed:
61 0 : fprintf(stderr, "Errors still exist after attempting to stubout code\n");
62 0 : return FALSE;
63 : case NaClValidationFailedOutOfMemory:
64 0 : fprintf(stderr, "Unable to stubout code, not enough memory\n");
65 0 : return FALSE;
66 : case NaClValidationFailedNotImplemented:
67 0 : fprintf(stderr, "Unable to stubout code, not implemented\n");
68 0 : return FALSE;
69 : case NaClValidationFailedCpuNotSupported:
70 : /* This shouldn't happen, but if it does, report the problem. */
71 0 : fprintf(stderr, "Unable to stubout code, cpu not supported\n");
72 0 : return FALSE;
73 : case NaClValidationFailedSegmentationIssue:
74 0 : fprintf(stderr, "Unable to stubout code, segmentation issues found\n");
75 0 : return FALSE;
76 : }
77 : }
78 :
79 : static void CheckBounds(unsigned char *data, size_t data_size,
80 30 : void *ptr, size_t inside_size) {
81 30 : CHECK(data <= (unsigned char *) ptr);
82 30 : CHECK((unsigned char *) ptr + inside_size <= data + data_size);
83 30 : }
84 :
85 1 : static Bool FixUpELF32(unsigned char *data, size_t data_size) {
86 : Elf32_Ehdr *header;
87 : int index;
88 1 : Bool fixed = TRUE; /* until proven otherwise. */
89 :
90 1 : header = (Elf32_Ehdr *) data;
91 1 : CheckBounds(data, data_size, header, sizeof(*header));
92 1 : CHECK(memcmp(header->e_ident, ELFMAG, strlen(ELFMAG)) == 0);
93 :
94 27 : for (index = 0; index < header->e_shnum; index++) {
95 : Elf32_Shdr *section = (Elf32_Shdr *) (data + header->e_shoff +
96 26 : header->e_shentsize * index);
97 26 : CheckBounds(data, data_size, section, sizeof(*section));
98 :
99 26 : if ((section->sh_flags & SHF_EXECINSTR) != 0) {
100 3 : CheckBounds(data, data_size,
101 : data + section->sh_offset, section->sh_size);
102 3 : if (!FixUpSection(section->sh_addr,
103 : data + section->sh_offset, section->sh_size)) {
104 0 : fixed = FALSE;
105 : }
106 : }
107 : }
108 1 : return fixed;
109 : }
110 :
111 : #if NACL_TARGET_SUBARCH == 64
112 : static Bool FixUpELF64(unsigned char *data, size_t data_size) {
113 : Elf64_Ehdr *header;
114 : int index;
115 : Bool fixed = TRUE; /* until proven otherwise. */
116 :
117 : header = (Elf64_Ehdr *) data;
118 : CheckBounds(data, data_size, header, sizeof(*header));
119 : CHECK(memcmp(header->e_ident, ELFMAG, strlen(ELFMAG)) == 0);
120 :
121 : for (index = 0; index < header->e_shnum; index++) {
122 : Elf64_Shdr *section = (Elf64_Shdr *) (data + header->e_shoff +
123 : header->e_shentsize * index);
124 : CheckBounds(data, data_size, section, sizeof(*section));
125 :
126 : if ((section->sh_flags & SHF_EXECINSTR) != 0) {
127 : CheckBounds(data, data_size,
128 : data + section->sh_offset, section->sh_size);
129 : if (!FixUpSection(section->sh_addr,
130 : data + section->sh_offset, section->sh_size)) {
131 : fixed = FALSE;
132 : }
133 : }
134 : }
135 : return fixed;
136 : }
137 : #endif
138 :
139 1 : static Bool FixUpELF(unsigned char *data, size_t data_size) {
140 : #if NACL_TARGET_SUBARCH == 64
141 : if (data_size > EI_CLASS && data[EI_CLASS] == ELFCLASS64)
142 : return FixUpELF64(data, data_size);
143 : #endif
144 1 : return FixUpELF32(data, data_size);
145 : }
146 :
147 1 : static Bool FixUpELFFile(const char *input_file, const char *output_file) {
148 : FILE *fp;
149 : size_t file_size;
150 : unsigned char *data;
151 : size_t got;
152 : size_t written;
153 :
154 : /* Read whole ELF file and write it back with modifications. */
155 1 : fp = fopen(input_file, "rb");
156 1 : if (fp == NULL) {
157 0 : fprintf(stderr, "Failed to open input file: %s\n", input_file);
158 0 : return FALSE;
159 : }
160 : /* Find the file size. */
161 1 : fseek(fp, 0, SEEK_END);
162 1 : file_size = ftell(fp);
163 1 : data = malloc(file_size);
164 1 : if (data == NULL) {
165 0 : fprintf(stderr, "Unable to create memory imate of input file: %s\n",
166 : input_file);
167 0 : return FALSE;
168 : }
169 1 : fseek(fp, 0, SEEK_SET);
170 1 : got = fread(data, 1, file_size, fp);
171 1 : if (got != file_size) {
172 0 : fprintf(stderr, "Unable to read data from input file: %s\n",
173 : input_file);
174 0 : return FALSE;
175 : }
176 1 : fclose(fp);
177 :
178 1 : if (!FixUpELF(data, file_size)) return FALSE;
179 :
180 1 : fp = fopen(output_file, "wb");
181 1 : if (fp == NULL) {
182 0 : fprintf(stderr, "Failed to open output file: %s\n", output_file);
183 0 : return FALSE;
184 : }
185 1 : written = fwrite(data, 1, file_size, fp);
186 1 : if (written != file_size) {
187 0 : fprintf(stderr, "Unable to write data to output file: %s\n",
188 : output_file);
189 0 : return FALSE;
190 : }
191 1 : fclose(fp);
192 1 : return TRUE;
193 : }
194 :
195 1 : int main(int argc, const char *argv[]) {
196 : /* Be sure to redirect validator error messages to stderr. */
197 : struct GioFile err;
198 1 : GioFileRefCtor(&err, stderr);
199 1 : NaClLogPreInitSetGio((struct Gio*) &err);
200 1 : NaClLogModuleInit();
201 1 : if (argc != 4 || strcmp(argv[2], "-o") != 0) {
202 0 : fprintf(stderr, "Usage: %s <input-file> -o <output-file>\n\n", argv[0]);
203 0 : fprintf(stderr,
204 : "This tool rewrites ELF objects to replace instructions that are\n"
205 : "rejected by the NaCl validator with safe HLT instructions.\n");
206 0 : GioFileDtor((struct Gio*) &err);
207 0 : return 1;
208 : }
209 1 : GioFileDtor((struct Gio*) &err);
210 1 : return FixUpELFFile(argv[1], argv[3]) ? 0 : 1;
211 : }
|