/* * SMPInstr.cpp - <see below>. * * Copyright (c) 2000, 2001, 2010 - University of Virginia * * This file is part of the Memory Error Detection System (MEDS) infrastructure. * This file may be used and modified for non-commercial purposes as long as * all copyright, permission, and nonwarranty notices are preserved. * Redistribution is prohibited without prior written consent from the University * of Virginia. * * Please contact the authors for restrictions applying to commercial use. * * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Author: University of Virginia * e-mail: jwd@virginia.com * URL : http://www.cs.virginia.edu/ * * Additional copyrights 2010, 2011 by Zephyr Software LLC * e-mail: {clc,jwd}@zephyr-software.com * URL : http://www.zephyr-software.com/ * */ // // SMPInstr.cpp // // This module performs the instruction level analyses needed for the // SMP project (Software Memory Protection). // using namespace std; #include <string> #include <cstring> #include <pro.h> #include <assert.h> #include <ua.hpp> #include <ida.hpp> #include <idp.hpp> #include <auto.hpp> #include <bytes.hpp> #include <funcs.hpp> #include <intel.hpp> #include <loader.hpp> #include <lines.hpp> #include <name.hpp> #include "SMPDBInterface.h" #include "SMPStaticAnalyzer.h" #include "SMPDataFlowAnalysis.h" #include "SMPInstr.h" #include "SMPProgram.h" #include "ProfilerInformation.h" // Set to 1 for debugging output #define SMP_DEBUG 1 #define SMP_DEBUG2 0 // verbose #define SMP_DEBUG_XOR 0 #define SMP_DEBUG_BUILD_RTL 1 // should be left on, serious errors! #define SMP_VERBOSE_DEBUG_BUILD_RTL 0 #define SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE 0 #define SMP_VERBOSE_DEBUG_INFER_TYPES 0 #define SMP_VERBOSE_DUMP 0 #define SMP_VERBOSE_FIND_POINTERS 0 #define STARS_DUMP_FG_INFO 1 // show signedness of DEFs and USEs in dump #define SMP_CALL_TRASHES_REGS 1 // Add DEFs of caller-saved regs to CALL instructions #define SMP_BASEREG_POINTER_TYPE 1 // Initialize Base Register USEs to type POINTER? #define SMP_OPTIMIZE_ADD_TO_NUMERIC 0 // optimizing annotation type -5 #define SMP_IDENTIFY_POINTER_ADDRESS_REG 0 // optimizing annotation POINTER #define SMP_CHILDACCESS_ALL_CODE 0 // CHILDACCESS annotations for all funcs, or just analyzed funcs? #define SPECIAL_CASE_CARRY_BORROW 0 // Treat sbb/adc different from sub/add annotations? #define SMP_BUILD_SPECIAL_ADC_SBB_RTL 0 // Explicit RTL subtree for carry flag? #define SMP_AGGRESSIVE_TYPE_INFERENCE 1 // Shorten iterations by quick propagation in InferOperatorType() #define SMP_ANNOTATE_ALL_MEMORY_OPERANDS 0 // Info annotation for all memory read and write operands? #define SMP_AGGRESSIVE_SIGN_TRANSFER 1 // More transfer of signedness across instructions #define STARS_NO_SHIFT_SIGNEDNESS_IN_SCALEFACTOR 1 // unsigned left shift for scale factors is really unknownsign multiply // Make the CF_CHG1 .. CF_CHG6 and CF_USE1..CF_USE6 macros more usable // by allowing us to pick them up with an array index. static ulong DefMacros[UA_MAXOP] = {CF_CHG1, CF_CHG2, CF_CHG3, CF_CHG4, CF_CHG5, CF_CHG6}; static ulong UseMacros[UA_MAXOP] = {CF_USE1, CF_USE2, CF_USE3, CF_USE4, CF_USE5, CF_USE6}; // Text to be printed in each optimizing annotation explaining why // the annotation was emitted. static const char *OptExplanation[LAST_TYPE_CATEGORY + 1] = { "NoOpt", "NoMetaUpdate", "AlwaysNUM", "NUMVia2ndSrcIMMEDNUM", "Always1stSrc", "1stSrcVia2ndSrcIMMEDNUM", "AlwaysPtr", "AlwaysNUM", "AlwaysNUM", "NUMViaFPRegDest", "NumericSources", "StackMemoryTracking", "NumericSources", "NumericMemDest", "NeverMemDest", "SafeIfNoIndexing" }; static const char *OperatorText[LAST_SMP_OPERATOR + 1] = { "SMP_NULL_OPERATOR", "SMP_CALL", "SMP_INPUT", "SMP_OUTPUT", "SMP_ADDRESS_OF", "SMP_U_LEFT_SHIFT", "SMP_S_LEFT_SHIFT", "SMP_U_RIGHT_SHIFT", "SMP_S_RIGHT_SHIFT", "SMP_ROTATE_LEFT", "SMP_ROTATE_LEFT_CARRY", "SMP_ROTATE_RIGHT", "SMP_ROTATE_RIGHT_CARRY", "SMP_DECREMENT", "SMP_INCREMENT", "SMP_ADD", "SMP_ADD_CARRY", "SMP_SUBTRACT", "SMP_SUBTRACT_BORROW", "SMP_U_MULTIPLY", "SMP_S_MULTIPLY", "SMP_U_DIVIDE", "SMP_S_DIVIDE", "SMP_U_REMAINDER", "SMP_SIGN_EXTEND", "SMP_ZERO_EXTEND", "SMP_ASSIGN", "SMP_BITWISE_AND", "SMP_BITWISE_OR", "SMP_BITWISE_NOT", "SMP_BITWISE_XOR", "SMP_BITWISE_AND_NOT", "SMP_NEGATE", "SMP_S_COMPARE", "SMP_U_COMPARE", "SMP_GENERAL_COMPARE", "SMP_LESS_THAN", "SMP_GREATER_THAN", "SMP_LESS_EQUAL", "SMP_GREATER_EQUAL", "SMP_EQUAL", "SMP_NOT_EQUAL", "SMP_LOGICAL_AND", "SMP_LOGICAL_OR", "SMP_UNARY_NUMERIC_OPERATION", "SMP_BINARY_NUMERIC_OPERATION", "SMP_SYSTEM_OPERATION", "SMP_UNARY_FLOATING_ARITHMETIC", "SMP_BINARY_FLOATING_ARITHMETIC", "SMP_REVERSE_SHIFT_U", "SMP_SHUFFLE", "SMP_COMPARE_EQ_AND_SET", "SMP_COMPARE_NE_AND_SET", "SMP_COMPARE_GT_AND_SET", "SMP_COMPARE_GE_AND_SET", "SMP_COMPARE_LT_AND_SET", "SMP_COMPARE_LE_AND_SET", "SMP_PACK_SIGNED", "SMP_PACK_UNSIGNED", "SMP_AVERAGE_UNSIGNED", "SMP_MULTIPLY_AND_ADD", "SMP_SUM_OF_DIFFS", "SMP_MAX_S", "SMP_MAX_U", "SMP_MIN_S", "SMP_MIN_U", "SMP_ABS_VALUE", "SMP_CONVERT_INT_TO_FP", "SMP_CONVERT_FP_TO_INT", "SMP_CREATE_MASK", "SMP_INTERLEAVE", "SMP_CONCATENATE", "SMP_EXTRACT_ZERO_EXTEND", "SMP_ENCRYPTION_OPERATION", "SMP_SIGNAL" }; #if 0 // Does the CurrOperator definitely indicate a signed or unsigned operation? bool OperatorHasSignedness(SMPoperator CurrOperator) { bool DetectedSignedness; switch (CurrOperator) { case SMP_NULL_OPERATOR: DetectedSignedness = false; break; case SMP_CALL: // CALL instruction DetectedSignedness = true; break; case SMP_INPUT: // input from port case SMP_OUTPUT: // output to port case SMP_ADDRESS_OF: // take effective address case SMP_GENERAL_COMPARE: DetectedSignedness = false; break; case SMP_U_LEFT_SHIFT: // unsigned left shift case SMP_U_RIGHT_SHIFT: // unsigned right shift case SMP_ROTATE_LEFT: case SMP_ROTATE_LEFT_CARRY: // rotate left through carry case SMP_ROTATE_RIGHT: case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry case SMP_U_MULTIPLY: case SMP_U_DIVIDE: case SMP_U_REMAINDER: case SMP_ZERO_EXTEND: case SMP_BITWISE_NOT: // unary operator case SMP_BITWISE_XOR: case SMP_BITWISE_AND_NOT: case SMP_U_COMPARE: // unsigned compare (AND-based) DetectedSignedness = true; break; case SMP_S_LEFT_SHIFT: // signed left shift case SMP_S_RIGHT_SHIFT: // signed right shift case SMP_S_MULTIPLY: case SMP_S_DIVIDE: case SMP_SIGN_EXTEND: case SMP_NEGATE: // unary negation case SMP_S_COMPARE: // signed compare (subtraction-based) case SMP_LESS_THAN: // boolean test operators case SMP_GREATER_THAN: case SMP_LESS_EQUAL: case SMP_GREATER_EQUAL: DetectedSignedness = true; break; case SMP_DECREMENT: case SMP_INCREMENT: case SMP_ADD: case SMP_ADD_CARRY: // add with carry case SMP_SUBTRACT: case SMP_SUBTRACT_BORROW: // subtract with borrow case SMP_ASSIGN: case SMP_BITWISE_AND: case SMP_BITWISE_OR: case SMP_EQUAL: case SMP_NOT_EQUAL: case SMP_LOGICAL_AND: case SMP_LOGICAL_OR: case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC case SMP_SHUFFLE: // Shuffle bytes, words, etc. within destination operation per source mask case SMP_SIGNAL: // signal or raise exception DetectedSignedness = false; break; case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC DetectedSignedness = true; break; case SMP_REVERSE_SHIFT_U: // Shift right operand by bit count in left operand case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision case SMP_AVERAGE_U: // Average of unsigned operands case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate) case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements case SMP_MAX_S: // dest := signed_max(dest, src) case SMP_MAX_U: // dest := unsigned_max(dest, src) case SMP_MIN_S: // dest := signed_min(dest, src) case SMP_MIN_U: // dest := unsigned_min(dest, src) case SMP_ABSOLUTE_VALUE: // take absolute value case SMP_CONVERT_INT_TO_FP: // convert integer to floating point case SMP_CONVERT_FP_TO_INT: // convert floating point to integer case SMP_INTERLEAVE: // extended-precision interleaving of bytes or words or dwords etc.; NUMERIC case SMP_CONCATENATE: // extended-precision concatenation; NUMERIC DetectedSignedness = true; break; default: DetectedSignedness = false; SMP_msg("ERROR: Unknown operator in OperatorHasSignedness: %d\n", CurrOperator); break; } // end switch on operator return DetectedSignedness; } // end of OperatorHasSignedness() #endif // ***************************************************************** // Class SMPGuard // ***************************************************************** // Constructor SMPGuard::SMPGuard(void) { this->LeftOperand.type = o_void; this->RightOperand.type = o_void; this->GuardOp = SMP_NULL_OPERATOR; return; } // Debug print void SMPGuard::Dump(void) const { SMP_msg("GUARD: "); PrintOperand(this->LeftOperand); SMP_msg(" %s ", OperatorText[this->GuardOp]); PrintOperand(this->RightOperand); SMP_msg(":"); return; } // end of SMPGuard::Dump() // ***************************************************************** // Class SMPRegTransfer // ***************************************************************** // Constructors SMPRegTransfer::SMPRegTransfer(void) { this->Guard = NULL; this->LeftOperand.type = o_void; this->RightOperand.type = o_void; this->RTop.oper = SMP_NULL_OPERATOR; this->RTop.type = UNINIT; #if SMP_TRACK_NONSPEC_OPER_TYPE this->RTop.NonSpeculativeType = UNINIT; #endif this->booleans1 = 0; this->RightRT = NULL; this->ParentInst = NULL; return; } // Destructor SMPRegTransfer::~SMPRegTransfer() { #if 0 SMP_msg("Destroying SMPRegTransfer.\n"); #endif if (NULL != this->RightRT) delete this->RightRT; if (NULL != this->Guard) delete this->Guard; return; } // Get the left operand, and normalize it if it is a stack operand. op_t SMPRegTransfer::GetLeftOperand(void) const { op_t TempOp = this->LeftOperand; // AreDefsNormalized and AreUsesNormalized should always agree, but we // use Defs for left operands and Uses for right operands, which is not // strictly true, but there is no difference in results. if (this->ParentInst->AreDefsNormalized()) { ea_t InstAddr = this->ParentInst->GetAddr(); TempOp = this->ParentInst->GetBlock()->GetFunc()->GetNormalizedOperand(InstAddr, TempOp); } return TempOp; } // end of SMPRegTransfer::GetLeftOperand() // Get the right operand, and normalize it if it is a stack operand. op_t SMPRegTransfer::GetRightOperand(void) const { op_t TempOp = this->RightOperand; if (this->ParentInst->AreDefsNormalized()) { ea_t InstAddr = this->ParentInst->GetAddr(); TempOp = this->ParentInst->GetBlock()->GetFunc()->GetNormalizedOperand(InstAddr, TempOp); } return TempOp; } // end of SMPRegTransfer::GetRightOperand() // Does RTL subtract a non-immediate value from the stack pointer? bool SMPRegTransfer::IsAllocaRTL(void) { bool AllocaFound = false; // Search for the pattern: stack_pointer := stack_pointer minus non-immediate if ((SMP_ASSIGN == this->GetOperator()) && (this->HasRightSubTree())) { op_t DefOp = this->GetLeftOperand(); if (DefOp.is_reg(MD_STACK_POINTER_REG)) { // We have the code pattern stack_pointer := ... SMPRegTransfer *RightRT = this->GetRightTree(); SMPoperator RightOperator = RightRT->GetOperator(); op_t RightDefOp = RightRT->GetLeftOperand(); if ((RightDefOp.is_reg(MD_STACK_POINTER_REG)) && (SMP_SUBTRACT == RightOperator)) { // We have the code pattern stack_pointer := stack_pointer minus ... if (RightRT->HasRightSubTree()) { AllocaFound = true; // not an immediate rightop, whatever it is } else { op_t RightUseOp = RightRT->GetRightOperand(); if (o_imm != RightUseOp.type) { AllocaFound = true; } } } } } return AllocaFound; } // end of SMPRegTransfer::IsAllocaRTL() // Compute operand-dependent change in stack pointer value. sval_t SMPRegTransfer::ComputeStackPointerAlteration(bool IsLeaveInstr, sval_t IncomingDelta, sval_t FramePtrDelta) { sval_t delta = 0; // Search for the pattern: stack_pointer := ... if (SMP_ASSIGN == this->GetOperator()) { op_t DefOp = this->GetLeftOperand(); if (DefOp.is_reg(MD_STACK_POINTER_REG)) { // We have the code pattern stack_pointer := ... // We expect to find an overall RT structure of: // stack_pointer := stack_pointer binaryoperator constant // If the binaryoperator is addition or subtraction, then // we use the constant to determine our return value. // If the binaryoperator is a bitwise AND, then we return // a special code. Delta is unknown but can probably be treated // as zero, assuming that no stack-pointer-relative accesses to // the stack region above the current stack pointer occur later. // If we have a non-constant operand, we return an error code // to indicate that we cannot compute the delta. // If we don't have a binary operator, we return an error code, // unless we find a stack frame deallocation: esp := ebp if (!this->HasRightSubTree()) { op_t UseOp = this->GetRightOperand(); if (UseOp.is_reg(MD_FRAME_POINTER_REG)) { // !!!!****!!!! Should validate that frame pointer reg is used as frame pointer // Found esp := ebp (deallocation of stack frame) delta = FramePtrDelta - IncomingDelta; assert(delta >= 0); // e.g. -4 - -12 => +8 for an 8-byte frame } else { // !!!!****!!!!**** Need to look up deltas for registers that // were used to hold a copy of the stack pointer. // For now, just consider it an error. delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE; } } else { SMPRegTransfer *RightRT = this->GetRightTree(); SMPoperator RightOperator = RightRT->GetOperator(); op_t RightDefOp = RightRT->GetLeftOperand(); if (IsLeaveInstr) { // Found the RT esp := ebp + wordsize that adjusts the stack pointer // for an x86 LEAVE instruction. esp := ebp is equivalent // to esp := <esp when frame ptr was set up>, so delta becomes // <esp delta when frame ptr was set up> - IncomingDelta + wordsize. assert(SMP_ADD == RightOperator); assert(!RightRT->HasRightSubTree()); assert(RightDefOp.is_reg(X86_FRAME_POINTER_REG)); delta = STARS_ISA_Bytewidth + FramePtrDelta - IncomingDelta; } else if (RightRT->HasRightSubTree()) { // Not the right code pattern; unknown effect on stack pointer. delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE; } else if (!RightDefOp.is_reg(MD_STACK_POINTER_REG)) { // We have stack_pointer := something1 operator something2 // where something1 is not the stack_pointer. It might be // the frame pointer, e.g. SP := FP-12. We can compute this // stack delta because we know the delta when the FP was set up. op_t RightUseOp = RightRT->GetRightOperand(); if (RightDefOp.is_reg(MD_FRAME_POINTER_REG) && (o_imm == RightUseOp.type)) { // We have SP := FP operator immediate delta = FramePtrDelta - IncomingDelta; // partial computation: SP := BP assert(delta >= 0); // e.g. -4 - -12 => +8 for an 8-byte frame if (SMP_ADD == RightOperator) { delta += RightUseOp.value; } else if (SMP_SUBTRACT == RightOperator) { delta -= RightUseOp.value; } else { // Not the right code pattern; unknown effect on stack pointer. delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE; } } else { // !!!!****!!!!**** Need to look up deltas for registers that // were used to hold a copy of the stack pointer. // For now, just consider it an error. delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE; } } else { // We have stack_pointer := stack_pointer operator ... op_t RightUseOp = RightRT->GetRightOperand(); // Check the operator if (SMP_BITWISE_AND == RightOperator) { delta = (sval_t) SMP_STACK_POINTER_BITWISE_AND_CODE; } else { if (o_imm != RightUseOp.type) { // Don't know how to deal with adding non-constant to stack pointer delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE; } else if (SMP_ADD == RightOperator) { delta = (sval_t) RightUseOp.value; } else if (SMP_SUBTRACT == RightOperator) { delta = (0 - ((sval_t) RightUseOp.value)); } else { delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE; } } } } } } return delta; } // end of SMPRegTransfer::ComputeStackPointerAlteration() // Evaluate constant expressions for SCCP algorithm struct STARS_SCCP_Const_Struct SMPRegTransfer::SCCPEvaluateRTLConsts(list<pair<int, int> > &SSAWorkList) { struct STARS_SCCP_Const_Struct ReturnConstStruct; ReturnConstStruct.ConstType = STARS_CONST_TOP; // default value SMPoperator CurrOper = this->GetOperator(); switch (CurrOper) { case SMP_ASSIGN: { op_t LeftOp = this->GetLeftOperand(); if (o_reg != LeftOp.type) { break; // Only looking to update registers now } bool NewValueAvailable; if (this->HasRightSubTree()) { ReturnConstStruct = this->GetRightTree()->SCCPEvaluateRTLConsts(SSAWorkList); // recurse into right subtree } else { op_t RightOp = this->GetRightOperand(); this->ParentInst->SCCPFetchConstUseValue(RightOp, ReturnConstStruct); } NewValueAvailable = (STARS_CONST_TOP != ReturnConstStruct.ConstType); // Update const map entry for DEF if (NewValueAvailable) { CanonicalizeOpnd(LeftOp); struct STARS_SCCP_Const_Struct OldConstStruct; OldConstStruct.ConstType = STARS_CONST_TOP; set<DefOrUse, LessDefUse>::iterator DefIter = this->ParentInst->FindDef(LeftOp); if (DefIter != this->ParentInst->GetLastDef()) { int DefSSANum = DefIter->GetSSANum(); int DefHashValue = HashGlobalNameAndSSA(LeftOp, DefSSANum); bool LocalName = this->ParentInst->GetBlock()->IsLocalName(LeftOp); this->ParentInst->SCCPFetchConstDefValue(LeftOp, OldConstStruct); bool HasOldValue = (STARS_CONST_TOP != OldConstStruct.ConstType); if (HasOldValue) { // must perform type lattice meet operation to update ReturnConstStruct STARSConstantTypeMeet(OldConstStruct, ReturnConstStruct); } if (ReturnConstStruct.ConstType != OldConstStruct.ConstType) { // type change map<int, struct STARS_SCCP_Const_Struct>::iterator NewConstIter; if (LocalName) { NewConstIter = this->ParentInst->GetBlock()->InsertLocalConstValue(DefHashValue, ReturnConstStruct); } else { NewConstIter = this->ParentInst->GetBlock()->GetFunc()->InsertGlobalConstValue(DefHashValue, ReturnConstStruct); } // Propagate along SSA edges. int BlockNum = this->ParentInst->GetBlock()->GetNumber(); pair<int, int> SSAEdge(BlockNum, DefHashValue); SSAWorkList.push_back(SSAEdge); } } } break; // end case for SMP_ASSIGN } case SMP_ADD: case SMP_SUBTRACT: case SMP_U_MULTIPLY: case SMP_S_MULTIPLY: case SMP_BITWISE_AND: case SMP_BITWISE_OR: case SMP_BITWISE_XOR: case SMP_BITWISE_AND_NOT: case SMP_S_COMPARE: case SMP_U_COMPARE: // binary arithmetic operators // We will initially deal with the simple case: The comparison is of a LeftOp register to a RightOp register or immediate. // If the result is definitely zero, we return zero as the const value (presumably to be put into the zero flag). if (!(this->HasRightSubTree())) { // simple compare of two operands should always be the case op_t LeftOp = this->GetLeftOperand(); op_t RightOp = this->GetRightOperand(); if ((LeftOp.type == o_reg) && ((RightOp.type == o_reg) || (RightOp.type == o_imm))) { CanonicalizeOpnd(LeftOp); CanonicalizeOpnd(RightOp); struct STARS_SCCP_Const_Struct LeftValue, RightValue; this->ParentInst->SCCPFetchConstUseValue(LeftOp, LeftValue); bool LeftValueAvailable = (STARS_CONST_TOP != LeftValue.ConstType); if (STARS_CONST_BOTTOM == LeftValue.ConstType) { ReturnConstStruct = LeftValue; break; // right operand is irrelevant if left is BOTTOM } bool RightValueAvailable = false; if (o_imm == RightOp.type) { RightValueAvailable = true; RightValue.ConstType = STARS_CONST_HAS_VALUE; RightValue.ConstValue = RightOp.value; } else { this->ParentInst->SCCPFetchConstUseValue(RightOp, RightValue); RightValueAvailable = (STARS_CONST_TOP != RightValue.ConstType); if (STARS_CONST_BOTTOM == RightValue.ConstType) { ReturnConstStruct = RightValue; break; // left operand is irrelevant if right is BOTTOM } } if (LeftValueAvailable && RightValueAvailable) { // Apply the operator to the values. ReturnConstStruct.ConstType = STARS_CONST_HAS_VALUE; uval_t TestValue; bool CompareFlag = ((CurrOper == SMP_S_COMPARE) || (CurrOper == SMP_U_COMPARE)); if (CurrOper == SMP_ADD) { TestValue = (LeftValue.ConstValue + RightValue.ConstValue); } else if ((CurrOper == SMP_U_MULTIPLY) || (CurrOper == SMP_S_MULTIPLY)) { TestValue = (LeftValue.ConstValue * RightValue.ConstValue); } else if ((CurrOper == SMP_SUBTRACT) || (CurrOper == SMP_S_COMPARE)) { // Signed compare is a subtraction operation TestValue = (LeftValue.ConstValue - RightValue.ConstValue); } else if ((CurrOper == SMP_U_COMPARE) || (CurrOper == SMP_BITWISE_AND)) { // Unsigned compare is a bitwise AND operation TestValue = (LeftValue.ConstValue & RightValue.ConstValue); } else if (CurrOper == SMP_BITWISE_OR) { TestValue = (LeftValue.ConstValue | RightValue.ConstValue); } else if (CurrOper == SMP_BITWISE_XOR) { TestValue = (LeftValue.ConstValue ^ RightValue.ConstValue); } else if (CurrOper == SMP_BITWISE_AND_NOT) { TestValue = (LeftValue.ConstValue & (~(RightValue.ConstValue))); } else { break; // should be unreachable } if ((0 != TestValue) && CompareFlag) { TestValue = 1; // i.e. zero flag will be set to false } ReturnConstStruct.ConstValue = TestValue; } } // end if two regs, or one reg and one immediate } // end if no right subtree break; // end of signed and unsigned compare operators case default: break; // ignore } // end switch(CurrOper) return ReturnConstStruct; } // end of SMPRegTransfer::SCCPEvaluateRTLConsts() // Debug print void SMPRegTransfer::Dump(void) const { if (NULL != this->Guard) this->Guard->Dump(); // Left operand if (o_void != this->LeftOperand.type) PrintOperand(this->LeftOperand); // Then the operator SMP_msg(" %s ", OperatorText[this->GetOperator()]); // then the right operand or subtree if (this->HasRightSubTree()) this->GetRightTree()->Dump(); else if (o_void != this->RightOperand.type) PrintOperand(this->RightOperand); return; } // ***************************************************************** // Class SMPRTL // ***************************************************************** // Constructor SMPRTL::SMPRTL() { this->ExtraKills.clear(); this->RTCount = 0; return; } // Destructor SMPRTL::~SMPRTL() { for (size_t index = 0; index < this->RTCount; ++index) { delete (this->RTvector[index]); } this->ExtraKills.clear(); return; } // Get methods SMPRegTransfer *SMPRTL::GetRT(size_t index) const { if (index > this->RTCount) return NULL; else return this->RTvector[index]; } // Set methods void SMPRTL::push_back(SMPRegTransfer *NewEffect) { assert(SMP_RT_LIMIT > this->RTCount); this->RTvector[this->RTCount] = NewEffect; ++(this->RTCount); return; } // Printing methods void SMPRTL::Dump(void) const { size_t index; if (0 < this->RTCount) { SMP_msg("RTL: "); for (index = 0; index < this->RTCount; ++index) { this->RTvector[index]->Dump(); } for (index = 0; index < this->ExtraKills.size(); ++index) { SMP_msg(" KILL: "); PrintOperand(this->ExtraKills.at(index)); } SMP_msg("\n"); } return; } // end of SMPRTL::Dump() // Accumulate stack pointer alteration total across all RTs. sval_t SMPRTL::TotalStackPointerAlteration(bool IsLeaveInstr, sval_t IncomingDelta, sval_t FramePtrDelta) { sval_t TotalDelta = 0; sval_t IncrementalDelta; for (size_t index = 0; index < this->RTCount; ++index) { IncrementalDelta = this->RTvector[index]->ComputeStackPointerAlteration(IsLeaveInstr, IncomingDelta, FramePtrDelta); if ((SMP_STACK_DELTA_ERROR_CODE == IncrementalDelta) || (SMP_STACK_POINTER_BITWISE_AND_CODE == IncrementalDelta)) { TotalDelta = IncrementalDelta; // pass code back break; // exit loop and return coded value } else { TotalDelta += IncrementalDelta; } } return TotalDelta; } // end of SMPRTL::TotalStackPointerAlteration() // ***************************************************************** // Class SMPInstr // ***************************************************************** // Constructor for instruction. SMPInstr::SMPInstr(ea_t addr) { this->SMPcmd.size = 0; this->address = addr; #if 0 this->ResetGoodRTL(); this->ResetJumpTarget(); this->ResetBlockTerm(); this->ResetTailCall(); this->ResetCondTailCall(); this->ResetCallUsedAsJump(); this->ResetDirectRecursiveCall(); this->ResetInterrupt(); #else this->booleans1 = 0; #endif #if 0 this->ResetNop(); this->ResetRegClearIdiom(); this->ResetDefsFlags(); this->ResetUsesFlags(); this->ResetFarBranchComputed(); this->ResetBranchesToFarChunk(); this->ResetIndirectMemWrite(); this->ResetIndirectMemRead(); #else this->booleans2 = 0; #endif #if 0 this->ResetLoadFromStack(); this->ResetMultiplicationBitsDiscarded(); this->ResetTypeInferenceComplete(); this->ResetCategoryInferenceComplete(); this->ResetDEFsTyped(); this->ResetUSEsTyped(); #else this->booleans3 = 0; #endif this->booleans4 = 0; this->CallTarget = BADADDR; this->FarBranchTarget = BADADDR; this->AddSubSourceType = UNINIT; this->AddSubUseType = UNINIT; this->AddSubSourceOp = InitOp; this->AddSubUseOp = InitOp; this->DEFMemOp = InitOp; this->USEMemOp = InitOp; this->MoveSource = InitOp; this->BasicBlock = NULL; this->features = 0; this->StackPtrOffset = 0; this->type = DEFAULT; this->OptType = 0; this->Defs.clear(); this->Uses.clear(); return; } // Destructor. SMPInstr::~SMPInstr() { this->Defs.clear(); this->Uses.clear(); return; } char *SMPInstr::GetDisasm(void) const { return DisAsmText.GetDisAsm(this->GetAddr()); } // Is the instruction the type that terminates a basic block? bool SMPInstr::IsBasicBlockTerminator() const { return ((type == JUMP) || (type == COND_BRANCH) || (type == INDIR_JUMP) || (type == RETURN)); } // Get non-flags DEF, usually for arithmetic opcode. set<DefOrUse, LessDefUse>::iterator SMPInstr::GetFirstNonFlagsDef(void) { set<DefOrUse, LessDefUse>::iterator DefIter; op_t DefOp; for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { DefOp = DefIter->GetOp(); if (!((o_reg == DefOp.type) && DefOp.is_reg(MD_FLAGS_REG))) break; // found a non-flags-reg DEF. } return DefIter; } // Is the destination operand a memory reference? bool SMPInstr::HasDestMemoryOperand(void) { return (o_void != this->DEFMemOp.type); } // end of SMPInstr::HasDestMemoryOperand() // Is a source operand a memory reference? bool SMPInstr::HasSourceMemoryOperand(void) { return (o_void != this->USEMemOp.type); } // end of SMPInstr::HasSourceMemoryOperand() // Get the first memory operand in the DEF list. op_t SMPInstr::MDGetMemDefOp(void) const { return this->DEFMemOp; // cached value } // end of SMPInstr::MDGetMemDefOp() // Get the first memory operand in the USE list. op_t SMPInstr::MDGetMemUseOp(void) const { return this->USEMemOp; // cached value } // end of SMPInstr::MDGetMemUseOp() // return original Lea instruction [pseudo-]memory operand. op_t SMPInstr::GetLeaMemUseOp(void) { if (this->BasicBlock == NULL) { return InitOp; } else { map<ea_t, op_t>::iterator MapIter = this->GetBlock()->GetFunc()->FindLeaOperand(this->GetAddr()); return MapIter->second; } } // return BADADDR if not jump, target addr otherwise. ea_t SMPInstr::GetJumpTarget(void) const { ea_t TargetAddr = BADADDR; if (this->HasGoodRTL()) { // We want to find an RTL of the form: inst_ptr_reg := code_addr SMPRegTransfer *CurrRT = this->RTL.GetRT(0); op_t DefOp = CurrRT->GetLeftOperand(); if (DefOp.is_reg(MD_INSTRUCTION_POINTER_REG)) { if ((SMP_ASSIGN == CurrRT->GetOperator()) && (!CurrRT->HasRightSubTree())) { op_t UseOp = CurrRT->GetRightOperand(); if ((o_near == UseOp.type) || (o_far == UseOp.type)) { // address TargetAddr = UseOp.addr; } } } } return TargetAddr; } // end SMPInstr::GetJumpTarget() // Does any USE have type NEGATEDPTR? bool SMPInstr::HasNegatedPtrUSE(void) { bool UseFound = false; set<DefOrUse, LessDefUse>::iterator UseIter; SMPOperandType UseType; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseType = UseIter->GetType(); if (IsEqType(UseType, NEGATEDPTR)) { UseFound = true; break; } } return UseFound; } // end of SMPInstr::HasNegatedPtrUSE() // Detect indirect memory DEFs or USEs void SMPInstr::AnalyzeIndirectRefs(bool UseFP) { op_t DefMemOp = this->MDGetMemDefOp(); op_t UseMemOp = this->MDGetMemUseOp(); if (o_void != DefMemOp.type) { // Found a memory DEF. Is it indirect? if (MDIsIndirectMemoryOpnd(DefMemOp, UseFP)) { this->SetIndirectMemWrite(); } } if (o_void != UseMemOp.type) { // Found a memory USE. Is it indirect? if (MDIsIndirectMemoryOpnd(UseMemOp, UseFP)) { this->SetIndirectMemRead(); } } return; } // end of SMPInstr::AnalyzeIndirectRefs() set<DefOrUse, LessDefUse>::iterator SMPInstr::GetPointerAddressReg(op_t MemOp) { int BaseReg; int IndexReg; ushort ScaleFactor; ea_t displacement; set<DefOrUse, LessDefUse>::iterator PtrIter; if ((NULL == this->BasicBlock) || (NULL == this->BasicBlock->GetFunc())) { SMP_msg("ERROR: NULL member pointers in SMPInstr::GetPointerAddressReg() at %lx \n", (unsigned long) this->address); return this->GetLastUse(); } bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer(); MDExtractAddressFields(MemOp, BaseReg, IndexReg, ScaleFactor, displacement); if ((R_none != BaseReg) && (!MDIsStackPtrReg(BaseReg, UseFP))) { op_t BaseOp = InitOp; BaseOp.type = o_reg; BaseOp.reg = (ushort) BaseReg; PtrIter = this->FindUse(BaseOp); assert(PtrIter != this->GetLastUse()); if (IsDataPtr(PtrIter->GetType())) { return PtrIter; } } if ((R_none != IndexReg) && (!MDIsStackPtrReg(IndexReg, UseFP))) { op_t IndexOp = InitOp; IndexOp.type = o_reg; IndexOp.reg = (ushort) IndexReg; PtrIter = this->FindUse(IndexOp); assert(PtrIter != this->GetLastUse()); if (IsDataPtr(PtrIter->GetType())) { return PtrIter; } } PtrIter = this->GetLastUse(); return PtrIter; } // end of SMPInstr::GetPointerAddressReg() // Does the instruction whose flags are in F have a numeric type // as the second source operand? // NOTE: We can only analyze immediate values now. When data flow analyses are implemented, // we will be able to analyze many non-immediate operands. #define IMMEDNUM_LOWER -8191 #define IMMEDNUM_UPPER 8191 bool SMPInstr::IsSecondSrcOperandNumeric(flags_t F) const { bool SecondOpImm = (this->SMPcmd.Operands[1].type == o_imm); uval_t TempImm; if (SecondOpImm) { TempImm = this->SMPcmd.Operands[1].value; } return (SecondOpImm && IsImmedNumeric(TempImm)); } // end of SMPInstr::IsSecondSrcOperandNumeric() // Determine the type of the USE-only operand for add and subtract // instructions. If it is NUMERIC or PROF_NUMERIC, an optimizing // annotation will result. // As a byproduct, find the type of the USE/DEF operand as well. void SMPInstr::SetAddSubSourceType(void) { // Walk the RTL and find the operands we care about. // The RTL should look like: opnd1 := (opnd1 op opnd2), where op is // and add or subtract operator. Within the parentheses, the type // of opnd1 is our AddSubUseType and opnd1 is our AddSubUseOp, while // the type of opnd2 is our AddSubSourceType. if (this->RTL.GetCount() < 1) return; // no RTL, no leave types as UNINIT. assert(this->RTL.GetRT(0)->HasRightSubTree()); SMPRegTransfer *RightTree = this->RTL.GetRT(0)->GetRightTree(); op_t LeftOp, RightOp; LeftOp = RightTree->GetLeftOperand(); // Use (also DEF) operand #if SMP_BUILD_SPECIAL_ADC_SBB_RTL if ((NN_adc != this->SMPcmd.itype) && (NN_sbb != this->SMPcmd.itype)) { assert(!(RightTree->HasRightSubTree())); RightOp = RightTree->GetRightOperand(); // Src (non-DEF) operand } else { // Add with carry and subtract with borrow have an extra level // to the tree RTL, e.g. for add with carry: // opnd1 := (opnd1 + (opnd2 + carryflag)) assert(RightTree->HasRightSubTree()); RightTree = RightTree->GetRightTree(); RightOp = RightTree->GetLeftOperand(); } #else assert(!(RightTree->HasRightSubTree())); RightOp = RightTree->GetRightOperand(); // Src (non-DEF) operand #endif set<DefOrUse, LessDefUse>::iterator UseIter, SrcIter; SrcIter = this->FindUse(RightOp); assert(SrcIter != this->GetLastUse()); this->AddSubSourceType = SrcIter->GetType(); this->AddSubSourceOp = RightOp; UseIter = this->FindUse(LeftOp); assert(UseIter != this->GetLastUse()); this->AddSubUseType = UseIter->GetType(); this->AddSubUseOp = LeftOp; return; } // end of SMPInstr::SetAddSubSourceType() // Are all DEFs in the DEF set NUMERIC type? bool SMPInstr::AllDefsNumeric(void) { bool AllNumeric = (this->Defs.GetSize() > 0); // false if no DEFs, true otherwise set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) { // We ignore the stack pointer for pop instructions and consider only // the register DEF of the pop. if (this->MDIsPopInstr() && CurrDef->GetOp().is_reg(R_sp)) continue; AllNumeric = (AllNumeric && IsNumeric(CurrDef->GetType())); } return AllNumeric; } // end of SMPInstr::AllDefsNumeric() // Were the types of any DEFs derived from profiler info? bool SMPInstr::AnyDefsProfiled(void) { bool profd = false; set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) { profd = (profd || IsProfDerived(CurrDef->GetType())); } return profd; } // Do all DEFs have DEF_METADATA_UNUSED status? bool SMPInstr::AllDefMetadataUnused(void) { bool AllUnused = (this->Defs.GetSize() > 0); // false if no DEFs, true otherwise set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) { AllUnused = (AllUnused && (DEF_METADATA_UNUSED == CurrDef->GetMetadataStatus())); } return AllUnused; } // end of SMPInstr::AllDefMetadataUnused() // DEBUG print operands for Inst. void SMPInstr::PrintOperands(void) const { op_t Opnd; for (int i = 0; i < UA_MAXOP; ++i) { Opnd = SMPcmd.Operands[i]; PrintOneOperand(Opnd, this->features, i); } SMP_msg(" \n"); return; } // end of SMPInstr::PrintOperands() // Complete DEBUG printing. void SMPInstr::Dump(void) { SMP_msg("%lx %d SMPitype: %d %s\n", (unsigned long) this->address, this->SMPcmd.size, (int) this->type, DisAsmText.GetDisAsm(this->GetAddr())); #if STARS_DUMP_FG_INFO SMP_msg("USEs: "); if (NULL == this->GetBlock()) { // during early improvement of disasm this->Uses.Dump(); SMP_msg("DEFs: "); this->Defs.Dump(); } else { set<DefOrUse, LessDefUse>::iterator UseIter, DefIter; op_t UseOp, DefOp; int UseHashValue, DefHashValue, UseSSANum, DefSSANum; unsigned short SignMiscInfo, SignMask; bool LocalName; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseIter->Dump(); UseOp = UseIter->GetOp(); if (o_reg == UseOp.type) { LocalName = this->GetBlock()->IsLocalName(UseOp); UseSSANum = UseIter->GetSSANum(); UseHashValue = HashGlobalNameAndSSA(UseOp, UseSSANum); if (LocalName) { SignMiscInfo = this->GetBlock()->GetUseSignMiscInfo(UseHashValue); } else { SignMiscInfo = this->GetBlock()->GetFunc()->GetUseSignMiscInfo(UseHashValue); } SignMask = SignMiscInfo & FG_MASK_SIGNEDNESS_BITS; if (SignMask == FG_MASK_SIGNED) { SMP_msg(" Si "); } else if (SignMask == FG_MASK_UNSIGNED) { SMP_msg(" Un "); } else if (SignMask == FG_MASK_INCONSISTENT_SIGN) { SMP_msg(" Xs "); } } SMP_msg("\n"); } SMP_msg("DEFs: "); for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { DefIter->Dump(); DefOp = DefIter->GetOp(); if (o_reg == DefOp.type) { LocalName = this->GetBlock()->IsLocalName(DefOp); DefSSANum = DefIter->GetSSANum(); DefHashValue = HashGlobalNameAndSSA(DefOp, DefSSANum); if (LocalName) { SignMiscInfo = this->GetBlock()->GetDefSignMiscInfo(DefHashValue); } else { SignMiscInfo = this->GetBlock()->GetFunc()->GetDefSignMiscInfo(DefHashValue); } SignMask = SignMiscInfo & FG_MASK_SIGNEDNESS_BITS; if (SignMask == FG_MASK_SIGNED) { SMP_msg(" Si "); } else if (SignMask == FG_MASK_UNSIGNED) { SMP_msg(" Un "); } else if (SignMask == FG_MASK_INCONSISTENT_SIGN) { SMP_msg(" Xs "); } } SMP_msg("\n"); } } #else SMP_msg("USEs: "); this->Uses.Dump(); SMP_msg("DEFs: "); this->Defs.Dump(); #endif this->RTL.Dump(); #if SMP_VERBOSE_DUMP this->PrintOperands(); #endif SMP_msg("\n"); return; } // end of SMPInstr::Dump() // Print out the destination operand list for the instruction, given // the OptCategory for the instruction as a hint. char * SMPInstr::DestString(int OptType) { static char DestList[MAXSTR]; int RegDestCount = 0; DestList[0] = 'Z'; // Make sure there are no leftovers from last call DestList[1] = 'Z'; DestList[2] = '\0'; set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) { op_t DefOpnd = CurrDef->GetOp(); if (DefOpnd.is_reg(X86_FLAGS_REG)) // don't print flags as a destination continue; // We want to ignore the stack pointer DEF for pops and just include // the register DEF for the pop. if (DefOpnd.is_reg(R_sp) && this->MDIsPopInstr()) continue; if (o_reg == DefOpnd.type) { ushort DestReg = DefOpnd.reg; if (0 == RegDestCount) { SMP_strncpy(DestList, RegNames[DestReg], 1 + strlen(RegNames[DestReg])); } else { SMP_strncat(DestList, " ", MAXSTR); SMP_strncat(DestList, RegNames[DestReg], MAXSTR); } ++RegDestCount; } } if (0 >= RegDestCount) { SMP_msg("WARNING: No destination registers: %s\n", DisAsmText.GetDisAsm(this->GetAddr())); } else { SMP_strncat(DestList, " ZZ ", MAXSTR); } return DestList; } // end of SMPInstr::DestString() // print the registers that are dead right before this instruction. void SMPInstr::PrintDeadRegs(FILE *OutputFile) { size_t RegNum = (size_t) MD_FLAGS_REG; // Start with the flags register. if (this->DeadRegsBitmap[RegNum]) { SMP_fprintf(OutputFile, "%s ", RegNames[RegNum]); } // Do all other registers for (RegNum = 0; RegNum < MD_FLAGS_REG; ++RegNum) { if (this->DeadRegsBitmap[RegNum]) { SMP_fprintf(OutputFile, "%s ", RegNames[RegNum]); } } for (RegNum = 1 + MD_FLAGS_REG; RegNum <= MD_LAST_REG_NO; ++RegNum) { if (this->DeadRegsBitmap[RegNum]) { SMP_fprintf(OutputFile, "%s ", RegNames[RegNum]); } } // Print sequence terminator. SMP_fprintf(OutputFile, "ZZ"); return; } // end of SMPInstr::PrintDeadRegs() // Equality operator for SMPInstr. Key field is address. int SMPInstr::operator==(const SMPInstr &rhs) const { if (this->address != rhs.GetAddr()) return 0; else return 1; } // Inequality operator for SMPInstr. Key field is address. int SMPInstr::operator!=(const SMPInstr &rhs) const { return (this->address != rhs.GetAddr()); } // Less than operator for sorting SMPInstr lists. Key field is address. int SMPInstr::operator<(const SMPInstr &rhs) const { return (this->address < rhs.GetAddr()); } // Less than or equal operator for sorting SMPInstr lists. Key field is address. int SMPInstr::operator<=(const SMPInstr &rhs) const { return (this->address <= rhs.GetAddr()); } #define MD_FIRST_ENTER_INSTR NN_enterw #define MD_LAST_ENTER_INSTR NN_enterq // Is this instruction one that allocates space on the // stack for the local variables? bool SMPInstr::MDIsFrameAllocInstr(void) { // The frame allocating instruction should look like: // sub esp,48 or add esp,-64 etc. op_t ESPOp = InitOp; ESPOp.type = o_reg; ESPOp.reg = R_sp; if ((SMPcmd.itype == NN_sub) || (SMPcmd.itype == NN_add)) { if (this->GetLastDef() != this->Defs.FindRef(ESPOp)) { // We know that an addition or subtraction is being // performed on the stack pointer. This should not be // possible within the prologue except at the stack // frame allocation instruction, so return true. We // could be more robust in this analysis in the future. **!!** // CAUTION: If a compiler allocates 64 bytes for locals // and 16 bytes for outgoing arguments in a single // instruction: sub esp,80 // you cannot insist on finding sub esp,LocSize // To make this more robust, we are going to insist that // an allocation of stack space is either performed by // adding a negative immediate value, or by subtracting // a positive immediate value. We will throw in, free of // charge, a subtraction of a register, which is how alloca() // usually allocates stack space. // PHASE ORDERING: Should we use the Operands[] instead of the USE list? **!!** set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) { if (o_imm == CurrUse->GetOp().type) { signed long TempImm = (signed long) CurrUse->GetOp().value; if (((0 > TempImm) && (this->SMPcmd.itype == NN_add)) || ((0 < TempImm) && (this->SMPcmd.itype == NN_sub))) { return true; } } else if ((o_reg == CurrUse->GetOp().type) && (!CurrUse->GetOp().is_reg(R_sp)) // skip the ESP operand && (this->SMPcmd.itype == NN_sub)) { // sub esp,reg: alloca() ? return true; } } } } else if ((this->SMPcmd.itype >= MD_FIRST_ENTER_INSTR) && (this->SMPcmd.itype <= MD_LAST_ENTER_INSTR)) { return true; } return false; } // end of SMPInstr::MDIsFrameAllocInstr() #define MD_FIRST_LEAVE_INSTR NN_leavew #define MD_LAST_LEAVE_INSTR NN_leaveq // Is this instruction in the epilogue the one that deallocates the local // vars region of the stack frame? bool SMPInstr::MDIsFrameDeallocInstr(bool UseFP, asize_t LocalVarsSize) { // The usual compiler idiom for the prologue on x86 is to // deallocate the local var space with: mov esp,ebp // It could be add esp,constant. We can be tricked by // add esp,constant when the constant is just the stack // adjustment after a call. We will have to insist that // the immediate operand have at least the value of // LocalVarsSize for this second form, and that UseFP be true // for the first form. set<DefOrUse, LessDefUse>::iterator FirstDef = this->GetFirstDef(); set<DefOrUse, LessDefUse>::iterator FirstUse = this->GetFirstUse(); if ((SMPcmd.itype >= MD_FIRST_LEAVE_INSTR) && (SMPcmd.itype <= MD_LAST_LEAVE_INSTR)) return true; else if (this->HasDestMemoryOperand() || this->HasSourceMemoryOperand()) { // Don't get fooled by USE or DEF entries of EBP or ESP that come // from memory operands, e.g. mov eax,[ebp-20] return false; } else if (UseFP && (this->SMPcmd.itype == NN_mov) && (FirstDef->GetOp().is_reg(MD_STACK_POINTER_REG)) && (FirstUse->GetOp().is_reg(MD_FRAME_POINTER_REG))) return true; else if ((this->SMPcmd.itype == NN_add) && (FirstDef->GetOp().is_reg(MD_STACK_POINTER_REG))) { set<DefOrUse, LessDefUse>::iterator SecondUse = ++FirstUse; if (SecondUse == this->Uses.GetLastRef()) return false; // no more USEs ... strange for ADD instruction if (SecondUse->GetOp().is_imm((uval_t) LocalVarsSize)) return true; else if (SecondUse->GetOp().type == o_imm) { signed long TempImm = (signed long) this->SMPcmd.Operands[1].value; if (0 > TempImm) // adding a negative to ESP; alloc, not dealloc return false; else { SMP_msg("Used imprecise LocalVarsSize to find dealloc instr.\n"); return true; } } else return false; } else return false; } // end of SMPInstr::MDIsFrameDeallocInstr() // Is instruction a no-op? There are 1-byte, 2-byte, etc., versions of no-ops. bool SMPInstr::MDIsNop(void) const { bool IsNop = false; ushort opcode = this->SMPcmd.itype; // NOTE: More examples have arisen, e.g. xchg reg with itself. !!!!!! if (NN_nop == opcode) IsNop = true; else if ((NN_mov == opcode) || (NN_xchg == opcode)) { if ((o_reg == this->SMPcmd.Operands[0].type) && this->SMPcmd.Operands[1].is_reg(this->SMPcmd.Operands[0].reg)) { // We have a register to register move with source == destination, // or a register exchanged with itself. IsNop = true; } } else if (NN_lea == opcode) { if ((o_reg == this->SMPcmd.Operands[0].type) && (o_displ == this->SMPcmd.Operands[1].type) && (0 == this->SMPcmd.Operands[1].addr)) { // We are looking for 6-byte no-ops like lea esi,[esi+0] ushort destreg = this->SMPcmd.Operands[0].reg; if ((this->SMPcmd.Operands[1].hasSIB) && (destreg == (ushort) sib_base(this->SMPcmd.Operands[1])) && (R_sp == sib_index(this->SMPcmd.Operands[1]))) { // R_sp signifies no SIB index register. So, we have // lea reg,[reg+0] with reg being the same in both place, // once as Operands[0] and once as the base reg in Operands[1]. IsNop = true; } else if (destreg == this->SMPcmd.Operands[1].reg) { IsNop = true; } } } return IsNop; } // end of SMPInstr::MDIsNop() // Opcode always produces an UNSIGNED DEF. bool SMPInstr::MDAlwaysUnsignedDEF(void) const { ushort opcode = this->SMPcmd.itype; return ((opcode == NN_bsf) || (opcode == NN_bsr) || (opcode == NN_div) || (opcode == NN_lahf) || (opcode == NN_lar) || (opcode == NN_lgs) || (opcode == NN_lss) || (opcode == NN_lds) || (opcode == NN_les) || (opcode == NN_lfs) || (opcode == NN_lsl) || (opcode == NN_movzx) || (opcode == NN_rcl) || (opcode == NN_rcr) || (opcode == NN_rol) || (opcode == NN_ror) || (opcode == NN_shl) || (opcode == NN_shr) || ((opcode >= NN_seta) && (opcode <= NN_setz)) || (opcode == NN_cpuid) || (opcode == NN_rdtsc) || (opcode == NN_rdpmc) || (opcode == NN_fstsw) || (opcode == NN_setalc) || (opcode == NN_packuswb) || (opcode == NN_paddusb) || (opcode == NN_paddusw) || (opcode == NN_psllw) || (opcode == NN_pslld) || (opcode == NN_psllq) || (opcode == NN_psrlw) || (opcode == NN_psrld) || (opcode == NN_psrlq) || (opcode == NN_psubusb) || (opcode == NN_psubusw) || (opcode == NN_pxor) || (opcode == NN_pavgusb) || (opcode == NN_pavgb) || (opcode == NN_pavgw) || (opcode == NN_pextrw) || (opcode == NN_pmaxub) || ((opcode >= NN_pminub) && (opcode <= NN_psadbw)) || (opcode == NN_movmskpd) || (opcode == NN_pmuludq) || (opcode == NN_pslldq) || (opcode == NN_psrldq) || ((opcode >= NN_pabsb) && (opcode <= NN_pabsd)) || (opcode == NN_rdtscp) || (opcode == NN_mpsadbw) || (opcode == NN_packusdw) || ((opcode >= NN_pcmpeqq) && (opcode <= NN_phminposuw)) || (opcode == NN_pmaxud) || (opcode == NN_pmaxuw) || (opcode == NN_pminud) || (opcode == NN_pminuw) || ((opcode >= NN_pmovzxbw) && (opcode <= NN_pmovzxdq)) || ((opcode >= NN_crc32) && (opcode <= NN_pcmpistrm)) || (opcode == NN_popcnt) || (opcode == NN_lzcnt) || ((opcode >= NN_aesenc) && (opcode <= NN_aeskeygenassist))); } // end of SMPInstr::MDAlwaysUnsignedDEF() // Opcode always produces a SIGNED DEF. bool SMPInstr::MDAlwaysSignedDEF(void) const { ushort opcode = this->SMPcmd.itype; return ((opcode == NN_cbw) || (opcode == NN_cwde) || (opcode == NN_cdqe) || (opcode == NN_cwd) || (opcode == NN_cdq) || (opcode == NN_cqo) || (opcode == NN_idiv) || (opcode == NN_movsx) || (opcode == NN_neg) || (opcode == NN_sal) || (opcode == NN_sar) || (opcode == NN_fist) || (opcode == NN_fistp) || (opcode == NN_fbstp) || (opcode == NN_packsswb) || (opcode == NN_packssdw) || (opcode == NN_paddsb) || (opcode == NN_paddsw) || (opcode == NN_pmaddwd) || (opcode == NN_pmulhw) || (opcode == NN_pmullw) || (opcode == NN_psraw) || (opcode == NN_psrad) || (opcode == NN_psubsb) || (opcode == NN_psubsw) || (opcode == NN_pfadd) || (opcode == NN_pfsub) || (opcode == NN_pfsubr) || (opcode == NN_pfacc) || (opcode == NN_pfmin) || (opcode == NN_pfmax) || (opcode == NN_pf2id) || (opcode == NN_pfrcp) || (opcode == NN_pfadd) || (opcode == NN_pfrsqrt) || (opcode == NN_pfmul) || (opcode == NN_pfrcpit1) || (opcode == NN_pfrsqit1) || (opcode == NN_pfrcpit2) || (opcode == NN_pmulhrw) || ((opcode >= NN_addps) && (opcode <= NN_andps)) || ((opcode >= NN_cvtpi2ps) && (opcode <= NN_divss)) || ((opcode >= NN_maxps) && (opcode <= NN_movlps)) || ((opcode >= NN_movss) && (opcode <= NN_sqrtss)) || (opcode == NN_subps) || (opcode == NN_subss) || (opcode == NN_unpckhps) || (opcode == NN_unpcklps) || (opcode == NN_pmaxsw) || (opcode == NN_pminsw) || (opcode == NN_movntps) || (opcode == NN_pf2iw) || (opcode == NN_pfnacc) || (opcode == NN_pfpnacc) || (opcode == NN_pi2fw) || ((opcode >= NN_addpd) && (opcode <= NN_andpd)) || ((opcode >= NN_cvtdq2pd) && (opcode <= NN_divsd)) || (opcode == NN_maxpd) || (opcode == NN_maxsd) || ((opcode >= NN_minpd) && (opcode <= NN_movapd)) || (opcode == NN_movhpd) || (opcode == NN_movlpd) || (opcode == NN_movntpd) || ((opcode >= NN_movsd) && (opcode <= NN_orpd)) || ((opcode >= NN_sqrtpd) && (opcode <= NN_subsd)) && ((opcode >= NN_unpckhpd) && (opcode <= NN_xorpd)) || ((opcode >= NN_movddup) && (opcode <= NN_movsxd)) || ((opcode >= NN_addsubpd) && (opcode <= NN_hsubps)) || (opcode == NN_fisttp) || ((opcode >= NN_psignb) && (opcode <= NN_psignd)) || ((opcode >= NN_pmulhrsw) && (opcode <= NN_phsubd)) || (opcode == NN_pfrcpv) || (opcode == NN_pfrsqrtv) || ((opcode >= NN_blendpd) && (opcode <= NN_insertps)) || (opcode == NN_pmaxsb) || (opcode == NN_pmaxsd) || (opcode == NN_pminsb) || (opcode == NN_pminsd) || ((opcode >= NN_pmovsxbw) && (opcode <= NN_pmovsxdq)) || (opcode == NN_pmuldq) || (opcode == NN_pmulld) || ((opcode >= NN_roundpd) && (opcode <= NN_roundss)) || (opcode == NN_movntsd) || (opcode == NN_movntss)); } // end of SMPInstr::MDAlwaysSignedDEF() bool SMPInstr::MDIsAddition(void) const { unsigned short opcode = this->SMPcmd.itype; bool FoundAddition = ((NN_adc == opcode) || (NN_add == opcode)); if (this->MDIsLoadEffectiveAddressInstr()) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); if (CurrRT->HasRightSubTree()) { CurrRT = CurrRT->GetRightTree(); FoundAddition = (SMP_ADD == CurrRT->GetOperator()); } } return FoundAddition; } // Is non-multiply arithmetic instruction that can possibly overflow? bool SMPInstr::MDIsOverflowingOpcode(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_adc == opcode) || (NN_add == opcode) || (NN_inc == opcode) || (NN_neg == opcode) || (NN_xadd == opcode)); } // Is non-multiply arithmetic instruction that can possibly underflow? bool SMPInstr::MDIsUnderflowingOpcode(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_dec == opcode) || (NN_sbb == opcode) || (NN_sub == opcode)); } // Is potentially benign overflow instruction? bool SMPInstr::MDIsMaybeBenignOverflowOpcode(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_adc == opcode) || (NN_add == opcode)); } // Is potentially benign underflow instruction? bool SMPInstr::MDIsMaybeBenignUnderflowOpcode(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_neg == opcode) || (NN_sbb == opcode) || (NN_sub == opcode)); } // Is definitely benign underflow instruction? // NOTE: Overlaps with MDIsMaybeBenignUnderflowOpcode(), so call this one first. bool SMPInstr::MDIsDefiniteBenignUnderflowOpcode(int &IdiomCode) { unsigned short opcode = this->SMPcmd.itype; // gcc use: sbb edx,edx as a tricky way to get all zeroes or all ones into edx. // (Some sort of saturation? Often treated as 0 or -1, i.e. it becomes SIGNED // even if it used to be UNSIGNED.) // The "underflow" on the subtraction is irrelevant and benign. bool benign = ((NN_sbb == opcode) && (this->SubtractsFromItself())); if (benign) { IdiomCode = 4; } return benign; } // Does a subtraction operator get applied to same left and right operands? bool SMPInstr::SubtractsFromItself(void) { bool SelfSubtract = false; size_t RTLCount = this->RTL.GetCount(); for (size_t index = 0; index < RTLCount; ++index) { SMPRegTransfer *CurrRT = this->RTL.GetRT(index); if ((CurrRT != NULL) && (CurrRT->HasRightSubTree())) { CurrRT = CurrRT->GetRightTree(); SMPoperator CurrOp = CurrRT->GetOperator(); if ((SMP_SUBTRACT_BORROW == CurrOp) || (SMP_SUBTRACT == CurrOp)) { if (!(CurrRT->HasRightSubTree())) { // NOTE: Must change this code when we build more precise SMP_SUBTRACT_BORROW RTL. op_t LeftOp = CurrRT->GetLeftOperand(); op_t RightOp = CurrRT->GetRightOperand(); SelfSubtract = IsEqOp(RightOp, LeftOp); } break; } } } return SelfSubtract; } // end of SMPInstr::SubtractsFromItself() // Does instruction subtract an immediate value that is often used in ASCII computations, // such as the ASCII code for '0', 'a', or 'A' ? bool SMPInstr::SubtractsImmedASCII(void) { return (this->MDIsMaybeBenignUnderflowOpcode() && (o_imm == this->AddSubSourceOp.type) && (('0' == this->AddSubSourceOp.value) || ('a' == this->AddSubSourceOp.value) || ('A' == this->AddSubSourceOp.value))); } // Does instruction compare a location to an immediate value that is often used in ASCII computations, // such as the ASCII code for '0' or '9', 'a' or 'A', 'z' or 'Z', or carriage return? #define SMP_ASCII_CARRIAGE_RETURN 13 bool SMPInstr::MDComparesImmedASCII(void) { bool ComparesToASCII = false; if (this->SMPcmd.itype == NN_cmp) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); if ((CurrRT != NULL) && (CurrRT->HasRightSubTree())) { CurrRT = CurrRT->GetRightTree(); if (!(CurrRT->HasRightSubTree())) { op_t LeftOp = CurrRT->GetLeftOperand(); op_t RightOp = CurrRT->GetRightOperand(); if ((o_imm == RightOp.type) && ((SMP_ASCII_CARRIAGE_RETURN == RightOp.value) || ('0' == RightOp.value) || ('9' == RightOp.value) || ('a' == RightOp.value) || ('z' == RightOp.value) || ('A' == RightOp.value) || ('Z' == RightOp.value))) { ComparesToASCII = true; } } } } return ComparesToASCII; } // end of SMPInstr::MDComparesImmedASCII() // Multiply by large constant; overflow is probably intentional. #define STARS_LARGE_UNSIGNED_MUL_CONSTANT_THRESHOLD 0x20000000 #define STARS_LARGE_SIGNED_MUL_CONSTANT_THRESHOLD 0x10000000 #define STARS_SMALL_SIGNED_MUL_CONSTANT_THRESHOLD ((int)(-STARS_LARGE_SIGNED_MUL_CONSTANT_THRESHOLD)) bool SMPInstr::IsMultiplyByLargeConstant(uval_t &ConstValue, unsigned short SignMask) { bool LargeConstFound = false; if (this->MDIsMultiply() && (SignMask != FG_MASK_INCONSISTENT_SIGN) && (SignMask != 0)) { set<DefOrUse, LessDefUse>::iterator UseIter; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { if (this->FindConstantValue(UseIter, ConstValue)) { if (FG_MASK_UNSIGNED == SignMask) { if (((uval_t) STARS_LARGE_UNSIGNED_MUL_CONSTANT_THRESHOLD) <= ConstValue) { LargeConstFound = true; break; } } else if (FG_MASK_SIGNED == SignMask) { int SignedConstValue = (int) ConstValue; if ( (((int) STARS_LARGE_SIGNED_MUL_CONSTANT_THRESHOLD) <= SignedConstValue) || (((int) STARS_SMALL_SIGNED_MUL_CONSTANT_THRESHOLD) >= SignedConstValue)) { LargeConstFound = true; break; } } } } } return LargeConstFound; } // end of SMPInstr::IsMultiplyByLargeConstant() // Subtraction of large constant; underflow is probably intentional. #define STARS_LARGE_UNSIGNED_SUB_CONSTANT_THRESHOLD 0x80000000 #define STARS_LARGE_SIGNED_SUB_CONSTANT_THRESHOLD 0x40000000 #define STARS_SMALL_SIGNED_SUB_CONSTANT_THRESHOLD ((int)(-STARS_LARGE_SIGNED_SUB_CONSTANT_THRESHOLD)) bool SMPInstr::IsSubtractionOfLargeConstant(uval_t &ConstValue, unsigned short SignMask) { bool LargeConstFound = false; if (this->MDIsUnderflowingOpcode() && (SignMask != FG_MASK_INCONSISTENT_SIGN) && (SignMask != 0)) { op_t SubtrahendOp = this->AddSubSourceOp; if (o_void != SubtrahendOp.type) { set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SubtrahendOp); if (this->FindConstantValue(UseIter, ConstValue)) { if (FG_MASK_UNSIGNED == SignMask) { if (((uval_t) STARS_LARGE_UNSIGNED_SUB_CONSTANT_THRESHOLD) <= ConstValue) { LargeConstFound = true; } } else if (FG_MASK_SIGNED == SignMask) { int SignedConstValue = (int) ConstValue; if ( (((int) STARS_LARGE_SIGNED_SUB_CONSTANT_THRESHOLD) <= SignedConstValue) || (((int) STARS_SMALL_SIGNED_SUB_CONSTANT_THRESHOLD) >= SignedConstValue)) { LargeConstFound = true; } } } } } return LargeConstFound; } // end of SMPInstr::IsSubtractionOfLargeConstant() // Subtraction of large constant; underflow is probably intentional. #define STARS_LARGE_UNSIGNED_ADD_CONSTANT_THRESHOLD 0x80000000 #define STARS_LARGE_SIGNED_ADD_CONSTANT_THRESHOLD 0x40000000 #define STARS_SMALL_SIGNED_ADD_CONSTANT_THRESHOLD ((int)(-STARS_LARGE_SIGNED_ADD_CONSTANT_THRESHOLD)) bool SMPInstr::IsAdditionOfLargeConstant(uval_t &ConstValue, unsigned short SignMask) { bool LargeConstFound = false; if (this->MDIsOverflowingOpcode() && (SignMask != FG_MASK_INCONSISTENT_SIGN) && (SignMask != 0)) { op_t AddendOp = this->AddSubSourceOp; if (o_void != AddendOp.type) { set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(AddendOp); if (this->FindConstantValue(UseIter, ConstValue)) { if (FG_MASK_UNSIGNED == SignMask) { if (((uval_t) STARS_LARGE_UNSIGNED_ADD_CONSTANT_THRESHOLD) <= ConstValue) { LargeConstFound = true; } } else if (FG_MASK_SIGNED == SignMask) { int SignedConstValue = (int) ConstValue; if ( (((int) STARS_LARGE_SIGNED_ADD_CONSTANT_THRESHOLD) <= SignedConstValue) || (((int) STARS_SMALL_SIGNED_ADD_CONSTANT_THRESHOLD) >= SignedConstValue)) { LargeConstFound = true; } } } } } return LargeConstFound; } // end of SMPInstr::IsAdditionOfLargeConstant() // MACHINE DEPENDENT: Is instruction a return instruction? bool SMPInstr::MDIsReturnInstr(void) const { return ((this->SMPcmd.itype == NN_retn) || (this->SMPcmd.itype == NN_retf)); } // MACHINE DEPENDENT: Is instruction a POP instruction? #define FIRST_POP_INST NN_pop #define LAST_POP_INST NN_popfq bool SMPInstr::MDIsPopInstr(void) const { return ((this->SMPcmd.itype >= FIRST_POP_INST) && (this->SMPcmd.itype <= LAST_POP_INST)); } // MACHINE DEPENDENT: Is instruction a PUSH instruction? #define FIRST_PUSH_INST NN_push #define LAST_PUSH_INST NN_pushfq bool SMPInstr::MDIsPushInstr(void) const { return ((this->SMPcmd.itype >= FIRST_PUSH_INST) && (this->SMPcmd.itype <= LAST_PUSH_INST)); } // MACHINE DEPENDENT: Is instruction an ENTER instruction? bool SMPInstr::MDIsEnterInstr(void) const { return ((this->SMPcmd.itype >= MD_FIRST_ENTER_INSTR) && (this->SMPcmd.itype <= MD_LAST_ENTER_INSTR)); } // MACHINE DEPENDENT: Is instruction a LEAVE instruction? bool SMPInstr::MDIsLeaveInstr(void) const { return ((this->SMPcmd.itype >= MD_FIRST_LEAVE_INSTR) && (this->SMPcmd.itype <= MD_LAST_LEAVE_INSTR)); } // MACHINE DEPENDENT: Is instruction a HALT instruction? bool SMPInstr::MDIsHaltInstr(void) const { return (NN_hlt == this->SMPcmd.itype); } #define MD_FIRST_COND_MOVE_INSTR NN_cmova #define MD_LAST_COND_MOVE_INSTR NN_fcmovnu // MACHINE DEPENDENT: Is instruction a conditional move? bool SMPInstr::MDIsConditionalMoveInstr(void) const { return ((this->SMPcmd.itype >= MD_FIRST_COND_MOVE_INSTR) && (this->SMPcmd.itype <= MD_LAST_COND_MOVE_INSTR)); } // MACHINE DEPENDENT: Is instruction any kind of move? bool SMPInstr::MDIsMoveInstr(void) const { return ((NN_mov == this->SMPcmd.itype) || (NN_movsx == this->SMPcmd.itype) || (NN_movzx == this->SMPcmd.itype) || this->MDIsConditionalMoveInstr()); } // MACHINE DEPENDENT: Do opcode/operands definitely indicate signed arithmetic? // Generally, this is only true for certain variants of multiplication and division. bool SMPInstr::MDIsSignedArithmetic(void) const { unsigned short opcode = this->SMPcmd.itype; if (NN_idiv == opcode) return true; if (NN_imul == opcode) { // If we discard the upper N bits of the multiplication result, then the // lower N bits are the same for signed and unsigned multiplication, and // gcc/g++ often use the IMUL opcode for both signed and unsigned multiplies // when only N bits of result are retained. Therefore, the SIGNED nature of // IMUL operands can only be inferred from the case in which 2N bits are kept. return (!(this->AreMultiplicationBitsDiscarded())); } else { // idiv and imul are only possible signed cases return false; } } // end of SMPInstr::MDIsSignedArithmetic() // MACHINE DEPENDENT: Is instruction a conditional jump based on an unsigned condition? bool SMPInstr::MDIsUnsignedBranch(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_ja == opcode) || (NN_jae == opcode) || (NN_jb == opcode) || (NN_jbe == opcode) || (NN_jna == opcode) || (NN_jnae == opcode) || (NN_jnb == opcode) || (NN_jnbe == opcode)); } // MACHINE DEPENDENT: Is instruction a conditional jump based on a signed condition? bool SMPInstr::MDIsSignedBranch(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_jg == opcode) || (NN_jge == opcode) || (NN_jl == opcode) || (NN_jle == opcode) || (NN_jng == opcode) || (NN_jnge == opcode) || (NN_jnl == opcode) || (NN_jnle == opcode) || (NN_js == opcode) || (NN_jns == opcode)); } // MACHINE DEPENDENT: Is instruction a boolean set based on an unsigned condition? bool SMPInstr::MDIsUnsignedSetValue(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_seta == opcode) || (NN_setae == opcode) || (NN_setb == opcode) || (NN_setbe == opcode) || (NN_setna == opcode) || (NN_setnae == opcode) || (NN_setnb == opcode) || (NN_setnbe == opcode)); } // MACHINE DEPENDENT: Is instruction a boolean set based on a signed condition? bool SMPInstr::MDIsSignedSetValue(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_setg == opcode) || (NN_setge == opcode) || (NN_setl == opcode) || (NN_setle == opcode) || (NN_setng == opcode) || (NN_setnge == opcode) || (NN_setnl == opcode) || (NN_setnle == opcode) || (NN_sets == opcode) || (NN_setns == opcode)); } // MACHINE DEPENDENT: Is instruction a boolean set based on any condition? bool SMPInstr::MDIsAnySetValue(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_seta <= opcode) && (NN_setz >= opcode)); } // Is kind of shift or rotate that is used in hash functions #define STARS_HASH_SHIFT_THRESHOLD 4 bool SMPInstr::MDIsHashingArithmetic(void) const { bool FoundHashShift = false; unsigned short opcode = this->SMPcmd.itype; // We are looking for shifts or rotates in the leftward direction. if ((opcode == NN_rcl) || (opcode == NN_rol) || (opcode == NN_shl) || (opcode == NN_shld)) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); assert(CurrRT->HasRightSubTree()); CurrRT = CurrRT->GetRightTree(); op_t ShiftCountOp = CurrRT->GetRightOperand(); if (o_imm == ShiftCountOp.type) { uval_t CountValue = ShiftCountOp.value; FoundHashShift = (CountValue >= STARS_HASH_SHIFT_THRESHOLD); } } return FoundHashShift; } // end of SMPInstr::MDIsHashingArithmetic() // Detect comparison of non-immediate to immediate. Return the two operands if true. bool SMPInstr::MDIsCompareToPositiveConstant(op_t &NonConstOperand, uval_t &ConstValue) const { bool CompareToConst = false; if (NN_cmp == this->SMPcmd.itype) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); assert(CurrRT->HasRightSubTree()); CurrRT = CurrRT->GetRightTree(); assert(!(CurrRT->HasRightSubTree())); op_t LeftOp = CurrRT->GetLeftOperand(); op_t RightOp = CurrRT->GetRightOperand(); if (o_imm == RightOp.type) { CompareToConst = true; ConstValue = RightOp.value; NonConstOperand = LeftOp; } else if (o_imm == LeftOp.type) { // rare to see immediate first CompareToConst = true; ConstValue = LeftOp.value; NonConstOperand = RightOp; } } return CompareToConst; } // end of SMPInstr::MDIsCompareToPositiveConstant() bool SMPInstr::MDIsSubtractionOfConstant(op_t &NonConstOperand, uval_t &ConstValue) const { bool SubtractOfConst = false; SMPRegTransfer *CurrRT = this->RTL.GetRT(0); if (CurrRT->HasRightSubTree()) { CurrRT = CurrRT->GetRightTree(); SMPoperator CurrOp = CurrRT->GetOperator(); if (SMP_SUBTRACT == CurrOp) { assert(!(CurrRT->HasRightSubTree())); op_t LeftOp = CurrRT->GetLeftOperand(); op_t RightOp = CurrRT->GetRightOperand(); if (o_imm == RightOp.type) { SubtractOfConst = true; ConstValue = RightOp.value; NonConstOperand = LeftOp; } } else if (this->MDIsLoadEffectiveAddressInstr() && (SMP_ADD == CurrOp)) { // We could have an addition of a negative constant via an lea opcode, // e.g. lea ecx,[eax-48] will look like ecx := eax+(-48) in the RTL. if (!(CurrRT->HasRightSubTree())) { op_t LeftOp = CurrRT->GetLeftOperand(); op_t RightOp = CurrRT->GetRightOperand(); if (o_imm == RightOp.type) { ConstValue = RightOp.value; int SignedConstValue = (int) ConstValue; if (0 > SignedConstValue) { SubtractOfConst = true; NonConstOperand = LeftOp; ConstValue = (uval_t)(-SignedConstValue); // make +(-x) into -(+x) } } } } } return SubtractOfConst; } // end of SMPInstr::MDIsSubtractionOfConstant() // is AND operation that masks off lower BytesMasked bytes bool SMPInstr::MDIsSubregMaskInst(size_t &BytesMasked) { bool MaskingFound = false; if (this->MDIsBitwiseAndOpcode()) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); if (CurrRT->HasRightSubTree()) { CurrRT = CurrRT->GetRightTree(); SMPoperator CurrOp = CurrRT->GetOperator(); assert(SMP_BITWISE_AND == CurrOp); op_t RightOp = CurrRT->GetRightOperand(); if (o_imm == RightOp.type) { uval_t MaskValue = RightOp.value; if (0x01000000 > MaskValue) { MaskingFound = true; if (0x100 > MaskValue) { BytesMasked = 1; } else if (0x10000 > MaskValue) { BytesMasked = 2; } else { BytesMasked = 3; } } } } } return MaskingFound; } // MACHINE DEPENDENT: Does instruction use a callee-saved register? bool SMPInstr::MDUsesCalleeSavedReg(void) { set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) { op_t CurrOp = CurrUse->GetOp(); if (CurrOp.is_reg(MD_FRAME_POINTER_REG) || CurrOp.is_reg(R_si) || CurrOp.is_reg(R_di) || CurrOp.is_reg(R_bx)) { return true; } } return false; } // end of SMPInstr::MDUsesCalleeSavedReg() // Is the instruction a register to register copy of a stack pointer or frame pointer // into a general purpose register (which mmStrata will now need to track as a stack // relative pointer)? bool SMPInstr::MDIsStackPointerCopy(bool UseFP) { // OptType 3 indicates a move instruction // The lea instruction can perform three operand arithmetic, e.g. // lea ebx,[esp+12] is just ebx:=esp+12, so it is a stack pointer copy. if (((this->OptType == 3) || (NN_lea == this->SMPcmd.itype)) && (this->GetFirstDef()->GetOp().type == o_reg) && (!(this->GetFirstDef()->GetOp().is_reg(R_sp))) && (!(this->HasSourceMemoryOperand()))) { // reg to reg move if (UseFP) { if (this->GetFirstUse()->GetOp().is_reg(MD_FRAME_POINTER_REG)) // Move of base pointer EBP into a general register return true; else if ((this->GetFirstUse()->GetOp().is_reg(MD_STACK_POINTER_REG)) && !(this->GetFirstDef()->GetOp().is_reg(MD_FRAME_POINTER_REG))) // Move of ESP into something besides a base pointer return true; } else if (this->GetFirstUse()->GetOp().is_reg(MD_STACK_POINTER_REG)) { // Move of ESP into a register; no base pointer used in this function return true; } } return false; } // end of SMPInstr::MDIsStackPointerCopy() // Does any RTL fit the alloca() pattern: stack_pointer -= non-immediate-operand bool SMPInstr::HasAllocaRTL(void) { bool FoundAlloca = false; size_t RTLCount = this->RTL.GetCount(); size_t RTLIndex; for (RTLIndex = 0; RTLIndex < RTLCount; ++RTLIndex) { SMPRegTransfer *CurrRT = this->RTL.GetRT(RTLIndex); if (CurrRT->IsAllocaRTL()) { FoundAlloca = true; break; } } return FoundAlloca; } // end of SMPInstr::HasAllocaRTL() // Determine if the instruction saves or restores a pointer into the stack frame. // If it saves a stack pointer, set Save to true, set the StackDelta saved, and set // the operand that received the saved stack pointer into CopyOp. and return true. // If it restores a stack pointer, set Save to false, set CopyOp to the operand that // held the value being restored, set RestoreOp to the stack pointer or frame pointer // register (whichever was restored), leave StackDelta alone for later computation // based on reaching definitions, and return true. // For most instructions, no save or restore of a stack pointer, so return false. bool SMPInstr::MDIsStackPtrSaveOrRestore(bool UseFP, sval_t FPDelta, bool &Save, sval_t &StackDelta, op_t &CopyOp, bool &Error) { bool StackPointerSaveOrRestore; size_t RTLCount = this->RTL.GetCount(); size_t RTLIndex; op_t TempOp; int BaseReg, IndexReg, CopyReg; ushort Scale; ea_t offset; SMPoperator CurrOper; bool LookUpStackDelta; // Get stack delta from reaching defs for TempOp sval_t DeltaAdjust; // add to StackDelta after computing from reaching defs, e.g. lea esp,[ecx-4] get TempOp of ecx // and DeltaAdjust of -4 Error = false; for (RTLIndex = 0; RTLIndex < RTLCount; ++RTLIndex) { bool FPRestore = false; // frame pointer is restored bool SPRestore = false; // stack pointer is restored StackPointerSaveOrRestore = false; // default unless we detect a save or restore of the stack or frame pointer TempOp = InitOp; LookUpStackDelta = false; DeltaAdjust = 0; Save = false; // default unless we detect a stack pointer save // The stack alignment instructions (SP := SP bitwise_and immediate_value) // look like something that needs to be processed here, but we always ignore // these instructions. They have a variable effect on the stack pointer, from zero // to -15 delta, but we assume that the delta is zero. This works for us because // no stack accesses will occur into the padding region. // Also, any instruction that definitely does not restore the stack pointer or // frame pointer from an arbitrary register or memory location, e.g. a leave instruction // in x86 CPUs, is already handled in normal stack delta computations and needs // no lookups from reaching defs, etc. if (this->IsStackAlignmentInst() || this->MDIsLeaveInstr() || this->MDIsFrameAllocInstr()) { break; // exit and return false } SMPRegTransfer *CurrRT = this->RTL.GetRT(RTLIndex); CurrOper = CurrRT->GetOperator(); if (SMP_ASSIGN != CurrOper) { break; // not a regular RTL } op_t LeftOp = CurrRT->GetLeftOperand(); if (LeftOp.is_reg(MD_STACK_POINTER_REG)) { SPRestore = true; // temporary; might just be a push or pop RTL, etc., in which case we will reset. } else if (UseFP && LeftOp.is_reg(MD_FRAME_POINTER_REG)) { FPRestore = true; // likewise temporary } if (!(SPRestore || FPRestore)) { #if 0 if (LeftOp.is_reg(MD_FLAGS_REG)) { break; // No point in looking for a save into the flags register } #endif Save = true; // Maybe; keep looking for save } // If we are assigning to the stack pointer reg or the frame pointer reg, we need to analyze the right // hand side of the RTL to see if it is a stack/frame pointer value, and not a simple push, pop, etc. if (!(CurrRT->HasRightSubTree())) { // Simple assignment. op_t RightOp = CurrRT->GetRightOperand(); if ((o_reg <= RightOp.type) && (o_displ >= RightOp.type)) { // register or memory if (RightOp.is_reg(MD_STACK_POINTER_REG)) { // Stack pointer reg is being saved. Save = true; StackDelta = this->GetStackPtrOffset(); // LeftOp := SP, so saved delta is just current delta CopyOp = LeftOp; StackPointerSaveOrRestore = true; FPRestore = false; // treat FP := SP as a save of SP rather than a restoration of FP break; } else if (!SPRestore && UseFP && RightOp.is_reg(MD_FRAME_POINTER_REG)) { // Frame pointer is being saved Save = true; StackDelta = FPDelta; CopyOp = LeftOp; StackPointerSaveOrRestore = true; break; } else if (SPRestore || FPRestore) { // stack or frame pointer is being restored; leave Save=false and set other outgoing arguments. TempOp = RightOp; CopyOp = RightOp; StackPointerSaveOrRestore = true; LookUpStackDelta = true; } else { // RightOp is register or non-stack-pointer memory expr; either might hold stack delta TempOp = RightOp; CopyOp = LeftOp; LookUpStackDelta = true; // See if RightOp is holding a stack delta StackPointerSaveOrRestore = true; // Maybe; flag tells us to keep looking } } else { if (SPRestore || FPRestore) { SMP_msg("ERROR: Invalid operand type for assignment to stack or frame pointer at %lx\n", (unsigned long) this->GetAddr()); } StackPointerSaveOrRestore = false; break; } } else { // we have a right subtree in the CurrRT SMPRegTransfer *RightRT = CurrRT->GetRightTree(); // In order to have a right subtree, we must have something like: // lea esp,[ecx-4] which produces the RTL: esp := ecx - 4 // We should consider any other RTL structure besides a basic addition or // subtraction on the right subtree to be invalid. CurrOper = RightRT->GetOperator(); if ((SMP_ADD == CurrOper) || (SMP_SUBTRACT == CurrOper)) { op_t RightLeftOp = RightRT->GetLeftOperand(); if (o_reg == RightLeftOp.type) { if (RightRT->HasRightSubTree()) { // Complex RTL such as lea esp,[ebx+ecx*4] ; cannot analyze StackPointerSaveOrRestore = false; } else { op_t RightRightOp = RightRT->GetRightOperand(); if (o_imm != RightRightOp.type) { // Complex RTL such as lea esp,[ebx+ecx] ; cannot analyze StackPointerSaveOrRestore = false; } else { TempOp = RightLeftOp; DeltaAdjust = (sval_t) RightRightOp.value; if (SMP_SUBTRACT == CurrOper) { // Negate the stack delta adjustment, e.g. lea esp,[ecx-4] needs DeltaAdjust of -4, not 4. DeltaAdjust = (0 - DeltaAdjust); } LookUpStackDelta = true; StackPointerSaveOrRestore = true; if (SPRestore || FPRestore) { CopyOp = RightLeftOp; } else { CopyOp = LeftOp; } } } } else { // weird RTL; LeftOp := (MemoryOp OPER ???) StackPointerSaveOrRestore = false; } } else { // not ADD or SUBTRACT StackPointerSaveOrRestore = false; } } if (LookUpStackDelta) { bool StackAccess = false; bool NonStackMemAccess = false; // We need to set StackDelta based on the reaching defs for TempOp // A reg is probably a general register, but could have lea ebx,[esp+4] so it could be stack or frame pointer. if (TempOp.is_reg(MD_STACK_POINTER_REG)) { // Weed out RTs that increment or decrement the stack pointer, e.g. SP := SP -4. // These are not the kind of "save" or "restore" RTs that we are tracking. if (CopyOp.is_reg(MD_STACK_POINTER_REG)) { StackPointerSaveOrRestore = false; SPRestore = false; FPRestore = false; Save = false; } else { StackDelta = this->GetStackPtrOffset(); StackDelta += DeltaAdjust; LookUpStackDelta = false; // just got it; no need for reaching defs StackPointerSaveOrRestore = true; } } else if (UseFP && TempOp.is_reg(MD_FRAME_POINTER_REG)) { StackDelta = FPDelta; StackDelta += DeltaAdjust; LookUpStackDelta = false; // just got it; no need for reaching defs StackPointerSaveOrRestore = true; } else if (o_reg == TempOp.type) { // general reg, not frame or stack pointer reg CopyReg = TempOp.reg; } else { MDExtractAddressFields(TempOp, BaseReg, IndexReg, Scale, offset); CopyReg = BaseReg; bool IndexedAccess = ((R_none != BaseReg) && (R_none != IndexReg)); if (IndexedAccess) { StackPointerSaveOrRestore = false; // Cannot analyze indexed accesses into the stack } else if (MDIsStackPtrReg(BaseReg, UseFP)) { StackAccess = true; } else { // memory expr that is not stack or frame pointer NonStackMemAccess = true; // something like [ecx] might actually turn out to be stack access DeltaAdjust = (sval_t) TempOp.addr; // get normalized delta from addr field } } if (StackPointerSaveOrRestore && LookUpStackDelta) { op_t FindOp = InitOp; if (StackAccess) { FindOp = TempOp; } else { FindOp.type = o_reg; FindOp.reg = CopyReg; } if (this->GetBlock()->GetFunc()->IsInStackPtrCopySet(FindOp)) { // Screened out time wasters that are not in copy set; now, // look up reaching defs. // We need to find out which are the reaching definitions for the FindOp at the current InstAddr. this->GetBlock()->GetFunc()->ComputeTempReachingDefs(FindOp, this->GetAddr()); this->GetBlock()->GetFunc()->ComputeTempStackDeltaReachesList(FindOp); // See if TempStackDeltaReachesList has a consistent delta value. StackPointerSaveOrRestore = this->GetBlock()->GetFunc()->FindReachingStackDelta(StackDelta); // consistent SavedDelta value across entire list StackDelta += DeltaAdjust; if (StackPointerSaveOrRestore && NonStackMemAccess) { // We have something like [ecx] or [ecx+DeltaAdjust]. It turns out that // ECX has a copy of the stack pointer or frame pointer in it, but that // does not mean that the memory location [ecx] has a copy of a stack or // frame pointer. We need to look up the normalized stack address [esp+StackDelta] // in the StackPtrCopySet just like we did for ECX before we conclude that a // stack pointer save or restore is happening. FindOp = InitOp; FindOp.type = o_displ; FindOp.reg = MD_STACK_POINTER_REG; FindOp.addr = StackDelta; if (this->GetBlock()->GetFunc()->IsInStackPtrCopySet(FindOp)) { // Screened out time wasters that are not in copy set; now, // look up reaching defs. // We need to find out which are the reaching definitions for the FindOp at the current InstAddr. this->GetBlock()->GetFunc()->ComputeTempReachingDefs(FindOp, this->GetAddr()); this->GetBlock()->GetFunc()->ComputeTempStackDeltaReachesList(FindOp); // See if TempStackDeltaReachesList has a consistent delta value. StackPointerSaveOrRestore = this->GetBlock()->GetFunc()->FindReachingStackDelta(StackDelta); // consistent SavedDelta value across entire list // StackPointerSaveOrRestore will now be true only if [ecx] pointed to a saved stack or frame pointer with consistent delta. } else { // E.g. [ecx] pointed to stack location that was not holding a saved stack or frame pointer. StackPointerSaveOrRestore = false; } } } else { StackPointerSaveOrRestore = false; // reset, not in stack pointer copy set } } } // end if (LookupStackDelta) if (!StackPointerSaveOrRestore && !Save && (SPRestore || FPRestore)) { // Any restore that could not be analyzed is an error. Error = true; break; // error exit } else if (StackPointerSaveOrRestore) { if (FPRestore) { // If we succeeded in looking up a stack delta that goes into the frame pointer reg, // then we want to consider this instruction to be a save of a stack delta into // a register (which happens to be the frame pointer reg in this case). FPRestore = false; Save = true; } break; // assume only one save or restore in an instruction; exit with success } } // end for all RTs in the RTL return StackPointerSaveOrRestore; } // end of SMPInstr::MDIsStackPtrSaveOrRestore() // If call instruction is to malloc(), set the DEF register EAX type to // HEAPPTR and return true. bool SMPInstr::MDFindMallocCall(op_t TargetOp) { bool changed = false; func_t *TargetFunc = get_func(TargetOp.addr); if (TargetFunc) { char FuncName[MAXSTR]; get_func_name(TargetFunc->startEA, FuncName, sizeof(FuncName) - 1); if (0 == strcmp("malloc", FuncName)) { // NOTE: Some compilers might call it __malloc ; make this more robust !!! #if SMP_VERBOSE_FIND_POINTERS SMP_msg("Found call to malloc at %x\n", this->addr); #endif op_t SearchOp = InitOp; SearchOp.type = o_reg; SearchOp.reg = R_ax; set<DefOrUse, LessDefUse>::iterator EAXDEF; EAXDEF = this->SetDefType(SearchOp, HEAPPTR); int SSANum = EAXDEF->GetSSANum(); changed = true; if (this->BasicBlock->IsLocalName(SearchOp)) { (void) this->BasicBlock->PropagateLocalDefType(SearchOp, HEAPPTR, this->GetAddr(), SSANum, false); } else { // global name this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false (void) this->BasicBlock->PropagateGlobalDefType(SearchOp, HEAPPTR, SSANum, false); } } // end if "malloc" } // end if (TargetFunc) return changed; } // end of SMPInstr::MDFindMallocCall() // Is instruction a branch (conditional or unconditional) to a // code target that is not in the current chunk? bool SMPInstr::IsBranchToFarChunk(void) { if (this->IsFarBranchComputed()) { // answer is cached return this->IsBranchesToFarChunk(); } func_t *CurrChunk = get_fchunk(this->address); bool FarBranch = false; if ((JUMP | COND_BRANCH) & this->GetDataFlowType()) { // Instruction is a direct branch, conditional or unconditional if (this->NumUses() > 0) { set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) { op_t JumpTarget = CurrUse->GetOp(); if ((o_near == JumpTarget.type) || (o_far == JumpTarget.type)) { // Branches to a code address // stdclib sometimes has jumps to zero and calls to zero. These are dead code. if (0 != JumpTarget.addr) { func_t *TargetChunk = get_fchunk(JumpTarget.addr); // Is target address within the same chunk as the branch? FarBranch = (NULL == TargetChunk) || (CurrChunk->startEA != TargetChunk->startEA); if (FarBranch) { this->FarBranchTarget = JumpTarget.addr; } } } } } } if (FarBranch) { this->SetBranchesToFarChunk(); } this->SetFarBranchComputed(); return FarBranch; } // end of SMPInstr::IsBranchToFarChunk() set<DefOrUse, LessDefUse>::iterator SMPInstr::SetUseSSA(op_t CurrOp, int SSASub) { return this->Uses.SetSSANum(CurrOp, SSASub); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefSSA(op_t CurrOp, int SSASub) { return this->Defs.SetSSANum(CurrOp, SSASub); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetUseType(op_t CurrOp, SMPOperandType CurrType) { return this->Uses.SetType(CurrOp, CurrType, this); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefType(op_t CurrOp, SMPOperandType CurrType) { return this->Defs.SetType(CurrOp, CurrType, this); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefMetadata(op_t CurrOp, SMPMetadataType Status) { return this->Defs.SetMetadata(CurrOp, Status); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefIndWrite(op_t CurrOp, bool IndWriteFlag) { return this->Defs.SetIndWrite(CurrOp, IndWriteFlag); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetUseNoTruncate(op_t CurrOp, bool NoTruncFlag) { return this->Uses.SetNoTruncation(CurrOp, NoTruncFlag); }; set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefNoOverflow(op_t DefOp, bool NoOverflowFlag) { return this->Defs.SetNoOverflow(DefOp, NoOverflowFlag); }; // Set the DeadRegsBitmap entry for Regnum. void SMPInstr::SetRegDead(size_t RegNum) { this->DeadRegsBitmap.set(RegNum); return; } // Analyze the instruction and its operands. void SMPInstr::Analyze(void) { bool DebugFlag = false; if (0x8049b00 == this->address) { // Setting up breakpoint line. DebugFlag = true; } // Fill cmd structure with disassembly of instr if (!SMPGetCmd(this->address, this->SMPcmd, this->features)) return; unsigned short opcode = this->SMPcmd.itype; // Record what type of instruction this is, simplified for the needs // of data flow and type analysis. this->type = DFACategory[opcode]; // Record optimization category. this->OptType = OptCategory[opcode]; if ((NN_int == opcode) || (NN_into == opcode) || (NN_int3 == opcode)) { this->SetInterrupt(); } else { this->ResetInterrupt(); } // Fix the IDA Pro mistakes in the operand list. this->MDFixupIDAProOperandList(); // See if instruction is an ASM idiom for clearing a register. if ((NN_xor == opcode) || (NN_lea == opcode)) { ushort FirstReg; if (o_reg == this->SMPcmd.Operands[0].type) { FirstReg = this->SMPcmd.Operands[0].reg; op_t SecondOpnd = this->SMPcmd.Operands[1]; if (NN_xor == opcode) { // Check for xor of reg with itself if (SecondOpnd.is_reg(FirstReg)) { this->SetRegClearIdiom(); } } else { // must be lea // check for lea reg,[nobasereg+nonindexreg+0] if ((SecondOpnd.type >= o_mem) && (SecondOpnd.type <= o_displ)) { int BaseReg, IndexReg; ushort ScaleFactor; ea_t Offset; MDExtractAddressFields(SecondOpnd, BaseReg, IndexReg, ScaleFactor, Offset); if ((R_none == BaseReg) && (R_none == IndexReg) && (0 == Offset)) { this->SetRegClearIdiom(); } } } } } // See if instruction is simple nop or ASM idiom for nop. if (this->MDIsNop()) { this->SetNop(); } // Build the DEF and USE lists for the instruction. this->FindMemOps(); this->BuildSMPDefUseLists(); // Determine whether the instruction is a jump target by looking // at its cross references and seeing if it has "TO" code xrefs. SMP_xref_t xrefs; for (bool ok = xrefs.SMP_first_to(this->address, XREF_FAR); ok; ok = xrefs.SMP_next_to()) { if ((xrefs.GetFrom() != 0) && (xrefs.GetIscode())) { this->SetJumpTarget(); break; } } // If instruction is a call or indirect call, see if a call target has been recorded // by IDA Pro. if (this->GetDataFlowType() == INDIR_CALL) { for (bool ok = xrefs.SMP_first_from(this->address, XREF_ALL); ok; ok = xrefs.SMP_next_from()) { if ((xrefs.GetTo() != 0) && (xrefs.GetIscode())) { // Found a code target, with its address in xrefs.to if (xrefs.GetTo() == (this->address + this->GetCmd().size)) { // A call instruction will have two targets: the fall through to the // next instruction, and the called function. We want to find // the called function. continue; } // We found a target, not the fall-through. this->CallTarget = xrefs.GetTo(); SMP_msg("Found indirect call target %lx at %lx\n", (unsigned long) xrefs.GetTo(), (unsigned long) this->address); break; } } // end for all code xrefs if (BADADDR == this->CallTarget) { SMP_msg("WARNING: Did not find indirect call target at %lx\n", (unsigned long) this->address); } } // end if INDIR_CALL else if (this->GetDataFlowType() == CALL) { set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) { optype_t OpType = CurrUse->GetOp().type; if ((OpType == o_near) || (OpType == o_far)) { this->CallTarget = CurrUse->GetOp().addr; } } if (BADADDR == this->CallTarget) { SMP_msg("ERROR: Target not found for direct call at %lx\n", (unsigned long) this->address); } } if (DebugFlag) { SMP_msg("Analyzed debug instruction at %lx\n", (unsigned long) this->address); } return; } // end of SMPInstr::Analyze() // Analyze the floating point NOP marker instruction at the top of the function. void SMPInstr::AnalyzeMarker(void) { // Fill member variable SMPcmd structure with disassembly of instr (void) memset(&(this->SMPcmd), 0, sizeof(this->SMPcmd)); this->SMPcmd.itype = NN_fnop; this->SMPcmd.size = 1; this->SMPcmd.ea = this->address; // Set the instr disassembly text. DisAsmText.SetMarkerInstText(this->GetAddr()); // Record what type of instruction this is, simplified for the needs // of data flow and type analysis. this->type = DFACategory[this->SMPcmd.itype]; // Record optimization category. this->OptType = OptCategory[this->SMPcmd.itype]; return; } // end of SMPInstr::AnalyzeMarker() // Detect oddities of call instructions, such as pseudo-calls that are // actually jumps within a function void SMPInstr::AnalyzeCallInst(ea_t FirstFuncAddr, ea_t LastFuncAddr) { if (BADADDR != this->CallTarget) { if (this->CallTarget == FirstFuncAddr) { this->SetDirectRecursiveCall(); } else { this->ResetDirectRecursiveCall(); if ((this->CallTarget > FirstFuncAddr) && (this->CallTarget < LastFuncAddr)) { this->SetCallUsedAsJump(); this->type = JUMP; } else { this->ResetCallUsedAsJump(); } } } return; } // end of SMPInstr::AnalyzeCallInst() sval_t SMPInstr::AnalyzeStackPointerDelta(sval_t IncomingDelta, sval_t PreAllocDelta) { uint16 InstType = this->SMPcmd.itype; sval_t InstDelta = StackAlteration[InstType]; SMPitype FlowType = this->GetDataFlowType(); bool TailCall = this->IsTailCall(); if (this->IsCallUsedAsJump() || this->MDIsInterruptCall() || this->IsCondTailCall()) { // Call is used within function as a jump. Happens when setting up // thunk offsets, for example; OR, call is an interrupt call, in which // the interrupt return cleans up the stack, leaving a delta of zero, but // we do not have the system call code to analyze, OR, the call is a conditional // jump to another function (conditional tail call), in which case the current // function must have a return statement to fall into which will clean up the // only thing left on the stack (the return address) and the conditional jump // has no effect on the stack pointer. ; // leave InstDelta equal to negative or zero value from StackAlterationTable[] } else if (this->IsRecursiveCall()) { // We don't have the net stack delta for our own function yet, so we cannot // look it up. We must assume that each call has no net effect on the stack delta. // Alternatively, we could call this->GetBlock()->GetFunc()->GetStackDeltaForCallee() as below. InstDelta = 0; } else if (this->IsAllocaCall()) { InstDelta = STARS_DEFAULT_ALLOCA_SIZE; } else if ((CALL == FlowType) || (INDIR_CALL == FlowType) || TailCall) { // A real call instruction, which pushes a return address on the stack, // not a call used as a branch within the function. A return instruction // will usually cancel out the stack push that is implicit in the call, which // means that the function will have a net stack ptr delta of +4, which will // cancel out the -4 value of the call instruction and set the delta to zero. // However, this is not true in all cases, so we get the net stack ptr delta // directly from the called function unless it is an unresolved indirect call, // in which case we assume +4. !!!!****!!!! In the future, we could analyze // the code around an unresolved indirect call to see if it seems to be // removing items left on the stack by the callee. // SPECIAL CASE: A jump used as a tail call will have a stack ptr effect that is equal // to the net stack ptr effect of its target function, usually +4, whereas a jump // would otherwise have a net stack ptr effect of 0. ea_t CalledFuncAddr = this->GetCallTarget(); if ((BADADDR == CalledFuncAddr) || (0 == CalledFuncAddr)) { InstDelta = 0; } else { // We have a call target SMPFunction *CalleeFunc = this->GetBlock()->GetFunc()->GetProg()->FindFunction(CalledFuncAddr); sval_t AdjustmentDelta; if (CalleeFunc) { if (!CalleeFunc->HasSTARSStackPtrAnalysisCompleted()) { // Phase ordering issue in the call graph. A mutually recursive clique of functions has to // be broken by starting processing somewhere, and all callees cannot be processed before // we start. If we got our stack down to zero and then made a tail call, then we have to assume // that the callee will use our return address, so we assume the default stack delta. If not a // tail call, we ask our function to see if the information is available from IDA Pro analyses, // or if it can be inferred from the fact that the call is followed by a stack adjustment. if (TailCall) { InstDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA; SMP_msg("WARNING: Callee stack ptr analysis not yet performed at tail call inst %lx ; normal delta assumed\n", (unsigned long) this->GetAddr()); } else { AdjustmentDelta = this->GetBlock()->GetFunc()->GetStackDeltaForCallee(CalledFuncAddr); InstDelta += AdjustmentDelta; SMP_msg("WARNING: Callee stack ptr analysis not yet performed at inst %lx ; stack adjustment used\n", (unsigned long) this->GetAddr()); } } else if (!CalleeFunc->StackPtrAnalysisSucceeded()) { // Callee analyses were done, but they failed. In order to proceed, we have to assume // the same situation as we just did in the case where analyses have not been performed. SMP_msg("WARNING: Callee stack ptr analysis failed at inst %lx ; normal delta assumed\n", (unsigned long) this->GetAddr()); if (TailCall) { InstDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA; } else { AdjustmentDelta = this->GetBlock()->GetFunc()->GetStackDeltaForCallee(this->GetAddr()); InstDelta += AdjustmentDelta; } } else { // Callee's analyses have succeeded, so get delta straight from callee. InstDelta += CalleeFunc->GetNetStackPtrDelta(); } } else { #if 0 SMP_msg("ERROR: SMPInstr::AnalyzeStackPointerDelta failed to find func at %lx in inst %lx\n", (unsigned long) CalledFuncAddr, (unsigned long) this->GetAddr()); InstDelta = SMP_STACK_DELTA_ERROR_CODE; #else SMP_msg("ERROR: SMPInstr::AnalyzeStackPointerDelta failed to find func at %lx in inst %lx\n", (unsigned long) CalledFuncAddr, (unsigned long) this->GetAddr()); if (TailCall) { InstDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA; } else { InstDelta = 0; } #endif } } } // end CALL or INDIR_CALL or TailCall case else if (1 == InstDelta) { // value of 1 is trigger to investigate the RTL for the // true value, which cannot be found simply by table lookup // In the special case of an x86 LEAVE instruction, the effect // on the stack pointer is to deallocate the local frame size, // plus pop the saved frame pointer into EBP. Helper functions // need to know whether to look for this special case. bool IsLeaveInstr = this->MDIsLeaveInstr(); InstDelta = this->RTL.TotalStackPointerAlteration(IsLeaveInstr, IncomingDelta, PreAllocDelta); } return InstDelta; } // end of SMPInstr::AnalyzeStackPointerDelta() // Total the stack adjustment bytes, as happens after a call to a function that leaves // outgoing args on the stack or swallows incoming args from the stack. sval_t SMPInstr::FindStackAdjustment(void) { uint16 InstType = this->SMPcmd.itype; sval_t InstDelta = StackAlteration[InstType]; if (1 == InstDelta) { // value of 1 is trigger to investigate the RTL for the // true value, which cannot be found simply by table lookup // In the special case of an x86 LEAVE instruction, the effect // on the stack pointer is to deallocate the local frame size, // plus pop the saved frame pointer into EBP. Helper functions // need to know whether to look for this special case. bool IsLeaveInstr = this->MDIsLeaveInstr(); if (!IsLeaveInstr) { InstDelta = this->RTL.TotalStackPointerAlteration(IsLeaveInstr, 0, 0); } else { InstDelta = 0; // LEAVE is not the kind of instr we are looking for } } return InstDelta; } // end of SMPInstr::FindStackAdjustment() // Normalize stack operands to have a displacement from the stack pointer value on entry to the function, // rather than the current stack pointer value. // UseFP indicates we are using a frame pointer in the function. // FPDelta holds the stack delta (normalized) for the frame pointer. // DefOp comes in with the operand to be normalized, and contains the normalized operand upon return. // Return true if operand is a register or stack location, false otherwise (true => include in data flow analysis sets and SSA.) bool SMPInstr::MDComputeNormalizedDataFlowOp(bool UseFP, sval_t FPDelta, op_t &DefOp) { if (o_reg == DefOp.type) { return true; } else if (MDIsStackAccessOpnd(DefOp, UseFP)) { op_t OldOp = DefOp; int SignedOffset = (int) DefOp.addr; sval_t NormalizedDelta; if (DefOp.hasSIB) { // We must deal with a potentially indexed memory expression. We want to // normalize two different cases here: e.g. [esp+ebx+4] will become [esp+ebx-24] // and [ebp+ebx-8] will become [esp+ebx-12] after normalization. A wrinkle // on the second case is when the base register and index register are swapped // in the SIB byte, and we make [ebx+ebp-4] into [esp+ebx-12], which involves // correcting the index/base reg order in the SIB, because an index reg of ESP // is the SIB encoding for "no index register" and we cannot leave it like that. int BaseReg = sib_base(DefOp); int IndexReg = (int) sib_index(DefOp); if (X86_STACK_POINTER_REG == IndexReg) // signifies no index register IndexReg = R_none; if (BaseReg == X86_STACK_POINTER_REG) { // We probably have an indexed ESP-relative operand. // We leave the sib byte alone and normalize the offset. NormalizedDelta = this->GetStackPtrOffset() + (sval_t) SignedOffset; } else { // Must be EBP-relative. NormalizedDelta = FPDelta + (sval_t) SignedOffset; // Unfortunately, when we are dealing with a SIB byte in the opcode, we cannot // just say DefOp.reg = MD_STACK_POINTER_REG to convert from the frame pointer // to the stack pointer. Instead, we have to get into the nasty machine code // level and change the SIB bits that specify either the base register or the // index register, whichever one is the frame pointer. if (BaseReg == X86_FRAME_POINTER_REG) { // The three least significant bits of the SIB byte are the base register. // They must contain a 5, which is the x86 value for register EBP, and we // want to convert it to a 4, denoting register ESP. We can just zero out // the least significant bit to accomplish that. DefOp.sib &= 0xfe; } else { // We sometimes have an instruction in which the frame pointer is used as // the "index" register in the SIB byte, and the true index register is // in the "base" register position in the SIB byte. assert(IndexReg == X86_FRAME_POINTER_REG); // The true index reg is in the lowest three bits, while the next three // bits must contain a 5 (register EBP) and we want to make them a 4 (ESP). // We must swap base and index regs as we normalize (see explanation above). char SIBtemp = DefOp.sib; char SIBindex = SIBtemp & 0x38; char SIBbase = SIBtemp & 0x07; assert ((SIBindex >> 3) == 5); // must be EBP SIBtemp &= 0xa0; // zero out lower 6 bits; upper 2 bits are scale factor - leave them alone SIBtemp &= (SIBbase << 3); // make old base reg (e.g. ebx) into a proper index reg SIBtemp |= 0x04; // make the new base reg be 4 (reg ESP) DefOp.sib = SIBtemp; } this->SetFPNormalizedToSP(); // Add the stack pointer to the USE set for the instruction. this->MDAddRegUse(X86_STACK_POINTER_REG, false); } } else if (DefOp.reg == MD_FRAME_POINTER_REG) { // If FPDelta is -4 and SignedOffset is +8, then we have [ebp+8] as DefOp, and this // is equivalent to [esp+4] where esp has its entry value, i.e. this would be the first incoming // argument. If SignedOffset is -12, we have [ebp-12] as DefOp, and this is [esp-16] when // normalized to the entry point value of the stack pointer. In both cases, we can see that the // normalized stack delta is just FPDelta+SignedOffset. NormalizedDelta = FPDelta + (sval_t) SignedOffset; // Now, we simply convert the memory operand from EBP to ESP and replace the SignedOffset with the // NormalizedDelta just computed. DefOp.reg = MD_STACK_POINTER_REG; this->SetFPNormalizedToSP(); // Add the stack pointer to the USE set for the instruction. this->MDAddRegUse(DefOp.reg, false); } else { assert(DefOp.reg == MD_STACK_POINTER_REG); // We only need to adjust the offset to reflect the change in the stack pointer since the function // was entered, e.g. [esp+4] is normalized to [esp-28] if the current esp value is 32 less than it // was upon function entry. We get the value "-32" in that case from a member variable. NormalizedDelta = this->GetStackPtrOffset() + (sval_t) SignedOffset; } DefOp.addr = (ea_t) NormalizedDelta; // common to frame and stack pointer cases if ((o_phrase == DefOp.type) && (0 != NormalizedDelta)) { // mov [esp],eax has an [esp] operand of type o_phrase, because there is no // displacement field. After normalization, it will have a displacement field, so // it has become an operand like [esp-32] and is now type o_displ. DefOp.type = o_displ; } this->GetBlock()->GetFunc()->AddNormalizedStackOperand(OldOp, this->GetAddr(), DefOp); return true; } else { return false; } } // end of SMPInstr::MDComputeNormalizedDataFlowOp() // Normalize stack operands in all DEFs and USEs to have stack deltas relative to the function entry stack pointer. // Return true if any stack DEFs or USEs were normalized. bool SMPInstr::MDNormalizeStackOps(bool UseFP, sval_t FPDelta, bool Recomputing, sval_t DeltaIncrement) { bool StackOpFound = false; bool OpNormalized; bool UniqueDEFMemOp = true; // Does DEFMemOp not match any DEFs? bool UniqueUSEMemOp = true; // Does USEMemOp not match any USEs? bool UniqueLeaUSEMemOp = true; // Does LeaUSEMemOp not match any USEs? bool UniqueMoveSource = true; // Does MoveSource not match any USEs? set<DefOrUse, LessDefUse>::iterator DefIter, UseIter; list<pair<set<DefOrUse, LessDefUse>::iterator, op_t> > DefWorkList, UseWorkList; list<pair<set<DefOrUse, LessDefUse>::iterator, op_t> >::iterator WorkIter; op_t OldOp, NewOp; // Find all the DEFs that need changing, and put their iterators into a list. // Normalizing stack ops could change their sort order, hence we could skip over // a DEF in the set by erasing a DEF and reinserting a normalized DEF, so we // make all the changes after we iterate through the DEFS set. for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { OldOp = DefIter->GetOp(); NewOp = OldOp; if ((o_reg != NewOp.type) && (o_imm != NewOp.type)) { if (Recomputing) { OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, true, NewOp); } else { OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, NewOp); } if (OpNormalized) { StackOpFound = true; if (IsEqOp(OldOp, this->DEFMemOp)) { UniqueDEFMemOp = false; } pair<set<DefOrUse, LessDefUse>::iterator, op_t> DefItem(DefIter, NewOp); DefWorkList.push_back(DefItem); } } } // Now go through the DEF worklist and change stack operands to normalized stack operands. for (WorkIter = DefWorkList.begin(); WorkIter != DefWorkList.end(); ++WorkIter) { DefIter = WorkIter->first; DefIter = this->Defs.SetOp(DefIter, WorkIter->second); } // Normalize op_t private data member DEFs. if (Recomputing) { OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueDEFMemOp, this->DEFMemOp); } else { OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, this->DEFMemOp); } // Find all USEs that need changing, and build a second work list. for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { OldOp = UseIter->GetOp(); NewOp = OldOp; if ((o_reg != NewOp.type) && (o_imm != NewOp.type)) { if (Recomputing) { OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, true, NewOp); } else { OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, NewOp); } if (OpNormalized) { StackOpFound = true; if (IsEqOp(OldOp, this->USEMemOp)) { UniqueUSEMemOp = false; } if (IsEqOp(OldOp, this->GetLeaMemUseOp())) { UniqueLeaUSEMemOp = false; } if (IsEqOp(OldOp, this->MoveSource)) { UniqueMoveSource = false; } pair<set<DefOrUse, LessDefUse>::iterator, op_t> UseItem(UseIter, NewOp); UseWorkList.push_back(UseItem); } } } // Now go through the USE worklist and change stack operands to normalized stack operands. for (WorkIter = UseWorkList.begin(); WorkIter != UseWorkList.end(); ++WorkIter) { UseIter = WorkIter->first; UseIter = this->Uses.SetOp(UseIter, WorkIter->second); } // Normalize op_t private data member USEs. op_t TempLeaMemOp = this->GetLeaMemUseOp(); if (Recomputing) { OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueUSEMemOp, this->USEMemOp); OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueLeaUSEMemOp, TempLeaMemOp); if (OpNormalized) this->SetLeaMemUseOp(TempLeaMemOp); OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueMoveSource, this->MoveSource); } else { OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, this->USEMemOp); OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, TempLeaMemOp); if (OpNormalized) this->SetLeaMemUseOp(TempLeaMemOp); OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, this->MoveSource); } // Declare victory. this->SetDefsNormalized(); return StackOpFound; } // end of SMPInstr::MDNormalizeStackOps() // Renormalize SP-relative stack operands in functions that call alloca() by adding DeltaIncrement to their stack displacements. // DefOp comes in with the operand to be renormalized, and contains the normalized operand upon return. // Return true if operand is a register or stack location, false otherwise (true => include in data flow analysis sets and SSA.) bool SMPInstr::MDRecomputeNormalizedDataFlowOp(sval_t DeltaIncrement, bool UpdateMaps, op_t &DefOp) { op_t OldOp = DefOp; if (o_reg == DefOp.type) { return true; } else if (MDIsStackAccessOpnd(DefOp, this->GetBlock()->GetFunc()->UsesFramePointer())) { if (this->HasFPNormalizedToSP()) { // FP-relative operands do no change in alloca() functions when the alloca() // causes the SP to change. return true; } // The remaining cases are simple. The ESP-relative displacement is incremented by // DeltaIncrement, regardless of the presence of a SIB byte. int SignedOffset = (int) DefOp.addr; sval_t NormalizedDelta = DeltaIncrement + (sval_t) SignedOffset; DefOp.addr = (ea_t) NormalizedDelta; if ((o_phrase == DefOp.type) && (0 != NormalizedDelta)) { // mov [esp],eax has an [esp] operand of type o_phrase, because there is no // displacement field. After normalization, it will have a displacement field, so // it has become an operand like [esp-32] and is now type o_displ. DefOp.type = o_displ; } if (UpdateMaps) { // We don't update maps for duplicate entries, e.g. USEMemOp, DEFMemOp, MoveSource this->GetBlock()->GetFunc()->AddNormalizedStackOperand(OldOp, this->GetAddr(), DefOp); } return true; } else { return false; } } // end of SMPInstr::MDRecomputeNormalizedDataFlowOp() // If NormOp is a normalized stack memory operand, unnormalize it. void SMPInstr::MDGetUnnormalizedOp(op_t &NormOp) { sval_t SignedOffset; bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if (this->AreDefsNormalized() && MDIsStackAccessOpnd(NormOp, UseFP)) { if (this->HasFPNormalizedToSP()) { // Need to convert NormOp back to frame-pointer-relative address. if (NormOp.hasSIB) { // Convert base register from stack pointer back to frame pointer. NormOp.sib |= 0x01; } else { NormOp.reg = MD_FRAME_POINTER_REG; } SignedOffset = (sval_t) NormOp.addr; SignedOffset -= this->GetBlock()->GetFunc()->GetFramePtrStackDelta(); } else { // NormOp should remain stack-pointer-relative address, but it // should be a positive offset from the current stack pointer instead // of a negative offset from the entry point of the function. SignedOffset = (sval_t) NormOp.addr; SignedOffset -= this->GetStackPtrOffset(); assert(0 <= SignedOffset); } NormOp.addr = (ea_t) SignedOffset; } return; } // end of SMPInstr::MDGetUnnormalizedOp() // Find USE-not-DEF operand that is not the flags register. op_t SMPInstr::GetSourceOnlyOperand(void) { size_t OpNum; for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { if (this->features & DefMacros[OpNum]) { // DEF ; } else if (this->features & UseMacros[OpNum]) { // USE op_t CurrOp = this->SMPcmd.Operands[OpNum]; if (!(CurrOp.is_reg(X86_FLAGS_REG))) { return CurrOp; } } } // It is expected that increment, decrement, and floating point stores // will not have a USE-only operand. Increment and decrement have an // operand that is both USEd and DEFed, while the floating point stack // registers are implicit in most floating point opcodes. Also, exchange // and exchange-and-add instructions have multiple DEF-and-USE operands. int TypeGroup = SMPTypeCategory[this->SMPcmd.itype]; if ((TypeGroup != 2) && (TypeGroup != 4) && (TypeGroup != 9) && (TypeGroup != 12) && (TypeGroup != 13)) { SMP_msg("ERROR: Could not find source only operand at %lx in %s\n", (unsigned long) this->address, DisAsmText.GetDisAsm(this->GetAddr())); } return InitOp; } // end of SMPInstr::GetSourceOnlyOperand() // Should apparent memory operands be ignored? e.g. lea opcode on x86 bool SMPInstr::MDIgnoreMemOps(void) { bool leaInst = (NN_lea == this->SMPcmd.itype); return leaInst; } // Find memory DEFs and USEs, store in DEFMemOp and USEMemOp void SMPInstr::FindMemOps(void) { size_t OpNum; if (!(this->MDIgnoreMemOps())) { for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if ((TempOp.type >= o_mem) && (TempOp.type <= o_displ)) { // memory if (this->features & DefMacros[OpNum]) { // DEF if (this->DEFMemOp.type == o_void) { // only save first mem DEF this->DEFMemOp = TempOp; } } if (this->features & UseMacros[OpNum]) { // USE if (this->USEMemOp.type == o_void) { // only save first mem USE this->USEMemOp = TempOp; } } } } // end for (OpNum = 0; ...) } this->SetMemOpsFound(); return; } // end of SMPInstr::FindMemOps() // Fix problems with the operands list in SMPcmd. void SMPInstr::MDFixupIDAProOperandList(void) { // IDA Pro often takes the instruction imul eax,0x80 and creates the following operands and features bits: // Opnd[0] = EAX, both DEF and USE // Opnd[1] = EAX, just USE // Opnd[2] = immediate, neither DEF nor USE // Our RTL building keys in on the DEF/USE bits in features, so this looks like imul eax,eax to us. // We want it to look like: // Opnd[0] = EAX, both DEF and USE // Opnd[1] = immediate, just USE if (NN_imul == this->SMPcmd.itype) { op_t Opnd2 = this->SMPcmd.Operands[2]; if ((!(this->features & DefMacros[2])) && (!(this->features & UseMacros[2]))) { if (o_void != Opnd2.type) { // We have a third operand that is neither DEF nor USE. SMP_msg("INFO: Fixing IMUL operand list at %lx\n", (unsigned long) this->GetAddr()); this->Dump(); // Two cases: Operands[0] == Operands[1], e.g. imul eax,Opnd2 // or else three-operand form: e.g. imul eax,ecx,Opnd2 // For the three-operand form, make sure Opnd0 is DEF only, others // are USE only. For the two-operand form, make sure Opnd0 is DEF and USE, // Opnd1 is current Opnd2 and is USE only. op_t Opnd0 = this->SMPcmd.Operands[0]; op_t Opnd1 = this->SMPcmd.Operands[1]; if (IsEqOp(Opnd0, Opnd1)) { // No need for three-operand form. this->features |= DefMacros[0]; this->features |= UseMacros[0]; this->SMPcmd.Operands[1] = Opnd2; this->SMPcmd.Operands[2] = InitOp; } else { // Must have three-operand form. this->features |= UseMacros[2]; // set missing USE bit. this->features &= (~UseMacros[0]); // Ensure no USE of Opnd0. } this->Dump(); } } } return; } // SMPInstr::MDFixupIDAProOperandList() // Fill the Defs and Uses private data members. void SMPInstr::BuildSMPDefUseLists(void) { size_t OpNum; bool DebugFlag = (0x8049b00 == this->GetAddr()); bool WidthDoubler = this->MDDoublesWidth(); this->Defs.clear(); this->Uses.clear(); // Start with the Defs. for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { if (this->features & DefMacros[OpNum]) { // DEF op_t TempOp = this->SMPcmd.Operands[OpNum]; if (WidthDoubler) { // Opcodes that sign-extend a byte to a word, or a word to a dword, // have only one operand. It is implicit, and it is the shorter USE. // That means the DEF will have the same width as the USE, e.g. if // we are sign-extending AX to EAX, the USE and DEF both be AX without // a special fix. We fix this problem with the DEF operand now. if (TempOp.dtyp == dt_byte) { TempOp.dtyp = dt_word; TempOp.reg = MDCanonicalizeSubReg(TempOp.reg); } else if (TempOp.dtyp == dt_word) { TempOp.dtyp = dt_dword; TempOp.reg = MDCanonicalizeSubReg(TempOp.reg); } else if (TempOp.dtyp == dt_dword) { TempOp.dtyp = dt_qword; } else { SMP_msg("ERROR: Instruction operand %zu not 1,2, or 4 bytes at %lx dtyp: %d\n", OpNum, (unsigned long) this->address, TempOp.dtyp); } } if (MDKnownOperandType(TempOp)) { if (DebugFlag) { SMP_msg("DEBUG: Setting DEF for: "); PrintOperand(TempOp); SMP_msg("\n"); } this->Defs.SetRef(TempOp); } } } // end for (OpNum = 0; ...) if (this->IsRegClearIdiom()) { // Something like xor eax,eax clears eax but does not really // use eax. It is the same as mov eax,0 and we don't want to // extend the prior def-use chain for eax to this instruction // by treating the instruction as xor eax,eax. Instead, we // build the DEF and USE lists and RTL as if it were mov eax,0. op_t ImmOp = InitOp; ImmOp.type = o_imm; ImmOp.value = 0; SetOperandWidthToProcessorWidth(ImmOp); this->Uses.SetRef(ImmOp, NUMERIC); return; } // Now, do the Uses. Uses have special case operations, because // any memory operand could have register uses in the addressing // expression, and we must create Uses for those registers. For // example: mov eax,[ebx + esi*2 + 044Ch] // This is a two-operand instruction with one def: eax. But // there are three uses: [ebx + esi*2 + 044Ch], ebx, and esi. // The first use is an op_t of type o_phrase (memory phrase), // which can be copied from cmd.Operands[1]. Likewise, we just // copy cmd.Operands[0] into the defs list. However, we must create // op_t types for register ebx and register esi and append them // to the Uses list. This is handled by the machine dependent // method MDFixupDefUseLists(). for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { if (this->features & UseMacros[OpNum]) { // USE op_t TempOp = this->SMPcmd.Operands[OpNum]; if (MDKnownOperandType(TempOp)) { if (DebugFlag) { SMP_msg("DEBUG: Setting USE for: "); PrintOperand(TempOp); SMP_msg("\n"); } this->Uses.SetRef(TempOp); } } } // end for (OpNum = 0; ...) return; } // end of SMPInstr::BuildSMPDefUseLists() // Declare a branch/jump to be a tail call, clean up def/use lists. void SMPInstr::SetTailCall(void) { this->booleans1 |= INSTR_SET_TAIL_CALL; if (this->type == COND_BRANCH) { this->SetCondTailCall(); } else { this->ResetCondTailCall(); } this->CallTarget = this->FarBranchTarget; this->type = RETURN; this->GetBlock()->SetReturns(true); // We want to add the caller-saved registers to the USEs and DEFs lists this->MDAddRegDef(R_ax, false); this->MDAddRegDef(R_cx, false); this->MDAddRegDef(R_dx, false); this->MDAddRegUse(R_ax, false); this->MDAddRegUse(R_cx, false); this->MDAddRegUse(R_dx, false); } // end of SMPInstr::SetTailCall() // record original Lea instruction [pseudo-]memory operand. void SMPInstr::SetLeaMemUseOp(op_t NewLeaOperand) { if (NULL != this->BasicBlock) { this->GetBlock()->GetFunc()->AddLeaOperand(this->GetAddr(), NewLeaOperand); } return; } // If DefReg is not already in the DEF list, add a DEF for it. void SMPInstr::MDAddRegDef(ushort DefReg, bool Shown, SMPOperandType Type) { op_t TempDef = InitOp; TempDef.type = o_reg; TempDef.reg = DefReg; if (Shown) TempDef.set_showed(); else TempDef.clr_showed(); this->Defs.SetRef(TempDef, Type); return; } // end of SMPInstr::MDAddRegDef() // If UseReg is not already in the USE list, add a USE for it. void SMPInstr::MDAddRegUse(ushort UseReg, bool Shown, SMPOperandType Type) { op_t TempUse = InitOp; TempUse.type = o_reg; TempUse.reg = UseReg; if (Shown) TempUse.set_showed(); else TempUse.clr_showed(); this->Uses.SetRef(TempUse, Type); return; } // end of SMPInstr::MDAddRegUse() // Perform machine dependent ad hoc fixes to the def and use lists. // For example, some multiply and divide instructions in x86 implicitly // use and/or define register EDX. For memory phrase examples, see comment // in BuildSMPDefUseLists(). void SMPInstr::MDFixupDefUseLists(void) { // First, handle the uses hidden in memory addressing modes. Note that we do not // care whether we are dealing with a memory destination operand or source // operand, because register USEs, not DEFs, happen within the addressing expressions. size_t OpNum; SMPOperandType RefType; unsigned short opcode = this->SMPcmd.itype; int BaseReg; int IndexReg; ushort ScaleFactor; ea_t displacement; bool UseFP = true; bool HasIndexReg = false; bool SingleAddressReg = false; bool leaInst = (NN_lea == opcode); bool DebugFlag = (this->GetAddr() == 0x8086177); if (DebugFlag) { SMP_msg("DEBUG: Fixing up DEF-USE lists for debug location\n"); this->Dump(); } #if SMP_BASEREG_POINTER_TYPE // Some instructions are analyzed outside of any function or block when fixing up // the IDB, so we have to assume the block and func pointers might be NULL. if ((NULL != this->BasicBlock) && (NULL != this->BasicBlock->GetFunc())) UseFP = this->BasicBlock->GetFunc()->UsesFramePointer(); #endif if (DebugFlag) { SMP_msg("DEBUG: UseFP = %d\n", UseFP); } for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { op_t Opnd = SMPcmd.Operands[OpNum]; if ((Opnd.type == o_phrase) || (Opnd.type == o_displ) || (Opnd.type == o_mem)) { MDExtractAddressFields(Opnd, BaseReg, IndexReg, ScaleFactor, displacement); SingleAddressReg = ((0 == displacement) && ((R_none == BaseReg) || (R_none == IndexReg))); if (R_none != IndexReg) { op_t IndexOpnd = Opnd; // Init to current operand field values IndexOpnd.type = o_reg; // Change type and reg fields IndexOpnd.reg = (ushort) IndexReg; IndexOpnd.hasSIB = 0; IndexOpnd.set_showed(); if (0 == ScaleFactor) this->Uses.SetRef(IndexOpnd); else { // scaling == shift ==> NUMERIC HasIndexReg = true; this->Uses.SetRef(IndexOpnd, NUMERIC); } } if (R_none != BaseReg) { op_t BaseOpnd = Opnd; // Init to current operand field values BaseOpnd.type = o_reg; // Change type and reg fields BaseOpnd.reg = (ushort) BaseReg; BaseOpnd.hasSIB = 0; BaseOpnd.set_showed(); RefType = UNINIT; #if SMP_BASEREG_POINTER_TYPE // R_sp and R_bp will get type STACKPTR in SMPInstr::SetImmedTypes(). // Other registers used as base registers should get their USEs as // base registers typed as POINTER, which might get refined later // to STACKPTR, GLOBALPTR, HEAPPTR, etc. // NOTE: the NN_lea opcode is often used without a true base register. // E.g. lea eax,[eax+eax+5] is an x86 idiom for eax:=eax*2+5, which // could not be done in one instruction without using the addressing // modes of the machine to do the arithmetic. We don't want to set the // USE of EAX to POINTER in this case, so we will conservatively skip // all lea instructions here. // We cannot be sure that a register is truly a base register unless // there is also an index register. E.g. with reg+displacement, we // could have memaddr+indexreg or basereg+offset, depending on what // the displacement is. The exception is if there is no offset and only // one addressing register, e.g. mov eax,[ebx]. if (BaseOpnd.is_reg(MD_STACK_POINTER_REG) || (UseFP && BaseOpnd.is_reg(MD_FRAME_POINTER_REG)) || leaInst || (!HasIndexReg && !SingleAddressReg)) { ; } else { RefType = POINTER; } #endif this->Uses.SetRef(BaseOpnd, RefType); } // end if R_none != BaseReg } // end if (o_phrase or o_displ operand) } // end for (all operands) // The lea (load effective address) instruction looks as if it has // a memory USE: lea ebx,[edx+esi] // However, this instruction is really just: ebx := edx+esi // Now that the above code has inserted the "addressing" registers // into the USE list, we should remove the "memory USE". if (leaInst) { set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) { op_t UseOp = CurrUse->GetOp(); if ((o_mem <= UseOp.type) && (o_displ >= UseOp.type)) { this->SetLeaMemUseOp(UseOp); this->EraseUse(CurrUse); this->USEMemOp = InitOp; break; } } } // Next, handle repeat prefices in the instructions. The Intel REPE/REPZ prefix // is just the text printed for SCAS/CMPS instructions that have a REP prefix. // Only two distinct prefix codes are actually defined: REP and REPNE/REPNZ, and // REPNE/REPNZ only applies to SCAS and CMPS instructions. bool HasRepPrefix = (0 != (this->SMPcmd.auxpref & aux_rep)); bool HasRepnePrefix = (0 != (this->SMPcmd.auxpref & aux_repne)); if (HasRepPrefix && HasRepnePrefix) SMP_msg("REP and REPNE both present at %lx %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); if (HasRepPrefix || HasRepnePrefix) { // All repeating instructions use ECX as the countdown register. op_t BaseOpnd = InitOp; BaseOpnd.type = o_reg; // Change type and reg fields BaseOpnd.reg = R_cx; BaseOpnd.clr_showed(); this->Defs.SetRef(BaseOpnd, NUMERIC); this->Uses.SetRef(BaseOpnd, NUMERIC); } if ((opcode == NN_cmps) || (opcode == NN_scas) || (opcode == NN_movs) || (opcode == NN_stos)) { // ESI and EDI are USEd and DEFed to point to source and dest strings for CMPS/MOVS. // Only EDI is involved with SCAS/STOS. op_t BaseOpnd = InitOp; BaseOpnd.type = o_reg; // Change type and reg fields BaseOpnd.clr_showed(); if ((opcode == NN_cmps) || (opcode == NN_movs)) { BaseOpnd.reg = R_si; this->Defs.SetRef(BaseOpnd, POINTER); this->Uses.SetRef(BaseOpnd, POINTER); } BaseOpnd.reg = R_di; this->Defs.SetRef(BaseOpnd, POINTER); this->Uses.SetRef(BaseOpnd, POINTER); } else if ((NN_loopw <= opcode) && (NN_loopqne >= opcode)) { op_t LoopCounterOp = InitOp; LoopCounterOp.type = o_reg; LoopCounterOp.reg = R_cx; this->Defs.SetRef(LoopCounterOp, NUMERIC); this->Uses.SetRef(LoopCounterOp, NUMERIC); } // Now, handle special instruction categories that have implicit operands. if (NN_cmpxchg == opcode) { // x86 Compare and Exchange conditionally sets EAX. We must keep data flow analysis // sound by declaring that EAX is always a DEF. this->MDAddRegDef(R_ax, false); } // end if NN_cmpxchg else if (this->MDIsPopInstr() || this->MDIsPushInstr() || this->MDIsReturnInstr()) { // IDA does not include the stack pointer in the DEFs or USEs. this->MDAddRegDef(R_sp, false); this->MDAddRegUse(R_sp, false); if (!this->MDIsReturnInstr()) { // We always reference [esp+0] or [esp-4], so add it to the DEF or USE list. op_t StackOp = InitOp; StackOp.type = o_displ; StackOp.reg = R_sp; SetOperandWidthToProcessorWidth(StackOp); if (this->MDIsPopInstr()) { StackOp.addr = 0; // [ESP+0] this->Uses.SetRef(StackOp); // USE } else { StackOp.addr = (ea_t) -STARS_ISA_Bytewidth; // [ESP-4] this->Defs.SetRef(StackOp); // DEF } } } #if SMP_CALL_TRASHES_REGS else if ((this->type == CALL) || (this->type == INDIR_CALL) || this->IsTailCall()) { // We want to add the caller-saved registers to the USEs and DEFs lists this->MDAddRegDef(R_ax, false); this->MDAddRegDef(R_cx, false); this->MDAddRegDef(R_dx, false); this->MDAddRegUse(R_ax, false); this->MDAddRegUse(R_cx, false); this->MDAddRegUse(R_dx, false); #if 1 if (this->MDIsInterruptCall()) { #endif this->MDAddRegDef(R_bx, false); this->MDAddRegUse(R_bx, false); this->MDAddRegDef(R_si, false); this->MDAddRegUse(R_si, false); #if 1 } #endif } #endif else if (this->MDIsEnterInstr() || this->MDIsLeaveInstr()) { // Entire function prologue or epilogue microcoded. this->MDAddRegDef(MD_STACK_POINTER_REG, false); this->MDAddRegUse(MD_STACK_POINTER_REG, false); this->MDAddRegDef(MD_FRAME_POINTER_REG, false); this->MDAddRegUse(MD_FRAME_POINTER_REG, false); } else if ((opcode == NN_maskmovq) || (opcode == NN_maskmovdqu)) { this->MDAddRegUse(R_di, false, POINTER); } else if (8 == this->GetOptType()) { // This category implicitly writes to EDX:EAX. this->MDAddRegDef(R_dx, false); this->MDAddRegDef(R_ax, false); } // end else if (8 == GetOptType) else if (7 == this->GetOptType()) { // Category 7 instructions sometimes write implicitly to EDX:EAX or DX:AX. // DX is the same as EDX to IDA Pro (and SMP); ditto for EAX and AX. // DIV, IDIV, and MUL all have hidden EAX or AX operands (hidden in the IDA Pro // sense, because they are not displayed in the disassembly text). For example: // mul ebx means EDX:EAX <-- EAX*EBX, and mul bx means DX:AX <-- AX*BX. If the // source operand is only 8 bits wide, there is room to hold the result in AX // without using DX: mul bl means AX <-- AL*BL. // IMUL has forms with a hidden EAX or AX operand and forms with no implicit // operands: imul ebx means EDX:EAX <-- EAX*EBX, but imul ebx,edx means that // EBX*EDX gets truncated and the result placed in EBX (no hidden operands). for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { op_t TempUse = this->SMPcmd.Operands[OpNum]; if (!TempUse.showed()) { // hidden operand if (TempUse.is_reg(R_ax)) { // not R_al, so it is not 8 bits if ((NN_div == this->SMPcmd.itype) || (NN_idiv == this->SMPcmd.itype)) { this->MDAddRegUse(R_dx, false); } this->MDAddRegDef(R_ax, false); this->MDAddRegDef(R_dx, false); } } } } // end else if (7 == OptType) #if 0 // The floating point instructions in type categories 14 and 15 often USE and DEF // the floating point register stack, e.g. pushing a value onto that stack is a // massive copy downward of stack locations. We don't really care about the USE of // the stack if the value being pushed came from elsewhere than the stack. For example, // an "fld" opcode pushes its source onto the stack. We build RTLs with a simple // move structure, but the RTL building can be fooled by seeing two "source" operands // in the USE list. if ((14 == SMPTypeCategory[this->SMPcmd.itype]) || (15 == SMPTypeCategory[this->SMPcmd.itype])) { } #endif #if 0 // Not true for LOOP instructions that use only the ECX counter register. if (this->type == COND_BRANCH) { assert(SMPUsesFlags[opcode]); } #endif // The return value register EAX is not quite like a caller-save or callee-save // register (technically, it is caller-save). Within a callee, it might appear // that EAX has become dead by the time a return instruction is reached, but // the USE that would make it not dead is in the caller. To prevent type inference // from mistakenly thinking that all USEs of EAX have been seen in the callee, // we add EAX to the USE list for all return instructions, as well as for all // tail calls, which are essentially returns in terms of data flow analysis. // This USE of EAX will always be of type UNINIT unless its DEF has a known type // that propagates to it. Thus, it will prevent an invalid back inference of the // DEF type from "all" USE types that are visible in the callee; even if they // were all NUMERIC, this return USE will be UNINIT and inhibit the invalid // type inference. EAX could be loaded with a pointer from memory, for example, // and USEd only in a comparison instruction, making it falsely appear to be // a NUMERIC, without this extra USE at the return instruction. // Because some of the library functions pass values around in EBX, EDI, etc., // we will add these general purpose registers to the USE list for returns // in order to prevent erroneous analyses of dead registers or unused // metadata. if ((this->type == RETURN) || this->IsTailCall()) { this->MDAddRegUse(R_ax, false); this->MDAddRegUse(R_bx, false); this->MDAddRegUse(R_cx, false); this->MDAddRegUse(R_dx, false); if (!UseFP) this->MDAddRegUse(MD_FRAME_POINTER_REG, false); this->MDAddRegUse(R_si, false); this->MDAddRegUse(R_di, false); } // Next, add the flags register to the DEFs and USEs for those instructions that // are marked as defining or using flags. if (!this->IsDefsFlags() && SMPDefsFlags[opcode]) { this->MDAddRegDef(X86_FLAGS_REG, false); this->SetDefsFlags(); } if (!this->IsUsesFlags() && SMPUsesFlags[opcode]) { this->MDAddRegUse(X86_FLAGS_REG, false); this->SetUsesFlags(); } #if 1 if (this->IsNop()) { // Clear the DEFs and USEs for no-ops. // These include machine idioms for no-ops, e.g. mov esi,esi // or xchg ax,ax or lea esi,[esi]. this->Defs.clear(); this->Uses.clear(); this->MoveSource = InitOp; this->DEFMemOp = InitOp; this->USEMemOp = InitOp; this->SetLeaMemUseOp(InitOp); this->OptType = 1; } #endif if (DebugFlag) { SMP_msg("DEBUG after MDFixupDefUseLists:\n"); this->Dump(); } return; } // end of SMPInstr::MDFixupDefUseLists() // If we can definitely identify which part of the addressing expression // used in MemOp is the POINTER type, and it is not a STACKPTR or GLOBALPTR // immediate, set the USE type for that register to POINTER and return true. // If we can find definite NUMERIC addressing registers that are not already // typed as NUMERIC, set their USE types to NUMERIC and return true. bool SMPInstr::MDFindPointerUse(op_t MemOp, bool UseFP) { bool changed = false; int BaseReg; int IndexReg; op_t BaseOp = InitOp; op_t IndexOp = InitOp; SMPOperandType BaseType = UNKNOWN; SMPOperandType IndexType = UNKNOWN; ushort ScaleFactor; ea_t offset; set<DefOrUse, LessDefUse>::iterator BaseIter; set<DefOrUse, LessDefUse>::iterator IndexIter; if (NN_lea == this->SMPcmd.itype) return false; // lea instruction really has no memory operands if (NN_fnop == this->SMPcmd.itype) return false; // SSA marker instruction MDExtractAddressFields(MemOp, BaseReg, IndexReg, ScaleFactor, offset); if (R_none != IndexReg) { IndexOp.type = o_reg; IndexOp.reg = MDCanonicalizeSubReg((ushort) IndexReg); IndexOp.dtyp = STARS_ISA_dtyp; // Canonical reg width IndexIter = this->FindUse(IndexOp); assert(IndexIter != this->GetLastUse()); IndexType = IndexIter->GetType(); } if (R_none != BaseReg) { BaseOp.type = o_reg; BaseOp.reg = MDCanonicalizeSubReg((ushort) BaseReg); BaseOp.dtyp = STARS_ISA_dtyp; // Canonical reg width BaseIter = this->FindUse(BaseOp); assert(BaseIter != this->GetLastUse()); BaseType = BaseIter->GetType(); } if (MDIsStackPtrReg(BaseReg, UseFP)) { if ((R_none != IndexReg) && (!IsNumeric(IndexType))) { // We have an indexed access into the stack frame. // Set IndexReg USE type to NUMERIC. changed = true; IndexIter = this->SetUseType(IndexOp, NUMERIC); assert(IndexIter != this->GetLastUse()); } return changed; // stack accesses will get STACKPTR type in SetImmedTypes() } if (MDIsStackPtrReg(IndexReg, UseFP)) { if ((R_none != BaseReg) && (!IsNumeric(BaseType))) { // We have an indexed access into the stack frame. // Set BaseReg USE type to NUMERIC. // Note that BaseReg is really an IndexReg and vice versa. changed = true; BaseIter = this->SetUseType(BaseOp, NUMERIC); assert(BaseIter != this->GetLastUse()); SMP_msg("WARNING: BaseReg is index, IndexReg is base: %s\n", DisAsmText.GetDisAsm(this->GetAddr())); } return changed; // stack accesses will get STACKPTR type in SetImmedTypes() } if (IsImmedGlobalAddress(offset)) { if ((R_none != IndexReg) && (!IsNumeric(IndexType))) { // We have an indexed access into a global. // Set IndexReg USE type to NUMERIC. changed = true; IndexIter = this->SetUseType(IndexOp, NUMERIC); assert(IndexIter != this->GetLastUse()); } if ((R_none != BaseReg) && (!IsNumeric(BaseType))) { // We have an indexed access into a global. // Set BaseReg USE type to NUMERIC. // Note that BaseReg is really an index register. changed = true; BaseIter = this->SetUseType(BaseOp, NUMERIC); assert(BaseIter != this->GetLastUse()); #if SMP_VERBOSE_FIND_POINTERS SMP_msg("WARNING: BaseReg used as index: %s\n", DisAsmText.GetDisAsm(this->GetAddr())); #endif } return changed; // global immediate is handled in SetImmedTypes() } // At this point, we must have a base address in a register, not used // to directly address the stack or a global. if ((0 < ScaleFactor) || (R_none == IndexReg)) { // IndexReg is scaled, meaning it is NUMERIC, so BaseReg must // be a POINTER; or IndexReg is not present, so BaseReg is the // only possible holder of an address. if (R_none != BaseReg) { if (UNINIT == BaseIter->GetType()) { changed = true; BaseIter = this->SetUseType(BaseOp, POINTER); assert(BaseIter != this->GetLastUse()); } } } else if (R_none == BaseReg) { // We have an unscaled IndexReg and no BaseReg and offset was // not a global offset, so IndexReg must be a POINTER. if (R_none != IndexReg) { if (UNINIT == IndexType) { changed = true; IndexIter = this->SetUseType(IndexOp, POINTER); assert(IndexIter != this->GetLastUse()); } } } else { // We have BaseReg and an unscaled IndexReg. // The only hope for typing something like [ebx+edx] is for // one register to already be typed NUMERIC, in which case // the other one must be a POINTER, or if one register is // already POINTER, then the other one must be NUMERIC. if (IsNumeric(BaseType)) { if (UNINIT == IndexType) { // Set to POINTER or PROF_POINTER changed = true; IndexIter = this->SetUseType(IndexOp, POINTER); assert(IndexIter != this->GetLastUse()); } else if (IsNumeric(IndexType)) { SMP_msg("ERROR: BaseReg and IndexReg both NUMERIC at %lx: %s\n", (unsigned long) this->address, DisAsmText.GetDisAsm(this->GetAddr())); } } else { // BaseReg was not NUMERIC if (UNINIT == BaseType) { // BaseReg is UNINIT if (IsNumeric(IndexType)) { changed = true; BaseIter = this->SetUseType(BaseOp, POINTER); assert(BaseIter != this->GetLastUse()); } else if (IsDataPtr(IndexType)) { // IndexReg is POINTER, so make BaseReg NUMERIC. changed = true; BaseIter = this->SetUseType(BaseOp, NUMERIC); assert(BaseIter != this->GetLastUse()); } } else if (IsDataPtr(BaseType)) { // BaseReg was a pointer type. IndexReg must be NUMERIC. if (UNINIT == IndexType) { changed = true; IndexIter = this->SetUseType(IndexOp, NUMERIC); assert(IndexIter != this->GetLastUse()); } else if (IsDataPtr(IndexType)) { SMP_msg("ERROR: BaseReg and IndexReg both POINTER at %lx: %s\n", (unsigned long) this->address, DisAsmText.GetDisAsm(this->GetAddr())); } } } } return changed; } // end of SMPInstr::MDFindPointerUse() // Are all DEFs typed to something besides UNINIT? bool SMPInstr::AllDEFsTyped(void) { if (this->AreDEFsTyped()) { return true; } bool FoundUNINIT = false; set<DefOrUse, LessDefUse>::iterator DefIter; for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { if (IsEqType(UNINIT, DefIter->GetType())) { FoundUNINIT = true; break; } } if (!FoundUNINIT) { this->SetDEFsTyped(); } return (!FoundUNINIT); } // end of SMPInstr::AllDEFsTyped() // Are all USEs typed to something besides UNINIT? bool SMPInstr::AllUSEsTyped(void) { if (this->AreUSEsTyped()) { return true; } bool FoundUNINIT = false; set<DefOrUse, LessDefUse>::iterator UseIter; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { if (IsEqType(UNINIT, UseIter->GetType())) { FoundUNINIT = true; break; } } if (!FoundUNINIT) { this->SetUSEsTyped(); } return (!FoundUNINIT); } // end of SMPInstr::AllUSEsTyped() // Return true if UseOp is a USE reg, not just an address reg in a memory USE bool SMPInstr::IsNonAddressReg(op_t UseOp) const { bool FoundUse = false; ushort SearchReg = MDCanonicalizeSubReg(UseOp.reg); for (size_t OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { op_t Opnd = this->SMPcmd.Operands[OpNum]; if (this->features & UseMacros[OpNum]) { // USE if (Opnd.type == o_reg) { ushort TestReg = MDCanonicalizeSubReg(Opnd.reg); if (TestReg == SearchReg) { FoundUse = true; break; } } } } return FoundUse; } // end of SMPInstr::IsNonAddressReg() uval_t SMPInstr::MDGetShiftCount(void) const { uval_t ShiftCount = 0; if (this->MDIsShiftOrRotate()) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); assert(CurrRT->HasRightSubTree()); CurrRT = CurrRT->GetRightTree(); op_t ShiftCountOp = CurrRT->GetRightOperand(); if (o_imm == ShiftCountOp.type) { ShiftCount = ShiftCountOp.value; } } return ShiftCount; } // end of SMPInstr::MDGetShiftCount() // RTL shows DEF operand is subreg. bool SMPInstr::IsReducedWidthDef(void) const { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); op_t DefOp = CurrRT->GetLeftOperand(); return ((o_void != DefOp.type) && (DefOp.dtyp < 2)); } // Is a sub-register of UseOp used as a shift counter in the RTL? // For example, UseOp could be ECX on an x86 machine, and CL // could be used as a shift or rotate counter. bool SMPInstr::IsSubRegUsedAsShiftCount(op_t UseOp) { bool ShiftCounter = false; if ((o_reg == UseOp.type) && this->MDIsShiftOrRotate()) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); assert(CurrRT->HasRightSubTree()); CurrRT = CurrRT->GetRightTree(); op_t ShiftCountOp = CurrRT->GetRightOperand(); if (o_reg == ShiftCountOp.type) { ushort UseReg = UseOp.reg; ushort ShiftCountReg = ShiftCountOp.reg; ushort WideUseReg = MDCanonicalizeSubReg(UseReg); ushort WideShiftCountReg = MDCanonicalizeSubReg(ShiftCountReg); if ((UseReg != ShiftCountReg) && (WideUseReg == WideShiftCountReg)) { // Registers were not equal, but their canonical enclosing // registers are equal. Because shift counters that are not // immediate are the 8-bit subregister in x86 (MD here !!!!!!) // it must be that the ShiftCountReg is a subreg of UseReg. // This is the condition we are looking for. ShiftCounter = true; } } } return ShiftCounter; } // end of SMPInstr::IsSubRegUsedAsShiftCount() // Does UseOp ultimately come from a small positive constant? bool SMPInstr::IsOpSourceSmallPositiveConstant(op_t UseOp, int UseSSANum) { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) { return false; } bool FoundSmallConst = false; bool RegDef = (o_reg == UseOp.type); bool LocalName = this->GetBlock()->IsLocalName(UseOp); bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP); bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized()))); ea_t UseAddr = this->GetAddr(); ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)); if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) { // Try to find in the function level. UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum); } if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame || (UseDefAddr == BADADDR) || IndirectMemAccess) { // Cannot search for general memory DEFs; must be stack or register. // FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs // that are LiveIn to the function; pseudo-inst is not a bitwise not. // First block addr - 1 is pseudo-location that indicates live-in, UpExposed, // and LocalName means we will not find a DEF anywhere besides this block. // AboveStackFrame means an incoming arg, whose DEF will not be seen. FoundSmallConst = false; } else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from small constants // but we only need one of the Phi USEs to come from // a small constant to potentially lead to a false positive numeric error. We // will recurse on all Phi USEs, declaring success if we find a single one of them // to come from a small constant. size_t BlockNum = (size_t) UseDefAddr; assert(!LocalName); SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum); assert(NULL != PhiDefBlock); if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp); assert(DefPhiIter != PhiDefBlock->GetLastPhi()); size_t PhiListSize = DefPhiIter->GetPhiListSize(); PhiDefBlock->SetProcessed(true); // Prevent infinite recursion for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) { int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex); if (this->IsOpSourceSmallPositiveConstant(UseOp, PhiUseSSANum)) { FoundSmallConst = true; // only one success on all Phi USEs is needed break; } } } } else { bool ValueFound; uval_t ConstValue; SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); if (DefInst->MDIsSimpleAssignment(ValueFound, ConstValue)) { FoundSmallConst = (ValueFound && (ConstValue <= 2)); if (!FoundSmallConst && !ValueFound && DefInst->MDIsMoveInstr()) { // We have a non-immediate move. Trace back through move source to find small const. op_t CopyUseOp = DefInst->GetMoveSource(); CanonicalizeOpnd(CopyUseOp); set<DefOrUse, LessDefUse>::iterator UseIter = DefInst->FindUse(CopyUseOp); assert(UseIter != DefInst->GetLastUse()); int CopyUseSSANum = UseIter->GetSSANum(); FoundSmallConst = DefInst->IsOpSourceSmallPositiveConstant(CopyUseOp, CopyUseSSANum); } } } return FoundSmallConst; } // end of SMPInstr::IsOpSourceSmallPositiveConstant() // Does UseOp ultimately come from a bitwise not instruction? bool SMPInstr::IsOpSourceBitwiseNot(op_t UseOp, int UseSSANum) { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) { return false; } bool FoundBitwiseNotInst = false; bool RegDef = (o_reg == UseOp.type); bool LocalName = this->GetBlock()->IsLocalName(UseOp); bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP); bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized()))); ea_t UseAddr = this->GetAddr(); ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)); if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) { // Try to find in the function level. UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum); } if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame || (UseDefAddr == BADADDR) || IndirectMemAccess) { // Cannot search for general memory DEFs; must be stack or register. // FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs // that are LiveIn to the function; pseudo-inst is not a bitwise not. // First block addr - 1 is pseudo-location that indicates live-in, UpExposed, // and LocalName means we will not find a DEF anywhere besides this block. // AboveStackFrame means an incoming arg, whose DEF will not be seen. FoundBitwiseNotInst = false; } else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from bitwise nots // but we only need one of the Phi USEs to come from // a bitwise not to potentially lead to a false positive numeric error. We // will recurse on all Phi USEs, declaring success if we find a single one of them // to come from a bitwise not. size_t BlockNum = (size_t) UseDefAddr; assert(!LocalName); SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum); assert(NULL != PhiDefBlock); if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp); assert(DefPhiIter != PhiDefBlock->GetLastPhi()); size_t PhiListSize = DefPhiIter->GetPhiListSize(); PhiDefBlock->SetProcessed(true); // Prevent infinite recursion for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) { int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex); if (this->IsOpSourceBitwiseNot(UseOp, PhiUseSSANum)) { FoundBitwiseNotInst = true; // only one success on all Phi USEs is needed break; } } } } else { SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); if (DefInst->MDIsBitwiseNotOpcode()) { FoundBitwiseNotInst = true; } else if (DefInst->MDIsMoveInstr()) { op_t MoveUseOp = DefInst->GetMoveSource(); if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory CanonicalizeOpnd(MoveUseOp); set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp); assert(MoveUseIter != DefInst->GetLastUse()); int MoveUseSSANum = MoveUseIter->GetSSANum(); FoundBitwiseNotInst = DefInst->IsOpSourceBitwiseNot(MoveUseOp, MoveUseSSANum); // recurse } } else { // Not a move, not a bitwise not. We must return false. FoundBitwiseNotInst = false; } } return FoundBitwiseNotInst; } // end of SMPInstr::IsOpSourceBitwiseNot() // Does UseOp ultimately come from a set-condition-code instruction? bool SMPInstr::IsOpSourceConditionCode(op_t UseOp, int UseSSANum) { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) { return false; } bool FoundConditionalSetInst = false; bool RegDef = (o_reg == UseOp.type); bool LocalName = this->GetBlock()->IsLocalName(UseOp); bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP); bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized()))); ea_t UseAddr = this->GetAddr(); ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)); if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) { // Try to find in the function level. UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum); } if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame || (UseDefAddr == BADADDR) || IndirectMemAccess) { // Cannot search for general memory DEFs; must be stack or register. // FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs // that are LiveIn to the function; pseudo-inst is not a bitwise not. // First block addr - 1 is pseudo-location that indicates live-in, UpExposed, // and LocalName means we will not find a DEF anywhere besides this block. // AboveStackFrame means an incoming arg, whose DEF will not be seen. FoundConditionalSetInst = false; } else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from condition codes // but we only need one of the Phi USEs to come from // a condition code to potentially lead to a false positive numeric error. We // will recurse on all Phi USEs, declaring success if we find a single one of them // to come from a condition code. size_t BlockNum = (size_t) UseDefAddr; assert(!LocalName); SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum); assert(NULL != PhiDefBlock); if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp); assert(DefPhiIter != PhiDefBlock->GetLastPhi()); size_t PhiListSize = DefPhiIter->GetPhiListSize(); PhiDefBlock->SetProcessed(true); // Prevent infinite recursion for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) { int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex); if (this->IsOpSourceConditionCode(UseOp, PhiUseSSANum)) { FoundConditionalSetInst = true; // only one success on all Phi USEs is needed break; } } } } else { SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); if (DefInst->MDIsAnySetValue()) { FoundConditionalSetInst = true; } else if (DefInst->MDIsMoveInstr()) { op_t MoveUseOp = DefInst->GetMoveSource(); if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory CanonicalizeOpnd(MoveUseOp); set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp); assert(MoveUseIter != DefInst->GetLastUse()); int MoveUseSSANum = MoveUseIter->GetSSANum(); FoundConditionalSetInst = DefInst->IsOpSourceConditionCode(MoveUseOp, MoveUseSSANum); // recurse } } else { // Not a move, not a condition code transfer. We must return false. FoundConditionalSetInst = false; } } return FoundConditionalSetInst; } // end of SMPInstr::IsOpSourceConditionCode() // Does UseOp ultimately come from a move-with-zero-extension instruction? bool SMPInstr::IsOpSourceZeroExtendedMove(op_t UseOp, int UseSSANum, bool TruncationCheck) { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) { return false; } bool FoundMoveZX = false; bool RegDef = (o_reg == UseOp.type); bool LocalName = this->GetBlock()->IsLocalName(UseOp); bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP); bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized()))); ea_t UseAddr = this->GetAddr(); ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)); if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) { // Try to find in the function level. UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum); } if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame || (UseDefAddr == BADADDR) || IndirectMemAccess) { // Cannot search for general memory DEFs; must be stack or register. // FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs // that are LiveIn to the function; pseudo-inst is not a bitwise not. // First block addr - 1 is pseudo-location that indicates live-in, UpExposed, // and LocalName means we will not find a DEF anywhere besides this block. // AboveStackFrame means an incoming arg, whose DEF will not be seen. FoundMoveZX = false; } else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from zero-extended // moves into the UseOp register, but we only need one of the Phi USEs to come from // a zero-extended move to potentially lead to a false positive numeric error. We // will recurse on all Phi USEs, declaring success if we find a single one of them // to come from a zero-extended move. size_t BlockNum = (size_t) UseDefAddr; assert(!LocalName); SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum); assert(NULL != PhiDefBlock); if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp); assert(DefPhiIter != PhiDefBlock->GetLastPhi()); size_t PhiListSize = DefPhiIter->GetPhiListSize(); PhiDefBlock->SetProcessed(true); // Prevent infinite recursion for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) { int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex); if (this->IsOpSourceZeroExtendedMove(UseOp, PhiUseSSANum, TruncationCheck)) { FoundMoveZX = true; // only one success on all Phi USEs is needed break; } } } } else { SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); unsigned short SignMask; if (DefInst->MDIsSignedLoad(SignMask)) { FoundMoveZX = (FG_MASK_UNSIGNED == SignMask); } else if (DefInst->MDIsMoveInstr()) { op_t MoveUseOp = DefInst->GetMoveSource(); if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory CanonicalizeOpnd(MoveUseOp); set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp); assert(MoveUseIter != DefInst->GetLastUse()); int MoveUseSSANum = MoveUseIter->GetSSANum(); FoundMoveZX = DefInst->IsOpSourceZeroExtendedMove(MoveUseOp, MoveUseSSANum, TruncationCheck); // recurse } } else if (TruncationCheck && DefInst->MDIsNonOverflowingBitManipulation()) { // Not a move, not a zero-extended move. We must return false for the non-truncation case, // but we allow non-overflowing bit manipulation instructions in the chain for truncation checks. // This is because of a benign code pattern: // reg: = zero-extended move // reg := reg AND bit pattern // reg := reg OR bit pattern // store lower bits of reg // Compilers like to do 32-bit arithmetic. There was never any good reason otherwise to zero-extend the // value in the first instruction in the pattern. The lower bits that are stored at the end of the code // sequence are the only bits that ever mattered, so this is not really a truncation. set<DefOrUse, LessDefUse>::iterator BitUseIter = DefInst->FindUse(UseOp); if (BitUseIter != DefInst->GetLastUse()) { int BitUseSSANum = BitUseIter->GetSSANum(); FoundMoveZX = DefInst->IsOpSourceZeroExtendedMove(UseOp, BitUseSSANum, true); // recurse up the chain } } else { FoundMoveZX = false; } } return FoundMoveZX; } // end of SMPInstr::IsOpSourceZeroExtendedMove() // Does UseOp ultimately come from a move-with-zero-extension instruction OR from a condition code OR from a right shift? bool SMPInstr::IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(op_t UseOp, int UseSSANum, bool TruncationCheck) { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) { return false; } bool FoundMoveZXCC = false; bool RegDef = (o_reg == UseOp.type); bool LocalName = this->GetBlock()->IsLocalName(UseOp); bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP); bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized()))); ea_t UseAddr = this->GetAddr(); ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)); if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) { // Try to find in the function level. UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum); } if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame || (UseDefAddr == BADADDR) || IndirectMemAccess) { // Cannot search for general memory DEFs; must be stack or register. // FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs // that are LiveIn to the function; pseudo-inst is not a bitwise not. // First block addr - 1 is pseudo-location that indicates live-in, UpExposed, // and LocalName means we will not find a DEF anywhere besides this block. // AboveStackFrame means an incoming arg, whose DEF will not be seen. FoundMoveZXCC = false; } else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from zero-extended // moves into the UseOp register, but we only need one of the Phi USEs to come from // a zero-extended move to potentially lead to a false positive numeric error. We // will recurse on all Phi USEs, declaring success if we find a single one of them // to come from a zero-extended move. size_t BlockNum = (size_t) UseDefAddr; assert(!LocalName); SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum); assert(NULL != PhiDefBlock); if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp); assert(DefPhiIter != PhiDefBlock->GetLastPhi()); size_t PhiListSize = DefPhiIter->GetPhiListSize(); PhiDefBlock->SetProcessed(true); // Prevent infinite recursion for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) { int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex); if (this->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(UseOp, PhiUseSSANum, TruncationCheck)) { FoundMoveZXCC = true; // only one success on all Phi USEs is needed break; } } } } else { SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); unsigned short SignMask; if (DefInst->MDIsSignedLoad(SignMask)) { FoundMoveZXCC = (FG_MASK_UNSIGNED == SignMask); } else if (DefInst->MDIsAnySetValue() || DefInst->MDIsShiftRight()) { FoundMoveZXCC = true; } else if (DefInst->MDIsMoveInstr()) { op_t MoveUseOp = DefInst->GetMoveSource(); if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory CanonicalizeOpnd(MoveUseOp); set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp); assert(MoveUseIter != DefInst->GetLastUse()); int MoveUseSSANum = MoveUseIter->GetSSANum(); FoundMoveZXCC = DefInst->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(MoveUseOp, MoveUseSSANum, TruncationCheck); // recurse } } else if (TruncationCheck && (DefInst->MDIsNonOverflowingBitManipulation() || DefInst->MDIsSmallAdditionOrSubtraction())) { // Not a move, not a zero-extended move. We must return false for the non-truncation case, // but we allow non-overflowing bit manipulation instructions in the chain for truncation checks. // This is because of a benign code pattern: // reg: = zero-extended move // reg := reg AND bit pattern // reg := reg OR bit pattern // store lower bits of reg // Compilers like to do 32-bit arithmetic. There was never any good reason otherwise to zero-extend the // value in the first instruction in the pattern. The lower bits that are stored at the end of the code // sequence are the only bits that ever mattered, so this is not really a truncation. // NOTE: We combine into this case additions or subtractions of small values, as they only operate on the // lower bits of the register. set<DefOrUse, LessDefUse>::iterator BitUseIter = DefInst->FindUse(UseOp); if (BitUseIter != DefInst->GetLastUse()) { int BitUseSSANum = BitUseIter->GetSSANum(); FoundMoveZXCC = DefInst->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(UseOp, BitUseSSANum, true); // recurse up the chain } } else { FoundMoveZXCC = false; } } return FoundMoveZXCC; } // end of SMPInstr::IsOpSourceZeroExtendedMoveShiftRightOrConditionCode() // Trace through moves to any of the above cases, return source inst bool SMPInstr::IsOpSourceSpecial(op_t UseOp, int UseSSANum, bool TruncationCheck, ea_t &SourceInstAddr) { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) { return false; } bool FoundMoveNotZXCCSubreg = false; bool RegDef = (o_reg == UseOp.type); bool LocalName = this->GetBlock()->IsLocalName(UseOp); bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP); bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized()))); ea_t UseAddr = this->GetAddr(); ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)); if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) { // Try to find in the function level. UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum); } if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame || (UseDefAddr == BADADDR) || IndirectMemAccess) { // Cannot search for general memory DEFs; must be stack or register. // FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs // that are LiveIn to the function; pseudo-inst is not a bitwise not. // First block addr - 1 is pseudo-location that indicates live-in, UpExposed, // and LocalName means we will not find a DEF anywhere besides this block. // AboveStackFrame means an incoming arg, whose DEF will not be seen. FoundMoveNotZXCCSubreg = false; } else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from zero-extended // moves into the UseOp register, but we only need one of the Phi USEs to come from // a zero-extended move to potentially lead to a false positive numeric error. We // will recurse on all Phi USEs, declaring success if we find a single one of them // to come from a zero-extended move. size_t BlockNum = (size_t) UseDefAddr; assert(!LocalName); SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum); assert(NULL != PhiDefBlock); if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp); assert(DefPhiIter != PhiDefBlock->GetLastPhi()); size_t PhiListSize = DefPhiIter->GetPhiListSize(); PhiDefBlock->SetProcessed(true); // Prevent infinite recursion for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) { int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex); if (this->IsOpSourceSpecial(UseOp, PhiUseSSANum, TruncationCheck, SourceInstAddr)) { FoundMoveNotZXCCSubreg = true; // only one success on all Phi USEs is needed break; } } } } else { SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); unsigned short SignMask; if (DefInst->MDIsSignedLoad(SignMask)) { FoundMoveNotZXCCSubreg = ((FG_MASK_UNSIGNED == SignMask) || TruncationCheck); // Truncation chain cares if source was reduced width. if (FoundMoveNotZXCCSubreg) { SourceInstAddr = DefInst->GetAddr(); } } else if (DefInst->MDIsAnySetValue() || DefInst->MDIsShiftRight() || DefInst->MDIsBitwiseNotOpcode()) { FoundMoveNotZXCCSubreg = true; SourceInstAddr = DefInst->GetAddr(); } else if (DefInst->MDIsMoveInstr()) { op_t MoveUseOp = DefInst->GetMoveSource(); if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory CanonicalizeOpnd(MoveUseOp); set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp); assert(MoveUseIter != DefInst->GetLastUse()); int MoveUseSSANum = MoveUseIter->GetSSANum(); FoundMoveNotZXCCSubreg = DefInst->IsOpSourceSpecial(MoveUseOp, MoveUseSSANum, TruncationCheck, SourceInstAddr); // recurse } } else if (TruncationCheck && (DefInst->MDIsNonOverflowingBitManipulation() || DefInst->MDIsSmallAdditionOrSubtraction())) { // Not a move, not a zero-extended move. We must return false for the non-truncation case, // but we allow non-overflowing bit manipulation instructions in the chain for truncation checks. // This is because of a benign code pattern: // reg: = zero-extended move // reg := reg AND bit pattern // reg := reg OR bit pattern // store lower bits of reg // Compilers like to do 32-bit arithmetic. There was never any good reason otherwise to zero-extend the // value in the first instruction in the pattern. The lower bits that are stored at the end of the code // sequence are the only bits that ever mattered, so this is not really a truncation. // NOTE: We combine into this case additions or subtractions of small values, as they only operate on the // lower bits of the register. set<DefOrUse, LessDefUse>::iterator BitUseIter = DefInst->FindUse(UseOp); if (BitUseIter != DefInst->GetLastUse()) { int BitUseSSANum = BitUseIter->GetSSANum(); FoundMoveNotZXCCSubreg = DefInst->IsOpSourceSpecial(UseOp, BitUseSSANum, true, SourceInstAddr); // recurse up the chain } } else if (TruncationCheck) { // Check for reduced-width arithmetic. if (DefInst->IsReducedWidthDef()) { // chain like sub cl,3; mov eax,ecx; then "truncating" move of al FoundMoveNotZXCCSubreg = true; SourceInstAddr = DefInst->GetAddr(); } } } return FoundMoveNotZXCCSubreg; } // end of SMPInstr::IsOpSourceSpecial() // Is opcode a shift or rotate? // NOTE: We omit MMX/SSE unit shifts that do not use a general purpose // register as a shift counter, because right now this method is only // used as a helper for IsSubRegUsedAsShiftCount(). bool SMPInstr::MDIsShiftOrRotate(void) const { return (((NN_rcl <= SMPcmd.itype) && (NN_ror >= SMPcmd.itype)) || ((NN_sal <= SMPcmd.itype) && (NN_shr >= SMPcmd.itype)) || (NN_shld == SMPcmd.itype) || (NN_shrd == SMPcmd.itype)); } // end of SMPInstr::MDIsShiftOrRotate() // Is opcode a shift to the right? bool SMPInstr::MDIsShiftRight(void) const { return ((NN_sar == SMPcmd.itype) || (NN_shr == SMPcmd.itype)); } // Does the shift or rotate RTL move the upper HalfBitWidth bits // into the lower half of the register? Or, if MustBeHalfRegWidth is false, // do we shift right by HalfBitWidth bits? bool SMPInstr::ShiftMakesUpperBitsLower(size_t HalfBitWidth, bool MustBeHalfRegWidth) { bool FullCircle = false; if (!MustBeHalfRegWidth || (MD_NORMAL_MACHINE_BITWIDTH == (HalfBitWidth * 2))) { SMPRegTransfer *CurrRT = this->RTL.GetRT(0); if ((NULL != CurrRT) && (CurrRT->HasRightSubTree())) { CurrRT = CurrRT->GetRightTree(); SMPoperator CurrOper = CurrRT->GetOperator(); bool LeftRotate = (SMP_ROTATE_LEFT == CurrOper); if ((SMP_U_RIGHT_SHIFT == CurrOper) || (SMP_S_RIGHT_SHIFT == CurrOper) || LeftRotate || (SMP_ROTATE_RIGHT == CurrOper)) { if (CurrRT->HasRightSubTree()) { // double-word shift CurrRT = CurrRT->GetRightTree(); } assert(!(CurrRT->HasRightSubTree())); op_t ShiftCount = CurrRT->GetRightOperand(); if (o_imm == ShiftCount.type) { uval_t ImmVal = ShiftCount.value; // If we rotate left by e.g. 32-HalfBitWidth bits, then we are processing // bytes or halfregs one at a time; if we rotate or shift right by HalfBitWidth, // we are processing the register one HalfBitWidth at a time. We also a if (MustBeHalfRegWidth || (!LeftRotate)) { FullCircle = (HalfBitWidth == ImmVal); } else { // Left rotate amount plus HalfBitWidth must add up to full register width FullCircle = (MD_NORMAL_MACHINE_BITWIDTH == (ImmVal + HalfBitWidth)); } } } } } return FullCircle; } // SMPInstr::ShiftMakesUpperBitsLower() #if 0 // Find SearchDelta in StackDeltaSet, inserting it if not found. Return whether it was initially found. bool SMPInstr::FindStackPtrDelta(sval_t SearchDelta) const { bool found = (this->StackDeltaSet.find(SearchDelta) != this->StackDeltaSet.end()); if (!found) { this->StackDeltaSet.insert(SearchDelta); if (SearchDelta < this->StackPtrOffset) { // Mimic IDA Pro, which seems to keep the biggest stack frame possible. // With negative stack deltas, this means the smallest stack delta is kept. this->SetStackPtrOffset(SearchDelta); } } return found; } // end of SMPInstr::FindStackPtrDelta() #endif // Set the type of all immediate operands found in the USE set. // Set all flags and floating point register USEs and DEFs to NUMERIC also, // along with easily determined types for special cases. void SMPInstr::SetImmedTypes(bool UseFP) { set<DefOrUse, LessDefUse>::iterator CurrUse; set<DefOrUse, LessDefUse>::iterator CurrDef; op_t UseOp; op_t DefOp; uval_t ImmVal; bool DebugFlag = false; #if SMP_VERBOSE_DEBUG_BUILD_RTL DebugFlag = DebugFlag || (this->address == 0x805cd52) || (this->address == 0x805cd56); DebugFlag |= (0 == strncmp("__libc_csu_fini", this->BasicBlock->GetFunc()->GetFuncName(), 15)); #endif CurrUse = this->GetFirstUse(); while (CurrUse != this->GetLastUse()) { UseOp = CurrUse->GetOp(); if (DebugFlag) { SMP_msg("SetImmedTypes USE: "); PrintOperand(UseOp); SMP_msg("\n"); } if (o_imm == UseOp.type) { ImmVal = UseOp.value; if (IsImmedGlobalAddress((ea_t) ImmVal)) { if (DebugFlag) SMP_msg("Setting to GLOBALPTR\n"); CurrUse = this->SetUseType(UseOp, GLOBALPTR); } #if 0 else if (IsDataAddress((ea_t) ImmVal)) { // NOTE: We must call IsDataAddress() before we call IsImmedCodeAddress() // to catch the data addresses within the code address range. if (DebugFlag) SMP_msg("Setting to POINTER\n"); CurrUse = this->SetUseType(UseOp, POINTER); } #endif else if (this->MDIsInterruptCall() || IsImmedCodeAddress((ea_t) ImmVal)) { if (DebugFlag) SMP_msg("Setting to CODEPTR\n"); CurrUse = this->SetUseType(UseOp, CODEPTR); } else { // NUMERIC if (DebugFlag) SMP_msg("Setting to NUMERIC\n"); CurrUse = this->SetUseType(UseOp, NUMERIC); } } else if (o_reg == UseOp.type) { if (UseOp.is_reg(X86_FLAGS_REG)) { if (DebugFlag) SMP_msg("Setting flags reg to NUMERIC\n"); CurrUse = this->SetUseType(UseOp, NUMERIC); } #if 1 else if (MDIsStackOrFramePointerReg(UseOp, UseFP)) { if (DebugFlag) SMP_msg("Setting reg to STACKPTR\n"); CurrUse = this->SetUseType(UseOp, STACKPTR); } #endif } #if 0 // could these registers have pointers in them? else if ((o_trreg == UseOp.type) ||(o_dbreg == UseOp.type) || (o_crreg == UseOp.type)) { if (DebugFlag) SMP_msg("Setting special reg to NUMERIC\n"); CurrUse = this->SetUseType(UseOp, NUMERIC); } #endif else if ((o_fpreg == UseOp.type) || (o_mmxreg == UseOp.type) || (o_xmmreg == UseOp.type)) { if (DebugFlag) SMP_msg("Setting floating point reg to NUMERIC\n"); CurrUse = this->SetUseType(UseOp, NUMERIC); } else if ((o_mem == UseOp.type) || (o_phrase == UseOp.type) || (o_displ == UseOp.type)) { // For memory operands, we need to identify the POINTER value that // is used in the addressing mode, if possible. (void) this->MDFindPointerUse(UseOp, UseFP); } ++CurrUse; } // end while all USEs via CurrUse CurrDef = this->GetFirstDef(); while (CurrDef != this->GetLastDef()) { DefOp = CurrDef->GetOp(); if (DebugFlag) { SMP_msg("SetImmedTypes DEF: "); PrintOperand(DefOp); SMP_msg("\n"); } if (DebugFlag) SMP_msg("FuncName: %s\n", this->BasicBlock->GetFunc()->GetFuncName()); if (o_reg == DefOp.type) { if (DefOp.is_reg(X86_FLAGS_REG)) { if (DebugFlag) SMP_msg("Setting flags reg DEF to NUMERIC\n"); CurrDef = this->SetDefType(DefOp, NUMERIC); // No need to propagate this DEF type, as all flags will become NUMERIC. } #if 1 else if (MDIsStackOrFramePointerReg(DefOp, UseFP)) { if (DebugFlag) SMP_msg("Setting reg DEF to STACKPTR\n"); CurrDef = this->SetDefType(DefOp, STACKPTR); assert(CurrDef != this->Defs.GetLastRef()); // No need to propagate; all stack and frame pointers will become STACKPTR. } #endif } else if ((o_fpreg == DefOp.type) || (o_mmxreg == DefOp.type) || (o_xmmreg == DefOp.type)) { if (DebugFlag) SMP_msg("Setting floating point reg DEF to NUMERIC\n"); CurrDef = this->SetDefType(DefOp, NUMERIC); // No need to propagate; all FP reg uses will become NUMERIC anyway. } #if 0 // could these registers have pointers in them? else if ((o_trreg == DefOp.type) || (o_dbreg == DefOp.type) || (o_crreg == DefOp.type)) { if (DebugFlag) SMP_msg("Setting special reg DEF to NUMERIC\n"); CurrDef = this->SetDefType(DefOp, NUMERIC); } #endif else if ((o_mem == DefOp.type) || (o_phrase == DefOp.type) || (o_displ == DefOp.type)) { // For memory operands, we need to identify the POINTER value that // is used in the addressing mode, if possible. (void) this->MDFindPointerUse(DefOp, UseFP); } ++CurrDef; } // end while all DEFs via CurrDef return; } // end of SMPInstr::SetImmedTypes() // Is the instruction a load from the stack? void SMPInstr::MDFindLoadFromStack(bool UseFP) { set<DefOrUse, LessDefUse>::iterator UseIter; op_t UseOp; if ((3 == this->OptType) && (this->HasSourceMemoryOperand())) { // Loads and stores are OptCategory 3. We want only loads from the stack. for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseOp = UseIter->GetOp(); if (MDIsStackAccessOpnd(UseOp, UseFP)) { this->SetLoadFromStack(); break; } } } return; } // end of SMPInstr::MDFindLoadFromStack() // Determine if instr is inherently signed load instruction. // True if sign or zero-extended; pass out mask bits if true. bool SMPInstr::MDIsSignedLoad(unsigned short &SignMask) { unsigned short opcode = this->SMPcmd.itype; if (NN_movzx == opcode) { SignMask = FG_MASK_UNSIGNED; } else if (NN_movsx == opcode) { SignMask = FG_MASK_SIGNED; } else { return false; } return true; } // true if increment or addition of small positive immediate value #define STARS_SMALL_POS_VALUE_LIMIT 16 bool SMPInstr::MDIsSmallPositiveAddition(void) { unsigned short opcode = this->SMPcmd.itype; bool found = (NN_inc == opcode); uval_t ImmVal; if (!found && ((NN_add == opcode) || (NN_adc == opcode))) { set<DefOrUse, LessDefUse>::iterator UseIter; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { op_t UseOp = UseIter->GetOp(); if (o_imm == UseOp.type) { ImmVal = UseOp.value; if ((ImmVal <= STARS_SMALL_POS_VALUE_LIMIT) && (0 < ImmVal)) { found = true; break; } } } } else if (this->MDIsLoadEffectiveAddressInstr()) { // See if we have the form: lea reg1,[reg2+const] SMPRegTransfer *CurrRT = this->RTL.GetRT(0); if (CurrRT->HasRightSubTree()) { CurrRT = CurrRT->GetRightTree(); SMPoperator CurrOp = CurrRT->GetOperator(); if ((SMP_ADD == CurrOp) && (!CurrRT->HasRightSubTree())) { op_t RightOp = CurrRT->GetRightOperand(); if (o_imm == RightOp.type) { ImmVal = RightOp.value; if ((ImmVal <= STARS_SMALL_POS_VALUE_LIMIT) && (0 < ImmVal)) { found = true; } } } } } return found; } // end of SMPInstr::MDIsSmallPositiveAddition() // true if increment, decrement, or addition or subtraction of small immediate value bool SMPInstr::MDIsSmallAdditionOrSubtraction(void) { unsigned short opcode = this->SMPcmd.itype; bool found = ((NN_inc == opcode) || (NN_dec == opcode)); if ((NN_add == opcode) || (NN_adc == opcode) || (NN_sub == opcode) || (NN_sbb == opcode)) { set<DefOrUse, LessDefUse>::iterator UseIter; for (UseIter = this->GetFirstUse(); !found && (UseIter != this->GetLastUse()); ++UseIter) { op_t UseOp = UseIter->GetOp(); if (o_imm == UseOp.type) { uval_t ImmVal = UseOp.value; int SignedImmVal = (int) ImmVal; found = ((SignedImmVal >= (-STARS_SMALL_POS_VALUE_LIMIT)) && (SignedImmVal <= STARS_SMALL_POS_VALUE_LIMIT)); } } } return found; } // end of SMPInstr::MDIsSmallAdditionOrSubtraction() // Inst is move or register clear. bool SMPInstr::MDIsSimpleAssignment(bool &ValueFound, uval_t &ConstValue) { bool Simple = false; ValueFound = false; if (this->IsRegClearIdiom()) { Simple = true; ValueFound = true; ConstValue = 0; } else if (this->MDIsMoveInstr()) { Simple = true; if (o_imm == this->MoveSource.type) { ValueFound = true; ConstValue = this->MoveSource.value; } } return Simple; } // end of SMPInstr::MDIsSimpleAssignment() // Inst clears register or adds or subtracts small immediate value, as is done with counter variables. bool SMPInstr::IsCounterOperation(void) { bool CounterOperation = false; bool ImmedValueFound = false; uval_t ConstValue = 1; if (this->MDIsSimpleAssignment(ImmedValueFound, ConstValue)) { CounterOperation = (ImmedValueFound && (0 == ConstValue)); } else { CounterOperation = this->MDIsSmallAdditionOrSubtraction(); } return CounterOperation; } // end of SMPInstr::IsCounterOperation() // Inst does an AND, OR, XOR operation; does not do add, subtract, etc. bool SMPInstr::MDIsNonOverflowingBitManipulation(void) const { unsigned short opcode = this->SMPcmd.itype; return ((NN_and == opcode) || (NN_or == opcode) || (NN_shl == opcode) || ((NN_xor == opcode) && !this->IsRegClearIdiom())); } // end of SMPInstr::MDIsNonOverflowingBitManipulation() // return true if traced USE to a constant value bool SMPInstr::FindConstantValue(set<DefOrUse, LessDefUse>::iterator UseIter, uval_t &ConstValue) { bool FoundConst = false; assert(UseIter != this->GetLastUse()); op_t UseOp = UseIter->GetOp(); if (UseOp.type == o_imm) { ConstValue = UseOp.value; FoundConst = true; } else { bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if ((o_reg == UseOp.type) || MDIsDirectStackAccessOpnd(UseOp, UseFP)) { // We can trace registers and stack locations through SSA chains, if the stack location is directly accessed. int UseSSANum = UseIter->GetSSANum(); bool LocalName = this->GetBlock()->IsLocalName(UseOp); ea_t UseAddr = this->GetAddr(); ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName); if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) { // A block number was returned. That means the DEF is in a Phi Function. // We could trace all Phi USEs and see if all of them come from a single // constant value, but this is highly unlikely. Terminate search. ; } else if ((UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)) || (BADADDR == UseDefAddr)) { // The DEF is in a Phi function in the current block, or is BADADDR. Terminate search. ; } else { SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr); bool ValueFound = false; if (DefInst->MDIsSimpleAssignment(ValueFound, ConstValue)) { if (ValueFound) { FoundConst = true; // ConstValue holds the value that was found. } else { set<DefOrUse, LessDefUse>::iterator NewUseIter = DefInst->GetFirstUse(); // A simple assignment should have only one USE. Recurse on that USE. FoundConst = DefInst->FindConstantValue(NewUseIter, ConstValue); } } } } } return FoundConst; } // end of SMPInstr::FindConstantValue() // Infer sign, bit width, other type info for simple cases where all the info needed is // within the instruction or can be read from the FineGrainedStackTable in the SMPFunction. // NOTE: Must be called after SSA analysis is complete. void SMPInstr::MDSetWidthSignInfo(bool UseFP) { set<DefOrUse, LessDefUse>::iterator UseIter; set<DefOrUse, LessDefUse>::iterator DefIter; op_t UseOp, DefOp; struct FineGrainedInfo FGEntry; unsigned short SignMask, TempSign, WidthMask; int DefHashValue, UseHashValue; ea_t DefAddr; // for flags USE in conditional set int SSANum; // for flags USE in conditional set bool LocalFlags; // is flags register a local name? bool case1, case2, case3, case4, case5, case6, case7, case8; bool SignedSetOpcode = this->MDIsSignedSetValue(); bool UnsignedSetOpcode = this->MDIsUnsignedSetValue(); case1 = this->IsLoadFromStack(); case2 = this->MDIsSignedLoad(SignMask); // sets value of SignMask if it returns true case3 = (7 == this->OptType); // Multiplies and divides case4 = ((CALL == this->GetDataFlowType()) || (INDIR_CALL == this->GetDataFlowType())); case5 = (SignedSetOpcode || UnsignedSetOpcode); // set boolean based on flag condition case6 = this->MDDoublesWidth(); // convert byte to word, word to dword, etc. case7 = this->MDAlwaysUnsignedDEF(); case8 = this->MDAlwaysSignedDEF(); // Case 1: Load from stack location. if (case1) { bool success = false; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseOp = UseIter->GetOp(); if (MDIsStackAccessOpnd(UseOp, UseFP)) { // Found the stack location being loaded into a register. Now we need // to get the sign and width info from the fine grained stack frame // analysis. success = this->GetBlock()->GetFunc()->MDGetFGStackLocInfo(this->address, UseOp, FGEntry); assert(success); // Now we have signedness info in FGEntry. We need to OR it into the register target of the load. if (FGEntry.SignMiscInfo == 0) break; // nothing to OR in; save time for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { DefOp = DefIter->GetOp(); if (o_reg == DefOp.type) { CanonicalizeOpnd(DefOp); TempSign = FGEntry.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS; // Get both sign bit flags DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); if (this->BasicBlock->IsLocalName(DefOp)) { this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, TempSign); } else { this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, TempSign); } break; // Should be only one register target for stack load, and no flags are set. } } break; // Only concerned with the stack operand } } assert(success); } // end if this->IsLoadFromStack() // Case 2: Loads that are sign-extended or zero-extended imply signed and unsigned, respectively. // NOTE: If from the stack, they were handled in Case 1, and the signedness of the stack location // was recorded a long time ago in SMPFunction::FindOutgoingArgsSize(); else if (case2) { DefIter = this->GetFirstDef(); while (DefIter != this->GetLastDef()) { // All non-memory DEFs besides the flags register should get the new SignMask ORed in. // On x86, there should only be one DEF for this move, and no flags, but we will generalize // in case other architectures are odd. DefOp = DefIter->GetOp(); if (!(IsMemOperand(DefOp) || MDIsFlagsReg(DefOp))) { CanonicalizeOpnd(DefOp); DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); if (this->BasicBlock->IsLocalName(DefOp)) { this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, SignMask); } else { this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask); } } ++DefIter; } // If the signed load is from memory, the only USEs are the memory // operand and addressing registers. We do not want to claim that // EBX is signed in the instruction movsx eax,[ebx]. Only the DEF // register EAX and the memory location [EBX] are signed, and we // have no idea where [EBX] is, so we punt on all USEs if we have // a memory source operand. if (!(this->HasSourceMemoryOperand())) { UseIter = this->GetFirstUse(); while (UseIter != this->GetLastUse()) { // All non-memory USEs besides the flags register should get the new SignMask ORed in. UseOp = UseIter->GetOp(); if (!(IsMemOperand(UseOp) || MDIsFlagsReg(UseOp))) { CanonicalizeOpnd(UseOp); UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum()); if (this->BasicBlock->IsLocalName(UseOp)) { this->BasicBlock->UpdateUseSignMiscInfo(UseHashValue, SignMask); } else { this->BasicBlock->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, SignMask); } } ++UseIter; } } } // end of case 2 // Case 3: multiplies and divides can be signed or unsigned. else if (case3) { // Multiplies and divides are type 7. if (this->MDIsSignedArithmetic()) { SignMask = FG_MASK_SIGNED; } else if (this->MDIsUnsignedArithmetic()) { SignMask = FG_MASK_UNSIGNED; } else { SignMask = 0; // unknown, uninitialized } if (0 != SignMask) { DefIter = this->GetFirstDef(); while (DefIter != this->GetLastDef()) { // All DEFs besides the flags register should get the new SignMask ORed in. DefOp = DefIter->GetOp(); if ((DefOp.type == o_reg) && (!(DefOp.is_reg(X86_FLAGS_REG)))) { CanonicalizeOpnd(DefOp); DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); if (this->BasicBlock->IsLocalName(DefOp)) { this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, SignMask); } else { this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask); } } ++DefIter; } UseIter = this->GetFirstUse(); while (UseIter != this->GetLastUse()) { // All USEs besides the flags register should get the new SignMask ORed in. UseOp = UseIter->GetOp(); if ((UseOp.type == o_reg) && (!(UseOp.is_reg(X86_FLAGS_REG)))) { CanonicalizeOpnd(UseOp); UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum()); if (this->BasicBlock->IsLocalName(UseOp)) { this->BasicBlock->UpdateUseSignMiscInfo(UseHashValue, SignMask); } else { this->BasicBlock->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, SignMask); } } ++UseIter; } } // end if (0 != SignMask) } // end of case 3 (multiplies and divides) // Case 4: Calls to library functions can reveal the type of the return register. else if (case4) { // Get name of function called. string FuncName = this->GetTrimmedCalledFunctionName(); // Get FG info, if any, for called function. GetLibFuncFGInfo(FuncName, FGEntry); // See if anything was returned in FGEntry. if ((FGEntry.SignMiscInfo != 0) || (FGEntry.SizeInfo != 0)) { // Need to update the FG info for the DEF of the return register. DefOp = InitOp; DefOp.type = o_reg; DefOp.reg = MD_RETURN_VALUE_REG; DefIter = this->FindDef(DefOp); assert(DefIter != this->GetLastDef()); DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); if (this->BasicBlock->IsLocalName(DefOp)) { this->BasicBlock->UpdateDefFGInfo(DefHashValue, FGEntry); } else { this->BasicBlock->GetFunc()->UpdateDefFGInfo(DefHashValue, FGEntry); } } // See if we make a call to a library function that needs signedness checks on // unsigned incoming args. unsigned int ArgPosBits = 0; GetUnsignedArgPositionsForCallName(FuncName, ArgPosBits); if (0 < ArgPosBits) { // Mark our basic block as containing this type of call, so signedness checks // can be done later. this->GetBlock()->SetCallsUnsignedArgFunc(); // Find the argument assignments and mark them, to trigger later signedness checks. this->GetBlock()->MarkUnsignedArgs(this->GetAddr(), ArgPosBits); } } // end of case4 (function calls) else if (case5) { // signed or unsigned conditional set opcode if (UnsignedSetOpcode) { SignMask = FG_MASK_UNSIGNED; } else { assert(SignedSetOpcode); SignMask = FG_MASK_SIGNED; } // Find the flags USE. UseOp.type = o_reg; // set up a dummy op for searching UseOp.reg = X86_FLAGS_REG; UseIter = this->FindUse(UseOp); assert(UseIter != this->GetLastUse()); UseOp = UseIter->GetOp(); // get full info in all fields of UseOp SSANum = UseIter->GetSSANum(); LocalFlags = this->GetBlock()->IsLocalName(UseOp); DefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, this->GetAddr(), SSANum, LocalFlags); // Pass DefAddr to recursive helper function to propagate signedness of the set opcode. this->GetBlock()->PropagateBranchSignedness(DefAddr, UseOp, SignMask); } else if (case6) { // sign extend to double the width of USE operand into DEF operand DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); DefOp = DefIter->GetOp(); assert(o_reg == DefOp.type); SSANum = DefIter->GetSSANum(); DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum); UseIter = this->GetFirstUse(); assert(UseIter != this->GetLastUse()); UseOp = UseIter->GetOp(); assert(o_reg == UseOp.type); assert(UseOp.reg == DefOp.reg); UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum()); SignMask = FG_MASK_SIGNED; // opcodes do sign extension => signed // Mark DEF and USE as signed. if (this->GetBlock()->IsLocalName(DefOp)) { this->GetBlock()->UpdateDefSignMiscInfo(DefHashValue, SignMask); this->GetBlock()->UpdateUseSignMiscInfo(UseHashValue, SignMask); } else { this->GetBlock()->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask); this->GetBlock()->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, SignMask); } } else if (case7 || case8) { // always unsigned DEF or always signed DEF. DefIter = this->GetFirstNonFlagsDef(); if (DefIter != this->GetLastDef()) { DefOp = DefIter->GetOp(); if (o_reg == DefOp.type) { SSANum = DefIter->GetSSANum(); DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum); if (case7) SignMask = FG_MASK_UNSIGNED; else SignMask = FG_MASK_SIGNED; // Mark DEF as signed or unsigned. if (this->GetBlock()->IsLocalName(DefOp)) { this->GetBlock()->UpdateDefSignMiscInfo(DefHashValue, SignMask); } else { this->GetBlock()->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask); } } } } // For all register DEFs and USEs, we should get the obvious register width info // updated. Need to use the RTL operands to get accurate widths. SMPRegTransfer *CurrRT; for (size_t index = 0; index < this->RTL.GetCount(); ++index) { CurrRT = this->RTL.GetRT(index); DefOp = CurrRT->GetLeftOperand(); // Avoid setting def width for case 2; we leave it as zero so that // later uses can determine whether the zero-extension or sign-extension // bits ever got used. See more discussion in EmitIntegerErrorAnnotations() // for the CHECK TRUNCATION case. // NOTE: case2 can be set to true even in the case1/case2 overlap case that // only passes through the case1 code above. This is intentional. We want // to leave the DEF width set to 0 for all of case2 including the case1 overlap. if (!case2) { if (MDIsGeneralPurposeReg(DefOp)) { WidthMask = ComputeOperandBitWidthMask(DefOp, 0); CanonicalizeOpnd(DefOp); DefIter = this->FindDef(DefOp); assert(DefIter != this->GetLastDef()); DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); if (this->BasicBlock->IsLocalName(DefOp)) { this->BasicBlock->UpdateDefWidthTypeInfo(DefHashValue, WidthMask); } else { this->BasicBlock->GetFunc()->UpdateDefWidthTypeInfo(DefHashValue, WidthMask); } } } if (CurrRT->HasRightSubTree()) { this->MDSetRTLRegWidthInfo(CurrRT->GetRightTree()); } else { UseOp = CurrRT->GetRightOperand(); this->SetRTLUseOpRegWidthInfo(UseOp); } } // end for all RTLs return; } // end of SMPInstr::MDSetWidthSignInfo() // Infer sign from the SMP types for USEs and DEFs. void SMPInstr::InferSignednessFromSMPTypes(bool UseFP) { // Start with registers only, infer that all kids of pointers are UNSIGNED. set<DefOrUse, LessDefUse>::iterator DefIter, UseIter; op_t DefOp, UseOp; int SSANum; int DefHashValue, UseHashValue; SMPOperandType DefType, UseType; unsigned short DefSignMiscInfo = FG_MASK_UNSIGNED, UseSignMiscInfo = FG_MASK_UNSIGNED; bool GlobalName; for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { DefOp = DefIter->GetOp(); if (MDIsGeneralPurposeReg(DefOp)) { DefType = DefIter->GetType(); if (IsDataPtr(DefType) || (CODEPTR == DefType)) { GlobalName = this->BasicBlock->GetFunc()->IsGlobalName(DefOp); SSANum = DefIter->GetSSANum(); DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum); if (GlobalName) { this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, DefSignMiscInfo); } else { this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, DefSignMiscInfo); } } } } for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseOp = UseIter->GetOp(); if (MDIsGeneralPurposeReg(UseOp)) { UseType = UseIter->GetType(); if (IsDataPtr(UseType) || (CODEPTR == UseType)) { GlobalName = this->BasicBlock->GetFunc()->IsGlobalName(UseOp); SSANum = UseIter->GetSSANum(); UseHashValue = HashGlobalNameAndSSA(UseOp, SSANum); if (GlobalName) { this->BasicBlock->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, UseSignMiscInfo); } else { this->BasicBlock->UpdateUseSignMiscInfo(UseHashValue, UseSignMiscInfo); } } } } return; } // end of SMPInstr::InferSignednessFromSMPTypes() // Helper to set width info for a UseOp from an RTL void SMPInstr::SetRTLUseOpRegWidthInfo(op_t UseOp) { unsigned short WidthMask; set<DefOrUse, LessDefUse>::iterator UseIter; unsigned int UseHashValue; if (MDIsGeneralPurposeReg(UseOp)) { WidthMask = ComputeOperandBitWidthMask(UseOp, 0); CanonicalizeOpnd(UseOp); UseIter = this->FindUse(UseOp); assert(UseIter != this->GetLastUse()); UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum()); if (this->BasicBlock->IsLocalName(UseOp)) { this->BasicBlock->UpdateUseWidthTypeInfo(UseHashValue, WidthMask); } else { this->BasicBlock->GetFunc()->UpdateUseWidthTypeInfo(UseHashValue, WidthMask); } } return; } // end of SMPInstr::SetRTLUseOpRegWidthInfo() // Walk the RTL and update the register USE operands' width info. void SMPInstr::MDSetRTLRegWidthInfo(SMPRegTransfer *CurrRT) { op_t UseOp; UseOp = CurrRT->GetLeftOperand(); this->SetRTLUseOpRegWidthInfo(UseOp); if (CurrRT->HasRightSubTree()) { this->MDSetRTLRegWidthInfo(CurrRT->GetRightTree()); } else { UseOp = CurrRT->GetRightOperand(); this->SetRTLUseOpRegWidthInfo(UseOp); } return; } // end of SMPInstr::MDSetRTLRegWidthInfo() // Do we not consider truncation on this type of instruction to be an error? bool SMPInstr::IsBenignTruncation(int &IdiomCode) { bool benign = false; unsigned short SignMask; op_t UseOp, SearchOp; if (3 == this->GetOptType()) { // Move instruction bool ExtendedLoad = this->MDIsSignedLoad(SignMask); if (ExtendedLoad && (SignMask & FG_MASK_UNSIGNED)) { // We have a zero-extended load. Compilers zero-extend both // signed (unfortunately) and unsigned sub-regs when they know // from the source language types that only the lower bits matter, // e.g. when a char has been stored in the lower bits and regardless // of whether that char was sign-extended or zero-extended previously, // only the char itself is useful info. Otherwise, the compiler could // move the whole register, e.g. instead of edi := zero-extend(cx), the // compiler could have generated edi := ecx. The zero-extension loads // are therefore not good candidates for truncation checks, as they lead // to lots of false positives. benign = true; IdiomCode = 5; #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++BenignTruncationCount; #endif } else { // Move, and not extended load, which was handled above. // Next case: A move instruction whose USE falsely appears to be a truncation, // but in fact the apparently unused portion of the register is used later, e.g.: // mov [ebp-12],ax ; looks like EAX is being truncated to AX and stored // shr eax,16 ; gets upper 16 bits into lower 16 bits // mov [ebp-14],ax ; store what used to be the upper 16 bits of EAX // The first instruction will trigger a CHECK TRUNCATION annotation that // causes false positives. We need to analyze the context of the instruction // to see that the whole register EAX was used, so no truncation occurred. // The context analysis in the basic block will mark the second move as // a "truncation" that should be ignored, so we check the flag here to short // circuit redundant analysis. UseOp = this->GetMoveSource(); assert(o_void != UseOp.type); SearchOp = UseOp; if (o_reg == UseOp.type) { CanonicalizeOpnd(SearchOp); } set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SearchOp); assert(UseIter != this->GetLastUse()); if (UseIter->DoesNotTruncate()) { benign = true; IdiomCode = 2; #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++SuppressTruncationRegPiecesAllUsed; #endif } else { set<DefOrUse, LessDefUse>::iterator DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); int DefSSANum = DefIter->GetSSANum(); benign = this->GetBlock()->IsBenignTruncationDEF(DefIter->GetOp(), DefSSANum, this->GetAddr(), IdiomCode); if (!benign) { // Third case: value gets zero-extended, AND/OR instructions manipulate it, then // the lower bits get written. No adds, subtracts, etc. in the chain of operations. // This is just the typical unwillingness of compiler to do less than 32-bit arithmetic, // not an actual truncation. int UseSSANum = UseIter->GetSSANum(); // Prepare for possible recursive traversals through the function blocks. this->GetBlock()->GetFunc()->ResetProcessedBlocks(); if (this->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(SearchOp, UseSSANum, true)) { IdiomCode = 14; benign = true; } } } } } return benign; } // end of SMPInstr::IsBenignTruncation() // Do we not consider overflow or underflow on this type of instruction to be an error? bool SMPInstr::IsBenignOverflow(int &IdiomCode) { bool benign = false; set<DefOrUse, LessDefUse>::iterator DefIter, UseIter; SMPOperandType DefType, UseType; int DefSSANum; ea_t DefAddr; op_t DefOp; if (this->MDIsDefiniteBenignUnderflowOpcode(IdiomCode)) { // No further analysis at block or function scope is needed. benign = true; } else if (this->MDIsMaybeBenignUnderflowOpcode()) { // might have the subtract instruction in tricky sequence // We are looking to suppress overflow and underflow warnings on the following // code sequence: PTR1-PTR2+1 gets a loop invariant code motion optimization // that pulls temp := 1-PTR2 out of the loop, and leaves temp2 := PTR1+temp // inside the loop. The hoisted subtraction could underflow, and the addition // that is not hoisted could overflow. The net effect of these two instructions // is benign, however, so we want to suppress underflow and overflow checks on // both of them, but only if we can match the pair of instructions. DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); if (DefIter->DoesNotOverflow()) { benign = true; // short circuit; already analyzed } else { DefType = DefIter->GetType(); if (IsEqType(DefType, NEGATEDPTR)) { // We have candidate subtract instruction DefOp = DefIter->GetOp(); DefSSANum = DefIter->GetSSANum(); DefAddr = this->GetAddr(); benign = this->GetBlock()->GetFunc()->IsBenignUnderflowDEF(DefOp, DefSSANum, DefAddr, IdiomCode); if (!benign) { // If we did not find the complicated sequence, we still don't want to have a false positive // on any instruction that is producing a NEGATEDPTR. benign = true; IdiomCode = 13; } } } } else if (this->MDIsMaybeBenignOverflowOpcode()) { // might have the add instruction in tricky sequence DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); if (DefIter->DoesNotOverflow()) { benign = true; // short circuit; already analyzed IdiomCode = 6; } else { // Bad luck to encounter addition first. See what types tell us. if (this->HasNegatedPtrUSE()) { // We have candidate for addition. DefType = DefIter->GetType(); if (IsEqType(DefType, PTROFFSET)) { // Current instruction is definitely the addition instruction of some // benignly underflowing and overflowing pair of instructions. It does // not really matter that we have not found the subtraction yet; we will // get to it eventually. We should suppress overflow checks on this inst. DefOp = DefIter->GetOp(); this->Defs.SetNoOverflow(DefOp, true); benign = true; IdiomCode = 6; } else { // A bit of a quandary. Ideally, we would have successful SMP type inference // and always have PTROFFSET := POINTER + NEGATEDPTR. However, sometimes we // might have ?? := ?? + NEGATEDPTR. The instruction could be of the type // NEGATEDPTR := NUMERIC + NEGATEDPTR, so we cannot just assume here that // we have detected the benign case. However, these instructions are likely // to cause false positives and unlikely to be a source of false negatives, // so we will err on the side of false positive suppression based on experience // (even though we usually do the opposite). benign = true; IdiomCode = 13; } } } } if (!benign) { // Any overflow or underflow producing a PTROFFSET type should be suppressed. for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { DefType = DefIter->GetType(); if (IsEqType(DefType, PTROFFSET)) { benign = true; IdiomCode = 19; } } } if (!benign) { // Any overflow or underflow using a PTROFFSET type should be suppressed. for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseType = UseIter->GetType(); if (IsEqType(UseType, PTROFFSET)) { benign = true; IdiomCode = 19; } } } return benign; } // end of SMPInstr::IsBenignOverflow() // Do we detect a situation in which it is safe to check for signedness errors on // the stack write (return false), or not (return true to be safe). bool SMPInstr::SkipSignednessCheckOnStackWrite(int DefSSANum) { bool SkipCheck = (!(this->IsUnsignedArg())); op_t StackDefOp = this->DEFMemOp; size_t DefBitWidth = 8 * GetOpDataSize(StackDefOp); if (DefBitWidth < MD_NORMAL_MACHINE_BITWIDTH) { // If we are not dealing with a shortened bit width, then // the next load from the stack will not be sign-extended // or zero-extended. if (this->GetBlock()->IsStackOpNextUsedWithSignedness(StackDefOp, this->GetAddr(), DefSSANum)) { SkipCheck = false; } } return SkipCheck; } // end of SMPInstr::SkipSignednessCheckOnStackWrite() // Does inst pass an outgoing argument? If so, pass back argument position #, starting at zero for [esp], 1 for [esp+4], etc. bool SMPInstr::MDIsArgumentPass(size_t &ArgumentNumber) { bool OutArgPass = false; // Current model is writing outargs to stack. For other compiler targets in the // future, we would also include pushes onto the stack. if (this->HasDestMemoryOperand() && (this->GetOptType() == 3)) { // move to memory if (this->GetBlock()->GetFunc()->OutArgsRegionComputed()) { op_t DefOp = this->DEFMemOp; OutArgPass = this->GetBlock()->GetFunc()->IsInOutgoingArgsRegion(DefOp); if (OutArgPass) { op_t UnnormalizedDefOp = DefOp; this->MDGetUnnormalizedOp(UnnormalizedDefOp); size_t StandardByteSize = (MD_NORMAL_MACHINE_BITWIDTH / 8); ArgumentNumber = (UnnormalizedDefOp.addr / StandardByteSize); } } } return OutArgPass; } // Does UseOp arithmetically affect the value of the NonFlagsDef for this inst? bool SMPInstr::OperandTransfersValueToDef(op_t UseOp) { bool FoundTransfer = false; for (size_t index = 0; index < this->RTL.GetCount(); ++index) { SMPRegTransfer *CurrRT = this->RTL.GetRT(index); if (CurrRT->OperandTransfersValueToDef(UseOp)) { FoundTransfer = true; break; } } return FoundTransfer; } // end of SMPInstr::OperandTransfersValueToDef() // Evaluate constants in assignment statement for SCCP algorithm void SMPInstr::SCCPEvaluateAssignment(list<pair<int, int> > &SSAWorkList) { for (size_t index = 0; index < this->RTL.GetCount(); ++index) { SMPRegTransfer *CurrRT = this->RTL.GetRT(index); STARS_SCCP_Const_Struct ConstResult = CurrRT->SCCPEvaluateRTLConsts(SSAWorkList); if (ConstResult.ConstType == STARS_CONST_HAS_VALUE) { ++ConstantDEFCount; } } return; } // end of SMPInstr::SCCPEvaluateAssignment() // Evaluate conditional branch using constant values from SCCP void SMPInstr::SCCPEvaluateCondBranch(enum STARSBranchConst &BranchEval) { // The conditional branch RTL is just a jump with a Guard RT. // We evaluate the Guard RT to see if it is always true or always false. SMPRegTransfer *CurrRT = this->RTL.GetRT(0); SMPGuard *BranchCondition = CurrRT->GetGuard(); assert(NULL != BranchCondition); op_t LeftOp = BranchCondition->GetLeftOperand(); CanonicalizeOpnd(LeftOp); op_t RightOp = BranchCondition->GetRightOperand(); CanonicalizeOpnd(RightOp); STARS_SCCP_Const_Struct LeftValue, RightValue; this->SCCPFetchConstUseValue(LeftOp, LeftValue); this->SCCPFetchConstUseValue(RightOp, RightValue); if ((LeftValue.ConstType == STARS_CONST_HAS_VALUE) && (RightValue.ConstType == STARS_CONST_HAS_VALUE)) { SMPoperator GuardOper = BranchCondition->GetOperator(); if (GuardOper == SMP_EQUAL) { if (LeftValue.ConstValue == RightValue.ConstValue) { BranchEval = STARS_BRANCH_ALWAYS_TAKEN; ++AlwaysTakenBranchCount; #if 0 SMP_msg("INFO: SCCP found always taken conditional branch at %x\n", this->GetAddr()); #endif } else { BranchEval = STARS_BRANCH_NEVER_TAKEN; ++NeverTakenBranchCount; #if 0 SMP_msg("INFO: SCCP found never taken conditional branch at %x\n", this->GetAddr()); #endif } } else if (GuardOper == SMP_NOT_EQUAL) { if (LeftValue.ConstValue != RightValue.ConstValue) { BranchEval = STARS_BRANCH_ALWAYS_TAKEN; ++AlwaysTakenBranchCount; #if 0 SMP_msg("INFO: SCCP found always taken conditional branch at %x\n", this->GetAddr()); #endif } else { BranchEval = STARS_BRANCH_NEVER_TAKEN; ++NeverTakenBranchCount; #if 0 SMP_msg("INFO: SCCP found never taken conditional branch at %x\n", this->GetAddr()); #endif } } } else { BranchEval = STARS_BRANCH_UNKNOWN; } return; } // end of SMPInstr::SCCPEvaluateCondBranch() // Fetch constant value, if any, for a USE operand. void SMPInstr::SCCPFetchConstUseValue(op_t UseOp, STARS_SCCP_Const_Struct &ConstStruct) { ConstStruct.ConstType = STARS_CONST_TOP; // default if (o_imm == UseOp.type) { ConstStruct.ConstType = STARS_CONST_HAS_VALUE; ConstStruct.ConstValue = UseOp.value; } else if (o_reg == UseOp.type) { // Look up reg DEF value in local or global map op_t SearchOp = UseOp; CanonicalizeOpnd(SearchOp); set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SearchOp); if (UseIter != this->GetLastUse()) { int UseSSANum = UseIter->GetSSANum(); int UseHashValue = HashGlobalNameAndSSA(SearchOp, UseSSANum); if (this->GetBlock()->IsLocalName(SearchOp)) { // local name map<int, struct STARS_SCCP_Const_Struct>::iterator ConstValIter = this->GetBlock()->FindLocalConstValue(UseHashValue); if (ConstValIter != this->GetBlock()->GetLastLocalConstValueIter()) { // Has current const val entry ConstStruct = ConstValIter->second; } } else { // global name map<int, struct STARS_SCCP_Const_Struct>::iterator ConstValIter = this->GetBlock()->GetFunc()->FindConstValue(UseHashValue); if (ConstValIter != this->GetBlock()->GetFunc()->GetLastConstValueIter()) { // Has current const val entry ConstStruct = ConstValIter->second; } } } } return; } // end of SMPInstr::SCCPFetchConstUseValue() // Fetch constant value, if any, for a DEF operand. void SMPInstr::SCCPFetchConstDefValue(op_t DefOp, STARS_SCCP_Const_Struct &ConstStruct) { ConstStruct.ConstType = STARS_CONST_TOP; // default if (o_imm == DefOp.type) { ConstStruct.ConstType = STARS_CONST_HAS_VALUE; ConstStruct.ConstValue = DefOp.value; } else if (o_reg == DefOp.type) { // Look up reg DEF value in local or global map op_t SearchOp = DefOp; CanonicalizeOpnd(SearchOp); set<DefOrUse, LessDefUse>::iterator DefIter = this->FindDef(SearchOp); if (DefIter != this->GetLastDef()) { int DefSSANum = DefIter->GetSSANum(); int DefHashValue = HashGlobalNameAndSSA(SearchOp, DefSSANum); if (this->GetBlock()->IsLocalName(SearchOp)) { // local name map<int, struct STARS_SCCP_Const_Struct>::iterator ConstValIter = this->GetBlock()->FindLocalConstValue(DefHashValue); if (ConstValIter != this->GetBlock()->GetLastLocalConstValueIter()) { // Has current const val entry ConstStruct = ConstValIter->second; } } else { // global name map<int, struct STARS_SCCP_Const_Struct>::iterator ConstValIter = this->GetBlock()->GetFunc()->FindConstValue(DefHashValue); if (ConstValIter != this->GetBlock()->GetFunc()->GetLastConstValueIter()) { // Has current const val entry ConstStruct = ConstValIter->second; } } } } return; } // end of SMPInstr::SCCPFetchConstDefValue() // Trace UseOp through register moves back to its stack location or immediate value source. // Return true if we are passing an immediate or stack location back in UltSource. bool SMPInstr::TraceUltimateMoveSource(op_t UseOp, int UseSSANum, op_t &UltSource, bool &FPRelative) { // If we hit an immediate value or a stack location, we are done. bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); op_t NewUseOp; // next UseOp up the move chain op_t DefOp, ImmOp; int NewUseSSANum; set<DefOrUse,LessDefUse>::iterator UseIter; bool LocalName; ea_t DefAddr; SMPInstr *DefInst; UltSource = InitOp; bool StackOp = MDIsDirectStackAccessOpnd(UseOp, UseFP); bool RegisterOp = (UseOp.type == o_reg); if (this->GetOptType() == 3) { // move instruction if (UseOp.type == o_imm) { UltSource = UseOp; return true; } else if ((!RegisterOp) && (!StackOp)) { // We only trace the move chain through registers or stack locations to an ultimate // load-effective-address of a stack location, or a move of an immediate value. return false; } } else if (!this->MDIsLoadEffectiveAddressInstr()) { return false; } else { // Load effective address instruction. // If it is a stack location being loaded, trace succeeded, else it failed. if (StackOp) { UltSource = UseOp; FPRelative = this->HasFPNormalizedToSP(); return true; } else { return false; } } // If we reach this point, we have a move instruction but did not return true or false above. // Recursion case. Going back up the move chain has just produced a register or // a stack location, and we still need to find the stack address or immediate value // that was stored in the register or stack location. The stack location could hold // a pointer to a stack object, produced by an earlier LEA instruction, or it // could hold an immediate value (e.g. constant size argument passed to memset() or // similar function). LocalName = this->GetBlock()->IsLocalName(UseOp); DefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, this->GetAddr(), UseSSANum, LocalName); if ((BADADDR == DefAddr) || (DefAddr < (this->GetBlock()->GetFunc()->GetStartAddr() - 1))) { // Def was not found, or was found in Phi function (DefAddr was block number, not instruction addr). return false; } if (DefAddr < (this->GetBlock()->GetFirstAddr())) { // If DefAddr is 1 byte less than the first addr in the block, then // it is a pseudo-def in the global DU chains, signifying that the // value was LiveIn and the true DEF is in another block. We could // handle this in the future, but right now we will only deal with // the simpler case in which the move source can be traced // within the basic block. return false; } // Everything is OK so far; time to recurse up the move chain. DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(DefAddr); if (DefInst->GetOptType() == 3) { NewUseOp = DefInst->GetMoveSource(); } else if (DefInst->MDIsLoadEffectiveAddressInstr()) { NewUseOp = DefInst->GetLeaMemUseOp(); if (MDIsDirectStackAccessOpnd(NewUseOp, UseFP)) { UltSource = NewUseOp; FPRelative = DefInst->HasFPNormalizedToSP(); return true; } else { return false; } } // We don't have a move instruction or a load effective address instruction, which // can be used to move a stack address into a register. We don't try to trace through // arithmetic except for two easy cases. // Case 1: A register is cleared. Same as assigning immediate value zero to the reg. else if (DefInst->IsRegClearIdiom()) { UltSource.type = o_imm; UltSource.value = 0; // why would we memset a zero byte region? return true; } // Easy arithmetic Case 2: we have reg += ImmediateValue, and reg was DEFed by reg := LEA(StackLoc). else if (DefInst->MDIsAddImmediateToReg(DefOp, ImmOp)) { SMPInstr *NewDefInst; UseIter = DefInst->FindUse(DefOp); assert(UseIter != DefInst->GetLastUse()); NewUseSSANum = UseIter->GetSSANum(); LocalName = DefInst->GetBlock()->IsLocalName(DefOp); DefAddr = DefInst->GetBlock()->GetDefAddrFromUseAddr(DefOp, DefInst->GetAddr(), NewUseSSANum, LocalName); if ((BADADDR == DefAddr) || (DefAddr < (DefInst->GetBlock()->GetFunc()->GetStartAddr() - 1))) { // Def was not found, or was found in Phi function (DefAddr was block number, not instruction addr). return false; } NewDefInst = DefInst->GetBlock()->GetFunc()->GetInstFromAddr(DefAddr); if (NewDefInst->MDIsLoadEffectiveAddressInstr()) { NewUseOp = NewDefInst->GetLeaMemUseOp(); if (MDIsDirectStackAccessOpnd(NewUseOp, UseFP)) { // We have the code sequence we were searching for when we first saw the // addition of an immediate value to a register, e.g.: // lea ebx,[ebp-2000] // add ebx,1000 // // We are essentially making this sequence into a single instruction: // lea ebx,[ebp-1000] // by adding the immediate value to the address offset. With a stack that grows // downward, it does not matter if we add 1000 to [esp+500] to produce [esp+1500], // or we add 1000 to [ebp-2000] to make [ebp-1000]. Either way, we are simulating the // addition of 1000 as we move up in the stack frame. NewUseOp.addr += ImmOp.value; // perform the address arithmetic addition UltSource = NewUseOp; FPRelative = NewDefInst->HasFPNormalizedToSP(); return true; } else { return false; } } else { return false; } } else { // Not the kind of instruction we need; cut short the recursion. return false; } // NewUseOp is the move source operand that we seek. UseIter = DefInst->FindUse(NewUseOp); assert(UseIter != DefInst->GetLastUse()); NewUseSSANum = UseIter->GetSSANum(); // unused for immediates, used for regs and stack // Recurse return DefInst->TraceUltimateMoveSource(NewUseOp, NewUseSSANum, UltSource, FPRelative); } // end of SMPInstr::TraceUltimateMoveSource() // inst has no code xrefs? bool SMPInstr::HasNoCodeXrefs(void) { SMP_xref_t CurrXrefs; bool FoundCodeXref = false; for (bool ok = CurrXrefs.SMP_first_to(this->GetAddr(), XREF_ALL); ok; ok = CurrXrefs.SMP_next_to()) { ea_t FromAddr = CurrXrefs.GetFrom(); if ((FromAddr != 0) && (CurrXrefs.GetIscode())) { FoundCodeXref = true; break; } } return (!FoundCodeXref); } // end of SMPInstr::HasNoCodeXrefs() // Infer DEF, USE, and RTL SMPoperator types within the instruction based on the type // of operator, the type category of the instruction, and the previously known types // of the operands. bool SMPInstr::InferTypes(void) { bool changed = false; // return value int SSANum; int TypeCategory = SMPTypeCategory[this->SMPcmd.itype]; set<DefOrUse, LessDefUse>::iterator CurrDef; set<DefOrUse, LessDefUse>::iterator CurrUse; op_t DefOp = InitOp, UseOp = InitOp; bool DebugFlag = false; bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer(); bool SafeFunc = this->BasicBlock->GetFunc()->IsSafe(); bool IsMemOp; #if SMP_VERBOSE_DEBUG_INFER_TYPES DebugFlag |= (0 == strcmp("InputMove", this->BasicBlock->GetFunc()->GetFuncName())); #endif if (DebugFlag) { SMP_msg("opcode: %d TypeCategory: %d\n", this->SMPcmd.itype, TypeCategory); } // If we are already finished with all types, return false. if (this->IsTypeInferenceComplete()) return false; if (this->AllDEFsTyped() && this->AllUSEsTyped()) { this->SetTypeInferenceComplete(); return false; } if (this->HasDestMemoryOperand()) { changed |= this->MDFindPointerUse(this->MDGetMemDefOp(), UseFP); } if (this->HasSourceMemoryOperand()) { changed |= this->MDFindPointerUse(this->MDGetMemUseOp(), UseFP); } // The control flow instructions can be handled simply based on their type // and do not need an RTL walk. SMPitype DFAType = this->GetDataFlowType(); bool CallInst = ((DFAType == CALL) || (DFAType == INDIR_CALL) || this->IsTailCall()); ushort IndirCallReg = R_none; if (DebugFlag) { SMP_msg("DFAType: %d CategoryInferenceComplete: %d\n", DFAType, this->IsCategoryInferenceComplete()); } if (DFAType == INDIR_CALL) { op_t TargetOp = this->SMPcmd.Operands[0]; if (TargetOp.type == o_reg) IndirCallReg = TargetOp.reg; } if (((DFAType >= JUMP) && (DFAType <= INDIR_CALL)) || this->IsTailCall()) { // All USEs are either the flags (NUMERIC) or the target address (CODEPTR). // The exceptions are the USE list for interrupt calls, which includes // the caller-saved regs, and indirect calls through a memory // operand, such as call [ebx+esi+20h], where the memory operand // is a CODEPTR but the addressing registers are a BaseReg and // IndexReg as in any other memory addressing, and the saved // regs on any call. CurrUse = this->GetFirstUse(); while (CurrUse != this->GetLastUse()) { UseOp = CurrUse->GetOp(); if (UseOp.is_reg(X86_FLAGS_REG)) CurrUse = this->SetUseType(UseOp, NUMERIC); else if ((CurrUse->GetType() != CODEPTR) && (!(this->MDIsInterruptCall() && (o_reg == UseOp.type))) && (!(CallInst && (o_reg == UseOp.type))) && (!(this->HasSourceMemoryOperand() && (INDIR_CALL == this->GetDataFlowType()) && (o_reg == UseOp.type)))) { CurrUse = this->SetUseType(UseOp, CODEPTR); if (CallInst && (DFAType != INDIR_CALL)) { // If the call is to malloc(), then the DEF of the return // register is of type HEAPPTR. // ****!!!!**** Could have INDIR_CALL resolved to malloc. changed |= this->MDFindMallocCall(UseOp); } } else if ((CurrUse->GetType() != CODEPTR) && CallInst && UseOp.is_reg(IndirCallReg)) { CurrUse = this->SetUseType(UseOp, CODEPTR); } ++CurrUse; } this->SetTypeInferenceComplete(); return true; } // First, see if we can infer something about DEFs and USEs just from the // type category of the instruction. if (!this->IsCategoryInferenceComplete()) { bool MemPropagate = false; switch (TypeCategory) { case 0: // no inference possible just from type category case 1: // no inference possible just from type category case 3: // MOV instructions; inference will come from source to dest in RTL walk. case 5: // binary arithmetic; inference will come in RTL walk. case 10: // binary arithmetic; inference will come in RTL walk. case 11: // push and pop instructions; inference will come in RTL walk. case 12: // exchange instructions; inference will come in RTL walk. this->SetCategoryInferenceComplete(); break; case 2: // Result type is always NUMERIC. case 7: // Result type is always NUMERIC. case 8: // Result type is always NUMERIC. case 9: // Result type is always NUMERIC. case 13: // Result type is always NUMERIC. case 14: // Result type is always NUMERIC. !!!!****!!!! scas should not be here, or don't reset POINTERs to NUMERICs case 15: // Result type is always NUMERIC. CurrDef = this->GetFirstDef(); while (CurrDef != this->GetLastDef()) { if (!IsEqType(NUMERIC, CurrDef->GetType())) { DefOp = CurrDef->GetOp(); SSANum = CurrDef->GetSSANum(); CurrDef = this->SetDefType(DefOp, NUMERIC); changed = true; // Be conservative and only propagate register DEFs and SAFE stack locs. We // can improve this in the future. **!!** bool IsMemOp = (o_reg != DefOp.type); bool MemPropagate = MDIsDirectStackAccessOpnd(DefOp, UseFP); #if SMP_PROPAGATE_MEM_TYPES ; #else // Be conservative and only propagate register DEFs and SAFE stack locs. // We can improve this in the future. **!!** MemPropagate = MemPropagate && SafeFunc; #endif if ((o_reg == DefOp.type) || MemPropagate) { if (this->BasicBlock->IsLocalName(DefOp)) { (void) this->BasicBlock->PropagateLocalDefType(DefOp, NUMERIC, this->GetAddr(), SSANum, IsMemOp); } else { // global name this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false (void) this->BasicBlock->PropagateGlobalDefType(DefOp, NUMERIC, SSANum, IsMemOp); } } } ++CurrDef; } this->SetCategoryInferenceComplete(); break; case 4: // Unary INC, DEC, etc.: dest=source, so type remains the same assert(1 == this->RTL.GetCount()); assert(this->RTL.GetRT(0)->HasRightSubTree()); UseOp = this->RTL.GetRT(0)->GetLeftOperand(); // USE == DEF CurrUse = this->Uses.FindRef(UseOp); assert(CurrUse != this->GetLastUse()); if (UNINIT != CurrUse->GetType()) { // Only one USE, and it has a type assigned, so assign that type // to the DEF. CurrDef = this->GetFirstDef(); while (CurrDef != this->GetLastDef()) { // Two DEFs: EFLAGS is NUMERIC, dest==source DefOp = CurrDef->GetOp(); SSANum = CurrDef->GetSSANum(); if (DefOp.is_reg(X86_FLAGS_REG)) { ; // SetImmedTypes already made it NUMERIC } else { CurrDef = this->SetDefType(DefOp, CurrUse->GetType()); // Be conservative and only propagate register DEFs and SAFE stack locs. We // can improve this in the future. **!!** bool IsMemOp = (o_reg != DefOp.type); MemPropagate = MDIsDirectStackAccessOpnd(DefOp, UseFP); #if SMP_PROPAGATE_MEM_TYPES ; #else // Be conservative and only propagate register DEFs and SAFE stack locs. // We can improve this in the future. **!!** MemPropagate = MemPropagate && SafeFunc; #endif if ((o_reg == DefOp.type) || MemPropagate) { if (this->BasicBlock->IsLocalName(DefOp)) { (void) this->BasicBlock->PropagateLocalDefType(DefOp, CurrUse->GetType(), this->GetAddr(), SSANum, IsMemOp); } else { // global name this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false (void) this->BasicBlock->PropagateGlobalDefType(DefOp, CurrUse->GetType(), SSANum, IsMemOp); } } } ++CurrDef; } this->SetCategoryInferenceComplete(); changed = true; this->SetTypeInferenceComplete(); } break; case 6: // Result is always POINTER DefOp = this->GetFirstDef()->GetOp(); SSANum = this->GetFirstDef()->GetSSANum(); CurrDef = this->SetDefType(DefOp, POINTER); this->SetCategoryInferenceComplete(); changed = true; // Be conservative and only propagate register DEFs and SAFE stack locs. We // can improve this in the future. **!!** IsMemOp = (o_reg != DefOp.type); MemPropagate = MDIsDirectStackAccessOpnd(DefOp, UseFP); #if SMP_PROPAGATE_MEM_TYPES ; #else // Be conservative and only propagate register DEFs and SAFE stack locs. // We can improve this in the future. **!!** MemPropagate = MemPropagate && SafeFunc; #endif if ((o_reg == DefOp.type) || MemPropagate) { if (this->BasicBlock->IsLocalName(DefOp)) { (void) this->BasicBlock->PropagateLocalDefType(DefOp, POINTER, this->GetAddr(), SSANum, IsMemOp); } else { // global name this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false (void) this->BasicBlock->PropagateGlobalDefType(DefOp, POINTER, SSANum, IsMemOp); } } break; default: SMP_msg("ERROR: Unknown type category for %s\n", DisAsmText.GetDisAsm(this->GetAddr())); this->SetCategoryInferenceComplete(); break; } // end switch on TypeCategory } // end if (!CategoryInference) // Walk the RTL and infer types based on operators and operands. if (DebugFlag) { SMP_msg("RTcount: %zu\n", this->RTL.GetCount()); } for (size_t index = 0; index < this->RTL.GetCount(); ++index) { SMPRegTransfer *CurrRT = this->RTL.GetRT(index); if (SMP_NULL_OPERATOR == CurrRT->GetOperator()) // nothing to infer continue; if (!(CurrRT->IsTypeInferenceComplete())) { changed |= this->InferOperatorType(CurrRT); } if (DebugFlag) { SMP_msg("returned from InferOperatorType\n"); } } // end for all RTs in the RTL return changed; } // end of SMPInstr::InferTypes() // Infer the type of an operator within an RT based on the types of its operands and // based on the operator itself. Recurse down the tree if necessary. // Return true if the operator type of the RT is updated. bool SMPInstr::InferOperatorType(SMPRegTransfer *CurrRT) { bool updated = false; bool LeftNumeric, RightNumeric, OperNumeric; bool LeftPointer, RightPointer, OperPointer; bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer(); bool SafeFunc = this->BasicBlock->GetFunc()->IsSafe(); set<DefOrUse, LessDefUse>::iterator CurrDef; set<DefOrUse, LessDefUse>::iterator CurrUse; set<DefOrUse, LessDefUse>::iterator LeftUse; set<DefOrUse, LessDefUse>::iterator RightUse; SMPOperandType LeftType = UNINIT; SMPOperandType RightType = UNINIT; SMPOperandType OperType = UNINIT; op_t UseOp = InitOp, DefOp = InitOp, LeftOp = InitOp, RightOp = InitOp; SMPoperator CurrOp = CurrRT->GetOperator(); bool TypeInferenceFinished = false; #if SMP_VERBOSE_DEBUG_INFER_TYPES bool DebugFlag = false; #if 1 DebugFlag |= (0 == strcmp("InputMove", this->BasicBlock->GetFunc()->GetFuncName())); #endif DebugFlag = DebugFlag || ((this->address == 0x806453b) || (this->address == 0x806453e)); #endif #if SMP_VERBOSE_DEBUG_INFER_TYPES if (DebugFlag) { SMP_msg("Entered InferOperatorType for CurrOp: %d at %x\n", CurrOp, this->GetAddr()); } #endif if (CurrRT->IsTypeInferenceComplete()) { return updated; } switch (CurrOp) { case SMP_NULL_OPERATOR: case SMP_SIGNAL: // signal or raise exception TypeInferenceFinished = true; break; case SMP_CALL: // CALL instruction if (UNINIT == CurrRT->GetOperatorType()) { CurrRT->SetOperatorType(CODEPTR, this); updated = true; UseOp = CurrRT->GetRightOperand(); CurrUse = this->Uses.FindRef(UseOp); assert(CurrUse != this->GetLastUse()); if (UNINIT == CurrUse->GetType()) { CurrUse = this->SetUseType(UseOp, CODEPTR); } else if (CODEPTR != CurrUse->GetType()) { SMP_msg("WARNING: call target is type %d, setting to CODEPTR at %lx in %s\n", CurrUse->GetType(), (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); CurrUse = this->SetUseType(UseOp, CODEPTR); } } TypeInferenceFinished = true; break; case SMP_INPUT: // input from port if (UNINIT == CurrRT->GetOperatorType()) { CurrRT->SetOperatorType(NUMERIC, this); updated = true; } break; case SMP_OUTPUT: // output to port if (UNINIT == CurrRT->GetOperatorType()) { CurrRT->SetOperatorType(NUMERIC, this); updated = true; } break; case SMP_SIGN_EXTEND: case SMP_ZERO_EXTEND: // Should we infer that all operands are NUMERIC? !!!???!!!! break; case SMP_ADDRESS_OF: // take effective address if (UNINIT == CurrRT->GetOperatorType()) { CurrRT->SetOperatorType(POINTER, this); // Left operand is having its address taken, but we cannot infer what its // type is. updated = true; } break; case SMP_U_LEFT_SHIFT: // unsigned left shift case SMP_S_LEFT_SHIFT: // signed left shift case SMP_U_RIGHT_SHIFT: // unsigned right shift case SMP_S_RIGHT_SHIFT: // signed right shift case SMP_ROTATE_LEFT: case SMP_ROTATE_LEFT_CARRY: // rotate left through carry case SMP_ROTATE_RIGHT: case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry case SMP_U_MULTIPLY: case SMP_S_MULTIPLY: case SMP_U_DIVIDE: case SMP_S_DIVIDE: case SMP_U_REMAINDER: case SMP_BITWISE_XOR: case SMP_BITWISE_AND_NOT: case SMP_S_COMPARE: // signed compare (subtraction-based) case SMP_U_COMPARE: // unsigned compare (AND-based) case SMP_GENERAL_COMPARE: case SMP_LESS_THAN: // boolean test operators case SMP_GREATER_THAN: case SMP_LESS_EQUAL: case SMP_GREATER_EQUAL: case SMP_EQUAL: case SMP_NOT_EQUAL: case SMP_LOGICAL_AND: case SMP_LOGICAL_OR: case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC case SMP_REVERSE_SHIFT_U: // all the same to our type system; all NUMERIC case SMP_SHUFFLE: // all the same to our type system; all NUMERIC case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision case SMP_AVERAGE_U: // Average of unsigned operands case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate) case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements case SMP_MAX_S: // dest := signed_max(dest, src) case SMP_MAX_U: // dest := unsigned_max(dest, src) case SMP_MIN_S: // dest := signed_min(dest, src) case SMP_MIN_U: // dest := unsigned_min(dest, src) case SMP_ABSOLUTE_VALUE: // take absolute value case SMP_CONVERT_INT_TO_FP: // convert integer to floating point case SMP_CONVERT_FP_TO_INT: // convert floating point to integer case SMP_CREATE_MASK: // Create AND-mask from operand and byte/word/dword position # in immediate case SMP_INTERLEAVE: // interleave fields from two packed operands; NUMERIC case SMP_CONCATENATE: // all the same to our type system; all NUMERIC case SMP_ENCRYPTION_OPERATION: // encryption or decryption bit manipulation operation case SMP_EXTRACT_ZERO_EXTEND: // Extract sub-reg and zero-extend to reg length if (UNINIT == CurrRT->GetOperatorType()) { CurrRT->SetOperatorType(NUMERIC, this); updated = true; } // Left operand should be NUMERIC if it exists. UseOp = CurrRT->GetLeftOperand(); if (UseOp.type != o_void) { CurrUse = this->Uses.FindRef(UseOp); if (CurrUse == this->GetLastUse()) { SMP_msg("SERIOUS WARNING: Adding missing USE of "); PrintOperand(UseOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->Uses.SetRef(UseOp, NUMERIC, -1); updated = true; } else if (UNINIT == CurrUse->GetType()) { CurrUse = this->SetUseType(UseOp, NUMERIC); updated = true; } } // Right operand should be NUMERIC if it exists. if (CurrRT->HasRightSubTree()) { // Recurse into subtree #if SMP_AGGRESSIVE_TYPE_INFERENCE if (UNINIT == CurrRT->GetRightTree()->GetOperatorType()) { CurrRT->GetRightTree()->SetOperatorType(NUMERIC, this); } #endif updated |= this->InferOperatorType(CurrRT->GetRightTree()); } else { UseOp = CurrRT->GetRightOperand(); if (UseOp.type != o_void) { CurrUse = this->Uses.FindRef(UseOp); if (CurrUse == this->GetLastUse()) { SMP_msg("SERIOUS WARNING: Adding missing USE of "); PrintOperand(UseOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->Uses.SetRef(UseOp, NUMERIC, -1); updated = true; } else if (UNINIT == CurrUse->GetType()) { CurrUse = this->SetUseType(UseOp, NUMERIC); updated = true; } } } break; case SMP_BITWISE_NOT: // unary operator case SMP_NEGATE: // unary negation UseOp = CurrRT->GetLeftOperand(); assert(o_void != UseOp.type); CurrUse = this->Uses.FindRef(UseOp); if (CurrUse == this->GetLastUse()) { SMP_msg("SERIOUS WARNING: Adding missing USE of "); PrintOperand(UseOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); updated = true; } else { OperType = CurrRT->GetOperatorType(); LeftType = CurrUse->GetType(); // Only tricky cases are the negation of a POINTER or PTROFFSET. // Negation of PTROFFSET could be inefficient code that computed // PTR1 - PTR2 and later corrected it to PTR2 - PTR1 by negation. // The type remains PTROFFSET. Negating a POINTER could be an unusual // case similar to subtracting a POINTER from a NUMERIC. See comments // in the SMP_ADD case below, and also the SMP_SUBTRACT case. if (LeftType == PTROFFSET) { // Override any prior operator type, in case PTROFFSET was inferred late // in our analysis and the operator was set to NUMERIC. CurrRT->SetOperatorType(PTROFFSET, this); updated = true; if (CurrOp == SMP_BITWISE_NOT) SMP_msg("INFO: NOT of PTROFFSET at %lx %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } else if (IsDataPtr(LeftType)) { // Override any prior operator type, in case POINTER was inferred late // in our analysis and the operator was set to NUMERIC. CurrRT->SetOperatorType(NEGATEDPTR, this); SMP_msg("INFO: Pointer negated at %lx %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); updated = true; } else if (OperType == UNINIT) { // Default to NUMERIC for most negations. CurrRT->SetOperatorType(NUMERIC, this); // But, leave left operand type alone, in case an UNINIT operand // might be determined later to be PTROFFSET or NEGATEDPTR. // Leaving it alone causes us not to set TypeInferenceFinished to true // at the end of this function in the UNINIT case. updated = true; } } break; case SMP_INCREMENT: case SMP_DECREMENT: // The type of the left operand is propagated to the operator, or vice // versa, whichever receives a type first. assert(!CurrRT->HasRightSubTree()); UseOp = CurrRT->GetLeftOperand(); assert(o_void != UseOp.type); CurrUse = this->Uses.FindRef(UseOp); if (CurrUse == this->GetLastUse()) { SMP_msg("SERIOUS WARNING: Adding missing USE of "); PrintOperand(UseOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->Uses.SetRef(UseOp); updated = true; break; } if (UNINIT == CurrRT->GetOperatorType()) { if (UNINIT != CurrUse->GetType()) { // Propagate operand type up to the operator. CurrRT->SetOperatorType(CurrUse->GetType(), this); updated = true; } } else if (UNINIT == CurrUse->GetType()) { // Propagate operator type to operand. CurrUse = this->SetUseType(UseOp, CurrRT->GetOperatorType()); updated = true; } break; case SMP_ADD: case SMP_ADD_CARRY: // add with carry case SMP_BITWISE_AND: case SMP_BITWISE_OR: // Extract the current types of right and left operands and the operator. OperType = CurrRT->GetOperatorType(); LeftOp = CurrRT->GetLeftOperand(); CurrUse = this->Uses.FindRef(LeftOp); assert(CurrUse != this->GetLastUse()); // found it LeftType = CurrUse->GetType(); if (CurrRT->HasRightSubTree()) { updated |= this->InferOperatorType(CurrRT->GetRightTree()); RightType = CurrRT->GetRightTree()->GetOperatorType(); } else { RightOp = CurrRT->GetRightOperand(); if (o_void == RightOp.type) { SMP_msg("ERROR: void operand at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); return updated; } else { CurrUse = this->Uses.FindRef(RightOp); if (CurrUse == this->GetLastUse()) { SMP_msg("SERIOUS WARNING: Adding missing USE of "); PrintOperand(RightOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->Uses.SetRef(RightOp); updated = true; break; } else { RightType = CurrUse->GetType(); } } } // We have to know both operand types to infer the operator, or know the // operator type and one operand type to infer the other operand type. if ((UNINIT == OperType) && ((UNINIT == LeftType) || (UNINIT == RightType))) break; // If both operands are NUMERIC, operator and result are NUMERIC. // If one operand is NUMERIC and the other is a pointer type, // then the ADD operator and the result will inherit this second type, // while AND and OR operators will remain UNINIT (we don't know what // type "ptr AND 0xfffffff8" has until we see how it is used). LeftNumeric = IsEqType(NUMERIC, LeftType); RightNumeric = IsEqType(NUMERIC, RightType); LeftPointer = IsDataPtr(LeftType); RightPointer = IsDataPtr(RightType); if (UNINIT == OperType) { // Infer operator type from left and right operands. if (LeftNumeric && RightNumeric) { CurrRT->SetOperatorType(NUMERIC, this); updated = true; break; } else if (LeftNumeric || RightNumeric) { // ADD of NUMERIC to non-NUMERIC preserves non-NUMERIC type. // AND and OR operations should leave the operator UNINIT for now. if (LeftNumeric && (UNINIT != RightType) && ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp))) { CurrRT->SetOperatorType(RightType, this); updated = true; break; } else if (RightNumeric && (UNINIT != LeftType) && ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp))) { CurrRT->SetOperatorType(LeftType, this); updated = true; break; } } else if (LeftPointer && RightPointer) { // Arithmetic on two pointers if ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp)) { CurrRT->SetOperatorType(UNKNOWN, this); updated = true; } else { // bitwise AND or OR of two pointers SMP_msg("WARNING: hash of two pointers at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); // hash operation? leave operator as UNINIT } break; } else if ((LeftPointer && IsEqType(RightType, PTROFFSET)) || (RightPointer && IsEqType(LeftType, PTROFFSET))) { // Arithmetic on PTR and PTROFFSET if ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp)) { // We assume (A-B) is being added to B or vice versa **!!** CurrRT->SetOperatorType(POINTER, this); updated = true; } else { // bitwise AND or OR of pointer and pointer difference SMP_msg("WARNING: hash of PTROFFSET and POINTER at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); // hash operation? leave operator as UNINIT } break; } else if ((LeftPointer && IsEqType(RightType, NEGATEDPTR)) || (RightPointer && IsEqType(LeftType, NEGATEDPTR))) { // Compiler optimizations can take a ptr expression such as: // PTR1 - PTR2 + 1 // and hoist the loop-invariant subexpression " - PTR2 + 1" // out of the loop as "1 - PTR2", which produces a NEGATEDPTR type. // When PTR1 gets its value determined inside the loop, then the // addition of PTR1 finally happens, producing a PTROFFSET type, // which is what the whole expression is. if ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp)) { CurrRT->SetOperatorType(PTROFFSET, this); updated = true; } else { // bitwise AND or OR of pointer and pointer difference SMP_msg("WARNING: hash of NEGATEDPTR and POINTER at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); // hash operation? leave operator as UNINIT } break; } } // end if UNINIT operator type else { // operator has type other than UNINIT // We make add-with-carry and subtract-with-borrow exceptions // to the type propagation. LeftOp could have POINTER type // inferred later; these instructions can change the type of // the register from POINTER to NUMERIC, unlike regular // add and subtract opcodes. OperNumeric = IsEqType(NUMERIC, OperType); OperPointer = IsDataPtr(OperType); if (OperNumeric) { if ((UNINIT == LeftType) && (SMP_ADD_CARRY != CurrOp)) { CurrUse = this->SetUseType(LeftOp, CurrRT->GetOperatorType()); updated = true; assert(CurrUse != this->GetLastUse()); break; } if (CurrRT->HasRightSubTree()) { // Must need to iterate through the right tree again, as the operator // has been typed. if (UNINIT == RightType) { CurrRT->GetRightTree()->SetOperatorType(CurrRT->GetOperatorType(), this); updated = true; updated |= this->InferOperatorType(CurrRT->GetRightTree()); } break; } else { // right operand; propagate operator type if needed if (UNINIT == RightType) { CurrUse = this->SetUseType(RightOp, CurrRT->GetOperatorType()); updated = true; assert(CurrUse != this->GetLastUse()); break; } } } } break; case SMP_SUBTRACT: case SMP_SUBTRACT_BORROW: // subtract with borrow // Extract the current types of right and left operands and the operator. OperType = CurrRT->GetOperatorType(); LeftOp = CurrRT->GetLeftOperand(); LeftUse = this->Uses.FindRef(LeftOp); assert(LeftUse != this->GetLastUse()); // found it LeftType = LeftUse->GetType(); if (CurrRT->HasRightSubTree()) { updated |= this->InferOperatorType(CurrRT->GetRightTree()); RightType = CurrRT->GetRightTree()->GetOperatorType(); } else { RightOp = CurrRT->GetRightOperand(); if (o_void == RightOp.type) { SMP_msg("ERROR: void operand in %s\n", DisAsmText.GetDisAsm(this->GetAddr())); return false; } else { RightUse = this->Uses.FindRef(RightOp); if (RightUse == this->GetLastUse()) { SMP_msg("WARNING: Adding missing USE of "); PrintOperand(RightOp); SMP_msg(" in %s\n", DisAsmText.GetDisAsm(this->GetAddr())); this->Uses.SetRef(RightOp); updated = true; break; } else { RightType = RightUse->GetType(); } } } // If left operand is NUMERIC, operator is NUMERIC. LeftNumeric = IsEqType(NUMERIC, LeftType); RightNumeric = IsEqType(NUMERIC, RightType); LeftPointer = IsDataPtr(LeftType); RightPointer = IsDataPtr(RightType); if (LeftNumeric) { // Subtracting anything from a NUMERIC leaves it NUMERIC or NEGATEDPTR, // in the special case in which a POINTER is subtracted from a NUMERIC. // See NEGATEDPTR comments in the ADD/AND operators case above. if (RightPointer) { CurrRT->SetOperatorType(NEGATEDPTR, this); updated = true; } else if (UNINIT == OperType) { CurrRT->SetOperatorType(NUMERIC, this); updated = true; } else if (IsNotEqType(NUMERIC, OperType) && IsNotEqType(NEGATEDPTR, OperType)) { SMP_msg("ERROR: SMP_SUBTRACT from NUMERIC should be NUMERIC or NEGATEDPTR operator."); SMP_msg(" Operator type is %d in: %s\n", OperType, DisAsmText.GetDisAsm(this->GetAddr())); } #if 0 if (!RightNumeric) { // Right operand is being used as a NUMERIC, so propagate NUMERIC to it. if (CurrRT->HasRightSubTree()) { CurrRT->GetRightTree()->SetOperatorType(NUMERIC, this); } else { RightUse = this->SetUseType(RightOp, NUMERIC); } updated = true; } #endif } // end if LeftNumeric else if (LeftPointer) { if (UNINIT == OperType) { // If we subtract another pointer type, we produce PTROFFSET. if (RightPointer) { CurrRT->SetOperatorType(PTROFFSET, this); updated = true; } else if (RightType == PTROFFSET) { // We assume B - (B - A) == A **!!** CurrRT->SetOperatorType(POINTER, this); SMP_msg("WARNING: PTR - PTROFFSET produces PTR at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); updated = true; } else if (RightNumeric) { // pointer minus NUMERIC keeps same pointer type CurrRT->SetOperatorType(LeftType, this); updated = true; } } else { // we have an operator type for the SMP_SUBTRACT OperNumeric = IsEqType(NUMERIC, OperType); OperPointer = IsDataPtr(OperType); if (CurrRT->HasRightSubTree()) { // Might need to iterate through the right tree again, if its operator // can be typed. if (UNINIT == RightType) { if (OperPointer) { // PTR := PTR - ?? ==> ?? is NUMERIC Why? ?? could be PTROFFSET CurrRT->GetRightTree()->SetOperatorType(NUMERIC, this); updated = true; updated |= this->InferOperatorType(CurrRT->GetRightTree()); } else if (OperType == PTROFFSET) { // PTROFFSET := PTR - ?? ==> ?? is PTR CurrRT->GetRightTree()->SetOperatorType(LeftType, this); updated = true; updated |= this->InferOperatorType(CurrRT->GetRightTree()); } else if (OperNumeric) { SMP_msg("WARNING: PTR - ?? produces NUMERIC at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } } break; } else { // right operand; propagate operator type if needed if (UNINIT == RightType) { if (OperPointer) { // PTR := PTR - ?? ==> ?? is NUMERIC Why? ?? could be PTROFFSET RightUse = this->SetUseType(RightOp, NUMERIC); updated = true; assert(RightUse != this->GetLastUse()); } else if (OperType == PTROFFSET) { // PTROFFSET := PTR - ?? ==> ?? is PTR RightUse = this->SetUseType(RightOp, LeftType); updated = true; } else if (OperNumeric) { SMP_msg("WARNING: PTR - ?? produces NUMERIC at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } break; } } } // end if OperType is UNINIT ... else ... } // end if LeftNumeric ... else if LeftPointer ... else if (UNINIT == LeftType) { // We make add-with-carry and subtract-with-borrow exceptions // to the type propagation. LeftOp could have POINTER type // inferred later; these instructions can change the type of // the register from POINTER to NUMERIC, unlike regular // add and subtract opcodes. if ((UNINIT != OperType) && (SMP_SUBTRACT_BORROW != CurrOp)) { LeftUse = this->SetUseType(LeftOp, OperType); assert(LeftUse != this->GetLastUse()); updated = true; } } break; case SMP_ASSIGN: // Extract the current types of right and left operands and SMP_ASSIGN operator. OperType = CurrRT->GetOperatorType(); DefOp = CurrRT->GetLeftOperand(); CurrDef = this->Defs.FindRef(DefOp); assert(CurrDef != this->GetLastDef()); // found it LeftType = CurrDef->GetType(); if (CurrRT->HasRightSubTree()) { updated |= this->InferOperatorType(CurrRT->GetRightTree()); RightType = CurrRT->GetRightTree()->GetOperatorType(); } else { UseOp = CurrRT->GetRightOperand(); if (o_void == UseOp.type) { SMP_msg("ERROR: void operand for SMP_ASSIGN in %s\n", DisAsmText.GetDisAsm(this->GetAddr())); return false; } else { CurrUse = this->Uses.FindRef(UseOp); if (CurrUse == this->GetLastUse()) { SMP_msg("WARNING: Adding missing USE of "); PrintOperand(UseOp); SMP_msg(" in %s\n", DisAsmText.GetDisAsm(this->GetAddr())); this->Uses.SetRef(UseOp); updated = true; break; } else { RightType = CurrUse->GetType(); } } } #if SMP_VERBOSE_DEBUG_INFER_TYPES if (DebugFlag) { SMP_msg("%x LeftType: %d OperatorType: %d RightType: %d\n", this->address, LeftType, OperType, RightType); } #endif if ((UNINIT == RightType) && (UNINIT == LeftType)) { break; } else if (UNINIT == OperType) { // UNINIT SMP_ASSIGN operator, but either LeftType or RightType is not UNINIT. bool UpdatedOperType = false; if (UNINIT != RightType) { // We have to special case conditional moves. Only if both operands // (the source and the prior value of the potential destination, // which was added to the USE set by BuildMoveRTL()) agree in type // can we propagate their common type to the operator and ultimately // to the DEF. if ((!this->MDIsConditionalMoveInstr()) || this->Uses.TypesAgreeNoFlags()) { CurrRT->SetOperatorType(RightType, this); updated = true; OperType = RightType; UpdatedOperType = true; } } else { // LeftType must not be UNINIT CurrRT->SetOperatorType(LeftType, this); updated = true; UpdatedOperType = true; } // Speed up type propagation by passing the RightType/OperType to the Def // on this iteration. if (UpdatedOperType) { // Propagate the new DEF type unless it is an indirect memory access. // Future: Propagate until re-DEF of addressing register terminates // the propagation. **!!** CurrDef = this->SetDefType(DefOp, OperType); LeftType = OperType; if (!MDIsIndirectMemoryOpnd(DefOp, this->BasicBlock->GetFunc()->UsesFramePointer())) { bool IsMemOp = (o_reg != DefOp.type); bool MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP); #if SMP_PROPAGATE_MEM_TYPES ; #else // Be conservative and only propagate register DEFs and SAFE stack locs. // We can improve this in the future. **!!** MemPropagate = MemPropagate && SafeFunc; #endif if ((o_reg == DefOp.type) || MemPropagate) { int SSANum = CurrDef->GetSSANum(); if (this->BasicBlock->IsLocalName(DefOp)) { (void) this->BasicBlock->PropagateLocalDefType(DefOp, LeftType, this->GetAddr(), SSANum, IsMemOp); } else { // global name this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false (void) this->BasicBlock->PropagateGlobalDefType(DefOp, LeftType, SSANum, IsMemOp); } } } } break; } else if (UNINIT == LeftType) { // SMP_ASSIGN operator has type, so propagate it. CurrDef = this->SetDefType(DefOp, OperType); LeftType = OperType; updated = true; // Propagate the new DEF type unless it is an indirect memory access. // Future: Propagate until re-DEF of addressing register terminates // the propagation. **!!** if (!MDIsIndirectMemoryOpnd(DefOp, this->BasicBlock->GetFunc()->UsesFramePointer())) { bool IsMemOp = (o_reg != DefOp.type); bool MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP); #if SMP_PROPAGATE_MEM_TYPES ; #else // Be conservative and only propagate register DEFs and SAFE stack locs. // We can improve this in the future. **!!** MemPropagate = MemPropagate && SafeFunc; #endif if ((o_reg == DefOp.type) || MemPropagate) { int SSANum = CurrDef->GetSSANum(); if (this->BasicBlock->IsLocalName(DefOp)) { (void) this->BasicBlock->PropagateLocalDefType(DefOp, LeftType, this->GetAddr(), SSANum, IsMemOp); } else { // global name this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false (void) this->BasicBlock->PropagateGlobalDefType(DefOp, LeftType, SSANum, IsMemOp); } } } break; } else if (UNINIT == RightType) { // SMP_ASSIGN operator has type, so propagate it. if (CurrRT->HasRightSubTree()) { CurrRT->GetRightTree()->SetOperatorType(OperType, this); updated = true; updated |= this->InferOperatorType(CurrRT->GetRightTree()); } else { // For conditional moves, propagate to the pseudo-USE of the // destination register as well as the source operand. if (this->MDIsConditionalMoveInstr()) { CurrUse = this->FindUse(DefOp); assert(CurrUse != this->GetLastUse()); if (UNINIT == CurrUse->GetType()) CurrUse = this->SetUseType(DefOp, OperType); else if (OperType != CurrUse->GetType()) { SMP_msg("WARNING: Avoiding lattice oscillation from type %d to %d at %lx for: ", CurrUse->GetType(), OperType, (unsigned long) this->address); PrintOperand(CurrUse->GetOp()); SMP_msg("\n"); } } CurrUse = this->SetUseType(UseOp, OperType); updated = true; } break; } break; default: SMP_msg("ERROR: Unknown operator %d at %lx in %s\n", CurrOp, (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); break; } // end switch on operator // Determine if type inference is finished for this register transfer. if (updated && (!TypeInferenceFinished)) { bool FinishedRight = false; bool FinishedLeft = false; bool FinishedOperator = (CurrRT->GetOperatorType() != UNINIT); if (FinishedOperator) { switch (CurrOp) { case SMP_INPUT: // input from port case SMP_OUTPUT: // output to port case SMP_SIGN_EXTEND: case SMP_ZERO_EXTEND: case SMP_ADDRESS_OF: // take effective address case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC case SMP_BITWISE_NOT: // unary operator case SMP_NEGATE: // unary negation case SMP_DECREMENT: case SMP_INCREMENT: case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC // Unary operators have no right operand. FinishedRight = true; break; default: // All binary operators come here if (CurrRT->HasRightSubTree()) { FinishedRight = CurrRT->GetRightTree()->IsTypeInferenceComplete(); } else { UseOp = CurrRT->GetRightOperand(); if (UseOp.type != o_void) { CurrUse = this->Uses.FindRef(UseOp); assert(CurrUse != this->GetLastUse()); FinishedRight = (CurrUse->GetType() != UNINIT); } else { // if o_void, no further type inference on it is possible. FinishedRight = true; } } break; } // end switch on CurrOp if (FinishedRight) { // no point checking left op if right op is not finished DefOp = CurrRT->GetLeftOperand(); if (DefOp.type != o_void) { if (SMP_ASSIGN == CurrOp) { CurrDef = this->Defs.FindRef(DefOp); assert(CurrDef != this->GetLastDef()); FinishedLeft = (CurrDef->GetType() != UNINIT); } else { // not ASSIGN, so really a UseOp not DefOp CurrUse = this->Uses.FindRef(DefOp); assert(CurrUse != this->GetLastUse()); FinishedLeft = (CurrUse->GetType() != UNINIT); } } else { // if o_void, no further type inference on it is possible. FinishedLeft = true; } } TypeInferenceFinished = (FinishedLeft && FinishedRight); } // end if (FinishedOperator) } // end if (updated && (!TypeInferenceFinished)) if (TypeInferenceFinished) { CurrRT->SetTypeInferenceComplete(); } return updated; } // end of SMPInstr::InferOperatorType() // Transfer function: Does operator propagate signedness of its operands to its result? bool SMPInstr::DoesOperatorTransferSign(SMPoperator CurrOp) { bool transfer = false; switch (CurrOp) { case SMP_NULL_OPERATOR: case SMP_CALL: // CALL instruction case SMP_INPUT: // input from port case SMP_OUTPUT: // output to port case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC case SMP_SIGNAL: // signal or raise exception // No concept of signedness for some operators break; case SMP_ADDRESS_OF: // take effective address case SMP_U_LEFT_SHIFT: // unsigned left shift case SMP_U_RIGHT_SHIFT: // unsigned right shift case SMP_ROTATE_LEFT: case SMP_ROTATE_LEFT_CARRY: // rotate left through carry case SMP_ROTATE_RIGHT: case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry case SMP_U_MULTIPLY: case SMP_U_DIVIDE: case SMP_U_REMAINDER: case SMP_ZERO_EXTEND: case SMP_BITWISE_NOT: // unary operator case SMP_BITWISE_XOR: case SMP_BITWISE_AND_NOT: case SMP_S_COMPARE: // signed compare (subtraction-based) case SMP_U_COMPARE: // unsigned compare (AND-based) case SMP_GENERAL_COMPARE: case SMP_S_LEFT_SHIFT: // signed left shift case SMP_S_RIGHT_SHIFT: // signed right shift case SMP_S_MULTIPLY: case SMP_S_DIVIDE: case SMP_SIGN_EXTEND: case SMP_NEGATE: // unary negation case SMP_LESS_THAN: // boolean test operators case SMP_GREATER_THAN: case SMP_LESS_EQUAL: case SMP_GREATER_EQUAL: case SMP_ENCRYPTION_OPERATION: // encryption or decryption bit manipulation operation case SMP_EXTRACT_ZERO_EXTEND: // Extract sub-reg and zero-extend to reg length // Inherently unsigned and signed operators force the signedness // of their results, rather than propagating the signedness of // their operands. break; case SMP_DECREMENT: case SMP_INCREMENT: case SMP_ADD: case SMP_ADD_CARRY: // add with carry case SMP_SUBTRACT: case SMP_SUBTRACT_BORROW: // subtract with borrow case SMP_ASSIGN: case SMP_BITWISE_AND: case SMP_BITWISE_OR: case SMP_EQUAL: case SMP_NOT_EQUAL: case SMP_LOGICAL_AND: case SMP_LOGICAL_OR: case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result transfer = true; break; case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC case SMP_REVERSE_SHIFT_U: // Shift right operand by bit count in left operand case SMP_SHUFFLE: // Shuffle bytes, words, etc. within destination operation per source mask case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision case SMP_AVERAGE_U: // Average of unsigned operands case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate) case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements case SMP_MAX_S: // dest := signed_max(dest, src) case SMP_MAX_U: // dest := unsigned_max(dest, src) case SMP_MIN_S: // dest := signed_min(dest, src) case SMP_MIN_U: // dest := unsigned_min(dest, src) case SMP_ABSOLUTE_VALUE: // take absolute value case SMP_CONVERT_INT_TO_FP: // convert integer to floating point case SMP_CONVERT_FP_TO_INT: // convert floating point to integer case SMP_CREATE_MASK: // Create AND-mask from operand and byte/word/dword position # in immediate case SMP_INTERLEAVE: // extended-precision interleaving of bytes or words or dwords etc.; NUMERIC case SMP_CONCATENATE: // extended-precision concatenation; NUMERIC transfer = true; break; default: SMP_msg("ERROR: Unknown operator in %s\n", DisAsmText.GetDisAsm(this->GetAddr())); break; } // end switch on operator return transfer; } // end of SMPInstr::DoesOperatorTransferSign() // Adjust signedness based on SSA def-use info. void SMPInstr::MDFixupSignedness(void) { set<DefOrUse, LessDefUse>::iterator DefIter = this->GetFirstNonFlagsDef(); set<DefOrUse, LessDefUse>::iterator UseIter; unsigned short SignMask; op_t DefOp = InitOp; int DefSSANum; int IdiomCode; bool UnderflowOpcode = this->MDIsUnderflowingOpcode(); bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if (DefIter != this->GetLastDef()) { DefOp = DefIter->GetOp(); DefSSANum = DefIter->GetSSANum(); } int TypeGroup = SMPTypeCategory[this->SMPcmd.itype]; if (this->IsNop()) TypeGroup = 1; // no-op idioms need their category reset if (5 == TypeGroup) { this->SetAddSubSourceType(); } if (this->MDIsSignedLoad(SignMask)) { // Compilers can generate zero-extensions just to clear unused bits in a register. // We can wrongly infer UNSIGNED for the DEF as a result. The problem can be detected // by seeing whether the zeroed-out bits are ever used. If all USEs of this register DEF // only use the same number of bits as the source operand for the move, then the // zero-extension bits do not mean anything, so we reset the signedness to unknown. // This will hopefully not conflict with other sources of an UNSIGNED inference that // could also have occurred (e.g. a MEDS type of POINTER is unlikely to occur with a // zero-extended operand). if (SignMask == FG_MASK_UNSIGNED) { op_t MoveSource = this->GetMoveSource(); size_t SourceBitWidth = 8 * GetOpDataSize(MoveSource); assert(DefIter != this->GetLastDef()); if (!(IsMemOperand(DefOp) || MDIsFlagsReg(DefOp))) { CanonicalizeOpnd(DefOp); int DefHashValue = HashGlobalNameAndSSA(DefOp, DefSSANum); struct FineGrainedInfo DefUseFGInfo; bool LocalName = this->BasicBlock->IsLocalName(DefOp); if (LocalName) { DefUseFGInfo = this->BasicBlock->GetUseFGInfo(DefHashValue); } else { DefUseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(DefHashValue); } size_t DefUseMaxBitWidth = LargestBitWidthFromMask(DefUseFGInfo.SizeInfo); if ((DefUseMaxBitWidth <= SourceBitWidth) && (0 != DefUseMaxBitWidth)) { // We never use more bits than the original source had before it was zero-extended. // We exclude the bitwidth==0 special case that signals that it was only used in // a zero-extension or sign-extension move. if (LocalName) { this->BasicBlock->ClearDefSignedness(DefHashValue); } else { this->BasicBlock->GetFunc()->ClearDefSignedness(DefHashValue); } } } } } // Second case: Subtraction of two values, each of which is just a condition code. // This can happen in optimized code that tests for which of two condition codes // has been set, e.g.: // [arithmetic operation or comparison] // seta al ; if "above" flag then al := 1 else al := 0 // setb bl ; if "below" flag then bl := 1 else bl := 0 // mov ecx,eax ; make copy of eax (including al) into ecx // sub al,bl ; al := al - bl // The resulting value in al will be 1 if the above flag was set, -1 if below, 0 if neither. // We see the seta/setb as implying unsigned values, which is true, but if al becomes -1 // we do not want to consider this to be an underflow. // The same logic applies to overflow opcodes: lea edx,[eax+eax-1] is hard to classify as // potential underflow or overflow. If eax comes from a condition code (seta eax), the values // will be small and no overflow check is needed. int UseSSANum; bool BenignUnderflow = false; if (UnderflowOpcode && MDIsDataFlowOpnd(DefOp, UseFP)) { UseIter = this->FindUse(DefOp); // DEF is also USE in subtraction assert(UseIter != this->GetLastUse()); UseSSANum = UseIter->GetSSANum(); if (this->IsOpSourceConditionCode(DefOp, UseSSANum)) { // If we had a decrement opcode, we are done, and underflow is benign. if (this->MDIsDecrement()) { BenignUnderflow = true; } else { // For subtraction, need to see if BOTH operands came from condition codes (flags). op_t UseOp = this->GetUseOnlyAddSubOp(); if (MDIsDataFlowOpnd(UseOp, UseFP)) { CanonicalizeOpnd(UseOp); UseIter = this->FindUse(UseOp); assert(UseIter != this->GetLastUse()); UseSSANum = UseIter->GetSSANum(); BenignUnderflow = this->IsOpSourceConditionCode(UseOp, UseSSANum); } } } if (BenignUnderflow) { // We detected a subtraction or decrement that is permitted to produce a negative // result. This means that the DEF is really SIGNED. We don't want to emit // any underflow checks on this instruction, and we also want to propagate SIGNED // as the type of DEF. this->SetSuppressNumericAnnotation(); SignMask = FG_MASK_SIGNED; struct FineGrainedInfo NewFG; NewFG.SignMiscInfo = SignMask; NewFG.SizeInfo = 0; // Note that old FG info should be null at this point in FG inference, // so we only have to pass in the SIGNED bits. bool MapsChanged = this->UpdateDefOpFGInfo(DefOp, NewFG); } } // end if Underflow on register if (this->MDIsDefiniteBenignUnderflowOpcode(IdiomCode) || this->MDIsSignBitFill()) { // We have a pattern that changes sign, e.g. sbb edx,edx puts // 0 or -1 in edx; likewise for sar edx,31. This needs no annotation. this->SetSuppressNumericAnnotation(); // Furthermore, if we find add edx,positive_immediate_value // in the SSA chain for this instruction, then we don't want // to get false positives from the addition, which often changes // the temporarily SIGNED value back to its original UNSIGNED: // sbb edx,edx ; if edx is UNSIGNED, false positive when we produce -1. // add edx,2 ; another false positive when we convert (0 or -1) to (1 or 2). this->GetBlock()->SuppressAnnotOnSignChangingAddition(DefOp, DefSSANum, this->GetAddr()); } return; } // end of SMPInstr::MDFixupSignedness() // Initial inferences (if any) about FG info of operand based solely on the RTL operator type above it in RTL. bool SMPInstr::InitFGInfoFromOperator(SMPoperator CurrOp, struct FineGrainedInfo &InitFG) { bool changed = false; switch (CurrOp) { case SMP_NULL_OPERATOR: break; case SMP_CALL: // CALL instruction InitFG.SignMiscInfo |= FG_MASK_UNSIGNED; // target address is unsigned 32-bit InitFG.SizeInfo |= (MD_NORMAL_BITWIDTH_MASK | FG_MASK_CODEPOINTER); changed = true; break; case SMP_INPUT: // input from port case SMP_OUTPUT: // output to port case SMP_ADDRESS_OF: // take effective address case SMP_U_COMPARE: // unsigned compare (AND-based) case SMP_S_COMPARE: // signed compare (subtraction-based) case SMP_GENERAL_COMPARE: // NOTE: The AND-based and subtraction-based comparisons are used // on lots of operands of all types, and the conditional jump that // follows determines signedness, not the operator. break; case SMP_U_LEFT_SHIFT: // unsigned left shift #if STARS_NO_SHIFT_SIGNEDNESS_IN_SCALEFACTOR if (!this->MDIsLoadEffectiveAddressInstr()) { // The left shift in the RTL for lea edx,[ebx+eax*4] // does not imply that EAX is UNSIGNED. For other // unsigned left shift RTLs, UNSIGNED is fine. InitFG.SignMiscInfo |= FG_MASK_UNSIGNED; changed = true; } break; #endif case SMP_U_RIGHT_SHIFT: // unsigned right shift case SMP_ROTATE_LEFT: case SMP_ROTATE_LEFT_CARRY: // rotate left through carry case SMP_ROTATE_RIGHT: case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry case SMP_U_MULTIPLY: case SMP_U_DIVIDE: case SMP_U_REMAINDER: case SMP_ZERO_EXTEND: case SMP_BITWISE_NOT: // unary operator case SMP_BITWISE_XOR: case SMP_BITWISE_AND_NOT: InitFG.SignMiscInfo |= FG_MASK_UNSIGNED; changed = true; break; case SMP_S_LEFT_SHIFT: // signed left shift case SMP_S_RIGHT_SHIFT: // signed right shift case SMP_S_MULTIPLY: case SMP_S_DIVIDE: case SMP_SIGN_EXTEND: case SMP_NEGATE: // unary negation case SMP_LESS_THAN: // boolean test operators case SMP_GREATER_THAN: case SMP_LESS_EQUAL: case SMP_GREATER_EQUAL: // Special case: If signed multiply operator, it might sometimes // be used for unsigned operands when upper bits of the result // are discarded, because there is no difference in the result bits // between unsigned and signed multiplication when only the lower // N bits are retained and the upper N bits are discarded. if ((SMP_S_MULTIPLY == CurrOp) && (!(this->MDIsSignedArithmetic()))) { break; } InitFG.SignMiscInfo |= FG_MASK_SIGNED; changed = true; break; case SMP_DECREMENT: case SMP_INCREMENT: case SMP_ADD: case SMP_ADD_CARRY: // add with carry case SMP_SUBTRACT: case SMP_SUBTRACT_BORROW: // subtract with borrow case SMP_ASSIGN: case SMP_BITWISE_AND: case SMP_BITWISE_OR: case SMP_EQUAL: case SMP_NOT_EQUAL: case SMP_LOGICAL_AND: case SMP_LOGICAL_OR: case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC case SMP_SHUFFLE: // Shuffle bytes, words, etc. within destination operation per source mask case SMP_CREATE_MASK: // Create AND-mask from operand and byte/word/dword position # in immediate case SMP_SIGNAL: // signal or raise exception break; case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC InitFG.SignMiscInfo |= FG_MASK_SIGNED; InitFG.SizeInfo |= FG_MASK_FLOAT_MMX; changed = true; break; case SMP_REVERSE_SHIFT_U: // Shift right operand by bit count in left operand case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision case SMP_MAX_S: // dest := signed_max(dest, src) case SMP_MIN_S: // dest := signed_min(dest, src) case SMP_ABSOLUTE_VALUE: // take absolute value case SMP_CONVERT_INT_TO_FP: // convert integer to floating point case SMP_CONVERT_FP_TO_INT: // convert floating point to integer InitFG.SignMiscInfo |= FG_MASK_SIGNED; InitFG.SizeInfo |= (FG_MASK_FLOAT_MMX | FG_MASK_BITWIDTH_128); changed = true; break; case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision case SMP_AVERAGE_U: // Average of unsigned operands case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate) case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements case SMP_MAX_U: // dest := unsigned_max(dest, src) case SMP_MIN_U: // dest := unsigned_min(dest, src) case SMP_ENCRYPTION_OPERATION: // encryption or decryption bit manipulation operation case SMP_EXTRACT_ZERO_EXTEND: // Extract sub-reg and zero-extend to reg length InitFG.SignMiscInfo |= FG_MASK_UNSIGNED; InitFG.SizeInfo |= (FG_MASK_FLOAT_MMX | FG_MASK_BITWIDTH_128); changed = true; break; case SMP_INTERLEAVE: // extended-precision interleaving of bytes or words or dwords etc.; NUMERIC case SMP_CONCATENATE: // extended-precision concatenation; NUMERIC InitFG.SignMiscInfo |= FG_MASK_SIGNED; InitFG.SizeInfo |= (FG_MASK_FLOAT_MMX | FG_MASK_BITWIDTH_128); changed = true; break; default: SMP_msg("ERROR: Unknown operator in %s\n", DisAsmText.GetDisAsm(this->GetAddr())); break; } // end switch on operator return changed; } // end of SMPInstr::InitFGInfoFromOperator() // Helper to take USE operand, find its SSANum, and return its UseHashValue. int SMPInstr::GetUseOpHashAndSSA(op_t UseOp, int &SSANum) { op_t SearchOp = UseOp; CanonicalizeOpnd(SearchOp); set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SearchOp); assert(UseIter != this->GetLastUse()); SSANum = UseIter->GetSSANum(); int UseHashValue = HashGlobalNameAndSSA(SearchOp, SSANum); return UseHashValue; } // end of SMPInstr::GetUseOpHashAndSSA() // Helper to take DEF operand, find its SSANum, and return its DefHashValue. int SMPInstr::GetDefOpHashAndSSA(op_t DefOp, int &SSANum) { op_t SearchOp = DefOp; CanonicalizeOpnd(SearchOp); set<DefOrUse, LessDefUse>::iterator DefIter = this->FindDef(SearchOp); assert(DefIter != this->GetLastDef()); SSANum = DefIter->GetSSANum(); int DefHashValue = HashGlobalNameAndSSA(SearchOp, SSANum); return DefHashValue; } // end of SMPInstr::GetDefOpHashAndSSA() // helper for InferOperatorFGInfo() to update DEF maps, return true if changed maps bool SMPInstr::UpdateDefOpFGInfo(op_t DefOp, struct FineGrainedInfo NewFG) { bool MapsChanged = false; // Changes to maps of name/SSA to FG info? set<DefOrUse, LessDefUse>::iterator DefIter; int SSANum; int DefHashValue; op_t SearchOp; bool LocalName; struct FineGrainedInfo OldFG, UnionFG; if (o_reg == DefOp.type) { // If operator is inherently signed, then we will have // a sign bit set in NewFG from InitFGInfoFromOperator(). DefHashValue = this->GetDefOpHashAndSSA(DefOp, SSANum); LocalName = this->BasicBlock->IsLocalName(DefOp); if (LocalName) { // Get old FG info from block level. OldFG = this->BasicBlock->GetDefFGInfo(DefHashValue); } else { // global name // Get old FG info from function level. OldFG = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue); } // Get rid of stack access bits being passed around in InferOperatorFGInfo() // for register operands in RTLs. NewFG.SignMiscInfo &= FG_MASK_SIGNEDNESS_BITS; UnionFG.SignMiscInfo = OldFG.SignMiscInfo | NewFG.SignMiscInfo; UnionFG.SizeInfo = OldFG.SizeInfo | NewFG.SizeInfo; if ((OldFG.SignMiscInfo != UnionFG.SignMiscInfo) || (OldFG.SizeInfo != UnionFG.SizeInfo)) { // The signs they are a-changin' (or the sizes). MapsChanged = true; if (LocalName) this->BasicBlock->UpdateDefFGInfo(DefHashValue, UnionFG); else this->BasicBlock->GetFunc()->UpdateDefFGInfo(DefHashValue, UnionFG); } } else if (MDIsDirectStackAccessOpnd(DefOp, this->GetBlock()->GetFunc()->UsesFramePointer())) { ea_t InstAddr = this->GetAddr(); MapsChanged = this->GetBlock()->GetFunc()->MDUpdateFGStackLocInfo(InstAddr, DefOp, NewFG); } return MapsChanged; } // end of SMPInstr::UpdateDefOpFGInfo() // helper for InferOperatorFGInfo() to update USE maps, return true if changed maps bool SMPInstr::UpdateUseOpFGInfo(op_t UseOp, struct FineGrainedInfo NewFG) { bool MapsChanged = false; // Changes to maps of name/SSA to FG info? int SSANum; int UseHashValue; bool LocalName; struct FineGrainedInfo OldFG, UnionFG; if (o_reg == UseOp.type) { // If operator is inherently signed, then we will have // a sign bit set in NewFG from InitFGInfoFromOperator(). UseHashValue = this->GetUseOpHashAndSSA(UseOp, SSANum); LocalName = this->BasicBlock->IsLocalName(UseOp); if (LocalName) { // Get old FG info from block level. OldFG = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // global name // Get old FG info from function level. OldFG = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } // Get rid of stack access bits being passed around in InferOperatorFGInfo() // for register operands in RTLs. NewFG.SignMiscInfo &= FG_MASK_SIGNEDNESS_BITS; UnionFG.SignMiscInfo = OldFG.SignMiscInfo | NewFG.SignMiscInfo; UnionFG.SizeInfo = OldFG.SizeInfo | NewFG.SizeInfo; if ((OldFG.SignMiscInfo != UnionFG.SignMiscInfo) || (OldFG.SizeInfo != UnionFG.SizeInfo)) { // The signs they are a-changin'. MapsChanged = true; if (LocalName) this->BasicBlock->UpdateUseFGInfo(UseHashValue, UnionFG); else this->BasicBlock->GetFunc()->UpdateUseFGInfo(UseHashValue, UnionFG); } } else if (MDIsDirectStackAccessOpnd(UseOp, this->GetBlock()->GetFunc()->UsesFramePointer())) { ea_t InstAddr = this->GetAddr(); MapsChanged = this->GetBlock()->GetFunc()->MDUpdateFGStackLocInfo(InstAddr, UseOp, NewFG); } return MapsChanged; } // end of SMPInstr::UpdateUseOpFGInfo() // Helper to fetch DEF signedness info for UseOp that has none. unsigned short SMPInstr::GetDefSignInfoFromUseOp(op_t UseOp) { int SSANum, UseHashValue; bool LocalName; UseHashValue = this->GetUseOpHashAndSSA(UseOp, SSANum); LocalName = this->BasicBlock->IsLocalName(UseOp); if (LocalName) { // Get old sign info from block level. return this->BasicBlock->GetDefSignMiscInfo(UseHashValue); } else { // global name // Get old sign info from function level. return this->BasicBlock->GetFunc()->GetDefSignMiscInfo(UseHashValue); } } // end of SMPInstr::GetDefSignInfoFromUseOp() // infer FG info, + width on FirstIter; pass out FG info for op subtree, return true if change made to any FG info map. bool SMPInstr::InferOperatorFGInfo(SMPRegTransfer *CurrRT, bool FirstIter, struct FineGrainedInfo &OpFG) { bool MapsChanged = false; // Changes to maps of name/SSA to FG info? bool NewChange = false; // Bit changes from InitFGInfoFromOperator() ? SMPoperator CurrOp = CurrRT->GetOperator(); struct FineGrainedInfo LeftFG, OldLeftFG; struct FineGrainedInfo RightFG, OldRightFG; op_t LeftOp, RightOp; unsigned short WidthMask, SignMask; bool CurrOpTransfersSign = this->DoesOperatorTransferSign(CurrOp); bool SpecialTransferCase = false; bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer(); bool success; int DefHashValue, UseHashValue, SSANum; #if SMP_AGGRESSIVE_SIGN_TRANSFER // Special case: If signed multiply operator, it might sometimes // be used for unsigned operands when upper bits of the result // are discarded, because there is no difference in the result bits // between unsigned and signed multiplication when only the lower // N bits are retained and the upper N bits are discarded. // As a result, InitFGInfoFromOperator() did not set FG_MASK_SIGNED // for the result of the multiplication. If we now detect that two // unsigned operands are being multiplied together using the "signed" // multiply operator, we need to transfer UNSIGNED to the result. if ((SMP_S_MULTIPLY == CurrOp) && (!(this->MDIsSignedArithmetic()))) { SpecialTransferCase = true; } #endif // Recurse to the right first, so we can do a depth-first accumulation of FG info. RightFG.SignMiscInfo = 0; RightFG.SizeInfo = 0; if (CurrRT->HasRightSubTree()) { if (FirstIter) { // Get width as well as signedness NewChange = this->InitFGInfoFromOperator(CurrOp, RightFG); } // end if (FirstIter) MapsChanged |= this->InferOperatorFGInfo(CurrRT->GetRightTree(), FirstIter, RightFG); } else { RightOp = CurrRT->GetRightOperand(); if (RightOp.type == o_imm) { // If immediate operand is a data address or code address, we can infer that it is unsigned. uval_t ImmVal = RightOp.value; if (IsImmedGlobalAddress((ea_t) ImmVal)) { // Data address (type GLOBALPTR) RightFG.SignMiscInfo |= FG_MASK_UNSIGNED; } else if (this->MDIsInterruptCall() || IsImmedCodeAddress((ea_t) ImmVal)) { // Code address (type GLOBALPTR) RightFG.SignMiscInfo |= FG_MASK_UNSIGNED; } } else if ((RightOp.type == o_reg) && !RightOp.is_reg(MD_INSTRUCTION_POINTER_REG)) { if (FirstIter) { // Get width as well as signedness NewChange = this->InitFGInfoFromOperator(CurrOp, RightFG); WidthMask = ComputeOperandBitWidthMask(RightOp, 0); RightFG.SizeInfo |= WidthMask; } // end if (FirstIter) #if SMP_AGGRESSIVE_SIGN_TRANSFER else { // On all iterations other than 1st, see if USE has FG info. UseHashValue = this->GetUseOpHashAndSSA(RightOp, SSANum); if (this->BasicBlock->IsLocalName(RightOp)) { // Get FG info from block. RightFG = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // Get FG info from function level. RightFG = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } } #endif // Propagate signedness on all iterations. // If operator is inherently signed, then we will have // a sign bit set in RightFG from InitFGInfoFromOperator(). if ((RightFG.SignMiscInfo == 0) && (CurrOpTransfersSign || SpecialTransferCase)) { // We have a USE with no sign info. See if we // can get sign info from the DEF of this USE so we can // transfer it up the RTL tree. RightFG.SignMiscInfo = (FG_MASK_SIGNEDNESS_BITS & (this->GetDefSignInfoFromUseOp(RightOp))); } if ((RightFG.SignMiscInfo != 0) || (RightFG.SizeInfo != 0)) MapsChanged |= this->UpdateUseOpFGInfo(RightOp, RightFG); } // end if (RightOP is o_reg) else if (MDIsDirectStackAccessOpnd(RightOp, UseFP)) { // We used to assume that all FG info transfers from stack locations to // the target registers of stack loads happened in SMPInstr::MDSetWidthSignInfo(), // in an early pass that needed no iteration. The FG info was loaded from the // StackFGInfo that was computed in SMPFunction::FindOutgoingArgsSize() based solely // on whether the load was sign-extended or zero-extended. Of course, many stack // locations have neither kind of signed/unsigned load. So, if we see a store to // a stack location with no signedness, we transfer the signedness of the RightFG // to the stack location FGInfo in the code below that processes the LeftOp. // As a result, we now have a need to examine regular loads from the stack to // see if there is signedness info for the stack location. success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, RightOp, RightFG); if (success) { SignMask = (RightFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); RightFG.SizeInfo = 0; // only want to transfer signedness } } } // end if (right subtree) else right operand LeftFG.SignMiscInfo = 0; LeftFG.SizeInfo = 0; LeftOp = CurrRT->GetLeftOperand(); bool OpIsDEF = (SMP_ASSIGN == CurrOp); // Skip control-flow assignments to the instruction pointer register. if ((LeftOp.type == o_reg) && !LeftOp.is_reg(MD_INSTRUCTION_POINTER_REG)) { if (FirstIter) { // Get width as well as signedness NewChange = this->InitFGInfoFromOperator(CurrOp, LeftFG); // Special case: For sign-extended and zero-extended loads, // we don't know whether the DEF will always be USEd as // the smaller or larger size. For example, we could // zero-extend a 16-bit stack location into a 32-bit register // just because the compiler always loads unsigned shorts // that way, but we might never use it as a 32-bit value. // So there is no truncation if we store only 16 bits later. // By setting the target of an extended load to zero width, // we signal that we want the maximum USE width to determine // whether the store is truncated (see EmitIntegerErrorAnnotations). WidthMask = ComputeOperandBitWidthMask(LeftOp, 0); if (OpIsDEF) { if (this->MDIsSignedLoad(SignMask)) { WidthMask = 0; } // DEF inherits sign from right hand side. LeftFG.SignMiscInfo |= RightFG.SignMiscInfo; } else if ((LeftFG.SignMiscInfo == 0) && (CurrOpTransfersSign || SpecialTransferCase)) { // We have a USE, not a DEF, with no sign info. See if we // can get sign info from the DEF of this USE so we can // transfer it up the RTL tree. LeftFG.SignMiscInfo = (FG_MASK_SIGNEDNESS_BITS & (this->GetDefSignInfoFromUseOp(LeftOp))); } LeftFG.SizeInfo |= WidthMask; if ((LeftFG.SignMiscInfo != 0) || (LeftFG.SizeInfo != 0)) { // Either NewChanged or CurrOpTransfersSign or SpecialTransferCase is true or we set WidthMask above. // See if we would change the FG map entry. if (OpIsDEF) { // Need DEF map info MapsChanged |= this->UpdateDefOpFGInfo(LeftOp, LeftFG); } else { // need USE map info MapsChanged |= this->UpdateUseOpFGInfo(LeftOp, LeftFG); } } // end if non-zero LeftFG info } // end if (FirstIter) #if SMP_AGGRESSIVE_SIGN_TRANSFER else { // On all iterations other than 1st, see if LeftOp has FG info. if (!OpIsDEF) { // LeftOp is a USE UseHashValue = this->GetUseOpHashAndSSA(LeftOp, SSANum); if (this->BasicBlock->IsLocalName(LeftOp)) { // Get FG info from block. LeftFG = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // Get FG info from function level. LeftFG = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } } else { // LeftOp is a DEF DefHashValue = this->GetDefOpHashAndSSA(LeftOp, SSANum); if (this->BasicBlock->IsLocalName(LeftOp)) { // Get FG info from block. LeftFG = this->BasicBlock->GetDefFGInfo(DefHashValue); } else { // Get FG info from function level. LeftFG = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue); } // See if RightFG has sign info to transfer to LeftFG. SignMask = (RightFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); unsigned short LeftSignMask = (LeftFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); bool UpdateLeftSignMask = ((SignMask != 0) && (SignMask != LeftSignMask)); #if 1 if (OpIsDEF && (0 != LeftSignMask)) { // We have a signedness value for the DEF operand. These values are // highly reliable, e.g. MEDS type inference detects that the DEF is // a POINTER and therefore it must be UNSIGNED, or the opcode always // produces a SIGNED result, etc. We do not want to produce a mixed // signedness DEF in these cases, regardless of what happens when // we examine operands in the RTLs, because compiler code generation // can produce screwy results that are less reliable than our early // determinations of DEF signedness (e.g. compilers can zero-extend // bytes even though they are signed, and hand-code assembly authors // can sign-extend bytes that are unsigned and get away with it // because the values are always very small and have a sign bit of zero.) UpdateLeftSignMask = false; } #endif if (UpdateLeftSignMask) { // SignMask from RightFG has bits that will change LeftFG.SignMiscInfo. LeftFG.SignMiscInfo |= SignMask; MapsChanged |= this->UpdateDefOpFGInfo(LeftOp, LeftFG); } } } #endif } // end of register case for LeftOp else if (OpIsDEF && MDIsDirectStackAccessOpnd(LeftOp, UseFP) && (!this->BasicBlock->GetFunc()->IsInOutgoingArgsRegion(LeftOp))) { // For stores into the stack, if the operand being stored has signedness // and the stack location has no signedness, then we have a case where // none of the loads from the stack location were signed, so it is // safe to infer signedness of the stack location based on what is being // stored into it, as no store signedness will conflict with load signedness. success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, LeftOp, LeftFG); assert(success); if (0 == (LeftFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS)) { // No previous signedness info for the stack location. // Get signedness info from RightFG. SignMask = (RightFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); if ((0 != SignMask) && (FG_MASK_INCONSISTENT_SIGN != SignMask)) { // Operand being stored has signedness. // Transfer the signedness to the stack location. struct FineGrainedInfo TempFG; TempFG.SignMiscInfo = SignMask; TempFG.SizeInfo = 0; // just update signedness success = this->BasicBlock->GetFunc()->MDUpdateFGStackLocInfo(this->address, LeftOp, TempFG); MapsChanged |= success; } } } // Prepare to return FG info for operator. First, OR the left and right FG infos. if (NewChange || MapsChanged || CurrOpTransfersSign || SpecialTransferCase) { OpFG.SignMiscInfo |= LeftFG.SignMiscInfo; OpFG.SizeInfo |= LeftFG.SizeInfo; OpFG.SignMiscInfo |= RightFG.SignMiscInfo; OpFG.SizeInfo |= RightFG.SizeInfo; } // An operator could override the width or signedness info of its operands. if (CurrOp == SMP_ADDRESS_OF) { // Result is 32-bit data pointer. OpFG.SizeInfo &= (~FG_MASK_BITWIDTH_FIELDS); // clear all width bits OpFG.SizeInfo |= (FG_MASK_BITWIDTH_32 | FG_MASK_DATAPOINTER); OpFG.SignMiscInfo &= (~FG_MASK_SIGNED); OpFG.SignMiscInfo |= FG_MASK_UNSIGNED; } return MapsChanged; } // end of SMPInstr::InferOperatorFGInfo() // infer width on first pass, signedness on all passes bool SMPInstr::InferFGInfo(unsigned short IterCount) { bool MapsChanged = false; // Changes to maps of name/SSA to FG info? struct FineGrainedInfo OpFG; SMPitype DFType = this->GetDataFlowType(); assert(0 < IterCount); // start IterCount at 1, not 0. if (DFType != DEFAULT) { // We have a control flow instruction, e.g. call, return, branch, jump // No data operands unless these instructions are indirect through a register, // and the indirect operand is a memory operand in that case, e.g. [eax]. return MapsChanged; } if (1 == IterCount) { // Fix signedness info now that complete SSA chains have been analyzed. this->MDFixupSignedness(); } for (size_t index = 0; index < this->RTL.GetCount(); ++index) { SMPRegTransfer *CurrRT = this->RTL.GetRT(index); if (SMP_NULL_OPERATOR == CurrRT->GetOperator()) // nothing to infer continue; OpFG.SignMiscInfo = 0; OpFG.SizeInfo = 0; MapsChanged |= this->InferOperatorFGInfo(CurrRT, (1 == IterCount), OpFG); if (SMP_CALL == CurrRT->GetOperator()) // no LeftOp DEF continue; } // end for all RTs in the RTL return MapsChanged; } // end of SMPInstr::InferFGInfo() // Get the meet of the metadata types of all non-flags DEFs. SMPMetadataType SMPInstr::GetDefMetadataType(void) { SMPMetadataType MeetType = DEF_METADATA_UNANALYZED; set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) { SMPMetadataType CurrType; op_t DefOp = CurrDef->GetOp(); if (DefOp.is_reg(X86_FLAGS_REG)) continue; // flags are always unused metadata; irrelevant CurrType = CurrDef->GetMetadataStatus(); if (MeetType == CurrType) continue; // no meet operation to perform // Any time we find USED metadata, that overrides all other types. if (CurrType == DEF_METADATA_USED) return CurrType; if (MeetType == DEF_METADATA_UNANALYZED) MeetType = CurrType; else if (MeetType < DEF_METADATA_REDUNDANT) { // Conflict between types of different DEFs. It could be that // a multiply or divide instruction DEFs EAX and EDX, and one // of them is used in a store and the other is unused. In that // case, the final MeetType is USED and we can return. Or, if // one type is UNUSED and the other is REDUNDANT, we can set // the final type to the REDUNDANT type and return. The USED case // is handled above, so we must have the UNUSED vs. REDUNDANT case. assert(CurrType >= DEF_METADATA_REDUNDANT); MeetType = CurrType; } else { // MeetType REDUNDANT, not equal to CurrType. if (CurrType >= DEF_METADATA_REDUNDANT) { // One type is profile derived, both are REDUNDANT. MeetType = DEF_METADATA_PROF_REDUNDANT; } else { assert(DEF_METADATA_UNUSED == CurrType); // leave MeetType as REDUNDANT } } } // end for all DEFs return MeetType; } // end of SMPInstr::GetDefMetadataType() // Are numeric values from a system call trusted input, so that all numeric errors // derived from the returned values shouold be treated as benign? bool SMPInstr::IsNumericTrustedSystemCall(void) { bool TrustedCall = false; if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) { // We have a resolved call target address, either via direct or indirect call. string FuncName = this->GetTrimmedCalledFunctionName(); TrustedCall = IsNumericSafeSystemCall(FuncName); } return TrustedCall; } // end of SMPInstr::IsNumericTrustedSystemCall() // Handle x86 opcode SIB byte annotations. void SMPInstr::MDAnnotateSIBStackConstants(FILE *AnnotFile, op_t Opnd, ea_t offset, bool UseFP) { int BaseReg; int IndexReg; ea_t displacement; ushort ScaleFactor; char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); int SignedOffset = (int) offset; MDExtractAddressFields(Opnd, BaseReg, IndexReg, ScaleFactor, displacement); if (BaseReg == MD_STACK_POINTER_REG) { // ESP cannot be IndexReg // ESP-relative constant offset SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDESP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm); } else if (UseFP && ((IndexReg == MD_FRAME_POINTER_REG) || (BaseReg == MD_FRAME_POINTER_REG))) { // EBP-relative constant offset SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDEBP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm); } return; } // end of MDAnnotateSIBStackConstants // Emit annotations for constants used as ptr offsets from EBP or // ESP into the stack frame. Only pay attention to EBP-relative // offsets if EBP is being used as a frame pointer (UseFP == true). void SMPInstr::AnnotateStackConstants(bool UseFP, FILE *AnnotFile) { op_t Opnd; ea_t offset; int BaseReg; int IndexReg; ushort ScaleFactor; char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); int SignedOffset; #if 0 if (this->address == 0x80925f4) { SMP_msg("PROBLEM INSTRUCTION: \n"); this->PrintOperands(); } #endif for (int i = 0; i < UA_MAXOP; ++i) { Opnd = this->SMPcmd.Operands[i]; if ((Opnd.type == o_displ) || (Opnd.type == o_phrase)) MDExtractAddressFields(Opnd, BaseReg, IndexReg, ScaleFactor, offset); SignedOffset = (int) offset; if (Opnd.type == o_displ) { if (Opnd.hasSIB) { MDAnnotateSIBStackConstants(AnnotFile, Opnd, offset, UseFP); } else { // no SIB if (BaseReg == MD_STACK_POINTER_REG) { // ESP-relative constant offset SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDESP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm); } else if (UseFP && (BaseReg == MD_FRAME_POINTER_REG)) { // EBP-relative constant offset SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDEBP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm); } } // end if (Opnd.hasSIB) ... else ... } // end if (Opnd.type == o_displ) else if (Opnd.type == o_phrase) { offset = 0; // mmStrata thinks [esp] is [esp+0] if (Opnd.hasSIB) { MDAnnotateSIBStackConstants(AnnotFile, Opnd, offset, UseFP); } else { // Something like [ecx]; is it [esp] or [ebp] ? if (BaseReg == MD_STACK_POINTER_REG) { // ESP-relative constant offset SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDESP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm); } else if (UseFP && (BaseReg == MD_FRAME_POINTER_REG)) { // EBP-relative constant offset SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDEBP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm); } } // end if (Opnd.hasSIB) ... else ... } // end else if (Opnd.type == o_phrase) } // end for all operands // If we move a stack pointer or frame pointer into another register, we // need to annotate the implicit zero offset, e.g. mov edi,esp == mov edi,esp+0 // and edi is becoming a stack pointer that mmStrata needs to track. if (this->MDIsStackPointerCopy(UseFP)) { // Two possibilities: a move of the stack pointer, or an "lea" // opcode, e.g. lea eax,[eap+8] ==> eax:=esp+8. In the move // instruction (e.g. mov eax,esp), we have the implicit zero // offset from the stack pointer register, but in the lea case, // we might have zero or some other offset (lea eax,[esp] has // the implicit zero). int ESPoffset = 0; if (NN_lea == this->SMPcmd.itype) { ESPoffset = this->MDGetImmedUse(); } // NOTE: Looks like this next line should be "else" because an lea instruction // looks like it has a memory operand, hence it has already been handled above. // We are getting duplicate annotations for lea instructions. else { if (UseFP && this->GetFirstUse()->GetOp().is_reg(MD_FRAME_POINTER_REG)) { SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDEBP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, ESPoffset, disasm); } else { SMP_fprintf(AnnotFile, "%10lx %6d PTRIMMEDESP STACK %d displ %s\n", (unsigned long) this->SMPcmd.ea, this->SMPcmd.size, ESPoffset, disasm); } } } return; } // end of SMPInstr::AnnotateStackConstants() // Emit all annotations for the instruction in the absence of RTL type inference. void SMPInstr::EmitAnnotations(bool UseFP, bool AllocSeen, bool NeedsFrame, FILE *AnnotFile, FILE *InfoAnnotFile) { ea_t addr = this->address; flags_t InstrFlags = getFlags(addr); bool MemDest = this->HasDestMemoryOperand(); bool MemSrc = this->HasSourceMemoryOperand(); bool SecondSrcOperandImmNum = this->IsSecondSrcOperandNumeric(InstrFlags); // assumes 2nd source is Imm or not-numeric?! bool NoWarnFlag = false; // NOWARN annotation emitted? char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); #if SMP_CHILDACCESS_ALL_CODE bool OrphanCode = (NULL == this->BasicBlock); ProfilerInformation *ProfInfo = NULL; if (!OrphanCode) ProfInfo = this->BasicBlock->GetFunc()->GetProg()->GetProfInfo(); #endif ++OptCount[OptType]; // keep count for debugging info #if SMP_ANNOTATE_ALL_MEMORY_OPERANDS // Emit informational annotations for memory operands. if (MemSrc) { op_t MemSrcOp = this->MDGetMemUseOp(); size_t SrcBitWidth = 8 * GetOpDataSize(MemSrcOp); op_t AnnotDefOp = MemSrcOp; // Need to unnormalize stack memory DEFs and USEs before printing annotations. this->MDGetUnnormalizedOp(AnnotDefOp); SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR MEMSRC %d", (unsigned long) addr, this->SMPcmd.size, SrcBitWidth); AnnotPrintOperand(AnnotDefOp, InfoAnnotFile); SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } if (MemDest) { op_t MemDestOp = this->MDGetMemDefOp(); size_t DestBitWidth = 8 * GetOpDataSize(MemDestOp); op_t AnnotDefOp = MemDestOp; // Need to unnormalize stack memory DEFs and USEs before printing annotations. this->MDGetUnnormalizedOp(AnnotDefOp); SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR MEMDEF %d", (unsigned long) addr, this->SMPcmd.size, DestBitWidth); AnnotPrintOperand(AnnotDefOp, InfoAnnotFile); SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } #endif // If the instruction is a CALL (or INDIR_CALL that has been resolved to // a single target address), then we need to see if the call is to a // function that has been forbidden by a security policy. If so, we // need to output a security alert. // In the near future, we will output SPRI instrumentation to prevent // the system/library call from executing. if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) { // We have a resolved call target address, either via direct or indirect call. string FuncName = this->GetTrimmedCalledFunctionName(); ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName); ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType); if (ZST_DISALLOW == FuncCallPolicy) { if ((NULL != this->GetBlock()) && (NULL != this->GetBlock()->GetFunc())) { SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %lx in %s\n", FuncName.c_str(), (unsigned long) this->address, this->GetBlock()->GetFunc()->GetFuncName()); } else { SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %lx\n", FuncName.c_str(), (unsigned long) this->address); } SMP_fprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]); SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR SECURITYCALL Disallow 1 1 %s \n", (unsigned long) addr, this->SMPcmd.size, disasm); } } #if SMP_DEBUG_MEM if (MemDest || MemSrc) { SMP_msg("OptType: %d %s", OptType, disasm); this->PrintOperands(); } #endif // Emit appropriate optimization annotations. bool SDTInstrumentation = false; switch (OptType) { case 0: // SDT will have to handle these { #if SMP_DEBUG_TYPE0 SMP_msg("OptType 0: %lx %s\n", (unsigned long) addr, disasm); #endif // mmStrata wants to suppress warnings on the PUSH // instructions that precede the LocalVarsAllocInstr // (i.e. the PUSHes of callee-saved regs). if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL NoWarn %s \n", (unsigned long) addr, -3, disasm); NoWarnFlag = true; } else { SDTInstrumentation = true; } break; } case 1: // nothing for SDT to do { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[OptType], disasm); ++AnnotationCount[OptType]; break; } case 4: // INC, DEC, etc.: no SDT work unless MemDest { if (MemDest || MemSrc) { SDTInstrumentation = true; break; // treat as category 0 } SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL Always1stSrc %s \n", (unsigned long) addr, -1, disasm); ++AnnotationCount[OptType]; break; } case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work. { if (MemDest || MemSrc) { SDTInstrumentation = true; break; // treat as category 0 } if (SecondSrcOperandImmNum && !this->MDIsFrameAllocInstr() #if SPECIAL_CASE_CARRY_BORROW && (this->SMPcmd.itype != NN_adc) && (this->SMPcmd.itype != NN_sbb) #endif ) { // treat as category 1 SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[OptType], disasm); ++AnnotationCount[OptType]; } else { SDTInstrumentation = true; } break; } case 6: // Only OS code should include these; problem for SDT { if (MemDest) { SDTInstrumentation = true; break; // treat as category 0 } SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL AlwaysPTR %s \n", (unsigned long) addr, -OptType, disasm); ++AnnotationCount[OptType]; break; } case 8: // Implicitly writes to EDX:EAX, always numeric. { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n EDX EAX ZZ %s %s \n", (unsigned long) addr, -2, OptExplanation[OptType], disasm); ++AnnotationCount[OptType]; SDTInstrumentation = true; break; } case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0) { if (MemDest) { #if SMP_DEBUG2 // MemDest seems to happen too much. SMP_msg("Floating point MemDest: %s \n", disasm); #endif SDTInstrumentation = true; break; // treat as category 0 } SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[OptType], disasm); ++AnnotationCount[OptType]; break; } case 10: // Implicitly writes to EDX:EAX and ECX, always numeric. { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n", (unsigned long) addr, -2, OptExplanation[OptType], disasm); ++AnnotationCount[OptType]; SDTInstrumentation = true; break; } default: // 2,3,7: Optimization possibilities depend on operands { #if SMP_DEBUG2 if (OptType == 3) { // MOV instr class if (MemDest) { SMP_msg("MemDest on MOV: %s\n", disasm); } else if (!SecondSrcOperandNum) { SMP_msg("MOV: not 2nd op numeric: %s\n", disasm); this->PrintOperands(); } } #endif SDTInstrumentation = true; if (MemDest) { #if SMP_DEBUG_XOR if (OptType == 2) SMP_msg("MemDest on OptType 2: %s\n", disasm); #endif break; // treat as category 0 } if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s %s %s \n", (unsigned long) addr, -2, this->DestString(OptType), OptExplanation[OptType], disasm); ++AnnotationCount[OptType]; } break; } } // end switch (OptType) // always emit stack constant annotations, in case strata is // instrumenting all instructions, or trying to verify speculative annotations. this->AnnotateStackConstants(UseFP, AnnotFile); // If mmStrata is going to have to deal with the // instruction, then we can annotate EBP and ESP // relative constant offsets. If we have emitted // an annotation of type -1, there is no point // in telling mmStrata about these constants. // Likewise, we can tell mmStrata if a MemDest is an // non-directly-accessed child object. if (SDTInstrumentation || NoWarnFlag) { if (this->DeadRegsBitmap.any()) { // Optimize by informing mmStrata of dead registers. It can avoid saving // and restoring dead state. This is particularly important for EFLAGS, // as restoring the flags is a pipeline serializing instruction. SMP_fprintf(AnnotFile, "%10lx %6d INSTR DEADREGS ", (unsigned long) addr, this->SMPcmd.size); this->PrintDeadRegs(AnnotFile); SMP_fprintf(AnnotFile, " %s \n", disasm); } #if SMP_CHILDACCESS_ALL_CODE int ChildOffset, ChildSize; if (MemDest && !OrphanCode && ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr, ChildOffset, ChildSize)) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR CHILDACCESS %d %d ZZ %s \n", (unsigned long) addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm); } #endif } return; } // end of SMPInstr::EmitAnnotations() /** * Emits Safe Returns * Mark the type of the annotation as "-4". Currently the SDT is ignoring this * annotation. */ void SMPInstr::EmitSafeReturn(FILE *AnnotFile) { char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL SafeReturn %s\n", (unsigned long) this->address, -4, disasm); } // Emit all annotations for the instruction using RTL type inference. void SMPInstr::EmitTypeAnnotations(bool UseFP, bool AllocSeen, bool NeedsFrame, FILE *AnnotFile, FILE *InfoAnnotFile) { ea_t addr = this->address; flags_t InstrFlags = getFlags(addr); int TypeGroup = SMPTypeCategory[this->SMPcmd.itype]; bool NumericDEFs = this->AllDefsNumeric(); // all DEFs are NUMERIC or CODEPTR bool ProfiledDEFs = this->AnyDefsProfiled(); // Some DEFs come from the profiler bool UnusedMetadata = this->AllDefMetadataUnused(); bool MemDest = this->HasDestMemoryOperand(); bool MemSrc = this->HasSourceMemoryOperand(); bool SecondSrcOperandImmNum = this->IsSecondSrcOperandNumeric(InstrFlags); // assumes 2nd source is imm or not-numeric?? bool NoWarnFlag = false; // NOWARN annotation emitted? bool CarryBorrow = ((this->SMPcmd.itype == NN_adc) || (this->SMPcmd.itype == NN_sbb)); // Do we have the special case in which a non-NUMERIC comes into // an add with carry or subtract with borrow and the result // has been inferred to be NUMERIC? bool TypeChange = CarryBorrow && (!IsNumeric(this->AddSubUseType)) && NumericDEFs; SMPMetadataType DefMetadataType = this->GetDefMetadataType(); ProfilerInformation *ProfInfo; ProfInfo = this->BasicBlock->GetFunc()->GetProg()->GetProfInfo(); char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); ++OptCount[this->OptType]; // keep count for debugging info if (this->IsNop()) TypeGroup = 1; // no-op idioms need their category reset // Emit appropriate optimization annotations. bool SDTInstrumentation = false; // In case we short circuit this function due to redundant or unused metadata, etc., // perform analyses that are useful to integer error annotations. #if SMP_ANNOTATE_ALL_MEMORY_OPERANDS // Emit informational annotations for memory operands. if (MemSrc) { op_t MemSrcOp = this->MDGetMemUseOp(); size_t SrcBitWidth = 8 * GetOpDataSize(MemSrcOp); op_t AnnotDefOp = MemSrcOp; // Need to unnormalize stack memory DEFs and USEs before printing annotations. this->MDGetUnnormalizedOp(AnnotDefOp); SMP_fprintf(InfoAnnotFile, "%10lx %6zu INSTR MEMSRC %zu", (unsigned long) addr, this->SMPcmd.size, SrcBitWidth); AnnotPrintOperand(AnnotDefOp, InfoAnnotFile); SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } if (MemDest) { op_t MemDestOp = this->MDGetMemDefOp(); size_t DestBitWidth = 8 * GetOpDataSize(MemDestOp); op_t AnnotDefOp = MemDestOp; // Need to unnormalize stack memory DEFs and USEs before printing annotations. this->MDGetUnnormalizedOp(AnnotDefOp); SMP_fprintf(InfoAnnotFile, "%10lx %6zu INSTR MEMDEF %zu", (unsigned long) addr, this->SMPcmd.size, DestBitWidth); AnnotPrintOperand(AnnotDefOp, InfoAnnotFile); SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } #endif // If the instruction is a CALL (or INDIR_CALL that has been resolved to // a single target address), then we need to see if the call is to a // function that has been forbidden by a security policy. If so, we // need to output a security alert. // In the near future, we will output SPRI instrumentation to prevent // the system/library call from executing. if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) { // We have a resolved call target address, either via direct or indirect call. string FuncName = this->GetTrimmedCalledFunctionName(); ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName); ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType); if (ZST_DISALLOW == FuncCallPolicy) { SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %lx in %s\n", FuncName.c_str(), (unsigned long) this->address, this->GetBlock()->GetFunc()->GetFuncName()); SMP_fprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]); SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR SECURITYCALL Disallow 1 1 %s \n", (unsigned long) addr, this->SMPcmd.size, disasm); } } // If the DEF metadata is all unused, mmStrata can skip the instruction. // We omit this for groups 1 and 14, so that the metadata analysis // does not get statistical credit for instructions that were already // getting -1 annotations without analysis. // We also cannot skip NN_adc and NN_sbb instructions that change the // type of the incoming register. if ((1 != TypeGroup) && (14 != TypeGroup) && (!this->MDIsInterruptCall()) && !TypeChange) { if (UnusedMetadata) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL MetadataUnused %s \n", (unsigned long) addr, -1, disasm); ++AnnotationCount[this->OptType]; return; } else if (DEF_METADATA_REDUNDANT == DefMetadataType) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL MetadataRedundant %s \n", (unsigned long) addr, -1, disasm); ++AnnotationCount[this->OptType]; return; } else if (DEF_METADATA_PROF_REDUNDANT == DefMetadataType) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL MetadataRedundant %s \n", (unsigned long) addr, -257, disasm); ++AnnotationCount[this->OptType]; // Profiler annotations could be backed off due to false // positives, in which case we will need stack constant // annotations. this->AnnotateStackConstants(UseFP, AnnotFile); return; } } switch (TypeGroup) { case 0: // SDT will have to handle these case 11: // PUSH/POP **!!** What if we push/pop NUMERIC type? Optimize? // --jdh // pop numeric's can be optimized with a numericdef annotation. // numeric push's can't immediately be optimized, but if the stack location // can be proven as dead metadata, then perhaps optimize. // --jdh // mmStrata wants to suppress warnings on the PUSH // instructions that precede the LocalVarsAllocInstr // (i.e. the PUSHes of callee-saved regs). if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL NoWarn %s \n", (unsigned long) addr, -3, disasm); NoWarnFlag = true; } else if (this->MDIsPopInstr() && NumericDEFs) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s NumericDEFs %s \n", (unsigned long) addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm); ++AnnotationCount[this->OptType]; } else { SDTInstrumentation = true; } break; case 1: // nothing for SDT to do case 14: if (MemDest) { SMP_msg("ERROR: MemDest in Type Category 1 or 14: %lx %s\n", (unsigned long) addr, disasm); SDTInstrumentation = true; break; } SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[this->OptType], disasm); ++AnnotationCount[this->OptType]; break; case 4: // INC, DEC, etc.: no SDT work unless MemDest if (MemDest || MemSrc) { // pretty conservative here? // could be more aggressive if we know there's no overflow. -- jdh SDTInstrumentation = true; break; // treat as category 0 } SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL Always1stSrc %s \n", (unsigned long) addr, -1, disasm); ++AnnotationCount[this->OptType]; break; case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work. #if 1 if (MemDest) { SDTInstrumentation = true; break; // treat as category 0 } #endif if (SecondSrcOperandImmNum && !this->MDIsFrameAllocInstr() && !TypeChange #if SPECIAL_CASE_CARRY_BORROW && (!CarryBorrow) #endif ) { // treat as category 1 SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[this->OptType], disasm); ++AnnotationCount[this->OptType]; } else if (IsEqType(NUMERIC, this->AddSubSourceType) && !this->MDIsFrameAllocInstr() && !TypeChange #if SPECIAL_CASE_CARRY_BORROW && (!CarryBorrow) #endif ) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL 2ndSrcNumeric %s \n", (unsigned long) addr, -1, disasm); ++AnnotationCount[this->OptType]; } else if (NumericDEFs) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s NumericDEFs %s \n", (unsigned long) addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm); ++AnnotationCount[this->OptType]; } #if SMP_OPTIMIZE_ADD_TO_NUMERIC else if ((NN_add == this->SMPcmd.itype) && (!MemSrc) && IsNumeric(this->AddSubUseType)) { // reg1 := reg1 + reg2, where reg1 comes in as NUMERIC, // means that reg1 will get DEFed to the type of reg2, // whatever it is. If reg2 were known to be NUMERIC, // we would have hit one of the annotation cases above. SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s := %s ZZ AddToNumeric %s \n", (unsigned long) addr, -5, RegNames[this->AddSubUseOp.reg], RegNames[this->AddSubSourceOp.reg], disasm); ++AnnotationCount[this->OptType]; } #endif else { SDTInstrumentation = true; } break; case 6: // Only OS code should include these; problem for SDT if (MemDest) { SDTInstrumentation = true; break; // treat as category 0 } SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL AlwaysPTR %s \n", (unsigned long) addr, -OptType, disasm); ++AnnotationCount[this->OptType]; break; case 8: // Implicitly writes to EDX:EAX, always numeric. if (this->OptType == 10) { // writes to ECX also SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n", (unsigned long) addr, -2, OptExplanation[this->OptType], disasm); } else { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n EDX EAX ZZ %s %s \n", (unsigned long) addr, -2, OptExplanation[this->OptType], disasm); } ++AnnotationCount[this->OptType]; SDTInstrumentation = true; break; case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0) if (MemDest) { SDTInstrumentation = true; #if 0 if (NumericDEFs) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s NumericDEFs %s \n", (unsigned long) addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm); ++AnnotationCount[this->OptType]; } #endif } else { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[this->OptType], disasm); ++AnnotationCount[this->OptType]; } break; case 10: // AND, OR, etc.: If all DEFs have been inferred to be // NUMERIC, then output optimizing annotation. SDTInstrumentation = true; if (MemDest) { // **!!** optimize with numeric annotation in future break; // treat as category 0 } else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s NumericDEFs %s \n", (unsigned long) addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm); ++AnnotationCount[this->OptType]; } break; case 12: // Exchange, exchange and add, conditional exchange: All NUMERIC // sources ==> NUMERIC DEFs, so nothing for mmStrata to do. if (MemDest) { // **!!** optimize with numeric annotation in future SDTInstrumentation = true; break; // treat as category 0 } else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, ProfiledDEFs ? -256-1 : -1, OptExplanation[TypeGroup], disasm); ++AnnotationCount[this->OptType]; } else SDTInstrumentation = true; break; case 13: case 15: // Floating point, NUMERIC, possible memory destination. // If not memory destination, fpreg dest, so nothing for mmStrata to do. if (MemDest) { // **!!** optimize with numeric annotation in future SDTInstrumentation = true; break; // treat as category 0 } else { // NUMERIC floating register result; these regs are always NUMERIC SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL %s %s \n", (unsigned long) addr, -1, OptExplanation[TypeGroup], disasm); ++AnnotationCount[this->OptType]; } break; default: // 2,3,7: Optimization possibilities depend on operands SDTInstrumentation = true; if (MemDest) { break; // treat as category 0 } if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s %s %s \n", (unsigned long) addr, -2, this->DestString(this->OptType), OptExplanation[this->OptType], disasm); ++AnnotationCount[this->OptType]; } else if (NumericDEFs) { // NUMERIC move instruction SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL n %s NumericDEFs %s \n", (unsigned long) addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm); ++AnnotationCount[this->OptType]; } break; } // end switch (OptType) // always annotate stack constants for the profiler, etc. this->AnnotateStackConstants(UseFP, AnnotFile); // If mmStrata is going to have to deal with the // instruction, then we can annotate EBP and ESP // relative constant offsets. If we have emitted // an annotation of type -1, there is no point // in telling mmStrata about these constants. // Likewise, we can tell mmStrata if a MemDest is an // non-directly-accessed child object. if (SDTInstrumentation || NoWarnFlag) { if (this->DeadRegsBitmap.any()) { // Optimize by informing mmStrata of dead registers. It can avoid saving // and restoring dead state. This is particularly important for EFLAGS, // as restoring the flags is a pipeline serializing instruction. SMP_fprintf(AnnotFile, "%10lx %6d INSTR DEADREGS ", (unsigned long) addr, this->SMPcmd.size); this->PrintDeadRegs(AnnotFile); SMP_fprintf(AnnotFile, " %s \n", disasm); } int ChildOffset, ChildSize; if (MemDest && ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr, ChildOffset, ChildSize)) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR CHILDACCESS %d %d ZZ %s \n", (unsigned long) addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm); } #if SMP_IDENTIFY_POINTER_ADDRESS_REG // WARNING: This old code was written prior to the normalization of stack operands. if (MemDest) { assert(this->HasDestMemoryOperand()); set<DefOrUse, LessDefUse>::iterator PtrUse; PtrUse = this->GetPointerAddressReg(this->DEFMemOp); if (PtrUse != this->GetLastUse()) { // found POINTER addr reg USE if (PtrUse->GetOp().type == o_reg) { ushort PtrReg = PtrUse->GetOp().reg; SMP_fprintf(AnnotFile, "%10lx %6d INSTR POINTER reg %s ZZ %s \n", (unsigned long) addr, this->SMPcmd.size, RegNames[PtrReg], disasm); } } } #endif } return; } // end of SMPInstr::EmitTypeAnnotations() // union of sign masks from 2 reg USEs for binary arithmetic unsigned short SMPInstr::SignMaskUnionFromUseRegs(void) { unsigned short UnionMask = 0; unsigned short UseSignMask, DefSignMask; set<DefOrUse, LessDefUse>::iterator UseIter; op_t UseOp; size_t RegOpCount = 0; int UseHashValue; struct FineGrainedInfo UseFGInfo, DefFGInfo; bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer(); if (this->HasSourceMemoryOperand()) { // Don't have binary arithmetic on just registers. return UnionMask; } for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseOp = UseIter->GetOp(); if (MDIsGeneralPurposeReg(UseOp) && (!MDIsStackPtrReg(UseOp.reg, UseFP))) { ++RegOpCount; UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum()); bool LocalUseName = this->BasicBlock->IsLocalName(UseOp); if (LocalUseName) { // Local name, find in basic block maps. UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // Global name, find in global maps. UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } UseSignMask = (FG_MASK_SIGNEDNESS_BITS & UseFGInfo.SignMiscInfo); if (0 == UseSignMask) { // Try to get signedness from DEF. if (LocalUseName) { // Local name, find in basic block maps. DefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue); } else { // Global name, find in global maps. DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue); } DefSignMask = (FG_MASK_SIGNEDNESS_BITS & DefFGInfo.SignMiscInfo); UnionMask |= DefSignMask; } else { UnionMask |= UseSignMask; } } } if (2 > RegOpCount) { // only interested in binary arithmetic on two registers UnionMask = 0; } return UnionMask; } // SMPInstr::SignMaskUnionFromUseRegs() // emit check annotations for signedness, overflow, truncation, etc. void SMPInstr::EmitIntegerErrorAnnotations(FILE *InfoAnnotFile, list<size_t> &LoopList) { set<DefOrUse, LessDefUse>::iterator UseIter, DefIter; op_t UseOp, DefOp; unsigned short UseWidthInfo, DefWidthInfo, SourceDefWidthInfo; unsigned short DefSignInfo, SourceDefSignInfo; unsigned short UseSignMask, DefSignMask, SourceDefSignMask; struct FineGrainedInfo UseFGInfo, DefFGInfo, SourceDefFGInfo; size_t UseBitWidth, DefBitWidth, UseMaxBitWidth, SourceDefBitWidth, DefMaxBitWidth; ea_t DefAddr; int UseHashValue, DefHashValue, SSANum, DefSSANum, UseSSANum; int IdiomCode = 0; // records code pattern number causing suppression of annotation. bool OverflowOpcode = this->MDIsOverflowingOpcode(); bool UnderflowOpcode = this->MDIsUnderflowingOpcode(); bool CheckForOverflow; bool UseIsSigned, DefIsSigned, UseIsUnsigned, DefIsUnsigned, SourceDefIsSigned, SourceDefIsUnsigned; bool UseSignMixed, SourceDefSignMixed; // inconsistent signedness bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer(); bool SuppressSignednessCheck = false; // If we are not confident in check, set to true. bool PartialStore; // Store is fewer bits than is defined for the target, e.g. overwriting last 8 bits // of an int or a pointer. Cannot have signedness error in that case, as sign bit // is not affected. char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); string SinkString(""); if (this->IsNumericAnnotationSuppressed()) { return; // Cannot figure out IdiomCode; suppressed from earlier instruction's analyses } // If the instruction is the beginning of an infinite loop, we want no annotations other than // the infinite loop annotation. if (this->IsFirstInBlock()) { if (this->GetBlock()->IsInfiniteSelfLoop()) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR INFINITELOOP %s \n", (unsigned long) this->address, this->SMPcmd.size, disasm); return; } } // Case 1: Overflow on addition. // Case 2: Underflow on subtraction. if (OverflowOpcode || UnderflowOpcode) { // If the flags register DEF is dead, we need a CHECK OVERFLOW/UNDERFLOW annotation. DefOp = InitOp; DefOp.type = o_reg; DefOp.reg = MD_FLAGS_REG; DefIter = this->FindDef(DefOp); assert(DefIter != this->GetLastDef()); if (this->BasicBlock->IsDefDead(this->address, DefOp)) { DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); DefOp = DefIter->GetOp(); SSANum = DefIter->GetSSANum(); SMPOperandType DefType = DefIter->GetType(); SMPOperandType MemType; bool IgnoreOverflow = this->IsBenignOverflow(IdiomCode); uval_t ConstValue = 0; // NOTE: We no longer suppress the IgnoreOverflow annotations. Instead, we print // them with an IdiomCode so that suppression can happen downstream, along with // customized continuation policies, statistical summarizing, etc. #if SMP_MEASURE_NUMERIC_ANNOTATIONS if (IgnoreOverflow) ++BenignOverflowInstCount; #endif // Don't worry about stack space allocation instructions. The // program will crash long before the stack pointer underflows // below zero. if (!((o_reg == DefOp.type) && DefOp.is_reg(MD_STACK_POINTER_REG))) { if (!IgnoreOverflow && (this->GetBlock()->IsBenignOverflowDEF(DefOp, SSANum, this->GetAddr(), UnderflowOpcode, IdiomCode))) { IgnoreOverflow = true; #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++BenignOverflowDefCount; #endif } if (o_reg == DefOp.type) { DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum); if (this->BasicBlock->IsLocalName(DefOp)) { // Local name, find in basic block maps. DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue); } else { // Global name, find in global maps. DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue); } } else if (MDIsDirectStackAccessOpnd(DefOp, UseFP)) { bool success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo); assert(success); } else { // non-stack memory address; we know nothing about it. DefFGInfo.SignMiscInfo = 0; DefFGInfo.SizeInfo = 0; } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++NumericAnnotationsCount12; #endif DefSignInfo = DefFGInfo.SignMiscInfo; DefSignMask = DefSignInfo & FG_MASK_SIGNEDNESS_BITS; DefWidthInfo = DefFGInfo.SizeInfo; DefBitWidth = LargestBitWidthFromMask(DefWidthInfo); if (0 == DefBitWidth) { // Could happen for non-stack memory operands, for example. DefBitWidth = MD_NORMAL_MACHINE_BITWIDTH; } if (DefSignMask == 0) { // If no sign info from DEF, see if we can get it from the USE regs. DefSignMask = this->SignMaskUnionFromUseRegs(); } if ((0 == IdiomCode) && (DefSignMask == FG_MASK_UNSIGNED)) { // UNSIGNED is often reliable because compilers zero-extend values // just so they can avoid having undefined register bits. Later, only // the lower bits get stored. We already detect this pattern as a benign // truncation, but we need to back off on the certainty of our UNSIGNED // inference on overflow/underflow operations along the path. UseIter = this->FindUse(DefOp); if (UseIter != this->GetLastUse()) { UseOp = UseIter->GetOp(); UseSSANum = UseIter->GetSSANum(); if ((o_reg == UseOp.type) || MDIsDirectStackAccessOpnd(UseOp, UseFP)) { if (this->IsOpSourceZeroExtendedMove(UseOp, UseSSANum, false)) { if (this->GetBlock()->IsOpDestTruncatedWrite(DefOp, SSANum, this->GetAddr())) { // Operand source was reduced-width, zero-extended; ultimate use is to store // only the lower bits. The zero extension is no longer a reliable source of // inference for UNSIGNEDness. DefSignMask = FG_MASK_INCONSISTENT_SIGN; } } else if (UnderflowOpcode && this->IsOpSourceSmallPositiveConstant(UseOp, UseSSANum)) { // subtract from small positive, danger of UNSIGNED UNDERFLOW. Most likely, the // inference of UNSIGNED is unreliable. DefSignMask = FG_MASK_INCONSISTENT_SIGN; } } } } if (OverflowOpcode) { if ((17 == IdiomCode) && (FG_MASK_UNSIGNED == DefSignMask)) { // An IdiomCode of 17 means not a benign overflow, but an addition that // produces a jump table offset, which can be an addition of mixed signedness // that produces false positives if treated as UNSIGNED. See discussion in // SMPBasicBlock::IsBenignOverflowDEF(). DefSignMask = FG_MASK_INCONSISTENT_SIGN; } else if (this->IsAdditionOfLargeConstant(ConstValue, DefSignMask)) { IgnoreOverflow = true; IdiomCode = 11; } SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu ", (unsigned long) this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth); } else { // must be UnderflowOpcode // (1) Two more benign case to exclude: // movzx eax, byte ptr [esp+37] ; get char, with misleading zero-extension // sub eax,0x65 ; subtract 'A' to see if char is in letter range // cmp eax,25 ; there are 26 capital letters // ja nextcheck ; not in range of 'A' to 'Z' // return true ; found an alphanumeric // These operations make eax look UNSIGNED. But it is an ASCII char, and the subtraction // will underflow whenever the char is less than 'A'. Similar checks occur for 'a' // and the digit '0' quite often. We do not want false positives on underflow checks. // // (2) sub eax,[large constant value] // We might have to follow the subtrahend operand back to its origin. A subtraction of a // number that has a very large absolute value tends to indicate that underflow is benign // and is being ignored. if (!IgnoreOverflow) { if ((DefSignMask == FG_MASK_UNSIGNED) && (this->SubtractsImmedASCII())) { IgnoreOverflow = true; IdiomCode = 9; } else if ((DefSignMask == FG_MASK_UNSIGNED) || (DefSignMask == FG_MASK_SIGNED)) { if (this->IsSubtractionOfLargeConstant(ConstValue, DefSignMask)) { IgnoreOverflow = true; IdiomCode = 11; } } } SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK UNDERFLOW %s %zu ", (unsigned long) this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth); } op_t AnnotDefOp = DefOp; if (o_reg != DefOp.type) { // Need to unnormalize stack memory DEFs and USEs before printing annotations. this->MDGetUnnormalizedOp(AnnotDefOp); } AnnotPrintOperand(AnnotDefOp, InfoAnnotFile); if (!IgnoreOverflow) { // See if we made a special detection of an operation involved in a hash function, which can // be expected to overflow benignly. if (this->IsHashOperation()) { IgnoreOverflow = true; IdiomCode = 15; } else if (IsDataPtr(DefType)) { // IDIOM 18 says to let C7 (buffer overflow/underflow) defenses handle the issues if it is a pointer. IgnoreOverflow = true; IdiomCode = 18; } else if (1 <= LoopList.size()) { // Limit search for address-reg-only case to loops for now. list<size_t>::iterator LoopIter; for (LoopIter = LoopList.begin(); LoopIter != LoopList.end(); ++LoopIter) { size_t Counter = 0; size_t LoopNum = (*LoopIter); MemType = UNINIT; this->GetBlock()->GetFunc()->ResetProcessedBlocks(); bool AddrRegOnly = this->GetBlock()->IsDefOnlyUsedAsAddressReg(this->GetAddr(), DefOp, LoopNum, Counter, MemType); if (AddrRegOnly && (0 < Counter)) { // Similarly, let C7 (buffer overflow/underflow) defenses handle the issues if it is only an index register. DefType = MemType; IgnoreOverflow = true; IdiomCode = 18; break; // Don't need to look at next loop } } } } if (IgnoreOverflow) { if (11 == IdiomCode) { SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d CONST %lu %s \n", IdiomCode, (unsigned long) ConstValue, disasm); } else if (18 == IdiomCode) { // Need a tag field telling what kind of pointer. if (IsEqType(DefType, STACKPTR)) { SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d STACKMEMSINK %s \n", IdiomCode, disasm); } else if (IsEqType(DefType, HEAPPTR)) { SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d HEAPMEMSINK %s \n", IdiomCode, disasm); } else if (IsEqType(DefType, GLOBALPTR)) { SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d GLOBALMEMSINK %s \n", IdiomCode, disasm); } else { // must be unrefined POINTER type. SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d MEMORYSINK %s \n", IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d %s \n", IdiomCode, disasm); } } else if (this->GetBlock()->GetFunc()->HasIntErrorCallSink(DefOp, SSANum, this->address, SinkString)) { SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } } // end if (!(DEF is stack ptr register)) #if SMP_MEASURE_NUMERIC_ANNOTATIONS else { ++SuppressStackPtrOverflowCount; } #endif } // end if flags reg is dead #if SMP_MEASURE_NUMERIC_ANNOTATIONS else { ++SuppressLiveFlagsOverflowCount; } #endif } // end cases 1-2 // Case 3: Overflow on multiplication with upper bits discarded. if (this->MDIsMultiply()) { // There are four overflow sub-cases for x86: (A) the multiplication result // can go into EDX:EAX for 32x32=>64 bit multiplication; (B) the result // can go into DX:AX for 16x16=>32 bit; (C) the result can be in AX // for 8x8=>16 bit; (D) see below. The latter case (C) will be detected most easily // as a truncation in a later instruction, i.e. if only AL gets stored // later, then we check the AH bits at that time for a truncation // error. Because our SSA numbering lumps AL, AH, AX, and EAX into // a single canonicalized register, we would have a hard time using // SSA-based def-use chains to determine if AH is dead. // For the other two sub-cases, the question is whether EDX becomes dead // starting with the DEF of EDX in the multiply instruction. // Case (D) is where the multiply instruction discards the upper bits // of the multiply. // Sub-cases A&B are detected by checking if EDX is dead, and if so, then // emitting an annotation to check for the overflow flag. The x86 sets // overflow and carry flags on multiplication instructions based on whether // the result carries out of the lower half of the result to the upper half. // Sub-case D is also detected using flags, but we don't need to check whether EDX // is dead. We just need to detect that EDX is not in the DEF set in the // first place. We have a private member flag for that case. CheckForOverflow = false; if (this->AreMultiplicationBitsDiscarded()) { // Sub-case D CheckForOverflow = true; assert(this->RTL.GetCount() > 0); DefOp = this->RTL.GetRT(0)->GetLeftOperand(); DefIter = this->FindDef(DefOp); assert(DefIter != this->GetLastDef()); DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); } else { // If the instruction were EDX:=EDX*foo, then it would be // the multiplication bits discarded case and would not // reach this else clause. Therefore, if we find EDX in // the DEF set, it is holding upper result bits of the // multiplication and we have the potential for sub-cases A&B // but not sub-case C. So, we check to see if the DEF of EDX // is dead. DefOp = InitOp; DefOp.type = o_reg; DefOp.reg = R_dx; DefIter = this->FindDef(DefOp); if (DefIter != this->GetLastDef()) { // We found DEF of EDX, so it is not AX:=AL*op8 sub-case C. // Now, is DEF of EDX dead (i.e. no uses?) CheckForOverflow = this->BasicBlock->IsDefDead(this->address, DefOp); if (CheckForOverflow) { DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum()); } } } // end if sub-case D else if sub-case A or B if (CheckForOverflow) { // need an annotation #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++NumericAnnotationsCount3; #endif if (this->BasicBlock->IsLocalName(DefOp)) { // Local name, find in basic block maps. DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue); } else { // Global name, find in global maps. DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue); } DefWidthInfo = DefFGInfo.SizeInfo; DefBitWidth = LargestBitWidthFromMask(DefWidthInfo); DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); if (DefSignMask == 0) { // If no sign info from DEF, see if we can get it from the USE regs. DefSignMask = this->SignMaskUnionFromUseRegs(); } // Next two statements exclude the inconsistent sign case and the no sign info known case. DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND uval_t ConstValue = 0; IdiomCode = 0; bool UnsignedAnnot = (this->MDIsUnsignedArithmetic() || DefIsUnsigned); bool SignedAnnot = (this->MDIsSignedArithmetic() || DefIsSigned); if (0 == DefSignMask) { // Prepare DefSignMask for use by IsMultiplyByLargeConst(), // as it is of no more use in case3. if (UnsignedAnnot) DefSignMask = FG_MASK_UNSIGNED; else if (SignedAnnot) DefSignMask = FG_MASK_SIGNED; } if (this->IsMultiplyByLargeConstant(ConstValue, DefSignMask)) { IdiomCode = 10; } if (UnsignedAnnot) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW UNSIGNED %zu %s ZZ", (unsigned long) this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg]); } else if (SignedAnnot) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW SIGNED %zu %s ZZ", (unsigned long) this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg]); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW UNKNOWNSIGN %zu %s ZZ", (unsigned long) this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg]); } if (0 < IdiomCode) { SMP_fprintf(InfoAnnotFile, " IDIOM %d CONST %lu %s \n", IdiomCode, (unsigned long) ConstValue, disasm); } else { SMP_fprintf(InfoAnnotFile, " %s \n", disasm); } } // end if (CheckForOverflow) #if SMP_MEASURE_NUMERIC_ANNOTATIONS else { ++LiveMultiplyBitsCount; } #endif } // end of case 3 // Case 4: Signedness error on move. // Case 5: Truncation error on move. IdiomCode = 0; UseOp = this->GetMoveSource(); if ((3 == this->OptType) && (o_reg == UseOp.type)) { int TruncationIdiomCode = 0; bool SuppressTruncation = this->IsBenignTruncation(TruncationIdiomCode); // Possibilities for a move: reg to reg, mem to reg, or reg to mem. If we load // from memory into a register, we cannot track signedness in memory unless it // is a stack location. In that case, we record the signedness in the stack // map and transfer it to the reg DEF in SMPInstr.MDSetWidthSignInfo(). That // determines the signedness of the reg DEF and it cannot be in conflict with // the stack memory USE. The load from stack to reg also determines width // of the stack operand and we cannot have a truncation. So, we can restrict // our analysis of cases 4-5 to register-source move instructions, as we // have done in the condition above. // // Similarly, we cannot detect a signedness conflict if the destination is a // memory location that is not known to be a particular stack offset location. // // So, we only concern ourselves with signedness errors // when the USE operand of the move is a register, and the destination is another // register or a stack location. // // We can have a truncation error and a signedness error on a single instruction, so // we group them into common code. For example, move the lower half of a 32-bit unsigned // into a 16-bit signed destination. Upper bits set to 1 and discarded would be a // truncation, and setting the sign bit of the 16-bit signed destination would be a // signedness error. // // NOTE: Signedness errors are different from overflow and truncation errors. We // can have incomplete knowledge about an instruction's operands and still determine // that truncation occurred. For example, if we do not know whether register EAX // is signed or unsigned, we can still say that storing only AX is a truncation error // if the upper half of EAX is a mixture of one and zero bits. If EAX is unsigned, // we could be more specific and insist that the upper half be all zero bits; if EAX // is signed, we could insist that the upper half of EAX be the sign-extension of AX. // We can avoid false positives by only declaring a truncation error when the upper // half of EAX is not all zero bits or all one bits. This approach allows a few // potential false negatives. With signedness, if we don't know the signedness // of one of the operands, we can only avoid false positives by doing no checks at // all. UseIter = this->FindUse(UseOp); assert(UseIter != this->GetLastUse()); UseBitWidth = 8 * GetOpDataSize(UseOp); // Now, the question is: Are we storing fewer bits than // we were using in our computations in this DEF-USE chain? // E.g. if we computed using 32 bits and then only store 16, // we have potential truncation error. But if we computed // using 16 bits all along, we have already checked for 16-bit // overflows on arithmetic in the DU chain and there can be no // truncation on this store. op_t SearchOp = UseOp; // Canonicalize sub-regs for searching DEFs and USEs. CanonicalizeOpnd(SearchOp); UseHashValue = HashGlobalNameAndSSA(SearchOp, UseIter->GetSSANum()); if (this->BasicBlock->IsLocalName(SearchOp)) { // Local name, find in basic block maps. SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue); UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // Global name, find in global maps. SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue); UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } SourceDefWidthInfo = SourceDefFGInfo.SizeInfo; UseWidthInfo = UseFGInfo.SizeInfo; SourceDefBitWidth = LargestBitWidthFromMask(SourceDefWidthInfo); UseMaxBitWidth = LargestBitWidthFromMask(UseWidthInfo); UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); SourceDefSignInfo = SourceDefFGInfo.SignMiscInfo; SourceDefSignMask = (SourceDefSignInfo & FG_MASK_SIGNEDNESS_BITS); #if 1 // If we have no signedness info at all for the UseSignMask, but // the SourceDefSignMask has info, then we want to use the // SourceDefSignMask as our signedness info. This is because of // simple instruction sequences in which no signedness can be inferred // from the use, e.g.: // // imul eax,ebx ; eax := eax*ebx and eax&ebx are signed // mov [ebp-8],al ; store al on stack // // If we don't know the signedness of stack location [ebp-8], // then we will end up in the S->? case below and we will emit // a CHECK TRUNCATION UNKNOWNSIGN annotation. This discards the // knowledge we have that EAX is signed, from the IMUL opcode. if (0 == UseSignMask) { UseSignMask = SourceDefSignMask; } #endif // Next six statements exclude the inconsistent sign case and the no sign info known case. UseIsSigned = (FG_MASK_SIGNED == UseSignMask); // exact, not bit-mask-AND UseIsUnsigned = (FG_MASK_UNSIGNED == UseSignMask); // exact, not bit-mask-AND SourceDefIsSigned = (FG_MASK_SIGNED == SourceDefSignMask); // exact, not bit-mask-AND SourceDefIsUnsigned = (FG_MASK_UNSIGNED == SourceDefSignMask); // exact, not bit-mask-AND UseSignMixed = (FG_MASK_INCONSISTENT_SIGN == UseSignMask); // exclude uninit sign case SourceDefSignMixed = (FG_MASK_INCONSISTENT_SIGN == SourceDefSignMask); // exclude uninit sign case // Not only the CHECK SIGNEDNESS annotations depend on the signedness of the // source and destination operands. The CHECK TRUNCATION annotations come // in SIGNED, UNSIGNED, and UNKNOWNSIGN variants, so we need to get the // signedness of the destination operand before we proceeed. DefOp = this->RTL.GetRT(0)->GetLeftOperand(); // RTL must be dest := rhs op_t DestSearchOp = DefOp; bool StackDestination = false; // Outargs locations are reused for different function calls, so no inference of their // signedness is valid. We maintain a flag to suppress signedness checks on writes to // outargs locations on the stack. We make an exception for the known unsigned args to // certain library functions. bool OutArgsWrite = false; bool IgnoreOutArgsWrite = false; bool NonStackMemDestination = false; if (o_reg == DestSearchOp.type) { StackDestination = false; CanonicalizeOpnd(DestSearchOp); } else if (!(MDIsDirectStackAccessOpnd(DefOp, UseFP))) { // If destination of move is not a register and is not // a stack location, we cannot track its signedness and width. NonStackMemDestination = true; } else { StackDestination = true; } DefIter = this->FindDef(DestSearchOp); DefSSANum = DefIter->GetSSANum(); if (StackDestination) { // Fetch FG info from stack map. bool success = this->GetBlock()->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo); assert(success); OutArgsWrite = this->GetBlock()->GetFunc()->IsInOutgoingArgsRegion(DefOp); IgnoreOutArgsWrite = OutArgsWrite && (!(this->IsUnsignedArg())); // ignore out args write iff not a known unsigned argument } else if (NonStackMemDestination) { // We can check truncation of USE operand, but we know nothing about // the DEF operand. DefFGInfo.SignMiscInfo = 0; DefFGInfo.SizeInfo = 0; } else { // Fetch FG info from register FG info maps. DefHashValue = HashGlobalNameAndSSA(DestSearchOp, DefSSANum); if (this->BasicBlock->IsLocalName(DestSearchOp)) { // Local name, find in basic block maps. DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue); } else { // Global name, find in global maps. DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue); } } DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); // Next two statements exclude the inconsistent sign case and the no sign info known case. DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND // Get the Def bit width and maximum bit width for special cases. DefWidthInfo = DefFGInfo.SizeInfo; DefMaxBitWidth = LargestBitWidthFromMask(DefWidthInfo); // max width over all defs DefBitWidth = 8 * GetOpDataSize(DefOp); // width of def in current instruction PartialStore = (DefBitWidth < DefMaxBitWidth); if (StackDestination) { // We only do signedness checks in limited, safe analysis cases on stack writes. SuppressSignednessCheck = this->SkipSignednessCheckOnStackWrite(DefSSANum); if (SuppressSignednessCheck) { IdiomCode = 7; } } else { // Check signedness on regs, but not on non-stack memory DEFs. SuppressSignednessCheck = NonStackMemDestination; } // If we set the (source) DEF bit width to 0, it means we wanted to have the USEs determine // the width. This happens on sign-extended and zero-extended loads. If we zero-extend // a 16-bit value to 32 bits, then immediately store the lower 16 bits to a 16-bit location, // then the upper bits cannot have any overflow info yet. But if we do 32-bit arithmetic // on the zero-extended value, and then store the lower 16 bits, we need to check for // truncation. So, the key is whether the value ever got used as a 32-bit value. If it // did, check for truncation; if not, there is no need to check. if ((SourceDefBitWidth > UseBitWidth) || ((SourceDefBitWidth == 0) && (UseMaxBitWidth > UseBitWidth))) { // Original DEF (or subsequent USE) was wider than what we are storing now. if (SourceDefBitWidth == 0) { // Convert for printing annotation. SourceDefBitWidth = 8 * GetOpDataSize(SearchOp); } // OK, we need to check for possible truncation. But, how we check depends on the // signedness combinations of the source and destination operands of the move. // Each operand can be signed, unsigned, or of unknown sign (and we lump the // inconsistent sign case into the unknown sign case). So, we have a set of 3x3=9 // possible combinations of signedness. // Now we have the DefSignMask to compare to the UseSignMask. The nine possible // combinations, and the annotations we want to emit for each, are shown below. // S = SIGNED, U = UNSIGNED, and ? = unknown or inconsistent sign. // S => U indicates a SIGNED being stored into an UNSIGNED, for example. // Assume without loss of generality that register EAX is the source of // all the move instructions, and that only subword register AX is being stored. // We can perform all truncation and signedness checks on EAX just prior to // the move instruction, which is cheaper than performing checks on the // destination if the destination is in memory. // // U => U // U => S // S => U // U => ? // ? => U // // In these first five cases, EAX must be the zero-extension of AX else there is // a truncation error. In the three cases in which the source (EAX/AX) is UNSIGNED, // discarding upper bits that are not zero is obviously truncation. In the case // of S => U, if the upper bits of EAX are not all zeroes, then we either have // a large positive value of EAX that is being truncated, or EAX is negative and // the lower bits will be misinterpreted in the unsigned destination. Finally, // the ? => U case must be either U => U or S => U, and these two cases already // share the demand that EAX be the zero-extension of AX. So, these five cases // will receive the annotation: CHECK TRUNCATION UNSIGNED 32 EAX 16 AX which // means that EAX is tested against AX to see if it is the 32-bit zero-extension // of 16-bit reg AX. // In the U => S case, we can have a signedness error as well as truncation. Even // if the truncation check passes (all upper half bits of EAX are zero), the top // bit of AX might be 1, and this will be misinterpreted as a sign bit in the // destination. So, this case receives a second annotation: CHECK SIGNEDNESS SIGNED 16 AX. // In the two cases that involve signedness uncertainty, there are possible signedness // errors that we are not checking at tun-time, because we do not have enough information // to perform the checks without generating many more false positives than true positives. // As a result, false negatives on signedness can occur. // // On to more of the 9 combinations: // // S => S // // In this case, EAX must be the sign-extension of AX. Because the destination is also // signed, nothing is lost if the sign-extension bits (all zeroes or all ones) are dropped. // We emit a CHECK TRUNCATION SIGNED 32 EAX 16 AX annotation to test EAX == sign-extended AX. // // S => ? // ? => S // ? => ? // // These final three cases all involve at least one operand of unknown signedness, and no // operands that are known to be unsigned. In each case, there are two possibilities: // either EAX must be the sign-extension of AX, or EAX must be the zero-extension of AX. // Because of the uncertainty that is represented by the question marks, we are not sure // which of these two cases we are dealing with. However, rather than just give up and // perform no run-time checks (to avoid false positives), we can still perform a run-time // check that will catch (perhaps most) true positives while causing no false positives. // We can insist that EAX must be EITHER the sign-extension or the zero-extension of AX. // To be neither type of extension of AX implies that some sort of truncation is happening. // So, we emit a CHECK TRUNCATION UNKNOWNSIGN 32 EAX 16 AX annotation, and the Strata // instrumentation will check for either EAX == sign-extended AX or EAX == zero-extended AX // being true. If neither is true, we raise a true positive alert. False negatives on // signedness errors are the result of the uncertainty, but all truncations are detected // for all nine cases. bool HasSinkString = this->GetBlock()->GetFunc()->HasIntErrorCallSink(DestSearchOp, DefSSANum, this->address, SinkString); if (DefIsUnsigned || UseIsUnsigned) { // First five cases above: any UNSIGNED operand leads to CHECK TRUNCATION UNSIGNED annotation. if (!SuppressTruncation) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION UNSIGNED %zu %s %zu %s", (unsigned long) this->address, this->SMPcmd.size, SourceDefBitWidth, MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp)); if (HasSinkString) { SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++TruncationAnnotationsCount; #endif } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION UNSIGNED %zu %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, SourceDefBitWidth, MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), TruncationIdiomCode, disasm); } if (UseIsUnsigned && DefIsSigned && !OutArgsWrite && !PartialStore) { if (!SuppressSignednessCheck && (0 == IdiomCode)) { unsigned short DummySignMask; if ((0 != TruncationIdiomCode) && this->MDIsSignedLoad(DummySignMask)) { // No truncation, extended load, so signedness check is unreliable. IdiomCode = 20; SuppressSignednessCheck = true; } } if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } } else if (DefIsSigned && UseIsSigned) { // S => S case above. Emit CHECK TRUNCATION SIGNED annotation. if (!SuppressTruncation) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s", (unsigned long) this->address, this->SMPcmd.size, SourceDefBitWidth, MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp)); if (HasSinkString) { SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++TruncationAnnotationsCount; #endif } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, SourceDefBitWidth, MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), TruncationIdiomCode, disasm); } } else { // S => ?, ? => S, ? => ? cases above: CHECK TRUNCATION UNKNOWNSIGN annotation. if (!SuppressTruncation) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION UNKNOWNSIGN %zu %s %zu %s", (unsigned long) this->address, this->SMPcmd.size, SourceDefBitWidth, MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp)); if (HasSinkString) { SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++TruncationAnnotationsCount; #endif } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION UNKNOWNSIGN %zu %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, SourceDefBitWidth, MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), TruncationIdiomCode, disasm); } } #if 1 // Now check for signedness conflicts between the UseOp USEs and its DEF. if (!OutArgsWrite && !PartialStore) { // Inferred signedness of outargs location is invalid anyway if (!SuppressSignednessCheck && (0 == IdiomCode)) { unsigned short DummySignMask; if ((0 != TruncationIdiomCode) && this->MDIsSignedLoad(DummySignMask)) { // No truncation, extended load, so signedness check is unreliable. IdiomCode = 20; SuppressSignednessCheck = true; } } if (UseIsSigned && SourceDefIsUnsigned) { if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } else if (UseIsUnsigned && SourceDefIsSigned) { // Currently same annotation, but might differ in the future for better forensics // and more precise diagnostic messages. if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } else if ((!SourceDefSignMixed) && UseSignMixed) { // DEF has consistent and known signedness, USE is inconsistent. if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, SignednessStrings[SourceDefSignMask], UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, SignednessStrings[SourceDefSignMask], UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } } #if SMP_MEASURE_NUMERIC_ANNOTATIONS if (SuppressSignednessCheck || (0 < IdiomCode)) { ++SuppressSignednessOnTruncation; } #endif #endif } // end if truncation else if (!IgnoreOutArgsWrite && !PartialStore) { // still need to check for signedness errors even if no truncation if (!SuppressSignednessCheck && (0 == IdiomCode)) { unsigned short DummySignMask; if (this->MDIsSignedLoad(DummySignMask)) { // No truncation, extended load, so signedness check is unreliable. IdiomCode = 20; SuppressSignednessCheck = true; } } if (this->IsUnsignedArg()) { DefIsUnsigned = true; } if (UseIsSigned && DefIsUnsigned) { if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } else if (UseIsUnsigned && DefIsSigned) { // Currently UNSIGNED and SIGNED annotations for signedness differ only in the // arithmetic saturation value applied as a recovery action. UNSIGNED means // left-hand-side is UNSIGNED so saturate a negative right-hand-side up to 0, // while SIGNED means lhs is SIGNED so saturate large rhs to 0x7fff.... // For inconsistent sign cases we default to SIGNED. if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } else if (UseIsSigned && SourceDefIsUnsigned) { if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } else if (UseIsUnsigned && SourceDefIsSigned) { // Currently same annotation, but might differ in the future for better forensics // and more precise diagnostic messages. if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm); } } #if 0 else if ((!SourceDefSignMixed) && UseSignMixed) { // source DEF has consistent and known signedness, source USE is inconsistent. SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %u %s ZZ %s \n", this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm); } #endif #if SMP_MEASURE_NUMERIC_ANNOTATIONS if (SuppressSignednessCheck && (0 == IdiomCode)) { ; } else if (!SuppressSignednessCheck && (0 == IdiomCode)) { ++SignednessWithoutTruncationCount; } else { ++SuppressSignednessOnTruncation; } #endif } // end if truncation else check signedness } // end of cases 4-5, (3 == OptType) checking for TRUNCATION and SIGNEDNESS errors else if ((this->MDIsLoadEffectiveAddressInstr()) && (!(this->IsNop() || this->IsRegClearIdiom()))) { // Case 6: Load Effective Address opcodes can do arithmetic that silently overflows. this->MDEmitLeaOpcodeOverflowAnnotations(InfoAnnotFile, LoopList); } // end of case 6, LoadEffectiveAddress instruction else if (this->MDDoublesWidth()) { // case 7: half of register is sign-extended into the full register. // Potential truncation error if entire register was DEFed first, because // only the lower half is used. That also makes a potential signedness // error, because the "sign bit" that is being extended is really a data bit, // not a sign bit. DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); DefOp = DefIter->GetOp(); assert(o_reg == DefOp.type); SSANum = DefIter->GetSSANum(); DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum); UseIter = this->GetFirstUse(); assert(UseIter != this->GetLastUse()); UseOp = UseIter->GetOp(); assert(o_reg == UseOp.type); SSANum = UseIter->GetSSANum(); UseHashValue = HashGlobalNameAndSSA(UseOp, SSANum); if (this->GetBlock()->IsLocalName(DefOp)) { DefFGInfo = this->GetBlock()->GetDefFGInfo(DefHashValue); SourceDefFGInfo = this->GetBlock()->GetDefFGInfo(UseHashValue); } else { DefFGInfo = this->GetBlock()->GetFunc()->GetDefFGInfo(DefHashValue); SourceDefFGInfo = this->GetBlock()->GetFunc()->GetDefFGInfo(UseHashValue); } DefWidthInfo = DefFGInfo.SizeInfo; DefMaxBitWidth = LargestBitWidthFromMask(DefWidthInfo); // max width over all defs SourceDefWidthInfo = SourceDefFGInfo.SizeInfo; SourceDefBitWidth = LargestBitWidthFromMask(SourceDefWidthInfo); // max width over all defs // Was the USE in the width-doubling instruction originally DEFed to have more bits // than are used in the width-doubling instruction? If so, we have truncation and // signedness errors to check at run time. UseBitWidth = (DefMaxBitWidth / 2); // USE should have been DEFed with this bit width if (SourceDefBitWidth > UseBitWidth) { // Make checks signed, because of the implications of sign extension by these opcodes. SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, DefMaxBitWidth, MDGetRegName(DefOp), UseBitWidth, MDGetRegName(UseOp), disasm); #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++WidthDoublingTruncationCount; #endif } } // end of case 7, doubles width inside register else if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) { // PEASOUP wants info on memset() calls. string FuncName = this->GetTrimmedCalledFunctionName(); if (0 == FuncName.compare("memset")) { op_t MemSetTarget; size_t MemSetSize; int StackOffset; bool FPRelativeTarget = false; // original target before stack delta normalization if (this->GetBlock()->AnalyzeMemSet(this->GetAddr(), MemSetTarget, MemSetSize, StackOffset, FPRelativeTarget)) { if (0 < MemSetSize) { // Emit annotation. // NOTE: We want the unnormalized stack operand, so that Strata and SPRI will get // annotations that match what is seen in the assembly language. if (FPRelativeTarget) { // Must be negative offset from EBP. assert(UseFP); StackOffset -= (int) this->GetBlock()->GetFunc()->GetFramePtrStackDelta(); SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR MEMSET STACKOFFSET_EBP %d SIZE %zu ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, StackOffset, MemSetSize, disasm); } else { // Must be non-negative offset from ESP. StackOffset -= this->GetStackPtrOffset(); SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR MEMSET STACKOFFSET_ESP %d SIZE %zu ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, StackOffset, MemSetSize, disasm); } } } } } // end of memset() case return; } // end of SMPInstr::EmitIntegerErrorAnnotations() #define MAX_OFFSET_STR_LEN 40 // Emit overflow annotations for lea opcodes that perform arithmetic that // can overflow without affecting the flags. void SMPInstr::MDEmitLeaOpcodeOverflowAnnotations(FILE *InfoAnnotFile, list<size_t> &LoopList) { set<DefOrUse, LessDefUse>::iterator UseIter, DefIter; size_t OpNum; int BaseReg; int IndexReg; ushort ScaleFactor; ea_t offset; int SignedOffset; bool ScaledIndexReg; bool SourceFound = false; bool DataPointer = false; int SSANum; SMPOperandType DefType, UseType; int UseHashValue; int IdiomCode = 0; struct FineGrainedInfo UseFGInfo, SourceDefFGInfo; unsigned short UseSignMask, SourceDefSignMask, BaseRegSignMask, TempSignMask; unsigned short BaseRegWidthInfo, UseBitWidthInfo; size_t BaseRegMaxWidth, IndexRegMaxWidth, TempMaxWidth; char OffsetString[MAX_OFFSET_STR_LEN]; op_t TempOp, DefOp; for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & UseMacros[OpNum]) { // USE if ((TempOp.type >= o_mem) && (TempOp.type <= o_displ)) { MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset); SignedOffset = (int) offset; // IDA Pro makes offset unsigned for some reason. SourceFound = true; break; } } } assert(SourceFound); if (MDIsStackAccessOpnd(TempOp, this->BasicBlock->GetFunc()->UsesFramePointer())) { // If the lea operand is something like lea ebx,[esp+4] we do not want to waste // time checking for overflow, because stack pointers and frame pointers // are kept within integer bounds (and if not, something big and terrible will // happen long before integer bounds are overflowed or underflowed). #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++SuppressStackPtrOverflowCount; #endif return; } else { // Check for the benign overflow cases we want to ignore. DefIter = this->GetFirstNonFlagsDef(); assert(DefIter != this->GetLastDef()); DefOp = DefIter->GetOp(); SSANum = DefIter->GetSSANum(); DefType = DefIter->GetType(); DataPointer = IsDataPtr(DefType); if (this->IsHashOperation()) { IdiomCode = 15; } else if (this->GetBlock()->IsBenignOverflowDEF(DefOp, SSANum, this->GetAddr(), this->MDIsUnderflowingOpcode(), IdiomCode)) { #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++BenignOverflowDefCount; #endif ; } } if (0 == IdiomCode) { // Any overflow or underflow producing a PTROFFSET type should be suppressed. for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) { DefType = DefIter->GetType(); if (IsEqType(DefType, PTROFFSET)) { IdiomCode = 19; } else if (IsEqType(DefType, NEGATEDPTR)) { IdiomCode = 13; } } } if (0 == IdiomCode) { // Any overflow or underflow using a PTROFFSET type should be suppressed. for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { UseType = UseIter->GetType(); if (IsEqType(UseType, PTROFFSET)) { IdiomCode = 19; } else if (IsEqType(UseType, NEGATEDPTR)) { IdiomCode = 13; } } } char *disasm = DisAsmText.GetDisAsm(this->GetAddr()); ScaledIndexReg = (0 < ScaleFactor); if (0 != SignedOffset) { (void) SMP_snprintf(OffsetString, MAX_OFFSET_STR_LEN-2, "%d", SignedOffset); } // Build up the computational string one operation at a time and emit // CHECK OVERFLOW annotations after each step. string CurrString(""); const char *ScaleStrings[4] = {"", "*2", "*4", "*8"}; string PtrString(""); if (IdiomCode == 0) { if (!DataPointer && (1 == LoopList.size())) { // Start with simple case of blocks only present in one loop SMPOperandType MemType = UNINIT; size_t Counter = 0; size_t LoopNum = LoopList.front(); this->GetBlock()->GetFunc()->ResetProcessedBlocks(); bool AddrRegOnly = this->GetBlock()->IsDefOnlyUsedAsAddressReg(this->GetAddr(), DefOp, LoopNum, Counter, MemType); if (AddrRegOnly && (0 < Counter)) { // Similarly, let C7 (buffer overflow/underflow) defenses handle the issues if it is only an index register. DefType = MemType; DataPointer = true; } } if (DataPointer) { if (IsEqType(STACKPTR, DefType)) PtrString = "STACKMEMSINK"; else if (IsEqType(HEAPPTR, DefType)) PtrString = "HEAPMEMSINK"; else if (IsEqType(GLOBALPTR, DefType)) PtrString = "GLOBALMEMSINK"; else PtrString = "MEMORYSINK"; IdiomCode = 18; } } op_t RegOp; RegOp.type = o_reg; // Gather signedness info about BaseReg, if any, that will be used in multiple cases below. if (R_none != BaseReg) { RegOp.reg = MDCanonicalizeSubReg(BaseReg); RegOp.dtyp = STARS_ISA_dtyp; UseIter = this->FindUse(RegOp); assert(UseIter != this->GetLastUse()); UseHashValue = HashGlobalNameAndSSA(RegOp, UseIter->GetSSANum()); if (this->BasicBlock->IsLocalName(RegOp)) { // Local name, find in basic block maps. SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue); UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // Global name, find in global maps. SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue); UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); SourceDefSignMask = (SourceDefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); if (UseSignMask == 0) { // Get signedness info wherever it is available. UseSignMask = SourceDefSignMask; } BaseRegSignMask = UseSignMask; if ((17 == IdiomCode) && (FG_MASK_UNSIGNED == BaseRegSignMask)) { // An IdiomCode of 17 means not a benign overflow, but an addition that // produces a jump table offset, which can be an addition of mixed signedness // that produces false positives if treated as UNSIGNED. See discussion in // SMPBasicBlock::IsBenignOverflowDEF(). BaseRegSignMask = FG_MASK_INCONSISTENT_SIGN; } BaseRegWidthInfo = (UseFGInfo.SizeInfo & FG_MASK_BITWIDTH_FIELDS); if (0 == BaseRegWidthInfo) { BaseRegWidthInfo = (SourceDefFGInfo.SizeInfo & FG_MASK_BITWIDTH_FIELDS); } BaseRegMaxWidth = LargestBitWidthFromMask(BaseRegWidthInfo); } else { BaseRegMaxWidth = 0; BaseRegSignMask = 0; } if (R_none != IndexReg) { // Get signedness info for IndexReg. RegOp.reg = MDCanonicalizeSubReg(IndexReg); RegOp.dtyp = STARS_ISA_dtyp; UseIter = this->FindUse(RegOp); assert(UseIter != this->GetLastUse()); UseHashValue = HashGlobalNameAndSSA(RegOp, UseIter->GetSSANum()); if (this->BasicBlock->IsLocalName(RegOp)) { // Local name, find in basic block maps. SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue); UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue); } else { // Global name, find in global maps. SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue); UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue); } UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); SourceDefSignMask = (SourceDefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); if (UseSignMask == 0) { // Get signedness info wherever it is available. UseSignMask = SourceDefSignMask; } if ((17 == IdiomCode) && (FG_MASK_UNSIGNED == UseSignMask)) { // An IdiomCode of 17 means not a benign overflow, but an addition that // produces a jump table offset, which can be an addition of mixed signedness // that produces false positives if treated as UNSIGNED. See discussion in // SMPBasicBlock::IsBenignOverflowDEF(). UseSignMask = FG_MASK_INCONSISTENT_SIGN; IdiomCode = 0; // reset; not a benign code idiom } UseBitWidthInfo = (UseFGInfo.SizeInfo & FG_MASK_BITWIDTH_FIELDS); IndexRegMaxWidth = LargestBitWidthFromMask(UseBitWidthInfo); TempSignMask = (BaseRegSignMask | UseSignMask); TempMaxWidth = (BaseRegMaxWidth >= IndexRegMaxWidth) ? BaseRegMaxWidth : IndexRegMaxWidth; if (ScaledIndexReg) { assert((ScaleFactor >= 1) && (ScaleFactor <= 3)); assert((IndexReg >= R_ax) && (IndexReg <= R_bh)); CurrString += RegNames[IndexReg]; CurrString += ScaleStrings[ScaleFactor]; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[UseSignMask], IndexRegMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[UseSignMask], IndexRegMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[UseSignMask], IndexRegMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif if (R_none != BaseReg) { // Have BaseReg+IndexReg*ScaleFactor string TempStr(CurrString); CurrString.clear(); CurrString += RegNames[BaseReg]; CurrString += "+"; CurrString += TempStr; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif } if (0 != SignedOffset) { // We have [ BaseReg + ] IndexReg*ScaleFactor+offset [BaseReg or not] // CurrString has everything through the end of the scalefactor in either case. #if 0 // easier for instrumentation to always use ADD, even for negative offsets if (0 < SignedOffset) #endif CurrString += "+"; CurrString += OffsetString; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif } } else if (R_none != BaseReg) { // We have BaseReg+IndexReg, unscaled. CurrString += RegNames[BaseReg]; CurrString += "+"; CurrString += RegNames[IndexReg]; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif if (0 != SignedOffset) { // We have BaseReg + IndexReg + offset #if 0 // easier for instrumentation to always use ADD, even for negative offsets if (0 < SignedOffset) #endif CurrString += "+"; CurrString += OffsetString; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif } } else if (0 != SignedOffset) { // We have just IndexReg+offset. CurrString += RegNames[IndexReg]; #if 0 // easier for instrumentation to always use ADD, even for negative offsets if (0 < SignedOffset) #endif CurrString += "+"; CurrString += OffsetString; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask], TempMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif } else { // Just IndexReg, no BaseReg or offset, so nothing to do. ; } } else if ((R_none != BaseReg) && (SignedOffset != 0)) { // No index reg, scaled or otherwise. Just BaseReg+offset CurrString += RegNames[BaseReg]; if (17 == IdiomCode) { IdiomCode = 0; // reset; not a benign code idiom } #if 0 // easier for instrumentation to always use ADD, even for negative offsets if (0 < SignedOffset) #endif CurrString += "+"; CurrString += OffsetString; if (IdiomCode > 0) { if (IdiomCode == 18) { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[BaseRegSignMask], BaseRegMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm); } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[BaseRegSignMask], BaseRegMaxWidth, CurrString.c_str(), IdiomCode, disasm); } } else { SMP_fprintf(InfoAnnotFile, "%10lx %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n", (unsigned long) this->address, this->SMPcmd.size, LeaSignednessStrings[BaseRegSignMask], BaseRegMaxWidth, CurrString.c_str(), disasm); } #if SMP_MEASURE_NUMERIC_ANNOTATIONS ++LeaInstOverflowCount; #endif } else { // Either just a BaseReg, or just an offset. Nothing to do. ; } return; } // end of SMPInstr::MDEmitLeaOpcodeOverflowAnnotations() // Go through the PUSH RTL and get the operand pushed. op_t SMPInstr::GetPushedOpnd(void) { op_t VoidOp = InitOp; if (NN_push == this->SMPcmd.itype) { for (size_t OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & UseMacros[OpNum]) { // USE return TempOp; } } SMP_msg("ERROR: Could not find PUSH operand at %lx %s\n", (unsigned long) this->address, DisAsmText.GetDisAsm(this->GetAddr())); return VoidOp; } else { return VoidOp; } } // end of SMPInstr::GetPushedOpnd() // Get the immediate value used in the instruction. Return zero // if no immediate was used. int SMPInstr::MDGetImmedUse(void) { int ImmedVal = 0; set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) { op_t UseOp = CurrUse->GetOp(); if (o_imm == UseOp.type) { ImmedVal = (int) UseOp.value; break; } } return ImmedVal; } // end of SMPInstr::MDGetImmedUse() // Get funcname from call inst and remove "." and "_" prefices. // Asserts if this is not a call instruction. string SMPInstr::GetTrimmedCalledFunctionName(void) { SMPitype DFType = this->GetDataFlowType(); assert((CALL == DFType) || (INDIR_CALL == DFType) || this->IsTailCall()); ea_t FuncAddr = this->CallTarget; char IDA_func_name[MAXSTR]; size_t SkipCount; char *TempFuncName; if (CALL == DFType) { // We should have a good call target for direct calls. assert(BADADDR != FuncAddr); (void) get_func_name(FuncAddr, IDA_func_name, (size_t)(MAXSTR - 1)); SkipCount = strspn(IDA_func_name, "._"); TempFuncName = &(IDA_func_name[SkipCount]); string FuncName(TempFuncName); return FuncName; } else { // INDIR_CALL // We might have a resolved call target for indirect calls. if (BADADDR != FuncAddr) { // We have a resolved address for the indirect call. (void) get_func_name(FuncAddr, IDA_func_name, (size_t)(MAXSTR - 1)); SkipCount = strspn(IDA_func_name, "._"); TempFuncName = &(IDA_func_name[SkipCount]); string IndirFuncName(TempFuncName); return IndirFuncName; } else { // INDIR_CALL, no resolved target. string DummyFuncName("ZST_NEVER_MATCH_THIS_FUNC_NAME"); return DummyFuncName; } } } // end of SMPInstr::GetTrimmedCalledFunctionName() // Build the RTL for an instruction with a unary opcode bool SMPInstr::BuildUnaryRTL(SMPoperator UnaryOp) { size_t OpNum; bool DestFound = false; bool WidthDoubler = this->MDDoublesWidth(); SMPRegTransfer *TempRT = NULL; op_t VoidOp = InitOp; op_t FPRegOp = InitOp; FPRegOp.type = o_fpreg; // floating point register stack op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; // Handle special cases first if (SMP_UNARY_FLOATING_ARITHMETIC == UnaryOp) { // Use of the floating register stack top is implicit DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(FPRegOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetLeftOperand(FPRegOp); RightRT->SetOperator(UnaryOp); RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); } else if ((NN_clc == this->SMPcmd.itype) || (NN_cld == this->SMPcmd.itype) || (NN_cmc == this->SMPcmd.itype) || (NN_stc == this->SMPcmd.itype) || (NN_std == this->SMPcmd.itype)) { // Flags register is implicit destination. DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(FlagsOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); if (NN_cmc == this->SMPcmd.itype) { // complement carry flag USEs old carry flag RightRT->SetLeftOperand(FlagsOp); RightRT->SetOperator(SMP_BITWISE_NOT); } else { RightRT->SetLeftOperand(VoidOp); RightRT->SetOperator(UnaryOp); } RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); } for (OpNum = 0; !DestFound && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; op_t LeftOp; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); LeftOp = TempOp; if (WidthDoubler) { // Opcodes that sign-extend a byte to a word, or a word to a dword, // have only one operand. It is implicit, and it is the shorter USE. // That means the DEF op will have the same width as the USE op, e.g. if // we are sign-extending AX to EAX, the USE op and DEF op will both be AX // without a special fix. We fix this problem with the DEF operand now. if (LeftOp.dtyp == dt_byte) { LeftOp.dtyp = dt_word; LeftOp.reg = MDCanonicalizeSubReg(LeftOp.reg); } else if (LeftOp.dtyp == dt_word) { LeftOp.dtyp = dt_dword; LeftOp.reg = MDCanonicalizeSubReg(LeftOp.reg); } else if (LeftOp.dtyp == dt_dword) { LeftOp.dtyp = dt_qword; } else { SMP_msg("ERROR: Instruction operand %zu not 1,2, or 4 bytes at %lx dtyp: %d\n", OpNum, (unsigned long) this->address, LeftOp.dtyp); } } TempRT->SetLeftOperand(LeftOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(UnaryOp); RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); } } } // end for (OpNum = 0; ...) #if SMP_DEBUG_BUILD_RTL if (!DestFound) { SMP_msg("ERROR: Could not find unary operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } #endif return DestFound; } // end of SMPInstr::BuildUnaryRTL() #if 0 // Cleaner, no special-case version of BuildUnary2OpndRTL() // Build the RTL for an instruction of the form dest := unary_operator(src), with src != dest bool SMPInstr::BuildUnaryTwoOperandRTL(SMPoperator UnaryOp) { size_t OpNum; bool DestFound = false; bool SrcFound = false; op_t DestOp, SrcOp; SMPRegTransfer *TempRT = NULL; op_t VoidOp = InitOp; for (OpNum = 0; (!(DestFound && SourceFound)) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; DestOp = TempOp; } } else { // not dest, see if valid source if (MDKnownOperandType(TempOp)) { SourceFound = true; } } } // end for (OpNum = 0; ...) if (DestFound && SourceFound) { TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(DestOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetLeftOperand(SrcOp); RightRT->SetOperator(UnaryOp); RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); } #if SMP_DEBUG_BUILD_RTL else { SMP_msg("ERROR: Could not find unary operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } #endif return DestFound; } // end of SMPInstr::BuildUnaryTwoOperandRTL() #endif // Build the RTL for an instruction with a binary arithmetic opcode bool SMPInstr::BuildBinaryRTL(SMPoperator BinaryOp, bool HiddenFPStackOp) { size_t OpNum; int opcode = this->SMPcmd.itype; bool DestFound = false; bool SourceFound = false; bool MemSrc = this->HasSourceMemoryOperand(); bool MemDest = this->HasDestMemoryOperand(); // Work around IDA pro error; they assumed that the pcmpeq/ne/gt/ge/lt/le // families of instructions were just compares, so they do not tag // either operand as a DEF. Actually, the first operand has byte or // word or dword fields set to all 1's or all 0's based on the result // of the comparison. bool SrcIsReallyDest = ((SMP_COMPARE_EQ_AND_SET <= BinaryOp) && (SMP_COMPARE_LE_AND_SET >= BinaryOp)); bool StackPointerModification = false; // SP := SP SMP_BITWISE_AND operand bool NeedsGuard = ((NN_arpl == opcode) || (NN_bound == opcode)); bool BuildOnlySignalRT = (NN_bound == opcode); SMPRegTransfer *TempRT = NULL; SMPRegTransfer *RightRT = new SMPRegTransfer; SMPGuard *Guard1 = NULL; RightRT->SetParentInst(this); op_t VoidOp = InitOp; op_t FPRegOp = InitOp; FPRegOp.type = o_fpreg; // floating point register stack // Handle special cases first if (NeedsGuard) { // Need a guard in the RTL. Guard1 = new SMPGuard; Guard1->SetOperator(SMP_U_COMPARE); } else if (HiddenFPStackOp) { // Use of the floating register stack top is implicit DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(FPRegOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(FPRegOp); RightRT->SetOperator(BinaryOp); RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); } for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if ((this->features & DefMacros[OpNum]) || (SrcIsReallyDest && (0 == OpNum))) { // DEF if (!DestFound && MDKnownOperandType(TempOp)) { // See comments just below for floating point sources. FP stores // are analogous to FP loads. if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); if (!BuildOnlySignalRT) { TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); } if (this->IsRegClearIdiom()) { op_t ImmOp = InitOp; ImmOp.type = o_imm; ImmOp.value = 0; TempRT->SetRightOperand(ImmOp); SourceFound = true; // cause loop exit } else { if (!BuildOnlySignalRT) { RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); if (TempOp.is_reg(MD_STACK_POINTER_REG) && (SMP_BITWISE_AND == BinaryOp)) { StackPointerModification = true; // searching for SP := SP AND immediate } } if (NeedsGuard) { Guard1->SetLeftOperand(TempOp); TempRT->SetGuard(Guard1); } } } else { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("WARNING: Skipping DEF operand: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else if (DestFound && (!HiddenFPStackOp)) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("ERROR: Found two DEF operands: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (!SourceFound && MDKnownOperandType(TempOp)) { // If this is a floating point instruction with the fpregs listed as // a USE and a memory operand also listed as a USE, then we want to // ignore the irrelevant USE of the fpreg stack. // Note that MemDest AND MemSrc means something like add mem,reg is being // processed, where the memory operand is both DEF and USE. if (!MemSrc || MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { SourceFound = true; if (!BuildOnlySignalRT) { RightRT->SetRightOperand(TempOp); if (StackPointerModification && (o_imm == TempOp.type)) { this->SetStackAlignmentInst(); } } } else { SMP_msg("ERROR: Operand not processed as USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } if (NeedsGuard) { Guard1->SetRightOperand(TempOp); } } if (!(this->features & UseMacros[OpNum])) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Operand neither DEF nor USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } // end if DEF ... else ... } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound) { assert(NULL != RightRT); if (DestFound && (NULL != TempRT)) delete TempRT; else delete RightRT; #if SMP_DEBUG_BUILD_RTL if (!DestFound) { SMP_msg("ERROR: Could not find binary DEF operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } else { SMP_msg("ERROR: Could not find binary operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } #endif } else { if (BuildOnlySignalRT) { TempRT->SetLeftOperand(VoidOp); TempRT->SetRightOperand(VoidOp); TempRT->SetOperator(SMP_SIGNAL); delete RightRT; } this->RTL.push_back(TempRT); // The "p" at the end of the opcode indicates that the floating point // register stack gets popped. if ((NN_fstp == opcode) || (NN_fbstp == opcode) || (NN_fistp == opcode)) { this->RTL.ExtraKills.push_back(FPRegOp); } } return (DestFound && SourceFound); } // end of SMPInstr::BuildBinaryRTL() // Build the RTL for a signal/exception raised based on a guarded comparison of operands bool SMPInstr::BuildGuardedSignalRTL(SMPoperator SignalOp) { size_t OpNum; bool Source1Found = false; bool Source2Found = false; SMPRegTransfer *TempRT = NULL; SMPGuard *Guard1 = new SMPGuard; op_t VoidOp = InitOp; Guard1->SetOperator(SMP_U_COMPARE); for (OpNum = 0; !(Source1Found && Source2Found) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("WARNING: Skipping DEF operand: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (!Source1Found && MDKnownOperandType(TempOp)) { Source1Found = true; Guard1->SetLeftOperand(TempOp); } else if ((!Source2Found) && MDKnownOperandType(TempOp)) { Source2Found = true; Guard1->SetRightOperand(TempOp); } if (!(this->features & UseMacros[OpNum])) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Operand neither DEF nor USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } // end if DEF ... else ... } // end for (OpNum = 0; ...) if (!Source1Found || !Source2Found) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL if (!Source1Found) { SMP_msg("ERROR: Could not find first USE operand for BOUND at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } else { SMP_msg("ERROR: Could not find second USE operand for BOUND at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } this->PrintOperands(); #endif delete Guard1; } else { TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(VoidOp); TempRT->SetRightOperand(VoidOp); TempRT->SetOperator(SignalOp); TempRT->SetGuard(Guard1); this->RTL.push_back(TempRT); } return (Source1Found && Source2Found); } // end of SMPInstr::BuildGuardedSignalRTL() // Build the RTL for an instruction with a binary arithmetic opcode plus an operator to apply to // the two source-only operands, one of which is an immediate value. bool SMPInstr::BuildBinaryPlusImmedRTL(SMPoperator BinaryOp, SMPoperator ImmedOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; bool ImmedFound = false; bool MemSrc = this->HasSourceMemoryOperand(); bool MemDest = this->HasDestMemoryOperand(); SMPRegTransfer *TempRT = NULL; SMPRegTransfer *RightRT = new SMPRegTransfer; SMPRegTransfer *ImmedRT = new SMPRegTransfer; RightRT->SetParentInst(this); ImmedRT->SetParentInst(this); for (OpNum = 0; !(DestFound && SourceFound && ImmedFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (!DestFound && MDKnownOperandType(TempOp)) { if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); } else { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("WARNING: Skipping DEF operand: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else if (DestFound) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("ERROR: Found two DEF operands: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (!SourceFound && MDKnownOperandType(TempOp)) { if (!MemSrc || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { SourceFound = true; ImmedRT->SetLeftOperand(TempOp); } } else if (!ImmedFound && (o_imm == TempOp.type)) { ImmedFound = true; ImmedRT->SetRightOperand(TempOp); ImmedRT->SetOperator(ImmedOp); RightRT->SetRightTree(ImmedRT); } if (!(this->features & UseMacros[OpNum])) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Operand neither DEF nor USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } // end if DEF ... else ... } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound || !ImmedFound) { assert(NULL != RightRT); if (DestFound && (NULL != TempRT)) delete TempRT; else { delete RightRT; if (!ImmedFound) delete ImmedRT; } #if SMP_DEBUG_BUILD_RTL if (!DestFound) { SMP_msg("ERROR: Could not find binary DEF operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } else if (!ImmedFound) { SMP_msg("ERROR: Could not find immediate operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } else { SMP_msg("ERROR: Could not find binary USE operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } #endif } else { this->RTL.push_back(TempRT); } return (DestFound && SourceFound && ImmedFound); } // end of SMPInstr::BuildBinaryPlusImmedRTL() // Build the RTL for an instruction with a binary arithmetic opcode, ignoring a third operand // which is an immediate value which is a control word for packed operations (e.g. SSE, XMM). bool SMPInstr::BuildBinaryIgnoreImmedRTL(SMPoperator BinaryOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; bool ImmedFound = false; bool MemSrc = this->HasSourceMemoryOperand(); bool MemDest = this->HasDestMemoryOperand(); bool ECXDest = ((NN_pcmpestri == this->SMPcmd.itype) || (NN_pcmpistri == this->SMPcmd.itype)); bool XMM0Dest = ((NN_pcmpestrm == this->SMPcmd.itype) || (NN_pcmpistrm == this->SMPcmd.itype)); bool SrcIsReallyDest = (ECXDest || XMM0Dest); // Dest is implicit, so ASM only has source operand SMPRegTransfer *TempRT = NULL; SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); for (OpNum = 0; !(DestFound && SourceFound && ImmedFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if ((this->features & DefMacros[OpNum]) || (SrcIsReallyDest && (0 == OpNum))) { // DEF if (!DestFound && MDKnownOperandType(TempOp)) { if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); if (ECXDest) { op_t ECXOp = InitOp; ECXOp.type = o_reg; ECXOp.reg = R_cx; ECXOp.dtyp = STARS_ISA_dtyp; TempRT->SetLeftOperand(ECXOp); } else if (XMM0Dest) { op_t XMMOp = InitOp; XMMOp.type = o_reg; XMMOp.reg = R_xmm0; XMMOp.dtyp = dt_byte16; TempRT->SetLeftOperand(XMMOp); } else { TempRT->SetLeftOperand(TempOp); } TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); } else { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("WARNING: Skipping DEF operand: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else if (DestFound) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("ERROR: Found two DEF operands: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (!SourceFound && MDKnownOperandType(TempOp)) { if (!MemSrc || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { SourceFound = true; RightRT->SetRightOperand(TempOp); } } else if (!ImmedFound && (o_imm == TempOp.type)) { ImmedFound = true; } if (!(this->features & UseMacros[OpNum])) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Operand neither DEF nor USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } // end if DEF ... else ... } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound || !ImmedFound) { assert(NULL != RightRT); if (DestFound && (NULL != TempRT)) delete TempRT; else delete RightRT; #if SMP_DEBUG_BUILD_RTL if (!DestFound) { SMP_msg("ERROR: Could not find binary DEF operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } else if (!ImmedFound) { SMP_msg("ERROR: Could not find immediate operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } else { SMP_msg("ERROR: Could not find binary USE operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } #endif } else { this->RTL.push_back(TempRT); } return (DestFound && SourceFound && ImmedFound); } // end of SMPInstr::BuildBinaryIgnoreImmedRTL() // Build the RTL for a load-effective-address instruction. bool SMPInstr::BuildLeaRTL(void) { size_t OpNum; bool DestFound = false; bool SourceFound = false; op_t DefOp = InitOp; op_t UseOp = InitOp; SMPRegTransfer *AssignRT = NULL; int BaseReg; int IndexReg; ushort ScaleFactor; ea_t offset; bool ScaledIndexReg; for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF DefOp = TempOp; DestFound = true; assert(o_reg == DefOp.type); } else { // USE if (!SourceFound && MDKnownOperandType(TempOp)) { if ((TempOp.type >= o_mem) && (TempOp.type <= o_displ)) { SourceFound = true; UseOp = TempOp; MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset); } else { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("WARNING: Skipping USE operand: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } if (!(this->features & UseMacros[OpNum])) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Operand neither DEF nor USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } // end if DEF ... else ... } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound) { #if SMP_DEBUG_BUILD_RTL if (!DestFound) { SMP_msg("ERROR: Could not find lea DEF operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } else { SMP_msg("ERROR: Could not find lea USE operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } #endif } else { // Ready to build the RTL // We build the RTL down to the right, in reverse order, with any multiplication // of the index register by a scale factor at the bottom of the RTL tree. // Note that almost any combination of BaseReg, IndexReg, and offset can be present // or absent. AssignRT = new SMPRegTransfer; AssignRT->SetParentInst(this); AssignRT->SetLeftOperand(DefOp); AssignRT->SetOperator(SMP_ASSIGN); ScaledIndexReg = ((ScaleFactor > 0) && (IndexReg != R_none)); op_t BaseOp = InitOp, IndexOp = InitOp, OffsetOp = InitOp, ScaleOp = InitOp; BaseOp.type = o_reg; BaseOp.reg = (ushort) BaseReg; IndexOp.type = o_reg; IndexOp.reg = (ushort) IndexReg; OffsetOp.type = o_imm; OffsetOp.value = (uval_t) offset; ScaleOp.type = o_imm; ScaleOp.value = (uval_t) ScaleFactor; if (ScaledIndexReg) { // First, build the subtree to scale the IndexReg. SMPRegTransfer *MultRT = new SMPRegTransfer; MultRT->SetParentInst(this); MultRT->SetLeftOperand(IndexOp); MultRT->SetOperator(SMP_U_LEFT_SHIFT); MultRT->SetRightOperand(ScaleOp); // Now, case on the possibilities for existence of the other address fields. if (0 != offset) { // Add the offset to the scaled index subtree. SMPRegTransfer *AddOffRT = new SMPRegTransfer; AddOffRT->SetParentInst(this); AddOffRT->SetLeftOperand(OffsetOp); AddOffRT->SetOperator(SMP_ADD); AddOffRT->SetRightTree(MultRT); // Add a BaseReg, if any. if (R_none != BaseReg) { SMPRegTransfer *AddBaseRT = new SMPRegTransfer; AddBaseRT->SetParentInst(this); AddBaseRT->SetLeftOperand(BaseOp); AddBaseRT->SetOperator(SMP_ADD); AddBaseRT->SetRightTree(AddOffRT); // Link into assignment root tree. AssignRT->SetRightTree(AddBaseRT); } else { // no BaseReg AssignRT->SetRightTree(AddOffRT); } } // end if nonzero offset else { // no offset to add // Add a BaseReg, if any. if (R_none != BaseReg) { SMPRegTransfer *AddBaseRT = new SMPRegTransfer; AddBaseRT->SetParentInst(this); AddBaseRT->SetLeftOperand(BaseOp); AddBaseRT->SetOperator(SMP_ADD); AddBaseRT->SetRightTree(MultRT); // Link into assignment root tree. AssignRT->SetRightTree(AddBaseRT); } else { // no BaseReg AssignRT->SetRightTree(MultRT); } } } // end if ScaleIndexReg else { // no scaled index register if (0 != offset) { if (R_none != IndexReg) { SMPRegTransfer *AddOffRT = new SMPRegTransfer; AddOffRT->SetParentInst(this); AddOffRT->SetLeftOperand(OffsetOp); AddOffRT->SetOperator(SMP_ADD); AddOffRT->SetRightOperand(IndexOp); // Add BaseReg, if any. if (R_none != BaseReg) { SMPRegTransfer *AddBaseRT = new SMPRegTransfer; AddBaseRT->SetParentInst(this); AddBaseRT->SetLeftOperand(BaseOp); AddBaseRT->SetOperator(SMP_ADD); AddBaseRT->SetRightTree(AddOffRT); // Link into assignment root tree. AssignRT->SetRightTree(AddBaseRT); } else { // no BaseReg AssignRT->SetRightTree(AddOffRT); } } // end if valid IndexReg else { // no IndexReg // Add BaseReg, if any. if (R_none != BaseReg) { SMPRegTransfer *AddBaseRT = new SMPRegTransfer; AddBaseRT->SetParentInst(this); AddBaseRT->SetLeftOperand(BaseOp); AddBaseRT->SetOperator(SMP_ADD); AddBaseRT->SetRightOperand(OffsetOp); // Link into assignment root tree. AssignRT->SetRightTree(AddBaseRT); } else { // no BaseReg, no IndexReg, just offset? if (UseOp.type != o_mem) { SMP_msg("WARNING: lea used as move at %lx for %s\n", (unsigned long) this->address, DisAsmText.GetDisAsm(this->GetAddr())); } AssignRT->SetRightOperand(OffsetOp); } } } // end if nonzero offset else { // no offset if ((R_none == BaseReg) || (R_none == IndexReg)) { SMP_msg("WARNING: lea used as move at %lx for %s\n", (unsigned long) this->address, DisAsmText.GetDisAsm(this->GetAddr())); if (R_none != BaseReg) { AssignRT->SetRightOperand(BaseOp); } else { if (R_none == IndexReg) { // Should be RegClearIdiom in this case, no longer Lea RTL assert(this->IsRegClearIdiom()); op_t ImmOp = InitOp; ImmOp.type = o_imm; ImmOp.value = 0; AssignRT->SetRightOperand(ImmOp); } else { AssignRT->SetRightOperand(IndexOp); } } } else { // we have a BaseReg and an IndexReg, unscaled, no offset SMPRegTransfer *AddBaseRT = new SMPRegTransfer; AddBaseRT->SetParentInst(this); AddBaseRT->SetLeftOperand(BaseOp); AddBaseRT->SetOperator(SMP_ADD); AddBaseRT->SetRightOperand(IndexOp); // Link into assignment root tree. AssignRT->SetRightTree(AddBaseRT); } } // end if nonzero offset ... else ... } // end if (ScaledIndexReg) ... else ... this->RTL.push_back(AssignRT); } return (DestFound && SourceFound); } // end of SMPInstr::BuildLeaRTL() // Build the RTL for an double-word shift instruction bool SMPInstr::BuildDoubleShiftRTL(SMPoperator BinaryOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; bool CountFound = false; SMPRegTransfer *TempRT = NULL; SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); SMPRegTransfer *LowerRightRT = new SMPRegTransfer; LowerRightRT->SetParentInst(this); // The doubleword shifts operate as follows: shift the DEF register right or left by // the number of bits specified by the count, and shift in bits from the USE register, // but do not change the USE register. This is hard to represent accurately in an RTL, // so we create an RTL as follows: // // ASSIGN // / \ // DEF SHIFT // / \ // DEF SHIFT // / \ // USE count // // This records all the operands, but it makes it look like the result of the // lower shift is the counter for the upper shift, which is unintentional and // will have to be special cased. In all single word shifts, the lower shift // subtree would be replaced by a single count operand. The presence of a sub-tree // instead of an operand is the identifying marker for double-word shifts. op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; for (OpNum = 0; !(DestFound && SourceFound && CountFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); LowerRightRT->SetOperator(BinaryOp); RightRT->SetRightTree(LowerRightRT); } } else { // USE if (MDKnownOperandType(TempOp)) { if (!SourceFound) { SourceFound = true; LowerRightRT->SetLeftOperand(TempOp); } else { CountFound = true; LowerRightRT->SetRightOperand(TempOp); } } } } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound || !CountFound) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find double-shift operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); // The carry flag gets the last shifted out bit. this->RTL.ExtraKills.push_back(FlagsOp); } return (DestFound && SourceFound && CountFound); } // end of SMPInstr::BuildDoubleShiftRTL() // Build the RTL for a multiply or divide, which can have implicit EAX and/or EDX operands bool SMPInstr::BuildMultiplyDivideRTL(SMPoperator BinaryOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; bool HiddenEAXUse = false; bool ImplicitEDXUse = false; SMPRegTransfer *TempRT = NULL; SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); op_t FPRegOp = InitOp; FPRegOp.type = o_fpreg; // floating point register stack op_t Immed1Op = InitOp; Immed1Op.type = o_imm; // immediate 1 for increment or decrement FPRegOp.value = 1; // Detect the cases in which EDX:EAX is the destination and EAX is a hidden operand. // See detailed comments on the multiply and divide instructions in MDFixupDefUseLists(). for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (!TempOp.showed()) { // hidden operand if (TempOp.is_reg(R_ax)) { // not R_al, so it is not 8 bits // This form always has a hidden use of EDX:EAX HiddenEAXUse = true; ImplicitEDXUse = true; } else if (TempOp.is_reg(R_al)) { // Use of AX register to hold 16-bit result is hidden, // but EDX is not needed to hold result bits. HiddenEAXUse = true; } } } for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); } } else { // USE if (MDKnownOperandType(TempOp)) { SourceFound = true; RightRT->SetRightOperand(TempOp); } } } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound) { assert(NULL != RightRT); if (DestFound && (NULL != TempRT)) delete TempRT; else delete RightRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find mul/div operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); if (ImplicitEDXUse) { // Need another effect for EDX, which was implicit. // Make a deep copy from existing EAX effect and change EAX dest to EDX. // For divisions, we also deep copy EAX effect and change EAX source to EDX. SMPRegTransfer *EDXRT = new SMPRegTransfer; EDXRT->SetParentInst(this); SMPRegTransfer *EDXRightRT = new SMPRegTransfer; EDXRightRT->SetParentInst(this); op_t EDXOp; EDXRT->SetOperator(SMP_ASSIGN); EDXOp = TempRT->GetLeftOperand(); assert(EDXOp.is_reg(R_ax)); EDXOp.reg = R_dx; EDXRT->SetLeftOperand(EDXOp); op_t SourceOp = RightRT->GetLeftOperand(); if ((NN_div == this->SMPcmd.itype) || (NN_idiv == this->SMPcmd.itype)) { // Need to change left operand of RightRT to EDX. i.e. we are // changing the effect from eax := eax DIV foo to edx := edx DIV foo. assert(SourceOp.is_reg(R_ax)); EDXRightRT->SetLeftOperand(EDXOp); } else { // just use same source operands for multiplies EDXRightRT->SetLeftOperand(SourceOp); } EDXRightRT->SetOperator(BinaryOp); EDXRightRT->SetRightOperand(RightRT->GetRightOperand()); EDXRT->SetRightTree(EDXRightRT); this->RTL.push_back(EDXRT); this->ResetMultiplicationBitsDiscarded(); } else { // No implicit EDX effect. // If we had 8x8=>16 bit multiply with AL*op8=>AX there // is no discarding of result bits, else there is discarding. if (!HiddenEAXUse) this->SetMultiplicationBitsDiscarded(); } } return (DestFound && SourceFound); } // end of SMPInstr::BuildMultiplyDivideRTL() // Build the RTL for an instruction with a tertiary arithmetic opcode applied to // two operands plus an implied FLAGS operand, e.g. add with carry adds the carry bit // and two operands together; rotate through carry, etc. bool SMPInstr::BuildBinaryPlusFlagsRTL(SMPoperator BinaryOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; SMPRegTransfer *TempRT = NULL; op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); SMPRegTransfer *FlagsRightRT = new SMPRegTransfer; FlagsRightRT->SetParentInst(this); for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); } } else { // USE if (MDKnownOperandType(TempOp)) { SourceFound = true; FlagsRightRT->SetLeftOperand(TempOp); FlagsRightRT->SetOperator(BinaryOp); FlagsRightRT->SetRightOperand(FlagsOp); RightRT->SetRightTree(FlagsRightRT); } } } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound) { if (DestFound) delete TempRT; // also deletes linked in RightRT else delete RightRT; // will also delete FlagsRightRT if SourceFound is true if (!SourceFound) // FlagsRightRT not linked into RightRT yet delete FlagsRightRT; // .. so delete FlagsRightRT separately #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find binary operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); } return (DestFound && SourceFound); } // end of SMPInstr::BuildBinaryPlusFlagsRTL() #define SMP_FIRST_SET_OPCODE NN_seta #define SMP_LAST_SET_OPCODE NN_setz // Build the RTL for an instruction of form dest := unary_operator(source), dest != source bool SMPInstr::BuildUnary2OpndRTL(SMPoperator UnaryOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); int opcode = this->SMPcmd.itype; bool ExtendedMove = ((NN_movsx == opcode) || (NN_movzx == opcode)); op_t VoidOp = InitOp; op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; op_t PortNumOp = InitOp; PortNumOp.type = o_reg; PortNumOp.reg = R_dx; op_t PortDataOp = InitOp; PortDataOp.type = o_reg; PortDataOp.reg = R_ax; // Handle special cases first. if ((SMP_FIRST_SET_OPCODE <= opcode) && (SMP_LAST_SET_OPCODE >= opcode)) { // Set instructions implicitly use the flags register. SourceFound = true; RightRT->SetLeftOperand(FlagsOp); } for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; if (NN_in == opcode) { #if 0 SMP_msg("ERROR: Explicit DEF for IN from port opcode at %x : ", this->GetAddr()); PrintOperand(TempOp); SMP_msg("\n"); #endif TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(UnaryOp); } else if (NN_out == opcode) { TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(UnaryOp); } else { TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetRightOperand(VoidOp); RightRT->SetOperator(UnaryOp); TempRT->SetRightTree(RightRT); } } } else { // USE if (MDKnownOperandType(TempOp)) { SourceFound = true; if (NN_in == opcode) { TempRT->SetRightOperand(TempOp); } else if (NN_out == opcode) { #if 0 SMP_msg("ERROR: Explicit USE for OUT to port opcode at %x : ", this->GetAddr()); PrintOperand(TempOp); SMP_msg("\n"); #endif TempRT->SetRightOperand(TempOp); } else { RightRT->SetLeftOperand(TempOp); if (ExtendedMove) this->MoveSource = TempOp; } } } } // end for (OpNum = 0; ...) if (!SourceFound && (NN_in == opcode)) { // Input from port is implicitly from port # in DX register if not // specified with an immediate operand. SourceFound = true; TempRT->SetRightOperand(PortNumOp); } if (!DestFound && (NN_in == opcode)) { // Input from port is implicitly to register AL, AX, or EAX // depending on the opcode and bit width mode. // NOTE: Two different machine codes for 8-bit and 32-bit widths. // We should check and set dtyp field accordingly. DestFound = true; TempRT->SetLeftOperand(PortDataOp); TempRT->SetOperator(UnaryOp); } if (!DestFound && (NN_out == opcode)) { // Output to port is implicitly to port # in DX register if not // specified with an immediate operand. // NOTE: Two different machine codes for 8-bit and 32-bit widths. // We should check and set dtyp field accordingly. DestFound = true; TempRT->SetLeftOperand(PortNumOp); TempRT->SetOperator(SMP_ASSIGN); } if (!SourceFound && (NN_out == opcode)) { // Output to port is implicitly from register AL, AX, or EAX // depending on the opcode and bit width mode. SourceFound = true; TempRT->SetRightOperand(PortDataOp); } if (!DestFound || !SourceFound) { if (!DestFound) delete RightRT; // never linked in to TempRT if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find binary operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); if ((NN_in == opcode) || (NN_out == opcode)) delete RightRT; // unused for port I/O } return (DestFound && SourceFound); } // end of SMPInstr::BuildUnary2OpndRTL() // Build the RTL for an instruction of form: dec ECX, loop to address with optional condition code guard bool SMPInstr::BuildLoopRTL(SMPoperator GuardOp) { size_t OpNum; bool SourceFound = false; SMPRegTransfer *LoopRT = new SMPRegTransfer; LoopRT->SetParentInst(this); SMPRegTransfer *DecCounterRT = new SMPRegTransfer; DecCounterRT->SetParentInst(this); op_t VoidOp = InitOp; op_t OneOp = InitOp; OneOp.type = o_imm; // immediate one OneOp.value = 1; op_t CountOp = InitOp; CountOp.type = o_reg; CountOp.reg = R_cx; op_t EIPOp = InitOp; EIPOp.type = o_reg; EIPOp.reg = MD_INSTRUCTION_POINTER_REG; // All x86 loop instructions behave as follows: // 1. Decrement RCX/ECX/CX depending on 64/32/16-bit mode of CPU. // 2. If counter decremented to zero, fall through to next instruction. // 3. For condition code variants, fall through if zero flag is 1 or 0, depending on variant. // 4. Otherwise, loop back by adding a signed 8-bit offset to the instruction counter. // // So, for LOOP, it is simply decrement ECX and loop back if ECX did not become zero. // For LOOPE a.k.a. LOOPZ, it is decrement ECX and loop back if ECX != 0 and ZF == 1, which means // that ZF (zero flag) was set to 1 by an instruction before the LOOPE instruction, not by the // decrement of ECX within the microcode for LOOPE. // For LOOPNE a.k.a. LOOPNZ, change ZF == 1 to ZF == 0 in above description. // // For RTLs, this means we need an RTL to decrement ECX, and a guarded RTL to add an immediate to the EIP reg. // The guard can be simple (ECX != 0) or compound ((ECX != 0) AND (ZF is 0 or 1)). However, because RTs in // an RTL happen in parallel, we compare ECX to 1 because the comparison happens before it is decremented by // another RT in the RTL. Note also that IDA Pro has converted the signed 8-bit relative offset to a near address // in the operand list. DecCounterRT->SetLeftOperand(CountOp); DecCounterRT->SetOperator(SMP_DECREMENT); DecCounterRT->SetRightOperand(VoidOp); for (OpNum = 0; (!SourceFound && (OpNum < UA_MAXOP)); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Unexpected DEF Operand in LOOP instruction: "); PrintOperand(TempOp); SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif ; } if (!SourceFound && (this->features & UseMacros[OpNum]) && MDKnownOperandType(TempOp)) { if (o_near == TempOp.type) { LoopRT->SetLeftOperand(EIPOp); LoopRT->SetOperator(SMP_ASSIGN); LoopRT->SetRightOperand(TempOp); SourceFound = true; } } } // end for (OpNum = 0; ...) if (!SourceFound) { if (NULL != LoopRT) delete LoopRT; if (NULL != DecCounterRT) delete DecCounterRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find loop operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { // If the move is conditional, set the guard expression. // NOTE: We do not want to complicate the SMPGuard class to handle compound expressions yet. // If the need arises, we can do so. For now, just treat all loop instructions as a simple LOOP // even if it is LOOPE or LOOPNE. SMPGuard *Guard1 = new SMPGuard; Guard1->SetLeftOperand(CountOp); Guard1->SetOperator(SMP_EQUAL); Guard1->SetRightOperand(OneOp); LoopRT->SetGuard(Guard1); this->RTL.push_back(DecCounterRT); this->RTL.push_back(LoopRT); } return (SourceFound); } // end of SMPInstr::BuildLoopRTL() // Build the RTL for an instruction of form dest := source, where dest != source bool SMPInstr::BuildMoveRTL(SMPoperator GuardOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; bool MemSrc = this->HasSourceMemoryOperand(); bool MemDest = this->HasDestMemoryOperand(); bool HasRepeatPrefix = (0 != (this->SMPcmd.auxpref & aux_rep)) || (0 != (this->SMPcmd.auxpref & aux_repne)); int opcode = this->SMPcmd.itype; #if IDA_SDK_VERSION < 600 if ((NN_ldmxcsr == opcode) || (NN_stmxcsr == opcode)) { // IDA 5.1 does not have the R_mxcsr enumeration value, // so we cannot handle these opcodes. return false; } #endif SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); op_t VoidOp = InitOp; op_t EAXOp = InitOp; EAXOp.type = o_reg; EAXOp.reg = R_ax; op_t ALOp = InitOp; ALOp.type = o_reg; ALOp.reg = R_al; ALOp.dtyp = dt_byte; op_t CountOp = InitOp; CountOp.type = o_reg; CountOp.reg = R_cx; op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; op_t FPRegOp = InitOp; FPRegOp.type = o_fpreg; // floating point register stack FPRegOp.reg = 0; op_t PortNumOp = InitOp; PortNumOp.type = o_reg; PortNumOp.reg = R_dx; op_t PortDataOp = InitOp; PortDataOp.type = o_reg; PortDataOp.reg = R_ax; #if IDA_SDK_VERSION > 599 op_t MXCSROp = InitOp; // MMX Control & Status Register MXCSROp.type = o_reg; MXCSROp.reg = R_mxcsr; #endif op_t ZeroOp = InitOp; ZeroOp.type = o_imm; // immediate zero ZeroOp.value = 0; #if SMP_DEBUG_BUILD_RTL if (MemSrc && MemDest && (NN_movs != opcode) && (NN_movss != opcode) && (NN_movsd != opcode)) { if ((NN_stos != opcode) && (NN_outs != opcode)) { SMP_msg("ERROR: IDA Pro error: MemDest and MemSrc in move at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } #if 1 else { // IDA incorrectly lists [EDI] as both DEF and USE, because reg EDI // is both DEF and USE in NN_stos. SMP_msg("WARNING: Ignoring IDA Pro error: MemDest and MemSrc in move at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); this->PrintOperands(); } #endif } #endif // First, handle special cases with implicit operands if (NN_lahf == opcode) { // load AH from flags TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(EAXOp); TempRT->SetRightOperand(FlagsOp); this->RTL.push_back(TempRT); return true; } if (NN_sahf == opcode) { // store AH to flags TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(FlagsOp); TempRT->SetRightOperand(EAXOp); this->RTL.push_back(TempRT); return true; } if ((NN_movs == opcode) || (NN_stos == opcode) || (NN_ins == opcode) || (NN_outs == opcode)) { // The ESI and EDI registers get incremented or decremented, depending // on the direction flag DF, for MOVS; only EDI for STOS and INS; // only ESI for OUTS. // This is true with or without a repeat prefix. op_t ESIOp = InitOp, EDIOp = InitOp; ESIOp.type = o_reg; ESIOp.reg = R_si; EDIOp.type = o_reg; EDIOp.reg = R_di; op_t ESIMemOp = InitOp, EDIMemOp = InitOp; // [esi] and [edi] ESIMemOp.type = o_phrase; ESIMemOp.reg = R_si; EDIMemOp.type = o_phrase; EDIMemOp.reg = R_di; if (NN_movs == opcode) { this->RTL.ExtraKills.push_back(ESIOp); this->RTL.ExtraKills.push_back(EDIOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(EDIMemOp); TempRT->SetRightOperand(ESIMemOp); DestFound = true; SourceFound = true; } else if (NN_stos == opcode) { this->RTL.ExtraKills.push_back(EDIOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(EDIMemOp); TempRT->SetRightOperand(ALOp); // default in case we don't find source later DestFound = true; } else if (NN_ins == opcode) { this->RTL.ExtraKills.push_back(EDIOp); TempRT->SetOperator(SMP_INPUT); TempRT->SetLeftOperand(EDIMemOp); TempRT->SetRightOperand(PortNumOp); DestFound = true; SourceFound = true; } else if (NN_outs == opcode) { this->RTL.ExtraKills.push_back(ESIOp); TempRT->SetOperator(SMP_OUTPUT); TempRT->SetLeftOperand(ESIMemOp); TempRT->SetRightOperand(PortNumOp); DestFound = true; SourceFound = true; } } // Some floating point instructions use the floating point register stack top as // an implicit source or destination, but the other operand of the load or store // is explicit, so we set the implicit operand and let control flow pass to the // main processing loop below. if ((NN_fld == opcode) || (NN_fbld == opcode) || (NN_fild == opcode)) { // Loads implicitly use the floating point stack top as destination. TempRT->SetLeftOperand(FPRegOp); TempRT->SetOperator(SMP_ASSIGN); DestFound = true; } else if ((NN_fst == opcode) || (NN_fstp == opcode) || (NN_fbstp == opcode) || (NN_fist == opcode) || (NN_fistp == opcode)) { // Stores implicitly use the floating point stack top as source TempRT->SetRightOperand(FPRegOp); SourceFound = true; // The "p" at the end of the opcode indicates that the floating point // register stack gets popped. if ((NN_fstp == opcode) || (NN_fbstp == opcode) || (NN_fistp == opcode)) { this->RTL.ExtraKills.push_back(FPRegOp); } } #if IDA_SDK_VERSION > 599 else if (NN_ldmxcsr == opcode) { // The MMX Control & Status Register is used implicitly. TempRT->SetLeftOperand(MXCSROp); DestFound = true; } else if (NN_stmxcsr == opcode) { // The MMX Control & Status Register is used implicitly. TempRT->SetRightOperand(MXCSROp); SourceFound = true; } #endif for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (!DestFound && MDKnownOperandType(TempOp)) { // See comments just below for floating point sources. FP stores // are analogous to FP loads. if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { DestFound = true; TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); } } } else { // USE if (!SourceFound && MDKnownOperandType(TempOp)) { // If this is a floating point instruction with the fpregs listed as // a USE and a memory operand also listed as a USE, then we want to // ignore the irrelevant USE of the fpreg stack. // Note that MemDest AND MemSrc means something like stosb is being // processed, where the memory operand is both DEF and USE to IDA // for mysterious reasons. if (!MemSrc || MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) { SourceFound = true; TempRT->SetRightOperand(TempOp); this->MoveSource = TempOp; } } if (this->features & UseMacros[OpNum]) { ; #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: Operand neither DEF nor USE: "); PrintOperand(TempOp); SMP_msg(" at %lx in %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find move operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { // If the move is conditional, set the guard expression. if (SMP_NULL_OPERATOR != GuardOp) { SMPGuard *Guard1 = new SMPGuard; Guard1->SetLeftOperand(FlagsOp); Guard1->SetOperator(GuardOp); Guard1->SetRightOperand(ZeroOp); TempRT->SetGuard(Guard1); if (this->MDIsConditionalMoveInstr()) { // We need to represent the possibility that the DEF operand will not // be set because the move is conditional. We will add the DEF operand // into the USE set and special case our type inferences so that the // USE and the pseudo-USE (prior SSA value of the DEF operand) must // agree in type before we can be sure of the result type. assert(this->Defs.GetSize() == 1); this->Uses.SetRef(this->Defs.GetFirstRef()->GetOp()); } } this->RTL.push_back(TempRT); // Now, create the repeat prefix effects if (HasRepeatPrefix) { // Must be MOVS or STOS or INS or OUTS // The repeat causes USE and DEF of ECX as a counter SMPRegTransfer *CounterRT = new SMPRegTransfer; CounterRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); CounterRT->SetLeftOperand(CountOp); CounterRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(CountOp); RightRT->SetOperator(SMP_UNARY_NUMERIC_OPERATION); RightRT->SetRightOperand(VoidOp); CounterRT->SetRightTree(RightRT); this->RTL.push_back(CounterRT); } } return (DestFound && SourceFound); } // end of SMPInstr::BuildLoopRTL() // Build the RTL for a load string instruction, which loads from DS:ESI into EAX // (or into AX or AL) and increments or decrements ESI based on the direction flag DF. bool SMPInstr::BuildLoadStringRTL(void) { bool DestFound = false; unsigned short ByteSize = 0; op_t DestOp = InitOp; DestOp.type = o_reg; DestOp.reg = R_al; for (size_t OpNum = 0; !DestFound && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (MDKnownOperandType(TempOp)) { if ((o_reg == TempOp.type) && (this->features & DefMacros[OpNum])) { // DEF DestFound = true; if (TempOp.is_reg(R_al)) { ByteSize = 1; } else if (TempOp.is_reg(R_ax)) { ByteSize = STARS_ISA_Bytewidth; DestOp.reg = R_ax; } else { SMP_msg("ERROR: Load string destination operand is neither AL nor EAX at %lx\n", (unsigned long) this->GetAddr()); ByteSize = 1; // default to AL destination } } } } // end for (OpNum = 0; ...) // Return false if we did not find a destination if (!DestFound) { return false; } op_t ZeroOp = InitOp; ZeroOp.type = o_imm; // immediate zero ZeroOp.value = 0; op_t OneOp = InitOp; OneOp.type = o_imm; // immediate one OneOp.value = 1; op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; op_t ESIOp = InitOp; ESIOp.type = o_reg; ESIOp.reg = R_si; op_t DerefESIOp = InitOp; DerefESIOp.type = o_phrase; DerefESIOp.reg = R_si; SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); SMPRegTransfer *GuardedIncRT = new SMPRegTransfer; GuardedIncRT->SetParentInst(this); SMPRegTransfer *GuardedDecRT = new SMPRegTransfer; GuardedDecRT->SetParentInst(this); SMPRegTransfer *RightIncRT = new SMPRegTransfer; RightIncRT->SetParentInst(this); SMPRegTransfer *RightDecRT = new SMPRegTransfer; RightDecRT->SetParentInst(this); // Build the load string RTL. Ignore DS segment register for now. // Load string is: AL := [ESI]; if (DF == 0) ESI += 1 else ESI -= 1; // for the 8-bit case, and EAX := [ESI]; if (DF == 0) ESI += 4 else ESI -= 4; // for the 32-bit case. // AL := [ESI] or EAX := [ESI] TempRT->SetLeftOperand(DestOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(DerefESIOp); this->RTL.push_back(TempRT); // Guarded increment RTL: If the DF (Direction Flag) in EFLAGS is 0, increment ESI // by ByteSize. op_t IncDecOp = InitOp; IncDecOp.type = o_imm; IncDecOp.value = ByteSize; SMPGuard *Guard1 = new SMPGuard; Guard1->SetLeftOperand(FlagsOp); Guard1->SetOperator(SMP_U_COMPARE); Guard1->SetRightOperand(ZeroOp); GuardedIncRT->SetGuard(Guard1); GuardedIncRT->SetLeftOperand(ESIOp); GuardedIncRT->SetOperator(SMP_ASSIGN); RightIncRT->SetLeftOperand(ESIOp); RightIncRT->SetOperator(SMP_ADD); RightIncRT->SetRightOperand(IncDecOp); GuardedIncRT->SetRightTree(RightIncRT); this->RTL.push_back(GuardedIncRT); // Guarded decrement RTL: If the DF (Direction Flag) in EFLAGS is 1, decrement ESI // by ByteSize. SMPGuard *Guard2 = new SMPGuard; Guard2->SetLeftOperand(FlagsOp); Guard2->SetOperator(SMP_U_COMPARE); Guard2->SetRightOperand(OneOp); GuardedDecRT->SetGuard(Guard2); GuardedDecRT->SetLeftOperand(ESIOp); GuardedDecRT->SetOperator(SMP_ASSIGN); RightDecRT->SetLeftOperand(ESIOp); RightDecRT->SetOperator(SMP_SUBTRACT); RightDecRT->SetRightOperand(IncDecOp); GuardedDecRT->SetRightTree(RightDecRT); this->RTL.push_back(GuardedDecRT); return true; } // end of SMPInstr::BuildLoadStringRTL() // Build the RTL for a compare string instruction, possibly with repeat prefix. bool SMPInstr::BuildCompareStringRTL(void) { size_t OpNum; bool Src1Found = false; bool Src2Found = false; bool HasRepeatPrefix = (0 != (this->SMPcmd.auxpref & aux_rep)) || (0 != (this->SMPcmd.auxpref & aux_repne)); op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; op_t CountOp = InitOp; CountOp.type = o_reg; CountOp.reg = R_cx; op_t VoidOp = InitOp; SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); for (OpNum = 0; !(Src1Found && Src2Found) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (MDKnownOperandType(TempOp)) { if (!Src1Found) { Src1Found = true; TempRT->SetLeftOperand(FlagsOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(SMP_U_COMPARE); TempRT->SetRightTree(RightRT); if (this->features & DefMacros[OpNum]) // DEF SMP_msg("CMPS 1st opnd is DEF\n"); else if (this->features & UseMacros[OpNum]) // USE SMP_msg("CMPS 1st opnd is USE\n"); else SMP_msg("CMPS 1st opnd neither DEF nor USE\n"); } else { Src2Found = true; RightRT->SetRightOperand(TempOp); if (this->features & DefMacros[OpNum]) // DEF SMP_msg("CMPS 2nd opnd is DEF\n"); else if (this->features & UseMacros[OpNum]) // USE SMP_msg("CMPS 2nd opnd is USE\n"); else SMP_msg("CMPS 2nd opnd neither DEF nor USE\n"); } } } // end for (OpNum = 0; ...) if (!Src1Found || !Src2Found) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find CMPS operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); // Now, create the repeat prefix effects if (HasRepeatPrefix) { // The repeat causes USE and DEF of ECX as a counter SMPRegTransfer *CounterRT = new SMPRegTransfer; CounterRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); CounterRT->SetLeftOperand(CountOp); CounterRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(CountOp); RightRT->SetOperator(SMP_UNARY_NUMERIC_OPERATION); RightRT->SetRightOperand(VoidOp); CounterRT->SetRightTree(RightRT); this->RTL.push_back(CounterRT); } } return (Src1Found && Src2Found); } // end of SMPInstr::BuildCompareStringRTL() // Build the RTL for an instruction of form dest := source, source := dest bool SMPInstr::BuildExchangeRTL(void) { size_t OpNum; bool Src1Found = false; bool Src2Found = false; SMPRegTransfer *TempRT = new SMPRegTransfer; // second effect, src := dest TempRT->SetParentInst(this); for (OpNum = 0; !(Src1Found && Src2Found) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (MDKnownOperandType(TempOp)) { if (!Src1Found) { Src1Found = true; TempRT->SetRightOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); #if SMP_VERBOSE_DEBUG_BUILD_RTL if (this->features & DefMacros[OpNum]) // DEF SMP_msg("XCHG 1st opnd is DEF\n"); else if (this->features & UseMacros[OpNum]) // USE SMP_msg("XCHG 1st opnd is USE\n"); else SMP_msg("XCHG 1st opnd neither DEF nor USE\n"); #endif } else { Src2Found = true; TempRT->SetLeftOperand(TempOp); if (this->features & DefMacros[OpNum]) // DEF SMP_msg("XCHG 2nd opnd is DEF\n"); else if (this->features & UseMacros[OpNum]) // USE SMP_msg("XCHG 2nd opnd is USE\n"); else SMP_msg("XCHG 2nd opnd neither DEF nor USE\n"); } } } // end for (OpNum = 0; ...) if (!Src1Found || !Src2Found) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find XCHG operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { // Create the first effect, dest := src SMPRegTransfer *FirstRT = new SMPRegTransfer; FirstRT->SetParentInst(this); FirstRT->SetLeftOperand(TempRT->GetRightOperand()); FirstRT->SetRightOperand(TempRT->GetLeftOperand()); FirstRT->SetOperator(SMP_ASSIGN); this->RTL.push_back(FirstRT); // Push the second effect on the list, src := dest this->RTL.push_back(TempRT); } return (Src1Found && Src2Found); } // end of SMPInstr::BuildExchangeRTL() // Build the RTL for an instruction of form dest := dest + source, source := dest bool SMPInstr::BuildExchangeAddRTL(void) { size_t OpNum; bool Src1Found = false; bool Src2Found = false; SMPRegTransfer *TempRT = new SMPRegTransfer; // second effect, src := dest TempRT->SetParentInst(this); for (OpNum = 0; !(Src1Found && Src2Found) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (MDKnownOperandType(TempOp)) { if (!Src1Found) { Src1Found = true; TempRT->SetRightOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); if (this->features & DefMacros[OpNum]) // DEF SMP_msg("XADD 1st opnd is DEF\n"); // should be the case else if (this->features & UseMacros[OpNum]) // USE SMP_msg("WARNING: XADD 1st opnd is USE\n"); else SMP_msg("WARNING: XADD 1st opnd neither DEF nor USE\n"); } else { Src2Found = true; TempRT->SetLeftOperand(TempOp); if (this->features & DefMacros[OpNum]) // DEF SMP_msg("WARNING: XADD 2nd opnd is DEF\n"); else if (this->features & UseMacros[OpNum]) // USE SMP_msg("XADD 2nd opnd is USE\n"); // should be the case else SMP_msg("WARNING: XADD 2nd opnd neither DEF nor USE\n"); } } } // end for (OpNum = 0; ...) if (!Src1Found || !Src2Found) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find XADD operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { // Create the first effect, dest := dest + src SMPRegTransfer *FirstRT = new SMPRegTransfer; FirstRT->SetParentInst(this); SMPRegTransfer *AddRT = new SMPRegTransfer; AddRT->SetParentInst(this); AddRT->SetLeftOperand(TempRT->GetRightOperand()); AddRT->SetOperator(SMP_ADD); AddRT->SetRightOperand(TempRT->GetLeftOperand()); FirstRT->SetLeftOperand(TempRT->GetRightOperand()); FirstRT->SetRightTree(AddRT); FirstRT->SetOperator(SMP_ASSIGN); this->RTL.push_back(FirstRT); // Push the second effect on the list, src := dest this->RTL.push_back(TempRT); } return (Src1Found && Src2Found); } // end of SMPInstr::BuildExchangeAddRTL() // Build the RTL for an instruction of form: // if (dest==EAX) dest := source else EAX := dest bool SMPInstr::BuildCompareExchangeRTL(void) { size_t OpNum; bool DestFound = false; bool SourceFound = false; op_t DestOp = InitOp; op_t SourceOp = InitOp; SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (MDKnownOperandType(TempOp)) { if (this->features & DefMacros[OpNum]) { // DEF if (!DestFound) { DestFound = true; DestOp = TempOp; } else { SMP_msg("CMPXCHG has two DEF operands.\n"); } } else if (this->features & UseMacros[OpNum]) { // USE if (!SourceFound) { SourceFound = true; SourceOp = TempOp; } else { SMP_msg("CMPXCHG has two USE operands.\n"); } } } } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find CMPXCHG operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { // Create the first effect, if (dest == EAX) dest := src SMPGuard *Guard1 = new SMPGuard; op_t EAXOp = InitOp; EAXOp.type = o_reg; EAXOp.reg = R_ax; Guard1->SetLeftOperand(DestOp); Guard1->SetOperator(SMP_EQUAL); Guard1->SetRightOperand(EAXOp); SMPRegTransfer *FirstRT = new SMPRegTransfer; FirstRT->SetParentInst(this); FirstRT->SetLeftOperand(DestOp); FirstRT->SetRightOperand(SourceOp); FirstRT->SetOperator(SMP_ASSIGN); FirstRT->SetGuard(Guard1); this->RTL.push_back(FirstRT); // Push the second effect on the list, if (dest!=EAX) dest := EAX SMPGuard *Guard2 = new SMPGuard; Guard2->SetLeftOperand(DestOp); Guard2->SetOperator(SMP_EQUAL); Guard2->SetRightOperand(EAXOp); TempRT->SetLeftOperand(DestOp); TempRT->SetRightOperand(EAXOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetGuard(Guard2); this->RTL.push_back(TempRT); } return (DestFound && SourceFound); } // end of SMPInstr::BuildCompareExchangeRTL() // Build the RTL for an extended FP concatenate and shift instruction bool SMPInstr::BuildPackShiftRTL(SMPoperator PackOp, SMPoperator ShiftOp) { size_t OpNum; bool DestFound = false; bool SourceFound = false; bool CountFound = false; SMPRegTransfer *TempRT = NULL; SMPRegTransfer *ShiftRT = new SMPRegTransfer; ShiftRT->SetParentInst(this); SMPRegTransfer *PackRT = new SMPRegTransfer; PackRT->SetParentInst(this); // RTL structure: top operator is assignment, next right operator is a reverse // shift with the shift count as its left operand, and lowest right operator // is the concatenation operator. Sequence of operations is pack, shift, assign. for (OpNum = 0; !(DestFound && SourceFound && CountFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); PackRT->SetLeftOperand(TempOp); PackRT->SetOperator(PackOp); ShiftRT->SetOperator(ShiftOp); ShiftRT->SetRightTree(PackRT); TempRT->SetRightTree(ShiftRT); } } else { // USE if (MDKnownOperandType(TempOp)) { if (!SourceFound) { SourceFound = true; PackRT->SetRightOperand(TempOp); } else { CountFound = true; ShiftRT->SetLeftOperand(TempOp); } } } } // end for (OpNum = 0; ...) if (!DestFound || !SourceFound || !CountFound) { if (NULL != TempRT) delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find MMX/XMM pack and shift operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); } return (DestFound && SourceFound && CountFound); } // end of SMPInstr::BuildPackShiftRTL() // Build the RTL for a compare or test instruction with an implicit EFLAGS destination operand bool SMPInstr::BuildFlagsDestBinaryRTL(SMPoperator BinaryOp) { size_t OpNum; int opcode = this->SMPcmd.itype; bool Source1Found = false; bool Source2Found = false; bool NoOperandsRequired = ((NN_scas == opcode) || (NN_cmps == opcode)); bool HasRepeatPrefix = (0 != (this->SMPcmd.auxpref & aux_rep)) || (0 != (this->SMPcmd.auxpref & aux_repne)); SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); op_t VoidOp = InitOp, FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; op_t CountOp = InitOp; CountOp.type = o_reg; CountOp.reg = R_cx; op_t FPRegOp = InitOp; FPRegOp.type = o_fpreg; // floating point register stack FPRegOp.reg = 0; // Some floating point instructions use the floating point register stack top as // an implicit source or destination, but the other operand of the load or store // is explicit, so we set the implicit operand and let control flow pass to the // main processing loop below. if ((NN_fcomi == opcode) || (NN_fucomi == opcode) || (NN_fcomip == opcode) || (NN_fucomip == opcode)) { // Compares implicitly use the floating point stack top as destination. TempRT->SetLeftOperand(FlagsOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(FPRegOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); Source1Found = true; // The "p" at the end of the opcode indicates that the floating point // register stack gets popped. if ((NN_fcomip == opcode) || (NN_fucomip == opcode)) { this->RTL.ExtraKills.push_back(FPRegOp); } } for (OpNum = 0; !(Source1Found && Source2Found) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Found destination for compare or test at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (MDKnownOperandType(TempOp)) { if (!Source1Found) { Source1Found = true; TempRT->SetLeftOperand(FlagsOp); TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(TempOp); RightRT->SetOperator(BinaryOp); TempRT->SetRightTree(RightRT); } else { assert(!Source2Found); Source2Found = true; RightRT->SetRightOperand(TempOp); } } } } // end for (OpNum = 0; ...) // The compare string instruction always uses DS:ESI and ES:EDI as its source // operands, regardless of the explicit operands given, and might not have // explicit operands; explicit operands are just for documentation. // The scan string instruction uses EAX/AX/AH/AL and ES:EDI as its source // operands and might not have any explicit operands at all. if ((!NoOperandsRequired) && (!Source1Found || !Source2Found)) { if (!Source1Found) delete RightRT; else delete TempRT; #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find CMP/TEST operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); // Now, create the repeat prefix effects if (HasRepeatPrefix) { // Must be CMPS or SCAS // The repeat causes USE and DEF of ECX as a counter SMPRegTransfer *CounterRT = new SMPRegTransfer; CounterRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); CounterRT->SetLeftOperand(CountOp); CounterRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(CountOp); RightRT->SetOperator(SMP_UNARY_NUMERIC_OPERATION); RightRT->SetRightOperand(VoidOp); CounterRT->SetRightTree(RightRT); this->RTL.push_back(CounterRT); } if ((NN_cmps == opcode) || (NN_scas == opcode)) { // The ESI and EDI registers get incremented or decremented, depending // on the direction flag DF, for CMPS; only EDI for SCAS. // This is true with or without a repeat prefix. op_t ESIOp = InitOp, EDIOp = InitOp; ESIOp.type = o_reg; ESIOp.reg = R_si; EDIOp.type = o_reg; EDIOp.reg = R_di; if (NN_cmps == opcode) { this->RTL.ExtraKills.push_back(ESIOp); } this->RTL.ExtraKills.push_back(EDIOp); } } return (NoOperandsRequired || (Source1Found && Source2Found)); } // end of SMPInstr::BuildFlagsDestBinaryRTL() // Build the RTL for a direct or indirect call instruction bool SMPInstr::BuildCallRTL(void) { size_t OpNum; bool SourceFound = false; op_t VoidOp = InitOp; SMPRegTransfer *TempRT = NULL; for (OpNum = 0; !SourceFound && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Found destination operand for call at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (MDKnownOperandType(TempOp)) { SourceFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(VoidOp); TempRT->SetOperator(SMP_CALL); TempRT->SetRightOperand(TempOp); } } } // end for (OpNum = 0; ...) if (!SourceFound) { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find CALL operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { this->RTL.push_back(TempRT); } return SourceFound; } // end of SMPInstr::BuildCallRTL() // Build the RTL for a return instruction, with or without extra bytes popped off stack bool SMPInstr::BuildReturnRTL(void) { size_t OpNum; uval_t PopBytes = STARS_ISA_Bytewidth; // default: pop off return address for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Found destination operand for RET at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (MDKnownOperandType(TempOp)) { if (o_imm == TempOp.type) { PopBytes += TempOp.value; } else { #if SMP_DEBUG_BUILD_RTL if (!(this->IsTailCall())) { SMP_msg("ERROR: Found unexpected operand for return at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } #endif } } } } // end for (OpNum = 0; ...) this->AddToStackPointer(PopBytes); return true; } // end of SMPInstr::BuildReturnRTL() // Build the RTL for an ENTER instruction bool SMPInstr::BuildEnterRTL(void) { // An "ENTER k,0" instruction with allocation k and nesting level 0 does the following: // push ebp // mov ebp,esp // sub esp,k // This can be modeled by the parallel effects: // [esp-4] := ebp; ebp := esp - 4; esp := esp - (k + 4) // If nesting level is greater than zero, we have a block structure language with // nested procedures, in which additional frame pointers are saved: // "ENTER k,n" pushes n additional frame pointers on the stack. We will only model // the change in the stack pointer here, and not attempt to transfer the display // pointers. A warning will be issued to the log file. Parallel effects are: // [esp-4] := ebp; ebp := esp - 4; esp := esp - (((k + n)*4)+4) // Note that k and n are immediate values so the final expression can be computed. size_t OpNum; uval_t NestingLevel = 0; uval_t AllocBytes = 0; bool AllocFound = false; bool NestingLevelFound = false; op_t StackPointerOp = InitOp; // ESP StackPointerOp.type = o_reg; StackPointerOp.reg = MD_STACK_POINTER_REG; op_t FramePointerOp = InitOp; // EBP FramePointerOp.type = o_reg; FramePointerOp.reg = MD_FRAME_POINTER_REG; op_t Immed4Op = InitOp; // 4 Immed4Op.type = o_imm; Immed4Op.value = STARS_ISA_Bytewidth; SetOperandWidthToProcessorWidth(Immed4Op); op_t SavedEBP = InitOp; // [ESP-4], location of saved EBP SavedEBP.type = o_displ; SavedEBP.addr = (ea_t) -(STARS_ISA_Bytewidth); SavedEBP.reg = MD_STACK_POINTER_REG; SetOperandWidthToProcessorWidth(SavedEBP); for (OpNum = 0; !(AllocFound && NestingLevelFound) && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Found destination operand for ENTER at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } else { // USE if (MDKnownOperandType(TempOp)) { if (o_imm == TempOp.type) { if (!AllocFound) { AllocBytes = TempOp.value; AllocFound = true; } else { NestingLevel = TempOp.value; NestingLevelFound = true; } } else { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Found unexpected operand for ENTER at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } } } } // end for (OpNum = 0; ...) if (!AllocFound) { #if SMP_DEBUG_BUILD_RTL SMP_msg("ERROR: Could not find allocation operand for ENTER at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); #endif } else { SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); // Add first effect: [esp-4] := ebp TempRT->SetLeftOperand(SavedEBP); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(FramePointerOp); this->RTL.push_back(TempRT); TempRT = NULL; // Add second effect: ebp := esp - wordsize TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(FramePointerOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetLeftOperand(StackPointerOp); RightRT->SetOperator(SMP_SUBTRACT); RightRT->SetRightOperand(Immed4Op); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); TempRT = NULL; RightRT = NULL; // Add final effect on stack pointer AllocBytes += (STARS_ISA_Bytewidth * (NestingLevel + 1)); if (0 != NestingLevel) { SMP_msg("WARNING: Nested procedures in ENTER instruction at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } this->SubFromStackPointer(AllocBytes); } return AllocFound; } // end of SMPInstr::BuildEnterRTL() // Build the RTL for an LEAVE instruction bool SMPInstr::BuildLeaveRTL(void) { // A LEAVE instruction simulates the following instructions: // mov ebp into esp (deallocates stack frame) // pop saved ebp off stack into ebp // We want to model these two instructions with three parallel effects: // esp := ebp; ebp := [ebp+0]; esp = esp + wordsize; // There cannot be two definitions of esp in the list of effects, so we do: // esp := ebp + wordsize; ebp := [ebp+0] as our two parallel effects op_t StackPointerOp = InitOp; // ESP StackPointerOp.type = o_reg; StackPointerOp.reg = MD_STACK_POINTER_REG; SetOperandWidthToProcessorWidth(StackPointerOp); op_t FramePointerOp = InitOp; // EBP FramePointerOp.type = o_reg; FramePointerOp.reg = MD_FRAME_POINTER_REG; SetOperandWidthToProcessorWidth(FramePointerOp); op_t Immed4Op = InitOp; // wordsize Immed4Op.type = o_imm; Immed4Op.value = STARS_ISA_Bytewidth; SetOperandWidthToProcessorWidth(Immed4Op); op_t SavedEBP = InitOp; // [EBP+0] SavedEBP.type = o_displ; SavedEBP.reg = MD_FRAME_POINTER_REG; SetOperandWidthToProcessorWidth(SavedEBP); // Build first effect: ESP := EBP + wordsize SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(StackPointerOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetOperator(SMP_ADD); RightRT->SetLeftOperand(FramePointerOp); RightRT->SetRightOperand(Immed4Op); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); TempRT = NULL; RightRT = NULL; // Build second effect: EBP := [EBP+0] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(FramePointerOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(SavedEBP); this->RTL.push_back(TempRT); TempRT = NULL; return true; } // end of SMPInstr::BuildLeaveRTL() // Build OptCategory 8 RTLs, which set system info into EDX:EAX. bool SMPInstr::BuildOptType8RTL(void) { op_t DestOp = InitOp; DestOp.type = o_reg; op_t VoidOp = InitOp; op_t LeftOp = InitOp; if (NN_xgetbv == this->SMPcmd.itype) { // AMD xgetbc opcode gets control register number to read out of ECX. LeftOp.type = o_reg; LeftOp.reg = R_cx; } // Create the effect on EDX. SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); DestOp.reg = R_dx; TempRT->SetLeftOperand(DestOp); TempRT->SetOperator(SMP_ASSIGN); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetLeftOperand(LeftOp); RightRT->SetOperator(SMP_SYSTEM_OPERATION); RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); // Create the effect on EAX. TempRT = NULL; RightRT = NULL; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); DestOp.reg = R_ax; TempRT->SetLeftOperand(DestOp); TempRT->SetOperator(SMP_ASSIGN); RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); RightRT->SetLeftOperand(LeftOp); RightRT->SetOperator(SMP_SYSTEM_OPERATION); RightRT->SetRightOperand(VoidOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); return true; } // end of BuildOptType8RTL() // Build the RTL for a direct or indirect jump instruction bool SMPInstr::BuildJumpRTL(SMPoperator CondBranchOp) { size_t OpNum; bool TargetFound = false; SMPRegTransfer *TempRT = NULL; op_t EIPOp = InitOp, ZeroOp = InitOp, FlagsOp = InitOp; EIPOp.type = o_reg; EIPOp.reg = R_ip; ZeroOp.type = o_imm; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; op_t CountOp = InitOp; CountOp.type = o_reg; CountOp.reg = R_cx; #if 0 if (this->IsTailCall()) return this->BuildReturnRTL(); #endif for (OpNum = 0; !TargetFound && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & UseMacros[OpNum]) { // USE if (MDKnownOperandType(TempOp)) { TargetFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(EIPOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(TempOp); if (CondBranchOp != SMP_NULL_OPERATOR) { // Set up a guard expression comparing EFLAGS to zero. // NOTE: This is imprecise for value-set purposes, but OK for types. SMPGuard *BranchCondition = new SMPGuard; BranchCondition->SetOperator(CondBranchOp); // The conditional jumps on ECX==0 compare to ECX, not EFLAGS. if ((NN_jcxz <= this->SMPcmd.itype) && (NN_jrcxz >= this->SMPcmd.itype)) BranchCondition->SetLeftOperand(CountOp); else BranchCondition->SetLeftOperand(FlagsOp); BranchCondition->SetRightOperand(ZeroOp); TempRT->SetGuard(BranchCondition); } this->RTL.push_back(TempRT); } } } // end for (OpNum = 0; ...) #if SMP_DEBUG_BUILD_RTL if (!TargetFound) { SMP_msg("ERROR: Could not find jump target at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } #endif return TargetFound; } // end of SMPInstr::BuildJumpRTL() // Add to the stack pointer to deallocate stack space, e.g. for a pop instruction. void SMPInstr::AddToStackPointer(uval_t delta) { SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); op_t StackOp = InitOp, DeltaOp = InitOp; StackOp.type = o_reg; StackOp.reg = R_sp; SetOperandWidthToProcessorWidth(StackOp); DeltaOp.type = o_imm; DeltaOp.value = delta; TempRT->SetLeftOperand(StackOp); // ESP := RightRT TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(StackOp); // ESP + delta RightRT->SetOperator(SMP_ADD); RightRT->SetRightOperand(DeltaOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); return; } // end of SMPInstr::AddToStackPointer() // Subtract from the stack pointer to allocate stack space, e.g. for a push instruction. void SMPInstr::SubFromStackPointer(uval_t delta) { SMPRegTransfer *TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); SMPRegTransfer *RightRT = new SMPRegTransfer; RightRT->SetParentInst(this); op_t StackOp = InitOp, DeltaOp = InitOp; StackOp.type = o_reg; StackOp.reg = R_sp; SetOperandWidthToProcessorWidth(StackOp); DeltaOp.type = o_imm; DeltaOp.value = delta; SetOperandWidthToProcessorWidth(DeltaOp); TempRT->SetLeftOperand(StackOp); // ESP := RightRT TempRT->SetOperator(SMP_ASSIGN); RightRT->SetLeftOperand(StackOp); // ESP - delta RightRT->SetOperator(SMP_SUBTRACT); RightRT->SetRightOperand(DeltaOp); TempRT->SetRightTree(RightRT); this->RTL.push_back(TempRT); return; } // end of SMPInstr::SubFromStackPointer() #define SMP_FIRST_POP_FLAGS NN_popfw #define SMP_LAST_POP_FLAGS NN_popfq #define SMP_FIRST_POP_ALL NN_popaw #define SMP_LAST_POP_ALL NN_popaq // Build the RTL for a pop instruction bool SMPInstr::BuildPopRTL(void) { size_t OpNum, OpSize; bool DestFound = false; SMPRegTransfer *TempRT = NULL; op_t StackOp = InitOp, FlagsOp = InitOp; StackOp.type = o_displ; StackOp.reg = R_sp; SetOperandWidthToProcessorWidth(StackOp); // StackOp.addr = 0; // [ESP+0] FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; SetOperandWidthToProcessorWidth(FlagsOp); // Handle special cases first. if ((SMP_FIRST_POP_FLAGS <= this->SMPcmd.itype) && (SMP_LAST_POP_FLAGS >= this->SMPcmd.itype)) { TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(FlagsOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // Now create the stack pointer increment effect. this->AddToStackPointer(STARS_ISA_Bytewidth); return true; } if ((SMP_FIRST_POP_ALL <= this->SMPcmd.itype) && (SMP_LAST_POP_ALL >= this->SMPcmd.itype)) { // We pop off 7 registers from the 8 that were pushed on the stack. // The pushed stack pointer is ignored. Instead, the stack pointer value is // adjusted at the end, per the Intel instruction manuals. op_t RegOp = InitOp; RegOp.type = o_reg; SetOperandWidthToProcessorWidth(RegOp); // EDI comes from [ESP+0] RegOp.reg = R_di; StackOp.addr = 0; // [ESP+0] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // ESI comes from [ESP+4] RegOp.reg = R_si; StackOp.addr = STARS_ISA_Bytewidth; // [ESP+4] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EBP comes from [ESP+8] RegOp.reg = MD_FRAME_POINTER_REG; StackOp.addr = 2*STARS_ISA_Bytewidth; // [ESP+8] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // Skip over saved ESP at [ESP+12] // EBX comes from [ESP+16] RegOp.reg = R_bx; StackOp.addr = 4*STARS_ISA_Bytewidth; // [ESP+16] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EDX comes from [ESP+20] RegOp.reg = R_dx; StackOp.addr = 5*STARS_ISA_Bytewidth; // [ESP+20] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // ECX comes from [ESP+24] RegOp.reg = R_cx; StackOp.addr = 6*STARS_ISA_Bytewidth; // [ESP+24] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EAX comes from [ESP+28] RegOp.reg = R_ax; StackOp.addr = 7*STARS_ISA_Bytewidth; // [ESP+28] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // Now create the stack pointer increment effect. this->AddToStackPointer(8*STARS_ISA_Bytewidth); return true; } // end for "pop all" instructions // If we reach this point, we have a simple POP instruction. for (OpNum = 0; !DestFound && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & DefMacros[OpNum]) { // DEF if (MDKnownOperandType(TempOp)) { DestFound = true; TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetLeftOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); StackOp.dtyp = TempOp.dtyp; // size of transfer TempRT->SetRightOperand(StackOp); this->RTL.push_back(TempRT); // Now create the stack pointer increment effect. OpSize = GetOpDataSize(TempOp); this->AddToStackPointer((uval_t) OpSize); } } } // end for (OpNum = 0; ...) #if SMP_DEBUG_BUILD_RTL if (!DestFound) { SMP_msg("ERROR: Could not find pop operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } #endif return DestFound; } // end of SMPInstr::BuildPopRTL() #define SMP_FIRST_PUSH_FLAGS NN_pushfw #define SMP_LAST_PUSH_FLAGS NN_pushfq #define SMP_FIRST_PUSH_ALL NN_pushaw #define SMP_LAST_PUSH_ALL NN_pushaq // Build the RTL for a push instruction bool SMPInstr::BuildPushRTL(void) { size_t OpNum, OpSize; bool SourceFound = false; SMPRegTransfer *TempRT = NULL; op_t StackOp = InitOp, FlagsOp = InitOp; StackOp.type = o_displ; StackOp.reg = R_sp; StackOp.addr = (ea_t) -STARS_ISA_Bytewidth; // [ESP-4] SetOperandWidthToProcessorWidth(StackOp); FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; SetOperandWidthToProcessorWidth(FlagsOp); // Handle special cases first. if ((SMP_FIRST_PUSH_FLAGS <= this->SMPcmd.itype) && (SMP_LAST_PUSH_FLAGS >= this->SMPcmd.itype)) { TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(FlagsOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); // Now create the stack pointer increment effect. this->SubFromStackPointer(STARS_ISA_Bytewidth); return true; } if ((SMP_FIRST_PUSH_ALL <= this->SMPcmd.itype) && (SMP_LAST_PUSH_ALL >= this->SMPcmd.itype)) { op_t RegOp = InitOp; RegOp.type = o_reg; // EDI goes to [ESP-32] RegOp.reg = R_di; StackOp.addr = (ea_t) -8*STARS_ISA_Bytewidth; // [ESP-32] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // ESI goes to [ESP-28] RegOp.reg = R_si; StackOp.addr = (ea_t) -7*STARS_ISA_Bytewidth; // [ESP-28] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EBP goes to [ESP-24] RegOp.reg = MD_FRAME_POINTER_REG; StackOp.addr = (ea_t) -6*STARS_ISA_Bytewidth; // [ESP-24] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // ESP goes to [ESP-20] RegOp.reg = MD_STACK_POINTER_REG; StackOp.addr = (ea_t) -5*STARS_ISA_Bytewidth; // [ESP-20] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EBX goes to [ESP-16] RegOp.reg = R_bx; StackOp.addr = (ea_t) -4*STARS_ISA_Bytewidth; // [ESP-16] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EDX goes to [ESP-12] RegOp.reg = R_dx; StackOp.addr = (ea_t) -3*STARS_ISA_Bytewidth; // [ESP-12] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // ECX goes to [ESP-8] RegOp.reg = R_cx; StackOp.addr = (ea_t) -2*STARS_ISA_Bytewidth; // [ESP-8] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // EAX goes to [ESP-4] RegOp.reg = R_ax; StackOp.addr = (ea_t) -STARS_ISA_Bytewidth; // [ESP-4] TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(RegOp); TempRT->SetOperator(SMP_ASSIGN); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // Now create the stack pointer increment effect. this->SubFromStackPointer(8*STARS_ISA_Bytewidth); return true; } // end for "pop all" instructions // If we reach this point, we have a simple PUSH instruction. for (OpNum = 0; !SourceFound && (OpNum < UA_MAXOP); ++OpNum) { op_t TempOp = this->SMPcmd.Operands[OpNum]; if (this->features & UseMacros[OpNum]) { // USE if (MDKnownOperandType(TempOp)) { SourceFound = true; OpSize = GetOpDataSize(TempOp); TempRT = new SMPRegTransfer; TempRT->SetParentInst(this); TempRT->SetRightOperand(TempOp); TempRT->SetOperator(SMP_ASSIGN); StackOp.dtyp = TempOp.dtyp; // size of transfer StackOp.addr = (ea_t) (-((signed int) OpSize)); TempRT->SetLeftOperand(StackOp); this->RTL.push_back(TempRT); TempRT = NULL; // Now create the stack pointer increment effect. this->SubFromStackPointer((uval_t) OpSize); #if 0 this->RTL.Dump(); #endif } } } // end for (OpNum = 0; ...) #if SMP_DEBUG_BUILD_RTL if (!SourceFound) { SMP_msg("ERROR: Could not find push operand at %lx for %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); } #endif return SourceFound; } // end of SMPInstr::BuildPushRTL() // Build RTL trees from the SMPcmd info. bool SMPInstr::BuildRTL(void) { op_t FlagsOp = InitOp; FlagsOp.type = o_reg; FlagsOp.reg = X86_FLAGS_REG; SMPRegTransfer *NopRT = NULL; // no-op register transfer // We don't want to explicitly represent the various no-ops except as NULL operations. // E.g. mov esi,esi should not generate DEF and USE of esi, because esi does not change. if (this->IsNop()) { NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; } switch (this->SMPcmd.itype) { case NN_aaa: // ASCII Adjust after Addition case NN_aad: // ASCII Adjust AX before Division case NN_aam: // ASCII Adjust AX after Multiply case NN_aas: // ASCII Adjust AL after Subtraction return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_adc: // Add with Carry #if SMP_BUILD_SPECIAL_ADC_SBB_RTL return this->BuildBinaryPlusFlagsRTL(SMP_ADD_CARRY); #else return this->BuildBinaryRTL(SMP_ADD_CARRY); #endif case NN_add: // Add return this->BuildBinaryRTL(SMP_ADD); case NN_and: // Logical AND return this->BuildBinaryRTL(SMP_BITWISE_AND); case NN_arpl: // Adjust RPL Field of Selector return this->BuildBinaryRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_bound: // Check Array Index Against Bounds return this->BuildGuardedSignalRTL(SMP_SIGNAL); case NN_bsf: // Bit Scan Forward case NN_bsr: // Bit Scan Reverse return this->BuildUnary2OpndRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_bt: // Bit Test return this->BuildFlagsDestBinaryRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_btc: // Bit Test and Complement case NN_btr: // Bit Test and Reset case NN_bts: // Bit Test and Set // Has effects on both the carry flag and the first operand this->RTL.ExtraKills.push_back(FlagsOp); return this->BuildBinaryRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_call: // Call Procedure case NN_callfi: // Indirect Call Far Procedure case NN_callni: // Indirect Call Near Procedure return this->BuildCallRTL(); case NN_cbw: // AL -> AX (with sign) case NN_cwde: // AX -> EAX (with sign) case NN_cdqe: // EAX -> RAX (with sign) return this->BuildUnaryRTL(SMP_SIGN_EXTEND); case NN_clc: // Clear Carry Flag case NN_cld: // Clear Direction Flag return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_cli: // Clear Interrupt Flag case NN_clts: // Clear Task-Switched Flag in CR0 // We don't track the interrupt flag or the special registers, // so we can just consider these to be no-ops. // NOTE: Shouldn't we killthe EFLAGS register on NN_cli ??!!??!! NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_cmc: // Complement Carry Flag return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_cmp: // Compare Two Operands return this->BuildFlagsDestBinaryRTL(SMP_S_COMPARE); case NN_cmps: // Compare Strings // Why do we no longer use BuildCompareStringRTL()? ****!!!!**** Test it! return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE); case NN_cwd: // AX -> DX:AX (with sign) case NN_cdq: // EAX -> EDX:EAX (with sign) case NN_cqo: // RAX -> RDX:RAX (with sign) return this->BuildUnary2OpndRTL(SMP_SIGN_EXTEND); case NN_daa: // Decimal Adjust AL after Addition case NN_das: // Decimal Adjust AL after Subtraction return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_dec: // Decrement by 1 return this->BuildUnaryRTL(SMP_DECREMENT); case NN_div: // Unsigned Divide return this->BuildMultiplyDivideRTL(SMP_U_DIVIDE); case NN_enterw: // Make Stack Frame for Procedure Parameters case NN_enter: // Make Stack Frame for Procedure Parameters case NN_enterd: // Make Stack Frame for Procedure Parameters case NN_enterq: // Make Stack Frame for Procedure Parameters return this->BuildEnterRTL(); case NN_hlt: // Halt // Treat as a no-op NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_idiv: // Signed Divide return this->BuildMultiplyDivideRTL(SMP_S_DIVIDE); case NN_imul: // Signed Multiply return this->BuildMultiplyDivideRTL(SMP_S_MULTIPLY); case NN_in: // Input from Port return this->BuildUnary2OpndRTL(SMP_INPUT); case NN_inc: // Increment by 1 return this->BuildUnaryRTL(SMP_INCREMENT); case NN_ins: // Input Byte(s) from Port to String return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_int: // Call to Interrupt Procedure case NN_into: // Call to Interrupt Procedure if Overflow Flag = 1 case NN_int3: // Trap to Debugger return this->BuildCallRTL(); case NN_iretw: // Interrupt Return case NN_iret: // Interrupt Return case NN_iretd: // Interrupt Return (use32) case NN_iretq: // Interrupt Return (use64) return this->BuildReturnRTL(); case NN_ja: // Jump if Above (CF=0 & ZF=0) case NN_jae: // Jump if Above or Equal (CF=0) case NN_jb: // Jump if Below (CF=1) case NN_jbe: // Jump if Below or Equal (CF=1 | ZF=1) case NN_jc: // Jump if Carry (CF=1) return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_jcxz: // Jump if CX is 0 case NN_jecxz: // Jump if ECX is 0 case NN_jrcxz: // Jump if RCX is 0 return this->BuildJumpRTL(SMP_EQUAL); // special case in BuildJumpRTL() case NN_je: // Jump if Equal (ZF=1) return this->BuildJumpRTL(SMP_EQUAL); case NN_jg: // Jump if Greater (ZF=0 & SF=OF) return this->BuildJumpRTL(SMP_GREATER_THAN); case NN_jge: // Jump if Greater or Equal (SF=OF) return this->BuildJumpRTL(SMP_GREATER_EQUAL); case NN_jl: // Jump if Less (SF!=OF) return this->BuildJumpRTL(SMP_LESS_THAN); case NN_jle: // Jump if Less or Equal (ZF=1 | SF!=OF) return this->BuildJumpRTL(SMP_LESS_EQUAL); case NN_jna: // Jump if Not Above (CF=1 | ZF=1) case NN_jnae: // Jump if Not Above or Equal (CF=1) case NN_jnb: // Jump if Not Below (CF=0) case NN_jnbe: // Jump if Not Below or Equal (CF=0 & ZF=0) a.k.a. ja case NN_jnc: // Jump if Not Carry (CF=0) return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_jne: // Jump if Not Equal (ZF=0) return this->BuildJumpRTL(SMP_NOT_EQUAL); case NN_jng: // Jump if Not Greater (ZF=1 | SF!=OF) a.k.a. jle return this->BuildJumpRTL(SMP_LESS_EQUAL); case NN_jnge: // Jump if Not Greater or Equal (SF != OF) ** return this->BuildJumpRTL(SMP_LESS_THAN); case NN_jnl: // Jump if Not Less (SF=OF) a.k.a. jge return this->BuildJumpRTL(SMP_GREATER_EQUAL); case NN_jnle: // Jump if Not Less or Equal (ZF=0 & SF=OF) a.k.a. jg return this->BuildJumpRTL(SMP_GREATER_THAN); case NN_jno: // Jump if Not Overflow (OF=0) case NN_jnp: // Jump if Not Parity (PF=0) case NN_jns: // Jump if Not Sign (SF=0) return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_jnz: // Jump if Not Zero (ZF=0) a.k.a. jne return this->BuildJumpRTL(SMP_NOT_EQUAL); case NN_jo: // Jump if Overflow (OF=1) case NN_jp: // Jump if Parity (PF=1) case NN_jpe: // Jump if Parity Even (PF=1) case NN_jpo: // Jump if Parity Odd (PF=0) case NN_js: // Jump if Sign (SF=1) return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_jz: // Jump if Zero (ZF=1) return this->BuildJumpRTL(SMP_EQUAL); case NN_jmp: // Jump case NN_jmpfi: // Indirect Far Jump case NN_jmpni: // Indirect Near Jump case NN_jmpshort: // Jump Short (not used) return this->BuildJumpRTL(SMP_NULL_OPERATOR); case NN_lahf: // Load Flags into AH Register return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_lar: // Load Access Right Byte return false; break; case NN_lea: // Load Effective Address return this->BuildLeaRTL(); break; case NN_leavew: // High Level Procedure Exit case NN_leave: // High Level Procedure Exit case NN_leaved: // High Level Procedure Exit case NN_leaveq: // High Level Procedure Exit return this->BuildLeaveRTL(); break; case NN_lgdt: // Load Global Descriptor Table Register case NN_lidt: // Load Interrupt Descriptor Table Register return false; break; case NN_lgs: // Load Full Pointer to GS:xx case NN_lss: // Load Full Pointer to SS:xx case NN_lds: // Load Full Pointer to DS:xx case NN_les: // Load Full Pointer to ES:xx case NN_lfs: // Load Full Pointer to FS:xx // These instructions differ from NN_lea only in setting // a segment register in addition to a pointer. We are // not yet tracking segment registers. return this->BuildLeaRTL(); break; case NN_lldt: // Load Local Descriptor Table Register case NN_lmsw: // Load Machine Status Word case NN_lock: // Assert LOCK# Signal Prefix return false; break; case NN_lods: // Load String return this->BuildLoadStringRTL(); break; case NN_loopw: // Loop while ECX != 0 case NN_loop: // Loop while CX != 0 case NN_loopd: // Loop while ECX != 0 case NN_loopq: // Loop while RCX != 0 case NN_loopwe: // Loop while CX != 0 and ZF=1 case NN_loope: // Loop while rCX != 0 and ZF=1 case NN_loopde: // Loop while ECX != 0 and ZF=1 case NN_loopqe: // Loop while RCX != 0 and ZF=1 case NN_loopwne: // Loop while CX != 0 and ZF=0 case NN_loopne: // Loop while rCX != 0 and ZF=0 case NN_loopdne: // Loop while ECX != 0 and ZF=0 case NN_loopqne: // Loop while RCX != 0 and ZF=0 return this->BuildLoopRTL(SMP_NULL_OPERATOR); break; case NN_lsl: // Load Segment Limit case NN_ltr: // Load Task Register return false; break; case NN_mov: // Move Data case NN_movsp: // Move to/from Special Registers case NN_movs: // Move Byte(s) from String to String return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_movsx: // Move with Sign-Extend return this->BuildUnary2OpndRTL(SMP_SIGN_EXTEND); case NN_movzx: // Move with Zero-Extend return this->BuildUnary2OpndRTL(SMP_ZERO_EXTEND); case NN_mul: // Unsigned Multiplication of AL or AX return this->BuildMultiplyDivideRTL(SMP_U_MULTIPLY); case NN_neg: // Two's Complement Negation return this->BuildUnaryRTL(SMP_NEGATE); case NN_nop: // No Operation NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_not: // One's Complement Negation return this->BuildUnaryRTL(SMP_BITWISE_NOT); case NN_or: // Logical Inclusive OR return this->BuildBinaryRTL(SMP_BITWISE_OR); case NN_out: // Output to Port return this->BuildUnary2OpndRTL(SMP_OUTPUT); case NN_outs: // Output Byte(s) to Port return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_pop: // Pop a word from the Stack case NN_popaw: // Pop all General Registers case NN_popa: // Pop all General Registers case NN_popad: // Pop all General Registers (use32) case NN_popaq: // Pop all General Registers (use64) case NN_popfw: // Pop Stack into Flags Register case NN_popf: // Pop Stack into Flags Register case NN_popfd: // Pop Stack into Eflags Register case NN_popfq: // Pop Stack into Rflags Register return this->BuildPopRTL(); case NN_push: // Push Operand onto the Stack case NN_pushaw: // Push all General Registers case NN_pusha: // Push all General Registers case NN_pushad: // Push all General Registers (use32) case NN_pushaq: // Push all General Registers (use64) case NN_pushfw: // Push Flags Register onto the Stack case NN_pushf: // Push Flags Register onto the Stack case NN_pushfd: // Push Flags Register onto the Stack (use32) case NN_pushfq: // Push Flags Register onto the Stack (use64) return this->BuildPushRTL(); case NN_rcl: // Rotate Through Carry Left return this->BuildBinaryPlusFlagsRTL(SMP_ROTATE_LEFT_CARRY); case NN_rcr: // Rotate Through Carry Right return this->BuildBinaryPlusFlagsRTL(SMP_ROTATE_RIGHT_CARRY); case NN_rol: // Rotate Left return this->BuildBinaryRTL(SMP_ROTATE_LEFT); case NN_ror: // Rotate Right return this->BuildBinaryRTL(SMP_ROTATE_RIGHT); case NN_rep: // Repeat String Operation case NN_repe: // Repeat String Operation while ZF=1 case NN_repne: // Repeat String Operation while ZF=0 return false; break; case NN_retn: // Return Near from Procedure case NN_retf: // Return Far from Procedure return this->BuildReturnRTL(); case NN_sahf: // Store AH into Flags Register return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_sal: // Shift Arithmetic Left return this->BuildBinaryRTL(SMP_S_LEFT_SHIFT); case NN_sar: // Shift Arithmetic Right return this->BuildBinaryRTL(SMP_S_RIGHT_SHIFT); case NN_shl: // Shift Logical Left return this->BuildBinaryRTL(SMP_U_LEFT_SHIFT); case NN_shr: // Shift Logical Right return this->BuildBinaryRTL(SMP_U_RIGHT_SHIFT); case NN_sbb: // Integer Subtraction with Borrow #if SMP_BUILD_SPECIAL_ADC_SBB_RTL return this->BuildBinaryPlusFlagsRTL(SMP_SUBTRACT_BORROW); #else return this->BuildBinaryRTL(SMP_SUBTRACT_BORROW); #endif case NN_scas: // Scan String return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE); case NN_seta: // Set Byte if Above (CF=0 & ZF=0) case NN_setae: // Set Byte if Above or Equal (CF=0) case NN_setb: // Set Byte if Below (CF=1) case NN_setbe: // Set Byte if Below or Equal (CF=1 | ZF=1) case NN_setc: // Set Byte if Carry (CF=1) case NN_sete: // Set Byte if Equal (ZF=1) case NN_setg: // Set Byte if Greater (ZF=0 & SF=OF) case NN_setge: // Set Byte if Greater or Equal (SF=OF) case NN_setl: // Set Byte if Less (SF!=OF) case NN_setle: // Set Byte if Less or Equal (ZF=1 | SF!=OF) case NN_setna: // Set Byte if Not Above (CF=1 | ZF=1) case NN_setnae: // Set Byte if Not Above or Equal (CF=1) case NN_setnb: // Set Byte if Not Below (CF=0) case NN_setnbe: // Set Byte if Not Below or Equal (CF=0 & ZF=0) case NN_setnc: // Set Byte if Not Carry (CF=0) case NN_setne: // Set Byte if Not Equal (ZF=0) case NN_setng: // Set Byte if Not Greater (ZF=1 | SF!=OF) case NN_setnge: // Set Byte if Not Greater or Equal (ZF=1) case NN_setnl: // Set Byte if Not Less (SF=OF) case NN_setnle: // Set Byte if Not Less or Equal (ZF=0 & SF=OF) case NN_setno: // Set Byte if Not Overflow (OF=0) case NN_setnp: // Set Byte if Not Parity (PF=0) case NN_setns: // Set Byte if Not Sign (SF=0) case NN_setnz: // Set Byte if Not Zero (ZF=0) case NN_seto: // Set Byte if Overflow (OF=1) case NN_setp: // Set Byte if Parity (PF=1) case NN_setpe: // Set Byte if Parity Even (PF=1) case NN_setpo: // Set Byte if Parity Odd (PF=0) case NN_sets: // Set Byte if Sign (SF=1) case NN_setz: // Set Byte if Zero (ZF=1) // Destination always get set to NUMERIC 0 or 1, depending on // the condition and the relevant flags bits. Best way to model // this in an RTL is to perform an unspecified unary NUMERIC // operation on the flags register and assign the result to the // destination operand, making it always NUMERIC. return this->BuildUnary2OpndRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_sgdt: // Store Global Descriptor Table Register case NN_sidt: // Store Interrupt Descriptor Table Register return false; break; case NN_shld: // Double Precision Shift Left return this->BuildDoubleShiftRTL(SMP_U_LEFT_SHIFT); case NN_shrd: // Double Precision Shift Right return this->BuildDoubleShiftRTL(SMP_U_RIGHT_SHIFT); case NN_sldt: // Store Local Descriptor Table Register case NN_smsw: // Store Machine Status Word return false; break; case NN_stc: // Set Carry Flag case NN_std: // Set Direction Flag return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_sti: // Set Interrupt Flag NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_stos: // Store String return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_str: // Store Task Register return false; break; case NN_sub: // Integer Subtraction return this->BuildBinaryRTL(SMP_SUBTRACT); case NN_test: // Logical Compare return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE); case NN_verr: // Verify a Segment for Reading case NN_verw: // Verify a Segment for Writing case NN_wait: // Wait until BUSY# Pin is Inactive (HIGH) NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; if (NN_wait != this->SMPcmd.itype) this->RTL.ExtraKills.push_back(FlagsOp); return true; case NN_xchg: // Exchange Register/Memory with Register return this->BuildExchangeRTL(); case NN_xlat: // Table Lookup Translation return false; break; case NN_xor: // Logical Exclusive OR return this->BuildBinaryRTL(SMP_BITWISE_XOR); // // 486 instructions // case NN_cmpxchg: // Compare and Exchange return this->BuildCompareExchangeRTL(); case NN_bswap: // Swap bits in EAX return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION); case NN_xadd: // t<-dest; dest<-src+dest; src<-t return this->BuildExchangeAddRTL(); case NN_invd: // Invalidate Data Cache case NN_wbinvd: // Invalidate Data Cache (write changes) case NN_invlpg: // Invalidate TLB entry NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; // // Pentium instructions // case NN_rdmsr: // Read Machine Status Register return this->BuildOptType8RTL(); case NN_wrmsr: // Write Machine Status Register return false; break; case NN_cpuid: // Get CPU ID return this->BuildOptType8RTL(); case NN_cmpxchg8b: // Compare and Exchange Eight Bytes return false; break; case NN_rdtsc: // Read Time Stamp Counter return this->BuildOptType8RTL(); case NN_rsm: // Resume from System Management Mode NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; // // Pentium Pro instructions // case NN_cmova: // Move if Above (CF=0 & ZF=0) case NN_cmovb: // Move if Below (CF=1) case NN_cmovbe: // Move if Below or Equal (CF=1 | ZF=1) return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_cmovg: // Move if Greater (ZF=0 & SF=OF) return this->BuildMoveRTL(SMP_GREATER_THAN); case NN_cmovge: // Move if Greater or Equal (SF=OF) return this->BuildMoveRTL(SMP_GREATER_EQUAL); case NN_cmovl: // Move if Less (SF!=OF) return this->BuildMoveRTL(SMP_LESS_THAN); case NN_cmovle: // Move if Less or Equal (ZF=1 | SF!=OF) return this->BuildMoveRTL(SMP_LESS_EQUAL); case NN_cmovnb: // Move if Not Below (CF=0) case NN_cmovno: // Move if Not Overflow (OF=0) case NN_cmovnp: // Move if Not Parity (PF=0) case NN_cmovns: // Move if Not Sign (SF=0) return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_cmovnz: // Move if Not Zero (ZF=0) return this->BuildMoveRTL(SMP_NOT_EQUAL); case NN_cmovo: // Move if Overflow (OF=1) case NN_cmovp: // Move if Parity (PF=1) case NN_cmovs: // Move if Sign (SF=1) return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_cmovz: // Move if Zero (ZF=1) return this->BuildMoveRTL(SMP_EQUAL); case NN_fcmovb: // Floating Move if Below return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_fcmove: // Floating Move if Equal return this->BuildMoveRTL(SMP_EQUAL); case NN_fcmovbe: // Floating Move if Below or Equal return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_fcmovu: // Floating Move if Unordered return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_fcmovnb: // Floating Move if Not Below return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_fcmovne: // Floating Move if Not Equal return this->BuildMoveRTL(SMP_NOT_EQUAL); case NN_fcmovnbe: // Floating Move if Not Below or Equal return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_fcmovnu: // Floating Move if Not Unordered return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION); case NN_fcomi: // FP Compare: result in EFLAGS case NN_fucomi: // FP Unordered Compare: result in EFLAGS case NN_fcomip: // FP Compare: result in EFLAGS: pop stack case NN_fucomip: // FP Unordered Compare: result in EFLAGS: pop stack return this->BuildFlagsDestBinaryRTL(SMP_S_COMPARE); break; case NN_rdpmc: // Read Performance Monitor Counter return this->BuildOptType8RTL(); // // FPP instructions // case NN_fld: // Load Real case NN_fst: // Store Real case NN_fstp: // Store Real and Pop return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_fxch: // Exchange Registers // FP registers remain NUMERIC anyway, so this is a no-op to our type system. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_fild: // Load Integer return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_fist: // Store Integer case NN_fistp: // Store Integer and Pop return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); case NN_fbld: // Load BCD case NN_fbstp: // Store BCD and Pop return this->BuildMoveRTL(SMP_NULL_OPERATOR); case NN_fadd: // Add Real case NN_faddp: // Add Real and Pop case NN_fiadd: // Add Integer case NN_fsub: // Subtract Real case NN_fsubp: // Subtract Real and Pop case NN_fisub: // Subtract Integer case NN_fsubr: // Subtract Real Reversed case NN_fsubrp: // Subtract Real Reversed and Pop case NN_fisubr: // Subtract Integer Reversed case NN_fmul: // Multiply Real case NN_fmulp: // Multiply Real and Pop case NN_fimul: // Multiply Integer case NN_fdiv: // Divide Real case NN_fdivp: // Divide Real and Pop case NN_fidiv: // Divide Integer case NN_fdivr: // Divide Real Reversed case NN_fdivrp: // Divide Real Reversed and Pop case NN_fidivr: // Divide Integer Reversed return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC, true); case NN_fsqrt: // Square Root case NN_fscale: // Scale: st(0) <- st(0) * 2^st(1) case NN_fprem: // Partial Remainder case NN_frndint: // Round to Integer case NN_fxtract: // Extract exponent and significand case NN_fabs: // Absolute value case NN_fchs: // Change Sign return this->BuildUnaryRTL(SMP_UNARY_FLOATING_ARITHMETIC); case NN_fcom: // Compare Real case NN_fcomp: // Compare Real and Pop case NN_fcompp: // Compare Real and Pop Twice case NN_ficom: // Compare Integer case NN_ficomp: // Compare Integer and Pop case NN_ftst: // Test case NN_fxam: // Examine // Floating comparison instructions use FP reg stack locations // as sources and set only the FP flags. All of these are numeric // type and we don't track any of them, so all such instructions // can be considered to be no-ops. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_fptan: // Partial tangent case NN_fpatan: // Partial arctangent case NN_f2xm1: // 2^x - 1 case NN_fyl2x: // Y * lg2(X) case NN_fyl2xp1: // Y * lg2(X+1) // We can consider it a unary operation when both arguments come // off the floating point register stack, unless we ever start // modeling the different locations in the FP register stack. return this->BuildUnaryRTL(SMP_UNARY_FLOATING_ARITHMETIC); case NN_fldz: // Load +0.0 case NN_fld1: // Load +1.0 case NN_fldpi: // Load PI=3.14... case NN_fldl2t: // Load lg2(10) case NN_fldl2e: // Load lg2(e) case NN_fldlg2: // Load lg10(2) case NN_fldln2: // Load ln(2) case NN_finit: // Initialize Processor case NN_fninit: // Initialize Processor (no wait) case NN_fsetpm: // Set Protected Mode case NN_fldcw: // Load Control Word case NN_fstcw: // Store Control Word case NN_fnstcw: // Store Control Word (no wait) case NN_fstsw: // Store Status Word case NN_fnstsw: // Store Status Word (no wait) case NN_fclex: // Clear Exceptions case NN_fnclex: // Clear Exceptions (no wait) // Floating point stack and control word and flags operations // with no memory operands are no-ops to us. // NOTE: We might want to deal with the memory operands in some // of these opcodes later. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_fstenv: // Store Environment case NN_fnstenv: // Store Environment (no wait) case NN_fldenv: // Load Environment case NN_fsave: // Save State case NN_fnsave: // Save State (no wait) case NN_frstor: // Restore State case NN_fincstp: // Increment Stack Pointer case NN_fdecstp: // Decrement Stack Pointer case NN_ffree: // Free Register // Floating point stack and control word and flags operations // with no memory operands are no-ops to us. // NOTE: We might want to deal with the memory operands in some // of these opcodes later. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_fnop: // No Operation NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_feni: // (8087 only) case NN_fneni: // (no wait) (8087 only) case NN_fdisi: // (8087 only) case NN_fndisi: // (no wait) (8087 only) return false; break; // // 80387 instructions // case NN_fprem1: // Partial Remainder ( < half ) case NN_fsincos: // t<-cos(st); st<-sin(st); push t case NN_fsin: // Sine case NN_fcos: // Cosine case NN_fucom: // Compare Unordered Real case NN_fucomp: // Compare Unordered Real and Pop case NN_fucompp: // Compare Unordered Real and Pop Twice // Floating point stack and control word and flags operations // with no memory operands are no-ops to us. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; // // Instructions added 28.02.96 // case NN_setalc: // Set AL to Carry Flag case NN_svdc: // Save Register and Descriptor case NN_rsdc: // Restore Register and Descriptor case NN_svldt: // Save LDTR and Descriptor case NN_rsldt: // Restore LDTR and Descriptor case NN_svts: // Save TR and Descriptor case NN_rsts: // Restore TR and Descriptor case NN_icebp: // ICE Break Point case NN_loadall: // Load the entire CPU state from ES:EDI return false; break; // // MMX instructions // case NN_emms: // Empty MMX state NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; break; case NN_movd: // Move 32 bits case NN_movq: // Move 64 bits return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_packsswb: // Pack with Signed Saturation (Word->Byte) case NN_packssdw: // Pack with Signed Saturation (Dword->Word) return this->BuildBinaryRTL(SMP_PACK_S); break; case NN_packuswb: // Pack with Unsigned Saturation (Word->Byte) return this->BuildBinaryRTL(SMP_PACK_U); break; case NN_paddb: // Packed Add Byte case NN_paddw: // Packed Add Word case NN_paddd: // Packed Add Dword case NN_paddsb: // Packed Add with Saturation (Byte) case NN_paddsw: // Packed Add with Saturation (Word) case NN_paddusb: // Packed Add Unsigned with Saturation (Byte) case NN_paddusw: // Packed Add Unsigned with Saturation (Word) return this->BuildBinaryRTL(SMP_ADD); break; case NN_pand: // Bitwise Logical And return this->BuildBinaryRTL(SMP_BITWISE_AND); break; case NN_pandn: // Bitwise Logical And Not return this->BuildBinaryRTL(SMP_BITWISE_AND_NOT); break; case NN_pcmpeqb: // Packed Compare for Equal (Byte) case NN_pcmpeqw: // Packed Compare for Equal (Word) case NN_pcmpeqd: // Packed Compare for Equal (Dword) return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET); break; case NN_pcmpgtb: // Packed Compare for Greater Than (Byte) case NN_pcmpgtw: // Packed Compare for Greater Than (Word) case NN_pcmpgtd: // Packed Compare for Greater Than (Dword) return this->BuildBinaryRTL(SMP_COMPARE_GT_AND_SET); break; case NN_pmaddwd: // Packed Multiply and Add return this->BuildBinaryRTL(SMP_MULTIPLY_AND_ADD); break; case NN_pmulhw: // Packed Multiply High case NN_pmullw: // Packed Multiply Low return this->BuildBinaryRTL(SMP_U_MULTIPLY); break; case NN_por: // Bitwise Logical Or return this->BuildBinaryRTL(SMP_BITWISE_OR); break; case NN_psllw: // Packed Shift Left Logical (Word) case NN_pslld: // Packed Shift Left Logical (Dword) case NN_psllq: // Packed Shift Left Logical (Qword) return this->BuildBinaryRTL(SMP_U_LEFT_SHIFT); break; case NN_psraw: // Packed Shift Right Arithmetic (Word) case NN_psrad: // Packed Shift Right Arithmetic (Dword) return this->BuildBinaryRTL(SMP_S_RIGHT_SHIFT); break; case NN_psrlw: // Packed Shift Right Logical (Word) case NN_psrld: // Packed Shift Right Logical (Dword) case NN_psrlq: // Packed Shift Right Logical (Qword) return this->BuildBinaryRTL(SMP_U_RIGHT_SHIFT); break; case NN_psubb: // Packed Subtract Byte case NN_psubw: // Packed Subtract Word case NN_psubd: // Packed Subtract Dword return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_psubsb: // Packed Subtract with Saturation (Byte) case NN_psubsw: // Packed Subtract with Saturation (Word) return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_psubusb: // Packed Subtract Unsigned with Saturation (Byte) case NN_psubusw: // Packed Subtract Unsigned with Saturation (Word) return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_punpckhbw: // Unpack High Packed Data (Byte->Word) case NN_punpckhwd: // Unpack High Packed Data (Word->Dword) case NN_punpckhdq: // Unpack High Packed Data (Dword->Qword) case NN_punpcklbw: // Unpack Low Packed Data (Byte->Word) case NN_punpcklwd: // Unpack Low Packed Data (Word->Dword) case NN_punpckldq: // Unpack Low Packed Data (Dword->Qword) return this->BuildBinaryRTL(SMP_INTERLEAVE); break; case NN_pxor: // Bitwise Logical Exclusive Or return this->BuildBinaryRTL(SMP_BITWISE_XOR); break; // // Undocumented Deschutes processor instructions // case NN_fxsave: // Fast save FP context case NN_fxrstor: // Fast restore FP context return false; break; // Pentium II instructions case NN_sysenter: // Fast Transition to System Call Entry Point case NN_sysexit: // Fast Transition from System Call Entry Point return false; break; // 3DNow! instructions case NN_pavgusb: // Packed 8-bit Unsigned Integer Averaging return this->BuildBinaryRTL(SMP_AVERAGE_U); break; case NN_pfadd: // Packed Floating-Point Addition return this->BuildBinaryRTL(SMP_ADD); break; case NN_pfsub: // Packed Floating-Point Subtraction case NN_pfsubr: // Packed Floating-Point Reverse Subtraction !!!!****!!!! Fix this reversal if we care about the details return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_pfacc: // Packed Floating-Point Accumulate return this->BuildBinaryRTL(SMP_ADD); break; case NN_pfcmpge: // Packed Floating-Point Comparison: Greater or Equal return this->BuildBinaryRTL(SMP_COMPARE_GE_AND_SET); break; case NN_pfcmpgt: // Packed Floating-Point Comparison: Greater return this->BuildBinaryRTL(SMP_COMPARE_GT_AND_SET); break; case NN_pfcmpeq: // Packed Floating-Point Comparison: Equal return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET); break; case NN_pfmin: // Packed Floating-Point Minimum return this->BuildBinaryRTL(SMP_MIN_S); break; case NN_pfmax: // Packed Floating-Point Maximum return this->BuildBinaryRTL(SMP_MAX_S); break; case NN_pi2fd: // Packed 32-bit Integer to Floating-Point return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_pf2id: // Packed Floating-Point to 32-bit Integer return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_pfrcp: // Packed Floating-Point Reciprocal Approximation case NN_pfrsqrt: // Packed Floating-Point Reciprocal Square Root Approximation return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC); break; case NN_pfmul: // Packed Floating-Point Multiplication return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_pfrcpit1: // Packed Floating-Point Reciprocal First Iteration Step case NN_pfrsqit1: // Packed Floating-Point Reciprocal Square Root First Iteration Step case NN_pfrcpit2: // Packed Floating-Point Reciprocal Second Iteration Step return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC); break; case NN_pmulhrw: // Packed Floating-Point 16-bit Integer Multiply with rounding return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_femms: // Faster entry/exit of the MMX or floating-point state NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; break; case NN_prefetch: // Prefetch at least a 32-byte line into L1 data cache case NN_prefetchw: // Prefetch processor cache line into L1 data cache (mark as modified) // Prefetch opcodes are no-ops to us. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; // Pentium III instructions case NN_addps: // Packed Single-FP Add case NN_addss: // Scalar Single-FP Add case NN_andnps: // Bitwise Logical And Not for Single-FP case NN_andps: // Bitwise Logical And for Single-FP return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_cmpps: // Packed Single-FP Compare case NN_cmpss: // Scalar Single-FP Compare case NN_comiss: // Scalar Ordered Single-FP Compare and Set EFLAGS return false; break; case NN_cvtpi2ps: // Packed signed INT32 to Packed Single-FP conversion return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_cvtps2pi: // Packed Single-FP to Packed INT32 conversion return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_cvtsi2ss: // Scalar signed INT32 to Single-FP conversion return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_cvtss2si: // Scalar Single-FP to signed INT32 conversion case NN_cvttps2pi: // Packed Single-FP to Packed INT32 conversion (truncate) case NN_cvttss2si: // Scalar Single-FP to signed INT32 conversion (truncate) return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_divps: // Packed Single-FP Divide case NN_divss: // Scalar Single-FP Divide return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_ldmxcsr: // Load Streaming SIMD Extensions Technology Control/Status Register return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_maxps: // Packed Single-FP Maximum case NN_maxss: // Scalar Single-FP Maximum return this->BuildBinaryRTL(SMP_MAX_S); break; case NN_minps: // Packed Single-FP Minimum case NN_minss: // Scalar Single-FP Minimum return this->BuildBinaryRTL(SMP_MIN_S); break; case NN_movaps: // Move Aligned Four Packed Single-FP case NN_movhlps: // Move High to Low Packed Single-FP case NN_movhps: // Move High Packed Single-FP case NN_movlhps: // Move Low to High Packed Single-FP case NN_movlps: // Move Low Packed Single-FP case NN_movmskps: // Move Mask to Register case NN_movss: // Move Scalar Single-FP case NN_movups: // Move Unaligned Four Packed Single-FP return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_mulps: // Packed Single-FP Multiply case NN_mulss: // Scalar Single-FP Multiply case NN_orps: // Bitwise Logical OR for Single-FP Data return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_rcpps: // Packed Single-FP Reciprocal case NN_rcpss: // Scalar Single-FP Reciprocal case NN_rsqrtps: // Packed Single-FP Square Root Reciprocal case NN_rsqrtss: // Scalar Single-FP Square Root Reciprocal return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC); break; case NN_shufps: // Shuffle Single-FP return this->BuildBinaryRTL(SMP_SHUFFLE); break; case NN_sqrtps: // Packed Single-FP Square Root case NN_sqrtss: // Scalar Single-FP Square Root return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC); break; case NN_stmxcsr: // Store Streaming SIMD Extensions Technology Control/Status Register return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_subps: // Packed Single-FP Subtract case NN_subss: // Scalar Single-FP Subtract return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_ucomiss: // Scalar Unordered Single-FP Compare and Set EFLAGS return false; break; case NN_unpckhps: // Unpack High Packed Single-FP Data case NN_unpcklps: // Unpack Low Packed Single-FP Data return this->BuildBinaryRTL(SMP_INTERLEAVE); break; case NN_xorps: // Bitwise Logical XOR for Single-FP Data return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_pavgb: // Packed Average (Byte) case NN_pavgw: // Packed Average (Word) return this->BuildBinaryRTL(SMP_AVERAGE_U); break; case NN_pextrw: // Extract Word return this->BuildBinaryPlusImmedRTL(SMP_EXTRACT_ZERO_EXTEND, SMP_CREATE_MASK); break; case NN_pinsrw: // Insert Word return this->BuildBinaryPlusImmedRTL(SMP_BITWISE_AND, SMP_CREATE_MASK); break; case NN_pmaxsw: // Packed Signed Integer Word Maximum return this->BuildBinaryRTL(SMP_MAX_S); break; case NN_pmaxub: // Packed Unsigned Integer Byte Maximum return this->BuildBinaryRTL(SMP_MAX_U); break; case NN_pminsw: // Packed Signed Integer Word Minimum return this->BuildBinaryRTL(SMP_MIN_S); break; case NN_pminub: // Packed Unsigned Integer Byte Minimum return this->BuildBinaryRTL(SMP_MIN_U); break; case NN_pmovmskb: // Move Byte Mask to Integer return this->BuildBinaryRTL(SMP_SHUFFLE); break; case NN_pmulhuw: // Packed Multiply High Unsigned return this->BuildBinaryRTL(SMP_U_MULTIPLY); break; case NN_psadbw: // Packed Sum of Absolute Differences return this->BuildBinaryRTL(SMP_SUM_OF_DIFFS); break; case NN_pshufw: // Packed Shuffle Word return this->BuildBinaryRTL(SMP_SHUFFLE); break; case NN_maskmovq: // Byte Mask write return false; break; case NN_movntps: // Move Aligned Four Packed Single-FP Non Temporal case NN_movntq: // Move 64 Bits Non Temporal return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_prefetcht0: // Prefetch to all cache levels case NN_prefetcht1: // Prefetch to all cache levels case NN_prefetcht2: // Prefetch to L2 cache case NN_prefetchnta: // Prefetch to L1 cache case NN_sfence: // Store Fence // Cache prefetch and store fence opcodes are no-ops to us. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; // Pentium III Pseudo instructions case NN_cmpeqps: // Packed Single-FP Compare EQ return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET); break; case NN_cmpltps: // Packed Single-FP Compare LT return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET); break; case NN_cmpleps: // Packed Single-FP Compare LE return this->BuildBinaryRTL(SMP_COMPARE_LE_AND_SET); break; case NN_cmpunordps: // Packed Single-FP Compare UNORD return false; break; case NN_cmpneqps: // Packed Single-FP Compare NOT EQ return this->BuildBinaryRTL(SMP_COMPARE_NE_AND_SET); break; case NN_cmpnltps: // Packed Single-FP Compare NOT LT return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET); break; case NN_cmpnleps: // Packed Single-FP Compare NOT LE return this->BuildBinaryRTL(SMP_COMPARE_GT_AND_SET); break; case NN_cmpordps: // Packed Single-FP Compare ORDERED return false; break; case NN_cmpeqss: // Scalar Single-FP Compare EQ return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET); break; case NN_cmpltss: // Scalar Single-FP Compare LT return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET); break; case NN_cmpless: // Scalar Single-FP Compare LE return this->BuildBinaryRTL(SMP_COMPARE_LE_AND_SET); break; case NN_cmpunordss: // Scalar Single-FP Compare UNORD return false; break; case NN_cmpneqss: // Scalar Single-FP Compare NOT EQ return this->BuildBinaryRTL(SMP_COMPARE_NE_AND_SET); break; case NN_cmpnltss: // Scalar Single-FP Compare NOT LT return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET); break; case NN_cmpnless: // Scalar Single-FP Compare NOT LE return this->BuildBinaryRTL(SMP_COMPARE_LE_AND_SET); break; case NN_cmpordss: // Scalar Single-FP Compare ORDERED return false; break; // AMD K7 instructions case NN_pf2iw: // Packed Floating-Point to Integer with Sign Extend return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_pfnacc: // Packed Floating-Point Negative Accumulate case NN_pfpnacc: // Packed Floating-Point Mixed Positive-Negative Accumulate return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_pi2fw: // Packed 16-bit Integer to Floating-Point return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_pswapd: // Packed Swap Double Word return this->BuildExchangeRTL(); break; // Undocumented FP instructions (thanks to norbert.juffa@adm.com) case NN_fstp1: // Alias of Store Real and Pop case NN_fcom2: // Alias of Compare Real case NN_fcomp3: // Alias of Compare Real and Pop case NN_fxch4: // Alias of Exchange Registers case NN_fcomp5: // Alias of Compare Real and Pop return false; break; case NN_ffreep: // Free Register and Pop; used in mplayer binary // Floating point stack and control word and flags operations // with no memory operands are no-ops to us. NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_fxch7: // Alias of Exchange Registers case NN_fstp8: // Alias of Store Real and Pop case NN_fstp9: // Alias of Store Real and Pop return false; break; // Pentium 4 instructions case NN_addpd: // Add Packed Double-Precision Floating-Point Values case NN_addsd: // Add Scalar Double-Precision Floating-Point Values case NN_andnpd: // Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values case NN_andpd: // Bitwise Logical AND of Packed Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_clflush: // Flush Cache Line case NN_cmppd: // Compare Packed Double-Precision Floating-Point Values case NN_cmpsd: // Compare Scalar Double-Precision Floating-Point Values case NN_comisd: // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS return false; break; case NN_cvtdq2pd: // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values case NN_cvtdq2ps: // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_cvtpd2dq: // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers case NN_cvtpd2pi: // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers case NN_cvtpd2ps: // Convert Packed Double-Precision Floating-Point Values to Packed Single-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_cvtpi2pd: // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_cvtps2dq: // Convert Packed Single-Precision Floating-Point Values to Packed Doubleword Integers case NN_cvtps2pd: // Convert Packed Single-Precision Floating-Point Values to Packed Double-Precision Floating-Point Values case NN_cvtsd2si: // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer case NN_cvtsd2ss: // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_cvtsi2sd: // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP); break; case NN_cvtss2sd: // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value case NN_cvttpd2dq: // Convert With Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers case NN_cvttpd2pi: // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers case NN_cvttps2dq: // Convert With Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers case NN_cvttsd2si: // Convert with Truncation Scalar Double-Precision Floating-Point Value to Doubleword Integer return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_divpd: // Divide Packed Double-Precision Floating-Point Values case NN_divsd: // Divide Scalar Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_lfence: // Load Fence NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_maskmovdqu: // Store Selected Bytes of Double Quadword return false; break; case NN_maxpd: // Return Maximum Packed Double-Precision Floating-Point Values case NN_maxsd: // Return Maximum Scalar Double-Precision Floating-Point Value return this->BuildBinaryRTL(SMP_MAX_S); break; case NN_mfence: // Memory Fence NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_minpd: // Return Minimum Packed Double-Precision Floating-Point Values case NN_minsd: // Return Minimum Scalar Double-Precision Floating-Point Value return this->BuildBinaryRTL(SMP_MIN_S); break; case NN_movapd: // Move Aligned Packed Double-Precision Floating-Point Values case NN_movdq2q: // Move Quadword from XMM to MMX Register case NN_movdqa: // Move Aligned Double Quadword case NN_movdqu: // Move Unaligned Double Quadword case NN_movhpd: // Move High Packed Double-Precision Floating-Point Values case NN_movlpd: // Move Low Packed Double-Precision Floating-Point Values return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_movmskpd: // Extract Packed Double-Precision Floating-Point Sign Mask return false; break; case NN_movntdq: // Store Double Quadword Using Non-Temporal Hint case NN_movnti: // Store Doubleword Using Non-Temporal Hint case NN_movntpd: // Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint case NN_movq2dq: // Move Quadword from MMX to XMM Register case NN_movsd: // Move Scalar Double-Precision Floating-Point Values case NN_movupd: // Move Unaligned Packed Double-Precision Floating-Point Values return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; case NN_mulpd: // Multiply Packed Double-Precision Floating-Point Values case NN_mulsd: // Multiply Scalar Double-Precision Floating-Point Values case NN_orpd: // Bitwise Logical OR of Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_paddq: // Add Packed Quadword Integers return this->BuildBinaryRTL(SMP_ADD); break; case NN_pause: // Spin Loop Hint NopRT = new SMPRegTransfer; NopRT->SetParentInst(this); NopRT->SetOperator(SMP_NULL_OPERATOR); this->RTL.push_back(NopRT); NopRT = NULL; return true; case NN_pmuludq: // Multiply Packed Unsigned Doubleword Integers return this->BuildBinaryRTL(SMP_U_MULTIPLY); break; case NN_pshufd: // Shuffle Packed Doublewords case NN_pshufhw: // Shuffle Packed High Words case NN_pshuflw: // Shuffle Packed Low Words return this->BuildBinaryRTL(SMP_SHUFFLE); break; case NN_pslldq: // Shift Double Quadword Left Logical return this->BuildBinaryRTL(SMP_U_LEFT_SHIFT); break; case NN_psrldq: // Shift Double Quadword Right Logical return this->BuildBinaryRTL(SMP_U_RIGHT_SHIFT); break; case NN_psubq: // Subtract Packed Quadword Integers return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_punpckhqdq: // Unpack High Data case NN_punpcklqdq: // Unpack Low Data return this->BuildBinaryRTL(SMP_INTERLEAVE); break; case NN_shufpd: // Shuffle Packed Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_SHUFFLE); break; case NN_sqrtpd: // Compute Square Roots of Packed Double-Precision Floating-Point Values case NN_sqrtsd: // Compute Square Rootof Scalar Double-Precision Floating-Point Value return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC); break; case NN_subpd: // Subtract Packed Double-Precision Floating-Point Values case NN_subsd: // Subtract Scalar Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_ucomisd: // Unordered Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS return false; break; case NN_unpckhpd: // Unpack and Interleave High Packed Double-Precision Floating-Point Values case NN_unpcklpd: // Unpack and Interleave Low Packed Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_INTERLEAVE); break; case NN_xorpd: // Bitwise Logical OR of Double-Precision Floating-Point Values return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; // AMD syscall/sysret instructions case NN_syscall: // Low latency system call case NN_sysret: // Return from system call // AMD64 instructions case NN_swapgs: // Exchange GS base with KernelGSBase MSR // New Pentium instructions (SSE3) case NN_movddup: // Move One Double-FP and Duplicate case NN_movshdup: // Move Packed Single-FP High and Duplicate case NN_movsldup: // Move Packed Single-FP Low and Duplicate return false; break; // Missing AMD64 instructions case NN_movsxd: // Move with Sign-Extend Doubleword case NN_cmpxchg16b: // Compare and Exchange 16 Bytes return false; break; // SSE3 instructions case NN_addsubpd: // Add /Sub packed DP FP numbers case NN_addsubps: // Add /Sub packed SP FP numbers case NN_haddpd: // Add horizontally packed DP FP numbers case NN_haddps: // Add horizontally packed SP FP numbers case NN_hsubpd: // Sub horizontally packed DP FP numbers case NN_hsubps: // Sub horizontally packed SP FP numbers case NN_monitor: // Set up a linear address range to be monitored by hardware case NN_mwait: // Wait until write-back store performed within the range specified by the MONITOR instruction return false; break; case NN_fisttp: // Store ST in intXX (chop) and pop return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT); break; case NN_lddqu: // Load unaligned integer 128-bit return this->BuildMoveRTL(SMP_NULL_OPERATOR); break; // SSSE3 instructions case NN_psignb: // Packed SIGN Byte case NN_psignw: // Packed SIGN Word case NN_psignd: // Packed SIGN Doubleword case NN_pshufb: // Packed Shuffle Bytes return this->BuildBinaryRTL(SMP_SHUFFLE); break; case NN_pmulhrsw: // Packed Multiply High with Round and Scale return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC); break; case NN_pmaddubsw: // Multiply and Add Packed Signed and Unsigned Bytes return this->BuildBinaryRTL(SMP_MULTIPLY_AND_ADD); break; case NN_phsubsw: // Packed Horizontal Subtract and Saturate return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_phaddsw: // Packed Horizontal Add and Saturate return this->BuildBinaryRTL(SMP_ADD); break; case NN_phaddw: // Packed Horizontal Add Word case NN_phaddd: // Packed Horizontal Add Doubleword return this->BuildBinaryRTL(SMP_ADD); break; case NN_phsubw: // Packed Horizontal Subtract Word case NN_phsubd: // Packed Horizontal Subtract Doubleword return this->BuildBinaryRTL(SMP_SUBTRACT); break; case NN_palignr: // Packed Align Right return this->BuildPackShiftRTL(SMP_CONCATENATE, SMP_REVERSE_SHIFT_U); break; case NN_pabsb: // Packed Absolute Value Byte case NN_pabsw: // Packed Absolute Value Word case NN_pabsd: // Packed Absolute Value Doubleword return this->BuildUnary2OpndRTL(SMP_ABSOLUTE_VALUE); break; // VMX instructions case NN_vmcall: // Call to VM Monitor case NN_vmclear: // Clear Virtual Machine Control Structure case NN_vmlaunch: // Launch Virtual Machine case NN_vmresume: // Resume Virtual Machine case NN_vmptrld: // Load Pointer to Virtual Machine Control Structure case NN_vmptrst: // Store Pointer to Virtual Machine Control Structure case NN_vmread: // Read Field from Virtual Machine Control Structure case NN_vmwrite: // Write Field from Virtual Machine Control Structure case NN_vmxoff: // Leave VMX Operation case NN_vmxon: // Enter VMX Operation return false; break; #if 599 < IDA_SDK_VERSION case NN_ud2: // Undefined Instruction return false; break; // Added with x86-64 case NN_rdtscp: // Read Time-Stamp Counter and Processor ID return false; break; // Geode LX 3DNow! extensions case NN_pfrcpv: // Reciprocal Approximation for a Pair of 32-bit Floats case NN_pfrsqrtv: // Reciprocal Square Root Approximation for a Pair of 32-bit Floats return false; break; // SSE2 pseudoinstructions case NN_cmpeqpd: // Packed Double-FP Compare EQ case NN_cmpltpd: // Packed Double-FP Compare LT case NN_cmplepd: // Packed Double-FP Compare LE case NN_cmpunordpd: // Packed Double-FP Compare UNORD case NN_cmpneqpd: // Packed Double-FP Compare NOT EQ case NN_cmpnltpd: // Packed Double-FP Compare NOT LT case NN_cmpnlepd: // Packed Double-FP Compare NOT LE case NN_cmpordpd: // Packed Double-FP Compare ORDERED case NN_cmpeqsd: // Scalar Double-FP Compare EQ case NN_cmpltsd: // Scalar Double-FP Compare LT case NN_cmplesd: // Scalar Double-FP Compare LE case NN_cmpunordsd: // Scalar Double-FP Compare UNORD case NN_cmpneqsd: // Scalar Double-FP Compare NOT EQ case NN_cmpnltsd: // Scalar Double-FP Compare NOT LT case NN_cmpnlesd: // Scalar Double-FP Compare NOT LE case NN_cmpordsd: // Scalar Double-FP Compare ORDERED return false; break; // SSSE4.1 instructions case NN_blendpd: // Blend Packed Double Precision Floating-Point Values case NN_blendps: // Blend Packed Single Precision Floating-Point Values case NN_blendvpd: // Variable Blend Packed Double Precision Floating-Point Values case NN_blendvps: // Variable Blend Packed Single Precision Floating-Point Values case NN_dppd: // Dot Product of Packed Double Precision Floating-Point Values case NN_dpps: // Dot Product of Packed Single Precision Floating-Point Values return false; break; case NN_extractps: // Extract Packed Single Precision Floating-Point Value case NN_insertps: // Insert Packed Single Precision Floating-Point Value case NN_movntdqa: // Load Double Quadword Non-Temporal Aligned Hint case NN_mpsadbw: // Compute Multiple Packed Sums of Absolute Difference case NN_packusdw: // Pack with Unsigned Saturation case NN_pblendvb: // Variable Blend Packed Bytes case NN_pblendw: // Blend Packed Words case NN_pcmpeqq: // Compare Packed Qword Data for Equal return false; break; case NN_pextrb: // Extract Byte case NN_pextrd: // Extract Dword case NN_pextrq: // Extract Qword return this->BuildBinaryPlusImmedRTL(SMP_EXTRACT_ZERO_EXTEND, SMP_CREATE_MASK); case NN_phminposuw: // Packed Horizontal Word Minimum return false; break; case NN_pinsrb: // Insert Byte case NN_pinsrd: // Insert Dword case NN_pinsrq: // Insert Qword return this->BuildBinaryPlusImmedRTL(SMP_BITWISE_AND, SMP_CREATE_MASK); break; case NN_pmaxsb: // Maximum of Packed Signed Byte Integers case NN_pmaxsd: // Maximum of Packed Signed Dword Integers case NN_pmaxud: // Maximum of Packed Unsigned Dword Integers case NN_pmaxuw: // Maximum of Packed Word Integers case NN_pminsb: // Minimum of Packed Signed Byte Integers case NN_pminsd: // Minimum of Packed Signed Dword Integers case NN_pminud: // Minimum of Packed Unsigned Dword Integers case NN_pminuw: // Minimum of Packed Word Integers case NN_pmovsxbw: // Packed Move with Sign Extend case NN_pmovsxbd: // Packed Move with Sign Extend case NN_pmovsxbq: // Packed Move with Sign Extend case NN_pmovsxwd: // Packed Move with Sign Extend case NN_pmovsxwq: // Packed Move with Sign Extend case NN_pmovsxdq: // Packed Move with Sign Extend case NN_pmovzxbw: // Packed Move with Zero Extend case NN_pmovzxbd: // Packed Move with Zero Extend case NN_pmovzxbq: // Packed Move with Zero Extend case NN_pmovzxwd: // Packed Move with Zero Extend case NN_pmovzxwq: // Packed Move with Zero Extend case NN_pmovzxdq: // Packed Move with Zero Extend case NN_pmuldq: // Multiply Packed Signed Dword Integers case NN_pmulld: // Multiply Packed Signed Dword Integers and Store Low Result return false; break; case NN_ptest: // Logical Compare return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE); case NN_roundpd: // Round Packed Double Precision Floating-Point Values case NN_roundps: // Round Packed Single Precision Floating-Point Values case NN_roundsd: // Round Scalar Double Precision Floating-Point Values case NN_roundss: // Round Scalar Single Precision Floating-Point Values return false; break; // SSSE4.2 instructions case NN_crc32: // Accumulate CRC32 Value return false; break; case NN_pcmpestri: // Packed Compare Explicit Length Strings, Return Index case NN_pcmpestrm: // Packed Compare Explicit Length Strings, Return Mask case NN_pcmpistri: // Packed Compare Implicit Length Strings, Return Index case NN_pcmpistrm: // Packed Compare Implicit Length Strings, Return Mask return BuildBinaryIgnoreImmedRTL(SMP_GENERAL_COMPARE); break; case NN_pcmpgtq: // Compare Packed Data for Greater Than case NN_popcnt: // Return the Count of Number of Bits Set to 1 return false; break; // AMD SSE4a instructions case NN_extrq: // Extract Field From Register case NN_insertq: // Insert Field case NN_movntsd: // Move Non-Temporal Scalar Double-Precision Floating-Point case NN_movntss: // Move Non-Temporal Scalar Single-Precision Floating-Point case NN_lzcnt: // Leading Zero Count return false; break; // xsave/xrstor instructions case NN_xgetbv: // Get Value of Extended Control Register return this->BuildOptType8RTL(); case NN_xrstor: // Restore Processor Extended States case NN_xsave: // Save Processor Extended States case NN_xsetbv: // Set Value of Extended Control Register return false; break; // Intel Safer Mode Extensions (SMX) case NN_getsec: // Safer Mode Extensions (SMX) Instruction return false; break; // AMD-V Virtualization ISA Extension case NN_clgi: // Clear Global Interrupt Flag case NN_invlpga: // Invalidate TLB Entry in a Specified ASID case NN_skinit: // Secure Init and Jump with Attestation case NN_stgi: // Set Global Interrupt Flag case NN_vmexit: // Stop Executing Guest, Begin Executing Host case NN_vmload: // Load State from VMCB case NN_vmmcall: // Call VMM case NN_vmrun: // Run Virtual Machine case NN_vmsave: // Save State to VMCB return false; break; // VMX+ instructions case NN_invept: // Invalidate Translations Derived from EPT case NN_invvpid: // Invalidate Translations Based on VPID return false; break; // Intel Atom instructions case NN_movbe: // Move Data After Swapping Bytes return false; break; // Intel AES instructions case NN_aesenc: // Perform One Round of an AES Encryption Flow case NN_aesenclast: // Perform the Last Round of an AES Encryption Flow case NN_aesdec: // Perform One Round of an AES Decryption Flow case NN_aesdeclast: // Perform the Last Round of an AES Decryption Flow case NN_aesimc: // Perform the AES InvMixColumn Transformation case NN_aeskeygenassist: // AES Round Key Generation Assist return this->BuildBinaryRTL(SMP_ENCRYPTION_OPERATION); break; // Carryless multiplication case NN_pclmulqdq: // Carry-Less Multiplication Quadword return BuildBinaryIgnoreImmedRTL(SMP_U_MULTIPLY); break; #endif // 599 < IDA_SDK_VERSION default: SMP_msg("ERROR: Unknown instruction opcode at %lx : %s\n", (unsigned long) this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr())); return false; break; } // end switch on opcode return true; } // end SMPInstr::BuildRTL() // Iterate through all reg transfers and call SyncRTLDefUse for each. void SMPInstr::SyncAllRTs(bool UseFP, sval_t FPDelta) { for (size_t index = 0; index < this->RTL.GetCount(); ++index) { this->SyncRTLDefUse(this->RTL.GetRT(index), UseFP, FPDelta); } return; } // end of SMPInstr:SyncAllRTs() // Ensure that each operand of the RTL is found in the appropriate DEF or USE list. void SMPInstr::SyncRTLDefUse(SMPRegTransfer *CurrRT, bool UseFP, sval_t FPDelta) { // The ExtraKills are almost never represented in the DEF // lists. When they are, they are added in MDFixupDefUseLists(), so we ignore them here. // The only DEFs should come from left operands of SMP_ASSIGN operators, i.e. the effects // of register transfers. op_t LeftOp, RightOp; set<DefOrUse, LessDefUse>::iterator CurrDef, CurrUse; bool DebugFlag = false; #if SMP_VERBOSE_DEBUG_BUILD_RTL DebugFlag |= (0 == strcmp("__libc_csu_fini", this->BasicBlock->GetFunc()->GetFuncName())); #endif if (DebugFlag) { SMP_msg("SyncRTLDefUse entered. Dump of USE list:\n"); this->Uses.Dump(); } LeftOp = CurrRT->GetLeftOperand(); if (SMP_ASSIGN == CurrRT->GetOperator()) { assert(o_void != LeftOp.type); assert(o_imm != LeftOp.type); CurrDef = this->Defs.FindRef(LeftOp); if (CurrDef == this->GetLastDef() && !LeftOp.is_reg(R_ip)) { #if SMP_VERBOSE_DEBUG_BUILD_RTL SMP_msg("WARNING: DEF not found for SMP_ASSIGN in %s ; added op:", DisAsmText.GetDisAsm(this->GetAddr())); PrintOperand(LeftOp); SMP_msg("\n"); #endif this->Defs.SetRef(LeftOp, CurrRT->GetOperatorType()); } } else { // not SMP_ASSIGN; left operand should be a USE if (o_void != LeftOp.type) { CurrUse = this->Uses.FindRef(LeftOp); if (CurrUse == this->GetLastUse()) { #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: USE not found for "); PrintOperand(LeftOp); SMP_msg(" in %s ; added\n", DisAsmText.GetDisAsm(this->GetAddr())); #endif this->Uses.SetRef(LeftOp); } } } if (!CurrRT->HasRightSubTree()) { RightOp = CurrRT->GetRightOperand(); // right operand should be a USE if (o_void != RightOp.type) { CurrUse = this->Uses.FindRef(RightOp); if (CurrUse == this->GetLastUse()) { #if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE SMP_msg("WARNING: USE not found for "); PrintOperand(RightOp); SMP_msg(" in %s ; added\n", DisAsmText.GetDisAsm(this->GetAddr())); #endif this->Uses.SetRef(RightOp); } } } else { // recurse into right subtree this->SyncRTLDefUse(CurrRT->GetRightTree(), UseFP, FPDelta); } // Guard operands can only be USEs. SMPGuard *GuardExpr = CurrRT->GetGuard(); if (NULL != GuardExpr) { LeftOp = GuardExpr->GetLeftOperand(); if ((o_void != LeftOp.type) && (o_imm != LeftOp.type)) { CurrUse = this->Uses.FindRef(LeftOp); if (CurrUse == this->GetLastUse()) { this->Uses.SetRef(LeftOp); } } RightOp = GuardExpr->GetRightOperand(); if ((o_void != RightOp.type) && (o_imm != RightOp.type)) { CurrUse = this->Uses.FindRef(RightOp); if (CurrUse == this->GetLastUse()) { this->Uses.SetRef(RightOp); } } } return; } // end of SMPInstr::SyncRTLDefUse() // SetOperatorType - set the type of the operator, take into account the speculative (profiler) status void SMPRegTransfer::SetOperatorType(SMPOperandType OpType, const SMPInstr* Instr) { SMPOperandType OldType = RTop.type; SMPOperandType NewType = OpType; if (Instr->GetBlock()->GetFunc()->GetIsSpeculative()) { NewType = (SMPOperandType) (((int)NewType) | PROF_BASE); #if SMP_TRACK_NONSPEC_OPER_TYPE if (!IsProfDerived(OldType)) RTop.NonSpeculativeType = OldType; #endif } RTop.type = NewType; } // end of SMPRegTransfer::SetOperatorType // Does UseOp arithmetically affect the value of the NonFlagsDef for this RTL? bool SMPRegTransfer::OperandTransfersValueToDef(op_t UseOp) const { bool FoundTransfer = false; op_t DefOp = this->GetLeftOperand(); SMPoperator CurrOp = this->GetOperator(); if ((SMP_ASSIGN == CurrOp) && (DefOp.type != o_void) && (!DefOp.is_reg(MD_FLAGS_REG))) { // We have an assignment to a non-flag DefOp. The only remaining question is whether // UseOp appears in the right hand side of the RT as something besides an address register // inside a memory operand. if (this->HasRightSubTree()) { FoundTransfer = this->GetRightTree()->OperandTransfersHelper(UseOp); } else { op_t RightOp = this->GetRightOperand(); if (IsEqOpIgnoreBitwidth(UseOp, RightOp)) { // Found UseOp. FoundTransfer = true; } } } return FoundTransfer; } // end of SMPRegTransfer::OperandTransfersValueToDef() // Does UseOp arithmetically affect the value of the NonFlagsDef for this RT? // Recursive helper for OperandTransfersValueToDef(). bool SMPRegTransfer::OperandTransfersHelper(op_t UseOp) const { bool FoundTransfer = false; op_t LeftOp = this->GetLeftOperand(); SMPoperator CurrOp = this->GetOperator(); // Have to check left and right operands to see if they are UseOp. if (IsEqOpIgnoreBitwidth(UseOp, LeftOp)) { FoundTransfer = true; // Found UseOp. } else if (this->HasRightSubTree()) { // recurse FoundTransfer = this->GetRightTree()->OperandTransfersHelper(UseOp); } else { op_t RightOp = this->GetRightOperand(); if (IsEqOpIgnoreBitwidth(UseOp, RightOp)) { // Found UseOp. FoundTransfer = true; } } return FoundTransfer; } // end of SMPRegTransfer::OperandTransfersHelper() // Update the memory source operands to have the new type from profiling info. void SMPInstr::UpdateMemLoadTypes(SMPOperandType newType) { bool MemSrc = false; op_t Opnd; set<DefOrUse, LessDefUse>::iterator UseIter; for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) { Opnd = UseIter->GetOp(); optype_t CurrType = Opnd.type; MemSrc = ((CurrType == o_mem) || (CurrType == o_phrase) || (CurrType == o_displ)); if (MemSrc) { SMPOperandType type = UseIter->GetType(); assert(newType == (NUMERIC|PROF_BASE)); if (type == UNINIT) { this->SetUseType(Opnd, newType); break; } else if (type >= POINTER) { this->SetUseType(Opnd, (SMPOperandType)(UNKNOWN|PROF_BASE)); break; } } } return ; } // end of SMPInstr::UpdateMemLoadTypes() // Return true if we have register DefOp += ImmOp. bool SMPInstr::MDIsAddImmediateToReg(op_t &DefOp, op_t &ImmOp) { bool FoundAddImmed = false; bool FoundImmed = false; bool FoundRegUse = false; if (NN_add == this->SMPcmd.itype) { set<DefOrUse, LessDefUse>::iterator UseIter = this->GetFirstUse(); while (UseIter != this->GetLastUse()) { op_t UseOp = UseIter->GetOp(); if (o_imm == UseOp.type) { ImmOp = UseOp; FoundImmed = true; } else if (o_reg == UseOp.type) { set<DefOrUse, LessDefUse>::iterator DefIter = this->GetFirstNonFlagsDef(); op_t TempDefOp = DefIter->GetOp(); if (o_reg != TempDefOp.type) { return false; } if (MDLessReg(UseOp.reg, TempDefOp.reg) || MDLessReg(TempDefOp.reg, UseOp.reg)) { return false; } // If we make it here, we have the same register DEFed as we found USEd. DefOp = TempDefOp; FoundRegUse = true; } ++UseIter; } FoundAddImmed = (FoundImmed && FoundRegUse); } return FoundAddImmed; } // end of SMPInstr::MDIsAddImmediateToReg()