| 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 |