OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. | 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 | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdio.h> | 8 #include <stdio.h> |
9 #include <stdlib.h> | 9 #include <stdlib.h> |
10 #include <string.h> | 10 #include <string.h> |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 string message; | 48 string message; |
49 Error(uint32_t offset, string message) : offset(offset), message(message) {} | 49 Error(uint32_t offset, string message) : offset(offset), message(message) {} |
50 }; | 50 }; |
51 | 51 |
52 | 52 |
53 struct UserData { | 53 struct UserData { |
54 Segment segment; | 54 Segment segment; |
55 vector<Error> *errors; | 55 vector<Error> *errors; |
56 vector<Jump> *jumps; | 56 vector<Jump> *jumps; |
57 set<uint32_t> *bad_jump_targets; | 57 set<uint32_t> *bad_jump_targets; |
| 58 uint32_t flags; |
58 UserData(Segment segment, | 59 UserData(Segment segment, |
59 vector<Error> *errors, | 60 vector<Error> *errors, |
60 vector<Jump> *jumps, | 61 vector<Jump> *jumps, |
61 set<uint32_t> *bad_jump_targets) | 62 set<uint32_t> *bad_jump_targets, |
| 63 uint32_t flags) |
62 : segment(segment), | 64 : segment(segment), |
63 errors(errors), | 65 errors(errors), |
64 jumps(jumps), | 66 jumps(jumps), |
65 bad_jump_targets(bad_jump_targets) { | 67 bad_jump_targets(bad_jump_targets), |
| 68 flags(flags) { |
66 } | 69 } |
67 }; | 70 }; |
68 | 71 |
69 | 72 |
70 Bool ProcessInstruction( | 73 Bool ProcessInstruction( |
71 const uint8_t *begin, const uint8_t *end, | 74 const uint8_t *begin, const uint8_t *end, |
72 uint32_t validation_info, void *user_data_ptr) { | 75 uint32_t validation_info, void *user_data_ptr) { |
73 | 76 |
74 UserData &user_data = *reinterpret_cast<UserData *>(user_data_ptr); | 77 UserData &user_data = *reinterpret_cast<UserData *>(user_data_ptr); |
75 vector<Error> &errors = *user_data.errors; | 78 vector<Error> &errors = *user_data.errors; |
(...skipping 11 matching lines...) Expand all Loading... |
87 user_data.segment.data); | 90 user_data.segment.data); |
88 if (validation_info & RELATIVE_8BIT) { | 91 if (validation_info & RELATIVE_8BIT) { |
89 program_counter += reinterpret_cast<const int8_t *>(end)[-1]; | 92 program_counter += reinterpret_cast<const int8_t *>(end)[-1]; |
90 user_data.jumps->push_back(Jump(offset, program_counter)); | 93 user_data.jumps->push_back(Jump(offset, program_counter)); |
91 } else if (validation_info & RELATIVE_32BIT) { | 94 } else if (validation_info & RELATIVE_32BIT) { |
92 program_counter += reinterpret_cast<const int32_t *>(end)[-1]; | 95 program_counter += reinterpret_cast<const int32_t *>(end)[-1]; |
93 user_data.jumps->push_back(Jump(offset, program_counter)); | 96 user_data.jumps->push_back(Jump(offset, program_counter)); |
94 } | 97 } |
95 } | 98 } |
96 | 99 |
| 100 // If we are not disabling non-temporals, they should pass validation, and |
| 101 // the corresponding validation_info bit is simply cleared. |
| 102 if (user_data.flags != DISABLE_NONTEMPORALS) |
| 103 validation_info &= ~UNSUPPORTED_INSTRUCTION; |
| 104 |
97 Bool result = (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET)) | 105 Bool result = (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET)) |
98 ? FALSE | 106 ? FALSE |
99 : TRUE; | 107 : TRUE; |
100 | 108 |
101 // We clear bit for each processed error, and in the end if there are still | 109 // We clear bit for each processed error, and in the end if there are still |
102 // errors we did not report, we produce generic error message. | 110 // errors we did not report, we produce generic error message. |
103 // This way, if validator interface was changed (new error bits were | 111 // This way, if validator interface was changed (new error bits were |
104 // introduced), we at least wouldn't ignore them completely. | 112 // introduced), we at least wouldn't ignore them completely. |
105 | 113 |
106 // TODO(shcherbina): deal with code duplication somehow. | 114 // TODO(shcherbina): deal with code duplication somehow. |
107 | 115 |
108 if (validation_info & UNRECOGNIZED_INSTRUCTION) { | 116 if (validation_info & UNRECOGNIZED_INSTRUCTION) { |
109 validation_info &= ~UNRECOGNIZED_INSTRUCTION; | 117 validation_info &= ~UNRECOGNIZED_INSTRUCTION; |
110 errors.push_back(Error(offset, "unrecognized instruction")); | 118 errors.push_back(Error(offset, "unrecognized instruction")); |
111 } | 119 } |
112 | 120 |
113 if (validation_info & DIRECT_JUMP_OUT_OF_RANGE) { | 121 if (validation_info & DIRECT_JUMP_OUT_OF_RANGE) { |
114 validation_info &= ~DIRECT_JUMP_OUT_OF_RANGE; | 122 validation_info &= ~DIRECT_JUMP_OUT_OF_RANGE; |
115 errors.push_back(Error(offset, "direct jump out of range")); | 123 errors.push_back(Error(offset, "direct jump out of range")); |
116 } | 124 } |
117 | 125 |
118 if (validation_info & CPUID_UNSUPPORTED_INSTRUCTION) { | 126 if (validation_info & CPUID_UNSUPPORTED_INSTRUCTION) { |
119 validation_info &= ~CPUID_UNSUPPORTED_INSTRUCTION; | 127 validation_info &= ~CPUID_UNSUPPORTED_INSTRUCTION; |
120 errors.push_back(Error(offset, "required CPU feature not found")); | 128 errors.push_back(Error(offset, "required CPU feature not found")); |
121 } | 129 } |
122 | 130 |
| 131 if (validation_info & UNSUPPORTED_INSTRUCTION) { |
| 132 validation_info &= ~UNSUPPORTED_INSTRUCTION; |
| 133 errors.push_back(Error(offset, "unsupported instruction")); |
| 134 } |
| 135 |
123 if (validation_info & FORBIDDEN_BASE_REGISTER) { | 136 if (validation_info & FORBIDDEN_BASE_REGISTER) { |
124 validation_info &= ~FORBIDDEN_BASE_REGISTER; | 137 validation_info &= ~FORBIDDEN_BASE_REGISTER; |
125 errors.push_back(Error(offset, "improper memory address - bad base")); | 138 errors.push_back(Error(offset, "improper memory address - bad base")); |
126 } | 139 } |
127 | 140 |
128 if (validation_info & UNRESTRICTED_INDEX_REGISTER) { | 141 if (validation_info & UNRESTRICTED_INDEX_REGISTER) { |
129 validation_info &= ~UNRESTRICTED_INDEX_REGISTER; | 142 validation_info &= ~UNRESTRICTED_INDEX_REGISTER; |
130 errors.push_back(Error(offset, "improper memory address - bad index")); | 143 errors.push_back(Error(offset, "improper memory address - bad index")); |
131 } | 144 } |
132 | 145 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
185 | 198 |
186 return result; | 199 return result; |
187 } | 200 } |
188 | 201 |
189 | 202 |
190 Bool ProcessError( | 203 Bool ProcessError( |
191 const uint8_t *begin, const uint8_t *end, | 204 const uint8_t *begin, const uint8_t *end, |
192 uint32_t validation_info, void *user_data_ptr) { | 205 uint32_t validation_info, void *user_data_ptr) { |
193 UNREFERENCED_PARAMETER(begin); | 206 UNREFERENCED_PARAMETER(begin); |
194 UNREFERENCED_PARAMETER(end); | 207 UNREFERENCED_PARAMETER(end); |
195 UNREFERENCED_PARAMETER(validation_info); | 208 UserData &user_data = *reinterpret_cast<UserData *>(user_data_ptr); |
196 UNREFERENCED_PARAMETER(user_data_ptr); | 209 |
197 return FALSE; | 210 // We do the same thing as in ProcessInstruction(): If we are not disabling |
| 211 // non-temporals, they should pass validation, and the corresponding |
| 212 // validation_info bit is simply cleared. |
| 213 if (user_data.flags != DISABLE_NONTEMPORALS) |
| 214 validation_info &= ~UNSUPPORTED_INSTRUCTION; |
| 215 |
| 216 Bool result = (validation_info & (VALIDATION_ERRORS_MASK | BAD_JUMP_TARGET)) |
| 217 ? FALSE |
| 218 : TRUE; |
| 219 return result; |
198 } | 220 } |
199 | 221 |
200 | 222 |
201 typedef Bool ValidateChunkFunc( | 223 typedef Bool ValidateChunkFunc( |
202 const uint8_t *data, size_t size, | 224 const uint8_t *data, size_t size, |
203 uint32_t options, | 225 uint32_t options, |
204 const NaClCPUFeaturesX86 *cpu_features, | 226 const NaClCPUFeaturesX86 *cpu_features, |
205 ValidationCallbackFunc user_callback, | 227 ValidationCallbackFunc user_callback, |
206 void *callback_data); | 228 void *callback_data); |
207 | 229 |
208 | 230 |
209 bool ValidateX86( | 231 bool ValidateX86( |
210 const Segment &segment, | 232 const Segment &segment, |
211 ValidateChunkFunc validate_chunk, | 233 ValidateChunkFunc validate_chunk, |
212 vector<Error> *errors) { | 234 vector<Error> *errors, |
| 235 uint32_t flags) { |
213 | 236 |
214 errors->clear(); | 237 errors->clear(); |
215 | 238 |
216 if (segment.vaddr % kBundleSize != 0) { | 239 if (segment.vaddr % kBundleSize != 0) { |
217 errors->push_back(Error( | 240 errors->push_back(Error( |
218 segment.vaddr, | 241 segment.vaddr, |
219 "Text segment offset in memory is not bundle-aligned.")); | 242 "Text segment offset in memory is not bundle-aligned.")); |
220 return false; | 243 return false; |
221 } | 244 } |
222 | 245 |
223 if (segment.size % kBundleSize != 0) { | 246 if (segment.size % kBundleSize != 0) { |
224 char buf[100]; | 247 char buf[100]; |
225 SNPRINTF(buf, sizeof buf, | 248 SNPRINTF(buf, sizeof buf, |
226 "Text segment size (0x%" NACL_PRIx32 ") is not " | 249 "Text segment size (0x%" NACL_PRIx32 ") is not " |
227 "multiple of bundle size.", | 250 "multiple of bundle size.", |
228 segment.size); | 251 segment.size); |
229 errors->push_back(Error(segment.vaddr + segment.size, buf)); | 252 errors->push_back(Error(segment.vaddr + segment.size, buf)); |
230 return false; | 253 return false; |
231 } | 254 } |
232 | 255 |
233 vector<Jump> jumps; | 256 vector<Jump> jumps; |
234 set<uint32_t> bad_jump_targets; | 257 set<uint32_t> bad_jump_targets; |
235 | 258 |
236 UserData user_data(segment, errors, &jumps, &bad_jump_targets); | 259 UserData user_data(segment, errors, &jumps, &bad_jump_targets, flags); |
237 | 260 |
238 // TODO(shcherbina): customize from command line | 261 // TODO(shcherbina): customize from command line |
239 | 262 |
240 // We collect all errors except bad jump targets, and we separately | 263 // We collect all errors except bad jump targets, and we separately |
241 // memoize all direct jumps (or calls) and bad jump targets. | 264 // memoize all direct jumps (or calls) and bad jump targets. |
242 Bool result = validate_chunk( | 265 Bool result = validate_chunk( |
243 segment.data, segment.size, | 266 segment.data, segment.size, |
244 CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, &kFullCPUIDFeatures, | 267 CALL_USER_CALLBACK_ON_EACH_INSTRUCTION, &kFullCPUIDFeatures, |
245 ProcessInstruction, &user_data); | 268 ProcessInstruction, &user_data); |
246 | 269 |
247 // Report destinations of jumps that lead to bad targets. | 270 // Report destinations of jumps that lead to bad targets. |
248 for (size_t i = 0; i < jumps.size(); i++) { | 271 for (size_t i = 0; i < jumps.size(); i++) { |
249 const Jump &jump = jumps[i]; | 272 const Jump &jump = jumps[i]; |
250 if (bad_jump_targets.count(jump.to) > 0) | 273 if (bad_jump_targets.count(jump.to) > 0) |
251 errors->push_back(Error(jump.from, "bad jump target")); | 274 errors->push_back(Error(jump.from, "bad jump target")); |
252 } | 275 } |
253 | 276 |
254 // Run validator as it is run in loader, without callback on each instruction, | 277 // Run validator as it is run in loader, without callback on each instruction, |
255 // and ensure that results match. If ncval is guaranteed to return the same | 278 // and ensure that results match. If ncval is guaranteed to return the same |
256 // result as actual validator, it can be reliably used as validator wrapper | 279 // result as actual validator, it can be reliably used as validator wrapper |
257 // for testing. | 280 // for testing. |
258 CHECK(result == validate_chunk( | 281 CHECK(result == validate_chunk( |
259 segment.data, segment.size, | 282 segment.data, segment.size, |
260 0, &kFullCPUIDFeatures, | 283 0, &kFullCPUIDFeatures, |
261 ProcessError, NULL)); | 284 ProcessError, &user_data)); |
262 | 285 |
263 return static_cast<bool>(result); | 286 return static_cast<bool>(result); |
264 } | 287 } |
265 | 288 |
266 | 289 |
267 class NcvalArmProblemReporter : public ProblemReporter { | 290 class NcvalArmProblemReporter : public ProblemReporter { |
268 public: | 291 public: |
269 explicit NcvalArmProblemReporter(vector<Error> *errors) : errors(errors) {} | 292 explicit NcvalArmProblemReporter(vector<Error> *errors) : errors(errors) {} |
270 | 293 |
271 protected: | 294 protected: |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
312 nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Sp()), | 335 nacl_arm_dec::RegisterList(nacl_arm_dec::Register::Sp()), |
313 &cpu_features); | 336 &cpu_features); |
314 | 337 |
315 NcvalArmProblemReporter reporter(errors); | 338 NcvalArmProblemReporter reporter(errors); |
316 return validator.validate(segments, &reporter); | 339 return validator.validate(segments, &reporter); |
317 } | 340 } |
318 | 341 |
319 | 342 |
320 void Usage() { | 343 void Usage() { |
321 fprintf(stderr, "Usage:\n"); | 344 fprintf(stderr, "Usage:\n"); |
322 fprintf(stderr, " ncval [-v] <ELF file>\n"); | 345 fprintf(stderr, " ncval [-vd] <ELF file>\n"); |
323 } | 346 } |
324 | 347 |
325 | 348 |
326 struct Options { | 349 struct Options { |
327 Options() : input_file(NULL), verbose(false) {} | 350 Options() : input_file(NULL), verbose(false), flags(0) {} |
328 const char *input_file; | 351 const char *input_file; |
329 bool verbose; | 352 bool verbose; |
| 353 uint32_t flags; |
330 }; | 354 }; |
331 | 355 |
332 | 356 |
333 void ParseOptions(int argc, char **argv, Options *options) { | 357 void ParseOptions(int argc, char **argv, Options *options) { |
334 int opt; | 358 int opt; |
335 while ((opt = getopt(argc, argv, "v")) != -1) { | 359 while ((opt = getopt(argc, argv, "vd")) != -1) { |
336 switch (opt) { | 360 switch (opt) { |
337 case 'v': | 361 case 'v': |
338 options->verbose = true; | 362 options->verbose = true; |
339 break; | 363 break; |
| 364 case 'd': |
| 365 options->flags = DISABLE_NONTEMPORALS; |
| 366 break; |
340 default: | 367 default: |
341 fprintf(stderr, "ERROR: unknown option: [%c]\n\n", opt); | 368 fprintf(stderr, "ERROR: unknown option: [%c]\n\n", opt); |
342 Usage(); | 369 Usage(); |
343 exit(-1); | 370 exit(-1); |
344 } | 371 } |
345 } | 372 } |
346 | 373 |
347 if (argc - optind != 1) { | 374 if (argc - optind != 1) { |
348 fprintf(stderr, "ERROR: too many arguments provided\n\n"); | 375 fprintf(stderr, "ERROR: too many arguments provided\n\n"); |
349 Usage(); | 376 Usage(); |
(...skipping 11 matching lines...) Expand all Loading... |
361 elf_load::Image image; | 388 elf_load::Image image; |
362 elf_load::ReadImage(options.input_file, &image); | 389 elf_load::ReadImage(options.input_file, &image); |
363 | 390 |
364 elf_load::Architecture architecture = elf_load::GetElfArch(image); | 391 elf_load::Architecture architecture = elf_load::GetElfArch(image); |
365 Segment segment = elf_load::GetElfTextSegment(image); | 392 Segment segment = elf_load::GetElfTextSegment(image); |
366 | 393 |
367 vector<Error> errors; | 394 vector<Error> errors; |
368 bool result = false; | 395 bool result = false; |
369 switch (architecture) { | 396 switch (architecture) { |
370 case elf_load::X86_32: | 397 case elf_load::X86_32: |
371 result = ValidateX86(segment, ValidateChunkIA32, &errors); | 398 result = ValidateX86(segment, ValidateChunkIA32, &errors, |
| 399 options.flags); |
372 break; | 400 break; |
373 case elf_load::X86_64: | 401 case elf_load::X86_64: |
374 result = ValidateX86(segment, ValidateChunkAMD64, &errors); | 402 result = ValidateX86(segment, ValidateChunkAMD64, &errors, |
| 403 options.flags); |
375 break; | 404 break; |
376 case elf_load::ARM: | 405 case elf_load::ARM: |
377 result = ValidateArm(segment, &errors); | 406 result = ValidateArm(segment, &errors); |
378 break; | 407 break; |
379 default: | 408 default: |
380 CHECK(false); | 409 CHECK(false); |
381 } | 410 } |
382 | 411 |
383 for (size_t i = 0; i < errors.size(); i++) { | 412 for (size_t i = 0; i < errors.size(); i++) { |
384 const Error &e = errors[i]; | 413 const Error &e = errors[i]; |
385 fprintf(stderr, "%8" NACL_PRIx32 ": %s\n", e.offset, e.message.c_str()); | 414 fprintf(stderr, "%8" NACL_PRIx32 ": %s\n", e.offset, e.message.c_str()); |
386 } | 415 } |
387 | 416 |
388 if (!result) { | 417 if (!result) { |
389 fprintf(stderr, "Invalid.\n"); | 418 fprintf(stderr, "Invalid.\n"); |
390 return 1; | 419 return 1; |
391 } | 420 } |
392 | 421 |
393 if (options.verbose) { | 422 if (options.verbose) { |
394 fprintf(stderr, "Valid.\n"); | 423 fprintf(stderr, "Valid.\n"); |
395 } | 424 } |
396 return 0; | 425 return 0; |
397 } | 426 } |
OLD | NEW |