Index: src/trusted/validator/x86/testing/enuminsts/enuminsts.c |
diff --git a/src/trusted/validator/x86/testing/enuminsts/enuminsts.c b/src/trusted/validator/x86/testing/enuminsts/enuminsts.c |
deleted file mode 100644 |
index 52df96f9f47bb279807ab2dfe081ebceb5cd1124..0000000000000000000000000000000000000000 |
--- a/src/trusted/validator/x86/testing/enuminsts/enuminsts.c |
+++ /dev/null |
@@ -1,1331 +0,0 @@ |
-/* |
- * Copyright (c) 2012 The Native Client Authors. All rights reserved. |
- * Use of this source code is governed by a BSD-style license that can be |
- * found in the LICENSE file. |
- */ |
- |
-/* enuminsts.c |
- * exhaustive instruction enumeration test for x86 Native Client decoder. |
- */ |
- |
-/* TODO(karl) - Fix the calls to the decoder for x86-32 to use the same decoder |
- * as the x86-32 validator, and document how to properly test the |
- * x86-32 decoder. |
- */ |
-#ifndef NACL_TRUSTED_BUT_NOT_TCB |
-#error("This file is not meant for use in the TCB.") |
-#endif |
- |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/enuminsts.h" |
- |
-#include <ctype.h> |
-#include <stdio.h> |
-#include <string.h> |
-#include <stdlib.h> |
-#include <stdarg.h> |
- |
-#include "native_client/src/include/portability_io.h" |
-#include "native_client/src/shared/platform/nacl_log.h" |
-#include "native_client/src/shared/utils/flags.h" |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/input_tester.h" |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/str_utils.h" |
-#include "native_client/src/trusted/validator/x86/testing/enuminsts/text2hex.h" |
- |
-/* When non-zero, prints out additional debugging messages. */ |
-#define kDebug 0 |
- |
-/* Defines the maximum buffer size used to hold text generated for the |
- * disassembly of instructions, and corresponding error messages. |
- */ |
-#define kBufferSize 1024 |
- |
-/* When true, print more messages (i.e. verbosely). */ |
-static Bool gVerbose = FALSE; |
- |
-/* When true, don't print out messages. That is, only print instructions |
- * defined by --print directives. |
- */ |
-static Bool gSilent = FALSE; |
- |
-/* When true, don't report consecutive errors for consecutive instructions |
- * with the same instruction mnemonic. |
- */ |
-static Bool gSkipRepeatReports = FALSE; |
- |
-/* When true, check opcode mnemonic differences when processing each |
- * instruction. |
- */ |
-static Bool gCheckMnemonics = TRUE; |
- |
-/* When true, check for operand differences when processing each instruction. */ |
-static Bool gCheckOperands = FALSE; |
- |
-/* Count of errors that have a high certainty of being exploitable. */ |
-static int gSawLethalError = 0; |
- |
-/* Defines the assumed text address for the test instruction */ |
-const int kTextAddress = 0x1000; |
- |
-/* If non-negative, defines the prefix to test. */ |
-static unsigned int gPrefix = 0; |
- |
-/* If non-negative, defines the opcode to test. */ |
-static int gOpcode = -1; |
- |
-/* If true, check if nacl instruction is NACL legal. */ |
-static Bool gNaClLegal = FALSE; |
- |
-/* If true, check if nacl instruction is also implemented in xed. */ |
-static Bool gXedImplemented = FALSE; |
- |
-/* If true, don't bother to do operand compares for nop's. */ |
-static Bool gNoCompareNops = FALSE; |
- |
-/* If true, only skip contiguous errors. */ |
-static Bool gSkipContiguous = FALSE; |
- |
-static const char* target_machine = "x86-" |
-#if NACL_TARGET_SUBARCH == 64 |
- "64" |
-#else |
- "32" |
-#endif |
- ; |
- |
-/* Defines maximum number of available decoders (See comments on |
- * struct NaClEnumeratorDecoder in enuminst.h for details on what |
- * a decoder is. |
- */ |
-#define NACL_MAX_AVAILABLE_DECODERS 10 |
- |
-/* Holds the set of available decoders. */ |
-NaClEnumeratorDecoder* kAvailableDecoders[NACL_MAX_AVAILABLE_DECODERS]; |
- |
-/* Holds the number of (pre)registered available decoders. */ |
-size_t kNumAvailableDecoders; |
- |
-/* This struct holds a list of instruction opcode sequences that we |
- * want to treat specially. Used to filter out problem cases from |
- * the enumeration. |
- */ |
-typedef struct { |
- /* Pointer to array of bytes for the instruction. */ |
- uint8_t* bytes_; |
- /* The size of bytes_. */ |
- size_t bytes_size_; |
- /* Pointer to array of instructions. Each element is |
- * the index into bytes_ where the corresponding byte sequence |
- * of the instruction begins. The next element in the array is |
- * the end point for the current instruction. |
- */ |
- size_t* insts_; |
- /* The size of insts_. */ |
- size_t insts_size_; |
- /* Number of instructions stored in insts_. */ |
- size_t num_insts_; |
- /* Number of bytes stored in bytes_. */ |
- size_t num_bytes_; |
-} InstList; |
- |
-/* This struct holds state concerning an instruction, both from the |
- * various available decoders. Some of the state information is |
- * redundant, preserved to avoid having to recompute it. |
- */ |
-typedef struct { |
- /* Holds the set of decoders to enumerate over. */ |
- NaClEnumerator _enumerator; |
- /* True if a decoder or comparison failed due to an error with |
- * the way the instruction was decoded. |
- */ |
- Bool _decoder_error; |
-} ComparedInstruction; |
- |
-/* The state to use to compare instructions. */ |
-static ComparedInstruction gCinst; |
- |
-/* The name of the executable (i.e. argv[0] from the command line). */ |
-static char *gArgv0 = "argv0"; |
- |
-static INLINE const char* BoolName(Bool b) { |
- return b ? "true" : "false"; |
-} |
- |
-/* Print out the bindings to command line arguments. */ |
-static void PrintBindings(ComparedInstruction* cinst) { |
- size_t i; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- fprintf(stderr, "--%s\n", cinst->_enumerator._decoder[i]->_id_name); |
- } |
- fprintf(stderr, "--checkmnemonics=%s\n", BoolName(gCheckMnemonics)); |
- fprintf(stderr, "--checkoperands=%s\n", BoolName(gCheckOperands)); |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- fprintf(stderr, "--%s=%s\n", |
- (cinst->_enumerator._decoder[i]->_legal_only |
- ? "legal" : "illegal"), |
- cinst->_enumerator._decoder[i]->_id_name); |
- } |
- fprintf(stderr, "--nacllegal=%s\n", BoolName(gNaClLegal)); |
-#ifdef NACL_REVISION |
- fprintf(stderr, "--nacl_revision=%d\n", NACL_REVISION); |
-#endif |
- if (gOpcode >= 0) fprintf(stderr, "--opcode=0x%02x\n", gOpcode); |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (cinst->_enumerator._decoder[i]->_print_opcode_sequence) { |
- fprintf(stderr, "--opcode_bytes=%s\n", |
- cinst->_enumerator._decoder[i]->_id_name); |
- } |
- } |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (cinst->_enumerator._decoder[i]->_print_opcode_sequence_plus_desc) { |
- fprintf(stderr, "--opcode_bytes_plus_desc=%s\n", |
- cinst->_enumerator._decoder[i]->_id_name); |
- } |
- } |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (cinst->_enumerator._decoder[i]->_print) { |
- fprintf(stderr, "--print=%s\n", cinst->_enumerator._decoder[i]->_id_name); |
- } |
- } |
-#ifdef NACL_XED_DECODER |
- fprintf(stderr, "--pin_version=\"%s\"\n", NACL_PINV); |
-#endif |
- if (gPrefix > 0) fprintf(stderr, "--prefix=0x%08x\n", gPrefix); |
- fprintf(stderr, "--nops=%s\n", BoolName(gNoCompareNops)); |
- fprintf(stderr, "--skipcontiguous=%s\n", BoolName(gSkipContiguous)); |
- fprintf(stderr, "--verbose=%s\n", BoolName(gVerbose)); |
- fprintf(stderr, "--xedimplemented=%s\n", BoolName(gXedImplemented)); |
- exit(1); |
-} |
- |
-/* Prints out summary of how to use this executable. and then exits. */ |
-static void Usage(void) { |
- size_t i; |
- fprintf(stderr, "usage: %s [decoders] [options] [hexbytes ...]\n", |
- gArgv0); |
- fprintf(stderr, "\n"); |
- fprintf(stderr, " Compare %s instruction decoders\n", |
- target_machine); |
- fprintf(stderr, "\n"); |
- fprintf(stderr, " With no arguments, enumerate all %s instructions.\n", |
- target_machine); |
- fprintf(stderr, " With arguments, decode each sequence of " |
- "opcode bytes.\n"); |
- fprintf(stderr, "\n"); |
- fprintf(stderr, "Available decoders are (select using --name):\n"); |
- fprintf(stderr, "\n"); |
- for (i = 0; i < kNumAvailableDecoders; ++i) { |
- fprintf(stderr, " %s: %s\n", |
- kAvailableDecoders[i]->_id_name, |
- kAvailableDecoders[i]->_usage_message); |
- } |
- fprintf(stderr, "\n"); |
- fprintf(stderr, "One or more filters are required:\n"); |
- fprintf(stderr, " --illegal=XX: Filter instructions to only consider " |
- "those instructions\n"); |
- fprintf(stderr, " that are illegal instructions, as defined by " |
- "decoder XX\n"); |
- fprintf(stderr, " --legal=XX: Filter instructions to only consider " |
- "those instructions\n"); |
- fprintf(stderr, " that are legal instructions, as defined by " |
- "decoder XX\n"); |
- fprintf(stderr, " --print=XX: Prints out set of enumerated " |
- "instructions,\n"); |
- fprintf(stderr, " for the specified decoder XX (may be " |
- "repeated).\n"); |
- fprintf(stderr, " Also registers decoder XX if needed.\n"); |
- |
- fprintf(stderr, "\nAdditional options:\n"); |
- fprintf(stderr, " --bindings: Prints out current (command-line) " |
- "bindings\n"); |
- fprintf(stderr, " --checkmnemonics: enables opcode mnemonic " |
- "comparisons\n"); |
- fprintf(stderr, " --checkoperands: enables operand comparison (slow)\n"); |
- fprintf(stderr, " --ignore_mnemonic=file: ignore mnemonic name " |
- "comparison\n"); |
- fprintf(stderr, " for instruction sequences in file (may be " |
- "repeated)\n"); |
- fprintf(stderr, " --ignored=<file>: ignore instruction sequences " |
- "in file (may be repeated)\n"); |
- fprintf(stderr, " --nacllegal: use validator checks/instruction type)\n"); |
- fprintf(stderr, " in addition to decoder errors with nacl\n"); |
- fprintf(stderr, " instruction filters.\n"); |
-#ifdef NACL_REVISION |
- fprintf(stderr, " --nacl_revision: print nacl revision used to build\n" |
- " the nacl decoder\n"); |
-#endif |
- fprintf(stderr, " --opcode=XX: only process given opcode XX for " |
- "each prefix\n"); |
- fprintf(stderr, " --opcode_bytes=XX: Prints out opcode bytes for set of\n" |
- " enumerated instructions. To be used by decoder --in\n"); |
- fprintf(stderr, |
- " --opcode_bytes_plus_desc=XX: Prints out opcode bytes for set\n" |
- " of enumerated instruction, plus a print description (as a\n" |
- " comment). To be used by decoder --n\n"); |
-#ifdef NACL_XED_DECODER |
- fprintf(stderr, " --pin_version: Prints out pin version used for xed " |
- "decoder\n"); |
-#endif |
- fprintf(stderr, " --prefix=XX: only process given prefix XX\n"); |
- fprintf(stderr, " --nops: Don't operand compare nops.\n"); |
- fprintf(stderr, " --skipcontiguous: Only skip contiguous errors\n"); |
- fprintf(stderr, " --verbose: add verbose comments to output\n"); |
- fprintf(stderr, " --xedimplemented: only compare NaCl instruction that " |
- "are also implemented in xed\n"); |
- exit(gSawLethalError); |
-} |
- |
-/* Records that unexpected internal error occurred. */ |
-void InternalError(const char *why) { |
- fprintf(stderr, "%s: Internal Error: %s\n", gArgv0, why); |
- gSawLethalError = 1; |
-} |
- |
-/* Records that a fatal (i.e. non-recoverable) error occurred. */ |
-void ReportFatalError(const char* why) { |
- char buffer[kBufferSize]; |
- SNPRINTF(buffer, kBufferSize, "%s - quitting!", why); |
- InternalError(buffer); |
- exit(1); |
-} |
- |
-/* Returns true if the given opcode sequence text is in the |
- * given instruction list. |
- */ |
-static Bool InInstructionList(InstList* list, uint8_t* itext, size_t nbytes) { |
- size_t i; |
- size_t j; |
- if (NULL == list) return FALSE; |
- for (i = 0; i < list->num_insts_; ++i) { |
- Bool found_match = TRUE; |
- size_t start = list->insts_[i]; |
- size_t end = list->insts_[i + 1]; |
- size_t inst_bytes = (end - start); |
- if (inst_bytes < nbytes) continue; |
- for (j = 0; j < inst_bytes; j++) { |
- if (itext[j] != list->bytes_[start + j]) { |
- found_match = FALSE; |
- break; |
- } |
- } |
- if (found_match) { |
- return TRUE; |
- } |
- } |
- return FALSE; |
-} |
- |
-/* Takes the given text and sends it to each of the instruction |
- * decoders to decode the first instruction in the given text. |
- * Commonly the decoded instruction will be shorter than nbytes. |
- */ |
-static void ParseFirstInstruction(ComparedInstruction *cinst, |
- uint8_t *itext, size_t nbytes) { |
- size_t i; |
- memcpy(cinst->_enumerator._itext, itext, nbytes); |
- cinst->_enumerator._num_bytes = nbytes; |
- cinst->_decoder_error = FALSE; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- cinst->_enumerator._decoder[i]-> |
- _parse_inst_fn(&cinst->_enumerator, kTextAddress); |
- } |
-} |
- |
-/* Prints out the instruction each decoder disassembled */ |
-static void PrintDisassembledInstructionVariants(ComparedInstruction* cinst) { |
- size_t i; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- cinst->_enumerator._decoder[i]->_print_inst_fn(&cinst->_enumerator); |
- } |
-} |
- |
-/* Prints out progress messages. */ |
-static void PrintVProgress(const char* format, va_list ap) { |
- if (gSilent) { |
- /* Generating opcode sequences, so add special prefix so that we |
- * can print these out when read by the corresponding input decoder. |
- */ |
- printf("#PROGRESS#"); |
- } |
- vprintf(format, ap); |
-} |
- |
-static void PrintProgress(const char* format, ...) ATTRIBUTE_FORMAT_PRINTF(1,2); |
- |
-/* Prints out progress messages. */ |
-static void PrintProgress(const char* format, ...) { |
- va_list ap; |
- va_start(ap, format); |
- PrintVProgress(format, ap); |
- va_end(ap); |
-} |
- |
-/* Initial value for last bad opcode. */ |
-#define NOT_AN_OPCODE "not an opcode" |
- |
-/* Holds the name of the last (bad) mnemonic name matched. |
- * Uses the mnemonic name associated with instructions decoded |
- * by the first decoder. |
- */ |
-static char last_bad_mnemonic[kBufferSize] = NOT_AN_OPCODE; |
- |
-/* Returns how many decoder errors were skipped for an opcode. */ |
-static int nSkipped = 0; |
- |
-/* Changes the last bad mnemonic name to the new value. */ |
-static void ChangeLastBadMnemonic(const char* new_value) { |
- cstrncpy(last_bad_mnemonic, new_value, kBufferSize); |
-} |
- |
-/* Reset the counters for skipping decoder errors, assuming |
- * the last_bad_opcode (if non-null) was the given value. |
- */ |
-static void ResetSkipCounts(const char* last_opcode) { |
- nSkipped = 0; |
- ChangeLastBadMnemonic((last_opcode == NULL) ? "not an opcode" : last_opcode); |
-} |
- |
-/* Report number of instructions with errors that were skipped, and |
- * then reset the skip counts. |
- */ |
-static void ReportOnSkippedErrors(ComparedInstruction* cinst, |
- const char* mnemonic) { |
- UNREFERENCED_PARAMETER(cinst); |
- if (nSkipped > 0 && gSilent) { |
- printf("...skipped %d errors for %s\n", nSkipped, last_bad_mnemonic); |
- } |
- ResetSkipCounts(mnemonic); |
-} |
- |
-/* Report a disagreement between decoders. To reduce |
- * noice from uninteresting related errors, gSkipRepeatReports will |
- * avoid printing consecutive errors for the same opcode. |
- */ |
-static void DecoderError(const char *why, |
- ComparedInstruction *cinst, |
- const char *details) { |
- size_t i; |
- cinst->_decoder_error = TRUE; |
- |
- /* Don't print errors when running silently. In such cases we |
- * are generating valid opcode sequences for a decoder, and problems |
- * should be ignored. |
- */ |
- if (gSilent) return; |
- |
- /* Check if we have already reported for instruction mnemonic, |
- * based on the mnemonic used by the first decoder. |
- */ |
- if (gSkipRepeatReports) { |
- /* Look for first possible name for instruction. */ |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- NaClEnumeratorDecoder *decoder = cinst->_enumerator._decoder[i]; |
- const char* mnemonic; |
- if (NULL == decoder->_get_inst_mnemonic_fn) continue; |
- mnemonic = cinst->_enumerator._decoder[0]-> |
- _get_inst_mnemonic_fn(&cinst->_enumerator); |
- if (strcmp(mnemonic, last_bad_mnemonic) == 0) { |
- nSkipped += 1; |
- return; |
- } |
- } |
- } |
- |
- /* If reached, did not skip, so report the error. */ |
- printf("**** ERROR: %s: %s\n", why, (details == NULL ? "" : details)); |
- PrintDisassembledInstructionVariants(cinst); |
-} |
- |
-#if NACL_TARGET_SUBARCH == 64 |
-/* The instructions: |
- * 48 89 e5 mov rbp, rsp |
- * 4a 89 e5 mov rbp, rsp |
- * look bad based on the simple rule, but are safe because they are |
- * moving a safe address between two protected registers. |
- */ |
-static int IsSpecialSafeRegWrite(ComparedInstruction *cinst) { |
- uint8_t byte0 = cinst->_enumerator._itext[0]; |
- uint8_t byte1 = cinst->_enumerator._itext[1]; |
- uint8_t byte2 = cinst->_enumerator._itext[2]; |
- |
- return ((byte0 == 0x48 && byte1 == 0x89 && byte2 == 0xe5) || |
- (byte0 == 0x48 && byte1 == 0x89 && byte2 == 0xec) || |
- (byte0 == 0x48 && byte1 == 0x8b && byte2 == 0xe5) || |
- (byte0 == 0x48 && byte1 == 0x8b && byte2 == 0xec) || |
- (byte0 == 0x4a && byte1 == 0x89 && byte2 == 0xe5) || |
- (byte0 == 0x4a && byte1 == 0x89 && byte2 == 0xec) || |
- (byte0 == 0x4a && byte1 == 0x8b && byte2 == 0xe5) || |
- (byte0 == 0x4a && byte1 == 0x8b && byte2 == 0xec)); |
-} |
- |
-/* If we get this far, and Xed says it writes a NaCl reserved |
- * register, this is a lethal error. |
- */ |
-static Bool BadRegWrite(ComparedInstruction *cinst) { |
- size_t i; |
- NaClEnumeratorDecoder* xed_decoder = NULL; |
- size_t noperands; |
- size_t ilength; |
- |
- /* First see if there is a xed decoder to compare against. */ |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (0 == strcmp("xed", cinst->_enumerator._decoder[i]->_id_name)) { |
- xed_decoder = cinst->_enumerator._decoder[i]; |
- break; |
- } |
- } |
- |
- /* Quit if we don't have the right support for this function. */ |
- if ((NULL == xed_decoder) || |
- !xed_decoder->_is_inst_legal_fn(&cinst->_enumerator) || |
- (NULL == xed_decoder->_get_inst_num_operands_fn) || |
- (NULL == xed_decoder->_writes_to_reserved_reg_fn)) return 0; |
- |
- /* If reached, we found the xed decoder. */ |
- noperands = xed_decoder->_get_inst_num_operands_fn(&cinst->_enumerator); |
- ilength = xed_decoder->_inst_length_fn(&cinst->_enumerator); |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- size_t j; |
- NaClEnumeratorDecoder* other_decoder = cinst->_enumerator._decoder[i]; |
- |
- /* don't compare against self. */ |
- if (xed_decoder == other_decoder) break; |
- |
- /* don't compare if the other decoder doesn't know how to validate. */ |
- if (NULL == other_decoder->_segment_validates_fn) break; |
- if (!other_decoder->_is_inst_legal_fn(&cinst->_enumerator)) break; |
- |
- for (j = 0; j < noperands ; j++) { |
- if (xed_decoder->_writes_to_reserved_reg_fn(&cinst->_enumerator, j)) { |
- if (other_decoder->_segment_validates_fn(&cinst->_enumerator, |
- cinst->_enumerator._itext, |
- ilength, |
- kTextAddress)) { |
- char sbuf[kBufferSize]; |
- /* Report problem if other decoder accepted instruction, but is |
- * not one of the special safe writes. |
- */ |
- if (!IsSpecialSafeRegWrite(cinst)) continue; |
- gSawLethalError = 1; |
- SNPRINTF(sbuf, kBufferSize, "(%s) xed operand %d\n", |
- other_decoder->_id_name, (int) j); |
- DecoderError("ILLEGAL REGISTER WRITE", cinst, sbuf); |
- return TRUE; |
- } |
- } |
- } |
- } |
- return FALSE; |
-} |
-#endif |
- |
-/* Compare operands of each decoder's disassembled instruction, reporting |
- * an error if decoders disagree. |
- */ |
-static Bool AreInstOperandsEqual(ComparedInstruction *cinst) { |
- size_t i; |
- size_t num_decoders = 0; |
- const char* operands[NACL_MAX_ENUM_DECODERS]; |
- NaClEnumeratorDecoder* operands_decoder[NACL_MAX_ENUM_DECODERS]; |
- NaClEnumerator* enumerator = &cinst->_enumerator; |
- |
- /* If no decoders, vacuously true. */ |
- if (0 == enumerator->_num_decoders) return FALSE; |
- |
- /* Collect operand lists and corresponding decoders. */ |
- for (i = 0; i < enumerator->_num_decoders; ++i) { |
- /* Start by verifying that we can find operands. */ |
- NaClEnumeratorDecoder *decoder = enumerator->_decoder[i]; |
- if (NULL == decoder->_get_inst_operands_text_fn) continue; |
- if (!decoder->_is_inst_legal_fn(enumerator)) continue; |
- |
- /* HACK ALERT! Special case "nops" by removing operands. We do this |
- * assuming it doesn't matter what the argumnents are, the effect is |
- * the same. |
- */ |
- if (gNoCompareNops && |
- (NULL != decoder->_get_inst_mnemonic_fn) && |
- (0 == strcmp("nop", decoder->_get_inst_mnemonic_fn(enumerator)))) { |
- operands[num_decoders] = ""; |
- operands_decoder[num_decoders++] = decoder; |
- continue; |
- } |
- |
- /* Not special case, record operand and decoder. */ |
- operands[num_decoders] = decoder->_get_inst_operands_text_fn(enumerator); |
- if (NULL == operands[num_decoders]) operands[num_decoders] = ""; |
- operands_decoder[num_decoders++] = decoder; |
- } |
- |
- /* Now test if operands compare between decoders. */ |
- for (i = 1; i < num_decoders; ++i) { |
- if (0 != strncmp(operands[i-1], operands[i], kBufferSize)) { |
- char sbuf[kBufferSize]; |
- SNPRINTF(sbuf, kBufferSize, "(%s) '%s' != (%s)'%s'", |
- operands_decoder[i-1]->_id_name, operands[i-1], |
- operands_decoder[i]->_id_name, operands[i]); |
- DecoderError("OPERAND MISMATCH", cinst, sbuf); |
- return FALSE; |
- } |
- } |
- |
- if (kDebug && (num_decoders > 0)) { |
- printf("operands match: %s\n", operands[0]); |
- } |
- return TRUE; |
-} |
- |
-/* If non-null, the list of instructions for which mnemonics should |
- * not be compared. |
- */ |
-static InstList* kIgnoreMnemonics = NULL; |
- |
-/* Compares mnemonic names between decoder's disassembled instructions, |
- * returning true if they agree on the mnemonic name. |
- */ |
-static Bool AreInstMnemonicsEqual(ComparedInstruction *cinst) { |
- size_t i; |
- size_t num_decoders = 0; |
- const char* name[NACL_MAX_ENUM_DECODERS]; |
- NaClEnumeratorDecoder* name_decoder[NACL_MAX_ENUM_DECODERS]; |
- |
- /* If no decoders, vacuously true. */ |
- if (0 == cinst->_enumerator._num_decoders) return 0; |
- |
- /* Collect mnemonics of corresponding decoders (if defined). */ |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- /* Start by verifying that we can get name name. */ |
- NaClEnumeratorDecoder *decoder = cinst->_enumerator._decoder[i]; |
- if (NULL == decoder->_get_inst_mnemonic_fn) continue; |
- if (!decoder->_is_inst_legal_fn(&cinst->_enumerator)) continue; |
- |
- /* If on ignore list, ignore. */ |
- if (InInstructionList(kIgnoreMnemonics, |
- cinst->_enumerator._itext, |
- decoder->_inst_length_fn(&cinst->_enumerator))) |
- continue; |
- |
- /* Record mnemonic name and decoder for comparisons below. */ |
- name[num_decoders] = decoder->_get_inst_mnemonic_fn(&cinst->_enumerator); |
- name_decoder[num_decoders++] = decoder; |
- } |
- |
- /* Now compare mnemonics that were defined. */ |
- for (i = 1; i < num_decoders; ++i) { |
- if (strncmp(name[i-1], name[i], kBufferSize) != 0) { |
- char sbuf[kBufferSize]; |
- SNPRINTF(sbuf, kBufferSize, "(%s) %s != (%s) %s", |
- name_decoder[i-1]->_id_name, name[i-1], |
- name_decoder[i]->_id_name, name[i]); |
- DecoderError("MNEMONIC MISMATCH", cinst, sbuf); |
- return FALSE; |
- } |
- } |
- |
- if (kDebug && (num_decoders > 0)) { |
- printf("names match: %s\n", name[0]); |
- } |
- return TRUE; |
-} |
- |
-/* Returns true if the decoder decodes the instruction correctly, |
- * and also (to the best it can determine) validates when |
- * specified to do so on the command line. |
- */ |
-static Bool ConsiderInstLegal(NaClEnumerator* enumerator, |
- NaClEnumeratorDecoder* decoder) { |
- if (!decoder->_is_inst_legal_fn(enumerator)) return FALSE; |
- if (!gNaClLegal) return TRUE; |
- if (NULL == decoder->_maybe_inst_validates_fn) return TRUE; |
- return decoder->_maybe_inst_validates_fn(enumerator); |
-} |
- |
-/* Returns true only if the legal filters allow the instruction to |
- * be processed. |
- */ |
-static Bool RemovedByInstLegalFilters(ComparedInstruction* cinst) { |
- size_t i; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (ConsiderInstLegal(&cinst->_enumerator, |
- cinst->_enumerator._decoder[i]) |
- != cinst->_enumerator._decoder[i]->_legal_only) { |
- return TRUE; |
- } |
- } |
- return FALSE; |
-} |
- |
-/* Returns true if the instruction has the same length for all decoders. |
- * Reports length differences if found. |
- */ |
-static Bool AreInstructionLengthsEqual(ComparedInstruction *cinst) { |
- size_t i; |
- size_t num_decoders = 0; |
- size_t length[NACL_MAX_ENUM_DECODERS]; |
- NaClEnumeratorDecoder* length_decoder[NACL_MAX_ENUM_DECODERS]; |
- |
- /* If no decoders, vacuously true. */ |
- if (0 == cinst->_enumerator._num_decoders) return TRUE; |
- |
- /* Collect the instruction length for each decoder. */ |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (cinst->_enumerator._decoder[i]-> |
- _is_inst_legal_fn(&cinst->_enumerator)) { |
- length[num_decoders] = cinst->_enumerator._decoder[i]-> |
- _inst_length_fn(&cinst->_enumerator); |
- length_decoder[num_decoders] = cinst->_enumerator._decoder[i]; |
- ++num_decoders; |
- } |
- } |
- |
- /* Print out where lengths differ, if they differ. */ |
- for (i = 1; i < num_decoders; ++i) { |
- if (length[i-1] != length[i]) { |
- char sbuf[kBufferSize]; |
- SNPRINTF(sbuf, kBufferSize, "(%s) %"NACL_PRIuS" != (%s) %"NACL_PRIuS, |
- length_decoder[i-1]->_id_name, length[i-1], |
- length_decoder[i]->_id_name, length[i]); |
- DecoderError("LENGTH MISMATCH", cinst, sbuf); |
- gSawLethalError = 1; |
- return FALSE; |
- } |
- } |
- |
- if (kDebug && (num_decoders > 0)) { |
- printf("length match: %"NACL_PRIuS"\n", length[0]); |
- } |
- return TRUE; |
-} |
- |
-/* Print out decodings if specified on the command line. Returns true |
- * instruction(s) are printed. This function is used |
- */ |
-static Bool PrintInst(ComparedInstruction *cinst) { |
- Bool result = FALSE; |
- size_t i; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- NaClEnumeratorDecoder* decoder = cinst->_enumerator._decoder[i]; |
- if (decoder->_print) { |
- cinst->_enumerator._decoder[i]->_print_inst_fn(&cinst->_enumerator); |
- result = TRUE; |
- } |
- if (decoder->_print_opcode_sequence || |
- decoder->_print_opcode_sequence_plus_desc) { |
- size_t length = decoder->_inst_length_fn(&cinst->_enumerator); |
- for (i = 0; i < length; ++i) { |
- printf("%02x", cinst->_enumerator._itext[i]); |
- } |
- if (decoder->_print_opcode_sequence_plus_desc && |
- decoder->_is_inst_legal_fn(&cinst->_enumerator) && |
- (NULL != decoder->_get_inst_mnemonic_fn) && |
- (NULL != decoder->_get_inst_operands_text_fn)) { |
- printf("#%s %s", decoder->_get_inst_mnemonic_fn(&cinst->_enumerator), |
- decoder->_get_inst_operands_text_fn(&cinst->_enumerator)); |
- } |
- printf("\n"); |
- result = TRUE; |
- } |
- } |
- return result; |
-} |
- |
-/* If non-null, the list of instruction bytes to ignore. */ |
-static InstList* kIgnoredInstructions = NULL; |
- |
-/* Test comparison for a single instruction. |
- */ |
-static void TryOneInstruction(ComparedInstruction *cinst, |
- uint8_t *itext, size_t nbytes) { |
- do { |
- if (gVerbose) { |
- size_t i; |
- printf("================"); |
- for (i = 0; i < nbytes; ++i) { |
- printf("%02x", itext[i]); |
- } |
- printf("\n"); |
- } |
- |
- /* Try to parse the sequence of test bytes. */ |
- ParseFirstInstruction(cinst, itext, nbytes); |
- |
- /* Don't bother to compare ignored instructions. */ |
- if (InInstructionList(kIgnoredInstructions, itext, nbytes)) break; |
- |
- /* Apply filters */ |
- if (RemovedByInstLegalFilters(cinst)) break; |
- |
- /* Apply comparison checks to the decoded instructions. */ |
- if (!AreInstructionLengthsEqual(cinst)) break; |
- if (gCheckMnemonics && !AreInstMnemonicsEqual(cinst)) break; |
- if (gCheckOperands && !AreInstOperandsEqual(cinst)) break; |
-#if NACL_TARGET_SUBARCH == 64 |
- if (BadRegWrite(cinst)) break; |
-#endif |
- |
- /* Print the instruction if print specified. */ |
- if (PrintInst(cinst)) break; |
- /* no error */ |
- if (gVerbose) { |
- PrintDisassembledInstructionVariants(cinst); |
- } |
- } while (0); |
- |
- /* saw error; should have already printed stuff. Only |
- * report on skipped errors if we found an error-free |
- * instruction. |
- */ |
- if (gSkipContiguous && (!cinst->_decoder_error)) { |
- ReportOnSkippedErrors(cinst, NULL); |
- } |
-} |
- |
-/* Returns true if for all decoders recognize legal instructions, they use |
- * less than len bytes. |
- */ |
-static Bool IsLegalInstShorterThan(ComparedInstruction *cinst, size_t len) { |
- size_t i; |
- Bool found_legal = FALSE; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (cinst->_enumerator._decoder[i]-> |
- _is_inst_legal_fn(&cinst->_enumerator)) { |
- found_legal = TRUE; |
- if (cinst->_enumerator._decoder[i]->_inst_length_fn(&cinst->_enumerator) |
- >= len) return FALSE; |
- } |
- } |
- return found_legal; |
-} |
- |
-/* Enumerate and test all 24-bit opcode+modrm+sib patterns for a |
- * particular prefix. |
- */ |
-static void TestAllWithPrefix(ComparedInstruction *cinst, |
- unsigned int prefix, size_t prefix_length) { |
- const int kInstByteCount = NACL_ENUM_MAX_INSTRUCTION_BYTES; |
- const int kIterByteCount = 3; |
- InstByteArray itext; |
- size_t i; |
- int op, modrm, sib; |
- int min_op; |
- int max_op; |
- |
- if ((gPrefix > 0) && (gPrefix != prefix)) return; |
- |
- PrintProgress("TestAllWithPrefix(%x)\n", prefix); |
- /* set up prefix */ |
- memcpy(itext, &prefix, prefix_length); |
- /* set up filler bytes */ |
- for (i = prefix_length + kIterByteCount; |
- i < NACL_ENUM_MAX_INSTRUCTION_BYTES; i++) { |
- itext[i] = (uint8_t)i; |
- } |
- if (gOpcode < 0) { |
- min_op = 0; |
- max_op = 256; |
- } else { |
- min_op = gOpcode; |
- max_op = gOpcode + 1; |
- } |
- for (op = min_op; op < max_op; op++) { |
- itext[prefix_length] = op; |
- ResetSkipCounts(NULL); |
- PrintProgress("%02x 00 00\n", op); |
- for (modrm = 0; modrm < 256; modrm++) { |
- itext[prefix_length + 1] = modrm; |
- for (sib = 0; sib < 256; sib++) { |
- itext[prefix_length + 2] = sib; |
- TryOneInstruction(cinst, itext, kInstByteCount); |
- /* If all decoders decode without using the sib byte, don't |
- * bother to try more variants. |
- */ |
- if (IsLegalInstShorterThan(cinst, prefix_length + 3)) break; |
- } |
- /* If all decoders decode without using the modrm byte, don't |
- * bother to try more variants. |
- */ |
- if (IsLegalInstShorterThan(cinst, prefix_length + 2)) break; |
- } |
- /* Force flushing of skipped errors, since we are now moving on |
- * to the next opcode. |
- */ |
- ReportOnSkippedErrors(cinst, NULL); |
- } |
-} |
- |
-/* For x86-64, enhance the iteration by looping through REX prefixes. |
- */ |
-static void TestAllWithPrefixREX(ComparedInstruction *cinst, |
- unsigned int prefix, size_t prefix_length) { |
-#if NACL_TARGET_SUBARCH == 64 |
- unsigned char REXp; |
- unsigned int rprefix; |
- /* test with REX prefixes */ |
- for (REXp = 0x40; REXp < 0x50; REXp++) { |
- rprefix = (prefix << 8 | REXp); |
- printf("Testing with prefix %x\n", rprefix); |
- TestAllWithPrefix(cinst, rprefix, prefix_length + 1); |
- } |
-#endif |
- /* test with no REX prefix */ |
- TestAllWithPrefix(cinst, prefix, prefix_length); |
-} |
- |
-/* For all prefixes, call TestAllWithPrefix() to enumrate and test |
- * all instructions. |
- */ |
-static void TestAllInstructions(ComparedInstruction *cinst) { |
- gSkipRepeatReports = TRUE; |
- /* NOTE: Prefix byte order needs to be reversed when written as |
- * an integer. For example, for integer prefix 0x3a0f, 0f will |
- * go in instruction byte 0, and 3a in byte 1. |
- */ |
- /* TODO(bradchen): extend enuminsts-64 to iterate over 64-bit prefixes. */ |
- TestAllWithPrefixREX(cinst, 0, 0); |
- TestAllWithPrefixREX(cinst, 0x0f, 1); |
- TestAllWithPrefixREX(cinst, 0x0ff2, 2); |
- TestAllWithPrefixREX(cinst, 0x0ff3, 2); |
- TestAllWithPrefixREX(cinst, 0x0f66, 2); |
- TestAllWithPrefixREX(cinst, 0x0f0f, 2); |
- TestAllWithPrefixREX(cinst, 0x380f, 2); |
- TestAllWithPrefixREX(cinst, 0x3a0f, 2); |
- TestAllWithPrefixREX(cinst, 0x380f66, 3); |
- TestAllWithPrefixREX(cinst, 0x380ff2, 3); |
- TestAllWithPrefixREX(cinst, 0x3a0f66, 3); |
-} |
- |
-/* Enumerate and test each instruction on stdin. */ |
-static void TestInputInstructions(ComparedInstruction *cinst) { |
- int i; |
- InstByteArray itext; |
- int num_bytes = 0; |
- while (TRUE) { |
- num_bytes = ReadAnInstruction(itext); |
- if (num_bytes == 0) return; |
- for (i = num_bytes; i < NACL_ENUM_MAX_INSTRUCTION_BYTES; ++i) { |
- itext[i] = (uint8_t) i; |
- } |
- TryOneInstruction(cinst, itext, NACL_ENUM_MAX_INSTRUCTION_BYTES); |
- } |
-} |
- |
-/* Used to test one instruction at a time, for example, in regression |
- * testing, or for instruction arguments from the command line. |
- */ |
-static void TestOneInstruction(ComparedInstruction *cinst, char *asciihex) { |
- InstByteArray ibytes; |
- int nbytes; |
- |
- nbytes = Text2Bytes(ibytes, asciihex, "Command-line argument", -1); |
- if (nbytes == 0) return; |
- if (gVerbose) { |
- int i; |
- printf("trying %s (", asciihex); |
- for (i = 0; i < nbytes; ++i) { |
- printf("%02x", ibytes[i]); |
- } |
- printf(")\n"); |
- } |
- TryOneInstruction(cinst, ibytes, (size_t) nbytes); |
-} |
- |
-/* A set of test cases that have caused problems in the past. |
- * This is a bit stale; most of the test cases came from xed_compare.py. |
- * Mostly this program has been tested by TestAllInstructions(), |
- * possible since this program is much faster than xed_compare.py |
- */ |
-static void RunRegressionTests(ComparedInstruction *cinst) { |
- TestOneInstruction(cinst, "0024c2"); |
- TestOneInstruction(cinst, "017967"); |
- TestOneInstruction(cinst, "0f12c0"); |
- TestOneInstruction(cinst, "0f13c0"); |
- TestOneInstruction(cinst, "0f17c0"); |
- TestOneInstruction(cinst, "0f01c1"); |
- TestOneInstruction(cinst, "0f00300000112233445566778899aa"); |
- TestOneInstruction(cinst, "cc"); |
- TestOneInstruction(cinst, "C3"); |
- TestOneInstruction(cinst, "0f00290000112233445566778899aa"); |
- TestOneInstruction(cinst, "80e4f7"); |
- TestOneInstruction(cinst, "e9a0ffffff"); |
- TestOneInstruction(cinst, "4883ec08"); |
- TestOneInstruction(cinst, "0f00040500112233445566778899aa"); |
- /* Below are newly discovered mistakes in call instructions, where the wrong |
- * byte length was required by x86-64 nacl validator. |
- */ |
- TestOneInstruction(cinst, "262e7e00"); |
- TestOneInstruction(cinst, "2e3e7900"); |
- /* From the AMD manual, "An instruction may have only one REX prefix */ |
- /* which must immediately precede the opcode or first excape byte */ |
- /* in the instruction encoding." */ |
- TestOneInstruction(cinst, "406601d8"); /* illegal; REX before data16 */ |
- TestOneInstruction(cinst, "664001d8"); /* legal; REX after data16 */ |
- TestOneInstruction(cinst, "414001d8"); /* illegal; two REX bytes */ |
-} |
- |
-/* Returns the decoder with the given name, if it is registered. Otherwise, |
- * returns NULL. |
- */ |
-static NaClEnumeratorDecoder* |
-NaClGetRegisteredDecoder(ComparedInstruction* cinst, |
- const char* decoder_name) { |
- size_t i; |
- for (i = 0; i < cinst->_enumerator._num_decoders; ++i) { |
- if (0 == strcmp(cinst->_enumerator._decoder[i]->_id_name, decoder_name)) { |
- return cinst->_enumerator._decoder[i]; |
- } |
- } |
- return NULL; |
-} |
- |
-/* Register the decoder with the given name, returning the corresponding |
- * decoder. |
- */ |
-static NaClEnumeratorDecoder* |
-NaClRegisterEnumeratorDecoder(ComparedInstruction* cinst, |
- const char* decoder_name) { |
- size_t i; |
- /* First check if already registered. If so, simply return it. */ |
- NaClEnumeratorDecoder* decoder = |
- NaClGetRegisteredDecoder(cinst, decoder_name); |
- if (NULL != decoder) return decoder; |
- |
- /* If reached, not registered yet. See if one with the given name |
- * is available (i.e. preregistered). |
- */ |
- for (i = 0; i < kNumAvailableDecoders; ++i) { |
- decoder = kAvailableDecoders[i]; |
- if (0 == strcmp(decoder->_id_name, decoder_name)) { |
- if (cinst->_enumerator._num_decoders < NACL_MAX_ENUM_DECODERS) { |
- cinst->_enumerator._decoder[cinst->_enumerator._num_decoders++] = |
- decoder; |
- return decoder; |
- } |
- } |
- } |
- |
- /* If reached, can't find a decoder with the given name, abort. */ |
- fprintf(stderr, "Can't find decoder '%s', aborting!\n", decoder_name); |
- exit(1); |
-} |
- |
-/* Install legal filter values for corresponding available decoders. */ |
-static void NaClInstallLegalFilter(ComparedInstruction* cinst, |
- const char* decoder_name, |
- Bool new_value) { |
- NaClRegisterEnumeratorDecoder(cinst, decoder_name)->_legal_only = new_value; |
-} |
- |
-/* The initial size for bytes_ when creating an instruction list. |
- */ |
-static const size_t kInitialInstBytesSize = 1024; |
- |
-/* The initial size for insts_ when creating an instruction list. |
- */ |
-static const size_t kInitialInstListInstsSize = 256; |
- |
-/* Creates an initially empty list of instructions. */ |
-static InstList* CreateEmptyInstList(void) { |
- InstList* list = (InstList*) malloc(sizeof(InstList)); |
- if (NULL == list) ReportFatalError("Out of memory"); |
- list->bytes_ = (uint8_t*) malloc(kInitialInstBytesSize); |
- list->bytes_size_ = kInitialInstBytesSize; |
- list->insts_ = (size_t*) malloc(kInitialInstListInstsSize); |
- list->insts_size_ = kInitialInstListInstsSize; |
- list->insts_[0] = 0; |
- list->num_insts_ = 0; |
- list->num_bytes_ = 0; |
- return list; |
-} |
-/* Expands the bytes_ field of the instruction list so that |
- * more instructions can be added. |
- */ |
-static void ExpandInstListBytes(InstList* list) { |
- size_t i; |
- uint8_t* new_buffer; |
- size_t new_size = list->bytes_size_ *2; |
- if (new_size < list->bytes_size_) { |
- ReportFatalError("Instruction list file too big"); |
- } |
- new_buffer = (uint8_t*) malloc(new_size); |
- if (NULL == new_buffer) ReportFatalError("Out of memory"); |
- for (i = 0; i < list->num_bytes_; ++i) { |
- new_buffer[i] = list->bytes_[i]; |
- } |
- free(list->bytes_); |
- list->bytes_ = new_buffer; |
- list->bytes_size_ = new_size; |
-} |
- |
-/* Expands the insts_ field of the instruction list so that |
- * more instructions can be added. |
- */ |
-static void ExpandInstListInsts(InstList* list) { |
- size_t i; |
- size_t* new_buffer; |
- size_t new_size = list->insts_size_ * 2; |
- |
- if (new_size < list->insts_size_) |
- ReportFatalError("Instruction list file too big"); |
- new_buffer = (size_t*) malloc(new_size); |
- if (NULL == new_buffer) ReportFatalError("Out of memory"); |
- for (i = 0; i < list->num_insts_; ++i) { |
- new_buffer[i] = list->insts_[i]; |
- } |
- free(list->insts_); |
- list->insts_ = new_buffer; |
- list->insts_size_ = new_size; |
-} |
- |
-/* Reads the bytes defined in line, and coverts it to the corresponding |
- * ignored instruction. Then adds it to the list of ignored instructions. |
- */ |
-static void ReadInstListInst(InstList* list, |
- char line[kBufferSize], |
- const char* context, |
- int line_number) { |
- int i; |
- InstByteArray ibytes; |
- int num_bytes = Text2Bytes(ibytes, line, context, line_number); |
- |
- /* Ignore line if no opcode sequence. */ |
- if (num_bytes == 0) return; |
- |
- /* First update the instruction pointers. */ |
- if (list->num_insts_ == list->insts_size_) { |
- ExpandInstListInsts(list); |
- } |
- ++list->num_insts_; |
- list->insts_[list->num_insts_] = |
- list->insts_[list->num_insts_ - 1]; |
- |
- /* Now install the bytes. */ |
- for (i = 0; i < num_bytes; ++i) { |
- /* Be sure we have room for the byte. */ |
- if (list->num_bytes_ == list->bytes_size_) { |
- ExpandInstListBytes(list); |
- } |
- |
- /* Record into the ignore instruction list. */ |
- list->bytes_[list->num_bytes_++] = ibytes[i]; |
- list->insts_[list->num_insts_] = list->num_bytes_; |
- } |
-} |
- |
-/* Reads a file containing a list of instruction bytes to ignore, |
- * and adds it to the end of the list of instructions. |
- */ |
-static void GetInstList(InstList** list, |
- FILE* file, |
- const char* filename) { |
- char line[kBufferSize]; |
- int line_number = 0; |
- if (NULL == *list) { |
- *list = CreateEmptyInstList(); |
- } |
- while (TRUE) { |
- ++line_number; |
- if (fgets(line, kBufferSize, file) == NULL) return; |
- ReadInstListInst(*list, line, filename, line_number); |
- } |
- return; |
-} |
- |
-/* Read the file containing a list of instruction bytes, |
- * and adds it to the end of the list of instructions. |
- */ |
-static void ReadInstList(InstList** list, const char* filename) { |
- FILE* file = fopen(filename, "r"); |
- if (NULL == file) { |
- char buffer[kBufferSize]; |
- SNPRINTF(buffer, kBufferSize, "%s: unable to open", filename); |
- ReportFatalError(buffer); |
- } |
- GetInstList(list, file, filename); |
- fclose(file); |
-} |
- |
-/* Very simple command line arg parsing. Returns index to the first |
- * arg that doesn't begin with '--', or argc if there are none. |
- */ |
-static int ParseArgs(ComparedInstruction* cinst, int argc, char *argv[]) { |
- int i; |
- uint32_t prefix; |
- uint32_t opcode; |
- char* cstr_value; |
- Bool bool_value; |
- int opcode_bytes_count = 0; |
- |
- for (i = 1; i < argc; i++) { |
- if (argv[i][0] == '-') { |
- do { |
- if (strcmp(argv[i], "--help") == 0) { |
- Usage(); |
- } else if (GrokBoolFlag("--checkoperands", argv[i], &gCheckOperands) || |
- GrokBoolFlag("--nacllegal", argv[i], |
- &gNaClLegal) || |
- GrokBoolFlag("--xedimplemented", argv[i], |
- &gXedImplemented) || |
- GrokBoolFlag("--nops", argv[i], &gNoCompareNops)|| |
- GrokBoolFlag("--skipcontiguous", argv[i], |
- &gSkipContiguous) || |
- GrokBoolFlag("--verbose", argv[i], &gVerbose)) { |
- } else if (GrokBoolFlag("--bindings", argv[i], &bool_value) && |
- bool_value) { |
- PrintBindings(cinst); |
-#ifdef NACL_XED_DECODER |
- } else if (GrokBoolFlag("--pin_version", argv[i], &bool_value) && |
- bool_value) { |
- fprintf(stderr, "pin version: %s\n", NACL_PINV); |
-#endif |
-#ifdef NACL_REVISION |
- } else if (GrokBoolFlag("--nacl_revision", argv[i], &bool_value) && |
- bool_value) { |
- fprintf(stderr, "nacl revsion: %d\n", NACL_REVISION); |
-#endif |
- } else if (GrokCstringFlag("--opcode_bytes", argv[i], &cstr_value)) { |
- NaClEnumeratorDecoder* decoder = |
- NaClRegisterEnumeratorDecoder(cinst, cstr_value); |
- decoder->_print_opcode_sequence = TRUE; |
- decoder->_print_opcode_sequence_plus_desc = FALSE; |
- gSilent = TRUE; |
- ++opcode_bytes_count; |
- } else if (GrokCstringFlag("--opcode_bytes_plus_desc", |
- argv[i], &cstr_value)) { |
- NaClEnumeratorDecoder* decoder = |
- NaClRegisterEnumeratorDecoder(cinst, cstr_value); |
- decoder->_print_opcode_sequence = FALSE; |
- decoder->_print_opcode_sequence_plus_desc = TRUE; |
- if ((NULL == decoder->_get_inst_mnemonic_fn) || |
- (NULL == decoder->_get_inst_operands_text_fn)) { |
- fprintf(stderr, "%s doesn't define how to print out description\n", |
- argv[i]); |
- } |
- gSilent = TRUE; |
- ++opcode_bytes_count; |
- /* Print out a special message to be picked up by the input decoder, |
- * so that it will look for instruction mnemonic and operands. |
- */ |
- printf("#OPCODEPLUSDESC#\n"); |
- } else if (GrokCstringFlag("--ignored", argv[i], &cstr_value)) { |
- ReadInstList(&kIgnoredInstructions, cstr_value); |
- } else if (GrokCstringFlag("--ignore_mnemonic", argv[i], &cstr_value)) { |
- ReadInstList(&kIgnoreMnemonics, cstr_value); |
- } else if (GrokCstringFlag("--print", argv[i], &cstr_value)) { |
- NaClRegisterEnumeratorDecoder(cinst, cstr_value)->_print = TRUE; |
- gSilent = TRUE; |
- } else if (GrokUint32HexFlag("--prefix", argv[i], &prefix)) { |
- gPrefix = (int) prefix; |
- printf("using prefix %x\n", gPrefix); |
- } else if (GrokUint32HexFlag("--opcode", argv[i], &opcode) && |
- opcode < 256) { |
- gOpcode = (int) opcode; |
- printf("using opcode %x\n", gOpcode); |
- } else if (GrokCstringFlag("--legal", argv[i], &cstr_value)) { |
- NaClInstallLegalFilter(cinst, cstr_value, TRUE); |
- } else if (GrokCstringFlag("--illegal", argv[i], &cstr_value)) { |
- NaClInstallLegalFilter(cinst, cstr_value, FALSE); |
- } else if (argv[i] == strfind(argv[i], "--")) { |
- NaClRegisterEnumeratorDecoder(cinst, argv[i] + 2); |
- } else { |
- fprintf(stderr, "Can't recognize option %s\n", argv[i]); |
- exit(1); |
- } |
- if (opcode_bytes_count > 1) { |
- fprintf(stderr, "Can't have more than one opcode_bytes command " |
- "line argument\n"); |
- exit(1); |
- } |
- } while (0); |
- } else return i; |
- } |
- return argc; |
-} |
- |
-/* Define set of available enumerator decoders. */ |
-static void NaClPreregisterEnumeratorDecoder(ComparedInstruction* cinst, |
- NaClEnumeratorDecoder* decoder) { |
- UNREFERENCED_PARAMETER(cinst); |
- if (kNumAvailableDecoders >= NACL_MAX_AVAILABLE_DECODERS) { |
- fprintf(stderr, "Too many preregistered enumerator decoders\n"); |
- exit(1); |
- } |
- decoder->_legal_only = TRUE; |
- decoder->_print = FALSE; |
- decoder->_print_opcode_sequence = FALSE; |
- decoder->_print_opcode_sequence_plus_desc = FALSE; |
- kAvailableDecoders[kNumAvailableDecoders++] = decoder; |
-} |
- |
-/* Define decoders that can be registered. */ |
-extern NaClEnumeratorDecoder* RegisterXedDecoder(void); |
-extern NaClEnumeratorDecoder* RegisterNaClDecoder(void); |
-extern NaClEnumeratorDecoder* RegisterRagelDecoder(void); |
- |
-/* Initialize the set of available decoders. */ |
-static void NaClInitializeAvailableDecoders(void) { |
- kNumAvailableDecoders = 0; |
-#ifdef NACL_XED_DECODER |
- NaClPreregisterEnumeratorDecoder(&gCinst, RegisterXedDecoder()); |
-#endif |
-#ifdef NACL_RAGEL_DECODER |
- NaClPreregisterEnumeratorDecoder(&gCinst, RegisterRagelDecoder()); |
-#endif |
- NaClPreregisterEnumeratorDecoder(&gCinst, RegisterNaClDecoder()); |
- NaClPreregisterEnumeratorDecoder(&gCinst, RegisterInputDecoder()); |
-} |
- |
-/* Initialize the ComparedInstruction data structure. */ |
-static void NaClInitializeComparedInstruction(ComparedInstruction* cinst) { |
- cinst->_enumerator._num_decoders = 0; |
-} |
- |
-/* Install parsed globals, as appropriate into the corresponding |
- * decoders. |
- */ |
-static void InstallFlags(NaClEnumerator* enumerator) { |
- size_t i; |
- for (i = 0; i < enumerator->_num_decoders; ++i) { |
- enumerator->_decoder[i]->_install_flag_fn(enumerator, |
- "--nops", |
- &gNoCompareNops); |
- enumerator->_decoder[i]->_install_flag_fn(enumerator, |
- "--xedimplemented", |
- &gXedImplemented); |
- } |
-} |
- |
-int main(int argc, char *argv[]) { |
- int testargs; |
- |
- NaClLogModuleInit(); |
- NaClLogSetVerbosity(LOG_FATAL); |
- NaClInitializeAvailableDecoders(); |
- NaClInitializeComparedInstruction(&gCinst); |
- |
- gArgv0 = argv[0]; |
- if (argc == 1) Usage(); |
- |
- testargs = ParseArgs(&gCinst, argc, argv); |
- InstallFlags(&gCinst._enumerator); |
- |
- |
- if (gCinst._enumerator._num_decoders == 0) { |
- fprintf(stderr, "No decoder specified, can't continue\n"); |
- exit(1); |
- } |
- |
- if (testargs == argc) { |
- if (NULL == NaClGetRegisteredDecoder(&gCinst, "in")) { |
- if (gPrefix == 0) RunRegressionTests(&gCinst); |
- TestAllInstructions(&gCinst); |
- } else { |
- TestInputInstructions(&gCinst); |
- } |
- } else { |
- int i; |
- gVerbose = TRUE; |
- for (i = testargs; i < argc; ++i) { |
- TestOneInstruction(&gCinst, argv[i]); |
- } |
- } |
- exit(gSawLethalError); |
-} |