| OLD | NEW |
| (Empty) |
| 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 /* vdiff.c | |
| 8 * exhaustive instruction enumeration test for x86 Native Client validators. | |
| 9 * | |
| 10 * This file is based on enuminsts.c, but specialized to comparing two | |
| 11 * validators instead of decoders. The enuminsts.c implementation also | |
| 12 * had a bunch of Xed-specific logic which complicated the validator | |
| 13 * comparison in unhelpful ways. | |
| 14 */ | |
| 15 | |
| 16 #ifndef NACL_TRUSTED_BUT_NOT_TCB | |
| 17 #error("This file is not meant for use in the TCB.") | |
| 18 #endif | |
| 19 #if NACL_WINDOWS | |
| 20 #define _CRT_RAND_S /* enable decl of rand_s() */ | |
| 21 #endif | |
| 22 | |
| 23 #include "native_client/src/trusted/validator/x86/testing/enuminsts/enuminsts.h" | |
| 24 | |
| 25 #include <ctype.h> | |
| 26 #include <stdio.h> | |
| 27 #include <string.h> | |
| 28 #include <stdlib.h> | |
| 29 #include <stdarg.h> | |
| 30 #include <time.h> | |
| 31 | |
| 32 #include "native_client/src/include/portability_io.h" | |
| 33 #include "native_client/src/shared/platform/nacl_log.h" | |
| 34 #include "native_client/src/shared/utils/flags.h" | |
| 35 #include "native_client/src/trusted/validator/x86/testing/enuminsts/str_utils.h" | |
| 36 #include "native_client/src/trusted/validator/x86/testing/enuminsts/text2hex.h" | |
| 37 | |
| 38 /* Defines the maximum buffer size used to hold text generated for the | |
| 39 * disassembly of instructions, and corresponding error messages. | |
| 40 */ | |
| 41 #define kBufferSize 1024 | |
| 42 | |
| 43 /* When true, print more messages (i.e. verbosely). */ | |
| 44 static Bool gVerbose = FALSE; | |
| 45 | |
| 46 /* When true, don't print out messages. That is, only print instructions | |
| 47 * defined by --print directives. | |
| 48 */ | |
| 49 static Bool gSilent = FALSE; | |
| 50 | |
| 51 /* Count of errors that have a high certainty of being exploitable. */ | |
| 52 static int gSawLethalError = 0; | |
| 53 | |
| 54 /* Defines the assumed text address for the test instruction */ | |
| 55 const int kTextAddress = 0x1000; | |
| 56 | |
| 57 /* If non-negative, defines the prefix to test. */ | |
| 58 static unsigned int gPrefix = 0; | |
| 59 | |
| 60 /* If non-negative, defines the opcode to test. */ | |
| 61 static int gOpcode = -1; | |
| 62 | |
| 63 /* This option triggers a set of behaviors that help produce repeatable | |
| 64 * output, for easier diffs on the buildbots. | |
| 65 */ | |
| 66 static Bool gEasyDiffMode; | |
| 67 | |
| 68 /* The production and new R-DFA validators */ | |
| 69 NaClEnumeratorDecoder* vProd; | |
| 70 NaClEnumeratorDecoder* vDFA; | |
| 71 | |
| 72 /* The name of the executable (i.e. argv[0] from the command line). */ | |
| 73 static const char *gArgv0 = "argv0"; | |
| 74 #define FLAG_EasyDiff "--easydiff" | |
| 75 | |
| 76 /* Records that unexpected internal error occurred. */ | |
| 77 void InternalError(const char *why) { | |
| 78 fprintf(stderr, "%s: Internal Error: %s\n", gArgv0, why); | |
| 79 gSawLethalError = 1; | |
| 80 } | |
| 81 | |
| 82 /* Records that a fatal (i.e. non-recoverable) error occurred. */ | |
| 83 void ReportFatalError(const char* why) { | |
| 84 char buffer[kBufferSize]; | |
| 85 SNPRINTF(buffer, kBufferSize, "%s - quitting!", why); | |
| 86 InternalError(buffer); | |
| 87 exit(1); | |
| 88 } | |
| 89 | |
| 90 /* Prints out the instruction each decoder disassembled */ | |
| 91 static void PrintDisassembledInstructionVariants(NaClEnumerator *pinst, | |
| 92 NaClEnumerator *dinst) { | |
| 93 vProd->_print_inst_fn(pinst); | |
| 94 vDFA->_print_inst_fn(dinst); | |
| 95 } | |
| 96 | |
| 97 /* Prints out progress messages. */ | |
| 98 static void PrintVProgress(const char* format, va_list ap) { | |
| 99 if (gSilent) { | |
| 100 /* Generating opcode sequences, so add special prefix so that we | |
| 101 * can print these out when read by the corresponding input decoder. | |
| 102 */ | |
| 103 printf("#PROGRESS#"); | |
| 104 } | |
| 105 vprintf(format, ap); | |
| 106 } | |
| 107 | |
| 108 static void PrintProgress(const char* format, ...) ATTRIBUTE_FORMAT_PRINTF(1,2); | |
| 109 | |
| 110 /* Prints out progress messages. */ | |
| 111 static void PrintProgress(const char* format, ...) { | |
| 112 va_list ap; | |
| 113 va_start(ap, format); | |
| 114 PrintVProgress(format, ap); | |
| 115 va_end(ap); | |
| 116 } | |
| 117 | |
| 118 /* Report a disagreement between decoders. | |
| 119 */ | |
| 120 static void DecoderError(const char *why, | |
| 121 NaClEnumerator *pinst, | |
| 122 NaClEnumerator *dinst, | |
| 123 const char *details) { | |
| 124 /* If reached, did not skip, so report the error. */ | |
| 125 printf("**** ERROR: %s: %s\n", why, (details == NULL ? "" : details)); | |
| 126 PrintDisassembledInstructionVariants(pinst, dinst); | |
| 127 } | |
| 128 | |
| 129 static void PrintBytes(FILE *f, uint8_t* bytes, size_t len) { | |
| 130 size_t i; | |
| 131 for (i = 0; i < len; i++) { | |
| 132 fprintf(f, "%02x", bytes[i]); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 struct vdiff_stats { | |
| 137 int64_t tried; | |
| 138 int64_t valid; | |
| 139 int64_t invalid; | |
| 140 int64_t errors; | |
| 141 int64_t ignored; | |
| 142 } gVDiffStats = {0, 0, 0, 0, 0}; | |
| 143 | |
| 144 static void IncrTried(void) { | |
| 145 gVDiffStats.tried += 1; | |
| 146 } | |
| 147 | |
| 148 static void IncrValid(void) { | |
| 149 gVDiffStats.valid += 1; | |
| 150 } | |
| 151 | |
| 152 static void IncrInvalid(void) { | |
| 153 gVDiffStats.invalid += 1; | |
| 154 } | |
| 155 | |
| 156 static void IncrErrors(void) { | |
| 157 gVDiffStats.errors += 1; | |
| 158 } | |
| 159 | |
| 160 static void IncrIgnored(void) { | |
| 161 gVDiffStats.ignored += 1; | |
| 162 } | |
| 163 | |
| 164 static void PrintStats(void) { | |
| 165 printf("Stats:\n"); | |
| 166 if (!gEasyDiffMode) { | |
| 167 printf("valid: %" NACL_PRIu64 "\n", gVDiffStats.valid); | |
| 168 printf("invalid: %" NACL_PRIu64 "\n", gVDiffStats.invalid); | |
| 169 } | |
| 170 printf("errors: %" NACL_PRIu64 "\n", gVDiffStats.errors); | |
| 171 printf("tried: %" NACL_PRIu64 "\n", gVDiffStats.tried); | |
| 172 printf("ignored: %" NACL_PRIu64 "\n", gVDiffStats.ignored); | |
| 173 printf(" =? %" NACL_PRIu64 " valid + invalid + errors + ignored\n", | |
| 174 gVDiffStats.valid + gVDiffStats.invalid + gVDiffStats.errors + | |
| 175 gVDiffStats.ignored); | |
| 176 } | |
| 177 | |
| 178 static void InitInst(NaClEnumerator *nacle, | |
| 179 uint8_t *itext, size_t nbytes) | |
| 180 { | |
| 181 memcpy(nacle->_itext, itext, nbytes); | |
| 182 nacle->_num_bytes = nbytes; | |
| 183 } | |
| 184 | |
| 185 /* Print out decodings if specified on the command line. */ | |
| 186 /* Test comparison for a single instruction. */ | |
| 187 static void TryOneInstruction(uint8_t *itext, size_t nbytes) { | |
| 188 NaClEnumerator pinst; /* for prod validator */ | |
| 189 NaClEnumerator dinst; /* for dfa validator */ | |
| 190 Bool prod_okay, rdfa_okay; | |
| 191 | |
| 192 IncrTried(); | |
| 193 do { | |
| 194 if (gVerbose) { | |
| 195 printf("================"); | |
| 196 PrintBytes(stdout, itext, nbytes); | |
| 197 printf("\n"); | |
| 198 } | |
| 199 | |
| 200 /* Try to parse the sequence of test bytes. */ | |
| 201 InitInst(&pinst, itext, nbytes); | |
| 202 InitInst(&dinst, itext, nbytes); | |
| 203 vProd->_parse_inst_fn(&pinst, kTextAddress); | |
| 204 vDFA->_parse_inst_fn(&dinst, kTextAddress); | |
| 205 prod_okay = vProd->_maybe_inst_validates_fn(&pinst); | |
| 206 rdfa_okay = vDFA->_maybe_inst_validates_fn(&dinst); | |
| 207 | |
| 208 if (prod_okay && rdfa_okay) { | |
| 209 if (vProd->_inst_length_fn(&pinst) == | |
| 210 vDFA->_inst_length_fn(&dinst)) { | |
| 211 /* Both validators see a legal instruction, */ | |
| 212 /* and they agree on critical details. */ | |
| 213 IncrValid(); | |
| 214 } else { | |
| 215 DecoderError("LENGTH MISMATCH", &pinst, &dinst, ""); | |
| 216 IncrErrors(); | |
| 217 } | |
| 218 } else if (prod_okay && !rdfa_okay) { | |
| 219 /* | |
| 220 * 32bit production validator by design is unable to distingush a lot of | |
| 221 * instructions (the ones which work only with memory or only with | |
| 222 * registers). To avoid commiting multimegabyte golden file don't count | |
| 223 * these differences as substantial. It's not a security problem if we | |
| 224 * reject some valid x86 instructions and if we'll lose something | |
| 225 * important hopefully developers will remind us. | |
| 226 */ | |
| 227 if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && | |
| 228 NACL_TARGET_SUBARCH == 32) { | |
| 229 IncrIgnored(); | |
| 230 } else { | |
| 231 /* Validators disagree on instruction legality */ | |
| 232 DecoderError("VALIDATORS DISAGREE (prod accepts, RDFA rejects)", | |
| 233 &pinst, | |
| 234 &dinst, | |
| 235 ""); | |
| 236 IncrErrors(); | |
| 237 } | |
| 238 } else if (!prod_okay && rdfa_okay) { | |
| 239 /* Validators disagree on instruction legality */ | |
| 240 DecoderError("VALIDATORS DISAGREE (prod rejects, RDFA accepts)", | |
| 241 &pinst, | |
| 242 &dinst, | |
| 243 ""); | |
| 244 IncrErrors(); | |
| 245 } else { | |
| 246 /* Both validators see an illegal instruction */ | |
| 247 IncrInvalid(); | |
| 248 } | |
| 249 | |
| 250 if (gVerbose) { | |
| 251 PrintDisassembledInstructionVariants(&pinst, &dinst); | |
| 252 } | |
| 253 } while (0); | |
| 254 } | |
| 255 | |
| 256 /* A function type for instruction "TestAll" functions. | |
| 257 * Parameters: | |
| 258 * prefix: up to four bytes of prefix. | |
| 259 * prefix_length: size_t on [0..4] specifying length of prefix. | |
| 260 * print_prefix: For easy diff of test output, avoid printing | |
| 261 * the value of a randomly selected REX prefix. | |
| 262 */ | |
| 263 typedef void (*TestAllFunction)(const unsigned int prefix, | |
| 264 const size_t prefix_length, | |
| 265 const char* print_prefix); | |
| 266 | |
| 267 /* Create a char* rendition of a prefix string, appending bytes | |
| 268 * in ps. When using a randomly generated REX prefix on the bots, | |
| 269 * it's useful to avoid printing the actual REX prefix so that | |
| 270 * output can be diffed from run-to-run. For example, instead of | |
| 271 * printing "0F45" you might print "0FXX". Parameters: | |
| 272 * prefix: The part of the prefix value to print | |
| 273 * ps: 'postscript', string to append to prefix value | |
| 274 * str: where to put the ASCII version of the prefix | |
| 275 */ | |
| 276 static char* StrPrefix(const unsigned int prefix, char* ps, char* str) { | |
| 277 sprintf(str, "%x%s", prefix, (ps == NULL) ? "" : ps); | |
| 278 return str; | |
| 279 } | |
| 280 | |
| 281 /* Enumerate and test all 24-bit opcode+modrm+sib patterns for a | |
| 282 * particular prefix. | |
| 283 */ | |
| 284 static void TestAllWithPrefix(const unsigned int prefix, | |
| 285 const size_t prefix_length, | |
| 286 const char* print_prefix) { | |
| 287 const size_t kInstByteCount = NACL_ENUM_MAX_INSTRUCTION_BYTES; | |
| 288 const size_t kIterByteCount = 3; | |
| 289 InstByteArray itext; | |
| 290 size_t i; | |
| 291 int op, modrm, sib; | |
| 292 int min_op; | |
| 293 int max_op; | |
| 294 | |
| 295 if ((gPrefix > 0) && (gPrefix != prefix)) return; | |
| 296 | |
| 297 PrintProgress("TestAllWithPrefix(%s)\n", print_prefix); | |
| 298 /* set up prefix */ | |
| 299 memcpy(itext, &prefix, prefix_length); | |
| 300 /* set up filler bytes */ | |
| 301 for (i = prefix_length + kIterByteCount; i < kInstByteCount; i++) { | |
| 302 itext[i] = (uint8_t)i; | |
| 303 } | |
| 304 if (gOpcode < 0) { | |
| 305 min_op = 0; | |
| 306 max_op = 256; | |
| 307 } else { | |
| 308 min_op = gOpcode; | |
| 309 max_op = gOpcode + 1; | |
| 310 } | |
| 311 for (op = min_op; op < max_op; op++) { | |
| 312 itext[prefix_length] = op; | |
| 313 if (!gEasyDiffMode) PrintProgress("%02x 00 00\n", op); | |
| 314 for (modrm = 0; modrm < 256; modrm++) { | |
| 315 itext[prefix_length + 1] = modrm; | |
| 316 for (sib = 0; sib < 256; sib++) { | |
| 317 itext[prefix_length + 2] = sib; | |
| 318 TryOneInstruction(itext, kInstByteCount); | |
| 319 } | |
| 320 } | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 /* For 3DNow!, the operand byte goes at the end. Format is: | |
| 325 * 0F 0F [ModRM] [SIB] [displacement] imm8_opcode | |
| 326 * See AMD doc 24594, page 435. | |
| 327 */ | |
| 328 static void TestAll3DNow(const unsigned int prefix, | |
| 329 const size_t prefix_length, | |
| 330 const char* print_prefix) { | |
| 331 const size_t kInstByteCount = NACL_ENUM_MAX_INSTRUCTION_BYTES; | |
| 332 const size_t kIterByteCount = 3; | |
| 333 InstByteArray itext; | |
| 334 size_t i; | |
| 335 int op, modrm, sib; | |
| 336 | |
| 337 if ((gPrefix > 0) && (gPrefix != prefix)) return; | |
| 338 | |
| 339 PrintProgress("TestAll3DNow(%s)\n", print_prefix); | |
| 340 /* set up prefix */ | |
| 341 memcpy(itext, &prefix, prefix_length); | |
| 342 /* set up filler bytes */ | |
| 343 for (i = prefix_length + kIterByteCount; i < kInstByteCount; i++) { | |
| 344 itext[i] = (uint8_t)i; | |
| 345 } | |
| 346 | |
| 347 for (op = 0; op < 256; op++) { | |
| 348 if (!gEasyDiffMode) PrintProgress("%02x 00 00\n", op); | |
| 349 /* Use opcode as fill byte, forcing iteration through 3DNow opcodes. */ | |
| 350 for (i = prefix_length + 2; i < kIterByteCount; i++) itext[i] = op; | |
| 351 | |
| 352 for (modrm = 0; modrm < 256; modrm++) { | |
| 353 itext[prefix_length] = modrm; | |
| 354 for (sib = 0; sib < 256; sib++) { | |
| 355 itext[prefix_length + 1] = sib; | |
| 356 TryOneInstruction(itext, kInstByteCount); | |
| 357 } | |
| 358 } | |
| 359 } | |
| 360 } | |
| 361 | |
| 362 #if NACL_TARGET_SUBARCH == 64 | |
| 363 /* REX prefixes range from 0x40 to 0x4f. */ | |
| 364 const uint32_t kREXBase = 0x40; | |
| 365 const uint32_t kREXRange = 0x10; | |
| 366 const uint32_t kREXMax = 0x50; /* kREXBase + kREXRange */ | |
| 367 | |
| 368 /* Generate a random REX prefix, to use for the entire run. */ | |
| 369 static uint32_t RandomRexPrefix(void) { | |
| 370 static uint32_t static_rex_prefix = 0; | |
| 371 | |
| 372 if (0 == static_rex_prefix) { | |
| 373 #if NACL_LINUX || NACL_OSX | |
| 374 static_rex_prefix = kREXBase + (random() % kREXRange); | |
| 375 #elif NACL_WINDOWS | |
| 376 if (rand_s(&static_rex_prefix) != 0) { | |
| 377 ReportFatalError("rand_s() failed\n"); | |
| 378 } else { | |
| 379 static_rex_prefix = kREXBase + (static_rex_prefix % kREXRange); | |
| 380 } | |
| 381 #else | |
| 382 # error "Unknown operating system." | |
| 383 #endif | |
| 384 } | |
| 385 return static_rex_prefix; | |
| 386 } | |
| 387 #endif | |
| 388 | |
| 389 #define AppendPrefixByte(oldprefix, pbyte) (((oldprefix) << 8) | (pbyte)) | |
| 390 /* For x86-64, enhance the iteration by looping through REX prefixes. | |
| 391 */ | |
| 392 static void WithREX(TestAllFunction testall, | |
| 393 const unsigned int prefix, | |
| 394 const size_t prefix_length) { | |
| 395 char pstr[kBufferSize]; | |
| 396 #if NACL_TARGET_SUBARCH == 64 | |
| 397 unsigned char irex; | |
| 398 unsigned int rprefix; | |
| 399 /* test with REX prefixes */ | |
| 400 printf("WithREX(testall, %x, %d, %d)\n", prefix, | |
| 401 (int)prefix_length, gEasyDiffMode); | |
| 402 if (gEasyDiffMode) { | |
| 403 printf("With random REX prefix.\n"); | |
| 404 irex = RandomRexPrefix(); | |
| 405 rprefix = AppendPrefixByte(prefix, irex); | |
| 406 testall(rprefix, prefix_length + 1, StrPrefix(prefix, "XX", pstr)); | |
| 407 } else { | |
| 408 for (irex = kREXBase; irex < kREXMax; irex++) { | |
| 409 rprefix = AppendPrefixByte(prefix, irex); | |
| 410 printf("With REX prefix %x\n", rprefix); | |
| 411 testall(rprefix, prefix_length + 1, StrPrefix(rprefix, "", pstr)); | |
| 412 } | |
| 413 } | |
| 414 #endif | |
| 415 /* test with no REX prefix */ | |
| 416 testall(prefix, prefix_length, StrPrefix(prefix, NULL, pstr)); | |
| 417 } | |
| 418 #undef AppendPrefixByte | |
| 419 | |
| 420 /* For all prefixes, call TestAllWithPrefix() to enumrate and test | |
| 421 * all instructions. | |
| 422 */ | |
| 423 static void TestAllInstructions(void) { | |
| 424 /* NOTE: Prefix byte order needs to be reversed when written as | |
| 425 * an integer. For example, for integer prefix 0x3a0f, 0f will | |
| 426 * go in instruction byte 0, and 3a in byte 1. | |
| 427 */ | |
| 428 WithREX(TestAllWithPrefix, 0, 0); | |
| 429 WithREX(TestAllWithPrefix, 0x0f, 1); /* two-byte opcode */ | |
| 430 WithREX(TestAllWithPrefix, 0x0ff2, 2); /* SSE2 */ | |
| 431 WithREX(TestAllWithPrefix, 0x0ff3, 2); /* SSE */ | |
| 432 WithREX(TestAllWithPrefix, 0x0f66, 2); /* SSE2 */ | |
| 433 WithREX(TestAllWithPrefix, 0x380f, 2); /* SSSE3 */ | |
| 434 WithREX(TestAllWithPrefix, 0x3a0f, 2); /* SSE4 */ | |
| 435 WithREX(TestAllWithPrefix, 0x380f66, 3); /* SSE4+ */ | |
| 436 WithREX(TestAllWithPrefix, 0x380ff2, 3); /* SSE4+ */ | |
| 437 WithREX(TestAllWithPrefix, 0x3a0f66, 3); /* SSE4+ */ | |
| 438 WithREX(TestAllWithPrefix, 0x0ff366, 3); /* SSE4+ */ | |
| 439 WithREX(TestAll3DNow, 0x0f0f, 2); | |
| 440 } | |
| 441 | |
| 442 /* Used to test one instruction at a time, for example, in regression | |
| 443 * testing, or for instruction arguments from the command line. | |
| 444 */ | |
| 445 static void TestOneInstruction(const char *asciihex) { | |
| 446 InstByteArray ibytes; | |
| 447 int nbytes; | |
| 448 | |
| 449 nbytes = Text2Bytes(ibytes, asciihex, "Command-line argument", -1); | |
| 450 if (nbytes == 0) return; | |
| 451 if (gVerbose) { | |
| 452 int i; | |
| 453 printf("trying %s (", asciihex); | |
| 454 for (i = 0; i < nbytes; ++i) { | |
| 455 printf("%02x", ibytes[i]); | |
| 456 } | |
| 457 printf(")\n"); | |
| 458 } | |
| 459 TryOneInstruction(ibytes, (size_t) nbytes); | |
| 460 } | |
| 461 | |
| 462 /* A set of test cases that have caused problems in the past. | |
| 463 * This is a bit stale; most of the test cases came from xed_compare.py. | |
| 464 * Mostly this program has been tested by TestAllInstructions(), | |
| 465 * possible since this program is much faster than xed_compare.py | |
| 466 */ | |
| 467 static void RunRegressionTests(void) { | |
| 468 TestOneInstruction("0024c2"); | |
| 469 TestOneInstruction("017967"); | |
| 470 TestOneInstruction("0f12c0"); | |
| 471 TestOneInstruction("0f13c0"); | |
| 472 TestOneInstruction("0f17c0"); | |
| 473 TestOneInstruction("0f01c1"); | |
| 474 TestOneInstruction("0f00300000112233445566778899aa"); | |
| 475 TestOneInstruction("cc"); | |
| 476 TestOneInstruction("C3"); | |
| 477 TestOneInstruction("0f00290000112233445566778899aa"); | |
| 478 TestOneInstruction("80e4f7"); | |
| 479 TestOneInstruction("e9a0ffffff"); | |
| 480 TestOneInstruction("4883ec08"); | |
| 481 TestOneInstruction("0f00040500112233445566778899aa"); | |
| 482 /* Below are newly discovered mistakes in call instructions, where the wrong | |
| 483 * byte length was required by x86-64 nacl validator. | |
| 484 */ | |
| 485 TestOneInstruction("262e7e00"); | |
| 486 TestOneInstruction("2e3e7900"); | |
| 487 /* From the AMD manual, "An instruction may have only one REX prefix */ | |
| 488 /* which must immediately precede the opcode or first excape byte */ | |
| 489 /* in the instruction encoding." */ | |
| 490 TestOneInstruction("406601d8"); /* illegal; REX before data16 */ | |
| 491 TestOneInstruction("664001d8"); /* legal; REX after data16 */ | |
| 492 TestOneInstruction("414001d8"); /* illegal; two REX bytes */ | |
| 493 | |
| 494 /* And some tests for degenerate prefix patterns */ | |
| 495 TestOneInstruction("666690"); | |
| 496 TestOneInstruction("6690"); | |
| 497 TestOneInstruction("666666666666666666666690"); | |
| 498 TestOneInstruction("66454490"); | |
| 499 TestOneInstruction("66454f90"); | |
| 500 TestOneInstruction("456690"); | |
| 501 } | |
| 502 | |
| 503 /* Define decoders that can be registered. */ | |
| 504 extern NaClEnumeratorDecoder* RegisterNaClDecoder(void); | |
| 505 extern NaClEnumeratorDecoder* RegisterRagelDecoder(void); | |
| 506 | |
| 507 /* Initialize the set of available decoders. */ | |
| 508 static void VDiffInitializeAvailableDecoders(void) { | |
| 509 vProd = RegisterNaClDecoder(); | |
| 510 vDFA = RegisterRagelDecoder(); | |
| 511 } | |
| 512 | |
| 513 static int ParseArgv(const int argc, const char* argv[]) { | |
| 514 int nextarg; | |
| 515 | |
| 516 gArgv0 = argv[0]; | |
| 517 nextarg = 1; | |
| 518 if (nextarg < argc && | |
| 519 0 == strcmp(argv[nextarg], FLAG_EasyDiff)) { | |
| 520 gEasyDiffMode = TRUE; | |
| 521 nextarg += 1; | |
| 522 } | |
| 523 return nextarg; | |
| 524 } | |
| 525 | |
| 526 static char g_standard_output_buffer[4 << 10]; | |
| 527 | |
| 528 int main(const int argc, const char *argv[]) { | |
| 529 int nextarg; | |
| 530 | |
| 531 NaClLogModuleInit(); | |
| 532 NaClLogSetVerbosity(LOG_FATAL); | |
| 533 if (0 != setvbuf(stdout, g_standard_output_buffer, _IOLBF, | |
| 534 sizeof g_standard_output_buffer)) { | |
| 535 NaClLog(LOG_FATAL, "vdiff: setvbuf failed\n"); | |
| 536 } | |
| 537 #if NACL_LINUX || NACL_OSX | |
| 538 srandom(time(NULL)); | |
| 539 #endif | |
| 540 VDiffInitializeAvailableDecoders(); | |
| 541 | |
| 542 nextarg = ParseArgv(argc, argv); | |
| 543 if (nextarg == argc) { | |
| 544 if (gPrefix == 0) RunRegressionTests(); | |
| 545 TestAllInstructions(); | |
| 546 } else { | |
| 547 int i; | |
| 548 gVerbose = TRUE; | |
| 549 for (i = nextarg; i < argc; ++i) { | |
| 550 TestOneInstruction(argv[i]); | |
| 551 } | |
| 552 } | |
| 553 PrintStats(); | |
| 554 | |
| 555 /* exit with non-zero error code if there were errors. */ | |
| 556 exit(gVDiffStats.errors != 0); | |
| 557 } | |
| OLD | NEW |