/* * SMPFunction.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, 2012, 2013 by Zephyr Software LLC * e-mail: {clc,jwd}@zephyr-software.com * URL : http://www.zephyr-software.com/ * */ // // SMPFunction.cpp // // This module performs the fundamental data flow analyses needed for the // SMP project (Software Memory Protection) at the function level. // using namespace std; #include <utility> #include <list> #include <set> #include <vector> #include <algorithm> #include <cstring> #include <cstdlib> #include <pro.h> #include <assert.h> #include <ida.hpp> #include <ua.hpp> #include <idp.hpp> #include <auto.hpp> #include <bytes.hpp> #include <funcs.hpp> #include <intel.hpp> #include <name.hpp> #include <struct.hpp> #include "SMPDBInterface.h" #include "SMPDataFlowAnalysis.h" #include "SMPStaticAnalyzer.h" #include "SMPFunction.h" #include "SMPBasicBlock.h" #include "SMPInstr.h" #include "SMPProgram.h" // Set to 1 for debugging output #define SMP_DEBUG 1 #define SMP_DEBUG2 0 // verbose #define SMP_DEBUG3 0 // verbose #define SMP_DEBUG_CONTROLFLOW 0 // tells what processing stage is entered #define SMP_DEBUG_XOR 0 #define SMP_DEBUG_CHUNKS 1 // tracking down tail chunks for functions #define SMP_DEBUG_FRAMEFIXUP 1 #define SMP_DEBUG_FRAMEFIXUP_VERBOSE 0 #define SMP_DEBUG_DATAFLOW 0 #define SMP_DEBUG_DATAFLOW_VERBOSE 0 #define SMP_DEBUG_TYPE_INFERENCE 0 #define SMP_DEBUG_PROFILED_TYPE_INFERENCE 0 #define SMP_DEBUG_STACK_GRANULARITY 0 #define SMP_DEBUG_FUNC 0 #define SMP_DEBUG_FUNC_SAFETY 1 #define SMP_VERBOSE_DEBUG_FUNC 0 #define SMP_DEBUG_BUILD_RTL 1 // leave this on; serious errors reported #define SMP_DEBUG_UNINITIALIZED_SSA_NAMES 1 #define SMP_WARN_UNUSED_DEFS 0 #define SMP_DEBUG_SWITCH_TABLE_INFO 0 #define SMP_OPTIMIZE_BLOCK_PROFILING 0 #define SMP_DECLARE_INDIRECT_TARGETS_UNSAFE 1 #define SMP_AUDIT_STACK_POINTER_DELTAS 0 #define SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS 1 #define STARS_AGGRESSIVE_SIGNEDNESS_PROPAGATION 1 #define STARS_BUILD_LOOP_BITSET 1 // Build bitset in this->FuncLoopsByBlock #define STARS_DEBUG_MEMORY_CORRUPTION 0 // Compute LVA/SSA or not? Turn it off for NICECAP demo on 31-JAN-2008 #define SMP_COMPUTE_LVA_SSA 1 // Compute fine-grained stack boundaries? #define SMP_COMPUTE_STACK_GRANULARITY 1 // Use conditional type propagation on phi functions #define SMP_CONDITIONAL_TYPE_PROPAGATION 1 // Kludges to fix IDA Pro 5.2 errors in cc1.ncexe #define SMP_IDAPRO52_WORKAROUND 0 // Basic block number 0 is the top of the CFG lattice. #define SMP_TOP_BLOCK 0 // Set SharedTailChunks to TRUE for entire printf family // After we restructure the parent/tail structure of the database, this // will go away. #define KLUDGE_VFPRINTF_FAMILY 1 // Used for binary search by function number in SMPStaticAnalyzer.cpp // to trigger debugging output and find which instruction in which // function is causing a crash. bool SMPBinaryDebug = false; using namespace std; // helper function to determine if an object is in a vector template <class T> bool vector_exists(const T &item, const vector<T> &vec) { for (size_t i = 0; i < vec.size(); ++i) { if (vec[i] == item) return true; } return false; } // Comparison function for sorting. bool LocalVarCompare(const LocalVar &LV1, const LocalVar &LV2) { return (LV1.offset < LV2.offset); } // ***************************************************************** // Class SMPFunction // ***************************************************************** // Constructor SMPFunction::SMPFunction(func_t *Info, SMPProgram* pgm) { this->Program = pgm; this->FuncInfo = *Info; this->FirstEA = this->FuncInfo.startEA; #if 0 this->FuncName[0] = '\0'; #endif this->BlockCount = 0; this->LoopCount = 0; this->FuncProcessed = false; this->UseFP = false; this->StaticFunc = false; this->LibFunc = false; this->HasReturnInst = false; this->IndirectCalls = false; this->UnresolvedIndirectCalls = false; this->IndirectJumps = false; this->UnresolvedIndirectJumps = false; this->DirectlyRecursive = false; this->SharedChunks = false; this->UnsharedChunks = false; this->CallsAlloca = false; this->PushAfterLocalVarAlloc = false; this->AnalyzedSP = false; this->STARSStackPtrAnalysisPerformed = false; this->StackAdjustmentComputed = false; this->BuiltRTLs = false; #if 1 // default to unsafe this->SafeFunc = false; this->SpecSafeFunc = false; this->SafeCallee = false; this->SpecSafeCallee = false; #else // default to safe this->SafeFunc = true; this->SpecSafeFunc = true; this->SafeCallee = true; this->SpecSafeCallee = true; #endif this->WritesAboveRA = false; this->NeedsStackReferent = true; this->SpecNeedsStackReferent = true; this->HasIndirectWrites = false; this->PossibleIndirectCallTarget = false; this->PossibleTailCallTarget = false; this->OutgoingArgsComputed = false; this->GoodLocalVarTable = false; this->OutgoingArgsSize = 0; this->TypedDefs = 0; this->UntypedDefs = 0; this->TypedPhiDefs = 0; this->UntypedPhiDefs = 0; this->SafeBlocks = 0; this->UnsafeBlocks = 0; this->Size = 0; this->LocalVarsSize = 0; this->CalleeSavedRegsSize = 0; this->RetAddrSize = 0; this->IncomingArgsSize = 0; this->OutgoingArgsSize = 0; this->LocalVarsAllocInstr = BADADDR; this->LocalVarsDeallocInstr = BADADDR; this->AllocPointDelta = 0; this->MinStackDelta = 0; this->MaxStackDelta = 0; this->MinStackAccessOffset = 0; this->MaxStackAccessLimit = 0; this->NetStackDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA; this->PreAllocStackDelta = CALLING_CONVENTION_DEFAULT_PREFRAMEALLOC_STACK_DELTA; this->FramePointerStackDelta = 0; this->GlobalStackAdjustment = 0; this->LocalVarOffsetLimit = 0; this->IDAReturnAddressOffset = 0; this->ReturnAddrStatus = FUNC_UNKNOWN; this->SetIsSpeculative(false); this->Instrs.clear(); this->Blocks.clear(); this->DirectCallTargets.clear(); this->IndirectCallTargets.clear(); this->AllCallTargets.clear(); this->AllCallSources.clear(); this->InstBlockMap.clear(); this->RPOBlocks.clear(); this->IDom.clear(); this->DomTree.clear(); this->GlobalNames.clear(); this->BlocksDefinedIn.clear(); this->SSACounter.clear(); this->SSAStack.clear(); this->LocalVarTable.clear(); this->StackFrameMap.clear(); this->FineGrainedStackTable.clear(); this->SavedRegLoc.clear(); this->ReturnRegTypes.clear(); this->LiveInSet.clear(); this->LiveOutSet.clear(); this->KillSet.clear(); this->GlobalDefAddrBySSA.clear(); for (int RegIndex = R_ax; RegIndex <= R_di; ++RegIndex) { this->SavedRegLoc.push_back(0); // zero offset means reg not saved this->ReturnRegTypes.push_back(UNINIT); } // The sizes of the three regions of the stack frame other than the // return address are stored in the function structure. this->LocalVarsSize = this->FuncInfo.frsize; this->CalleeSavedRegsSize = this->FuncInfo.frregs; this->IncomingArgsSize = this->FuncInfo.argsize; // The return address size can be obtained in a machine independent // way by calling get_frame_retsize(). this->RetAddrSize = get_frame_retsize(this->GetFuncInfo()); return; } // end of SMPFunction() constructor SMPFunction::~SMPFunction() { list<SMPInstr *>::iterator InstIter; for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); if (NULL != CurrInst) delete CurrInst; } list<SMPBasicBlock *>::iterator BlockIter; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { SMPBasicBlock *CurrBlock = (*BlockIter); if (NULL != CurrBlock) delete CurrBlock; } } // Get a non-stale pointer to the func_t info for the current function. func_t *SMPFunction::GetFuncInfo(void) { func_t *myPtr = SMP_get_func(this->FirstEA); assert(NULL != myPtr); return myPtr; } // Reset the Processed flags in all blocks to false. void SMPFunction::ResetProcessedBlocks(void) { list<SMPBasicBlock *>::iterator CurrBlock; for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { (*CurrBlock)->SetProcessed(false); } return; } // end of SMPFunction::ResetProcessedBlocks() // Set SCCPVisited flag to false in all blocks void SMPFunction::ResetSCCPVisitedBlocks(void) { list<SMPBasicBlock *>::iterator CurrBlock; for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { (*CurrBlock)->SetSCCPVisited(false); } return; } // end of SMPFunction::ResetSCCPVisitedBlocks() // Return an iterator for the beginning of the LiveInSet. set<op_t, LessOp>::iterator SMPFunction::GetFirstLiveIn(void) { return this->LiveInSet.begin(); } // end of SMPBasicBlock::GetFirstLiveIn() // Get termination iterator marker for the LiveIn set, for use by predecessors. set<op_t, LessOp>::iterator SMPFunction::GetLastLiveIn(void) { return this->LiveInSet.end(); } // Get iterator for the start of the LiveOut set. set<op_t, LessOp>::iterator SMPFunction::GetFirstLiveOut(void) { return this->LiveOutSet.begin(); } // Get termination iterator marker for the LiveOut set. set<op_t, LessOp>::iterator SMPFunction::GetLastLiveOut(void) { return this->LiveOutSet.end(); } // Get iterator for the start of the VarKill set. set<op_t, LessOp>::iterator SMPFunction::GetFirstVarKill(void) { return this->KillSet.begin(); } // Get termination iterator marker for the VarKill set. set<op_t, LessOp>::iterator SMPFunction::GetLastVarKill(void) { return this->KillSet.end(); } // Four methods to get values from the maps of global reg/SSA to FG info. // For local names, see corresponding methods in SMPBasicBlock. unsigned short SMPFunction::GetDefSignMiscInfo(int DefHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter != this->GlobalDefFGInfoBySSA.end()) return MapIter->second.SignMiscInfo; else return 0; } // end of SMPFunction::GetDefSignMiscInfo() unsigned short SMPFunction::GetStackDefSignMiscInfo(ea_t InstAddr) { map<ea_t, struct FineGrainedInfo>::iterator MapIter; MapIter = this->StackDefFGInfo.find(InstAddr); assert(MapIter != this->StackDefFGInfo.end()); return MapIter->second.SignMiscInfo; } unsigned short SMPFunction::GetUseSignMiscInfo(int UseHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalUseFGInfoBySSA.find(UseHashValue); if (MapIter != this->GlobalUseFGInfoBySSA.end()) return MapIter->second.SignMiscInfo; else return 0; } // end of SMPFunction::GetUseSignMiscInfo() unsigned short SMPFunction::GetStackUseSignMiscInfo(ea_t InstAddr) { map<ea_t, struct FineGrainedInfo>::iterator MapIter; MapIter = this->StackUseFGInfo.find(InstAddr); assert(MapIter != this->StackUseFGInfo.end()); return MapIter->second.SignMiscInfo; } unsigned short SMPFunction::GetDefWidthTypeInfo(int DefHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter != this->GlobalDefFGInfoBySSA.end()) return MapIter->second.SizeInfo; else return 0; } // end of SMPFunction::GetDefWidthTypeInfo() unsigned short SMPFunction::GetUseWidthTypeInfo(int UseHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalUseFGInfoBySSA.find(UseHashValue); if (MapIter != this->GlobalUseFGInfoBySSA.end()) return MapIter->second.SizeInfo; else return 0; } // end of SMPFunction::GetUseWidthTypeInfo() struct FineGrainedInfo SMPFunction::GetDefFGInfo(int DefHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter != this->GlobalDefFGInfoBySSA.end()) return MapIter->second; else { struct FineGrainedInfo EmptyFG; EmptyFG.SignMiscInfo = 0; EmptyFG.SizeInfo = 0; return EmptyFG; } } // end of SMPFunction::GetDefFGInfo() struct FineGrainedInfo SMPFunction::GetUseFGInfo(int UseHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalUseFGInfoBySSA.find(UseHashValue); if (MapIter != this->GlobalUseFGInfoBySSA.end()) return MapIter->second; else { struct FineGrainedInfo EmptyFG; EmptyFG.SignMiscInfo = 0; EmptyFG.SizeInfo = 0; return EmptyFG; } } // end of SMPFunction::GetUseFGInfo() // Add a caller to the list of all callers of this function. void SMPFunction::AddCallSource(ea_t addr) { // Convert call instruction address to beginning address of the caller. func_t *FuncInfo = SMP_get_func(addr); if (NULL == FuncInfo) { SMP_msg("SERIOUS WARNING: Call location %lx not in a function.\n", (unsigned long) addr); return; } ea_t FirstAddr = FuncInfo->startEA; assert(BADADDR != FirstAddr); this->AllCallSources.insert(FirstAddr); this->AllCallSites.insert(addr); return; } // end of SMPFunction::AddCallSource() // add map entry to LeaInstOpMap void SMPFunction::AddLeaOperand(ea_t addr, op_t LeaOperand) { pair<ea_t, op_t> InsertValue(addr, LeaOperand); pair<map<ea_t, op_t>::iterator, bool> InsertResult; InsertResult = this->LeaInstOpMap.insert(InsertValue); if (!(InsertResult.second)) { // already existed; replace map<ea_t, op_t>::iterator FindIter = this->LeaInstOpMap.find(addr); assert(FindIter != this->LeaInstOpMap.end()); FindIter->second = LeaOperand; } return; } // Add input arguments to the NormalizedStackOpsMap. void SMPFunction::AddNormalizedStackOperand(op_t OldOp, ea_t InstAddr, op_t NormalizedOp) { bool DuplicateCase = false; // e.g. inc [esp+8] will have [esp+8] as a DEF and a USE and maps will see [esp+8] twice bool DebugFlag = (InstAddr == 0xb79b); pair<map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator, bool> InsertResult; pair<map<pair<op_t, ea_t>, map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator, LessDefinition>::iterator, bool> InverseInsertResult; pair<op_t, ea_t> OldValue(OldOp, InstAddr); pair<op_t, ea_t> InverseValue(OldOp, InstAddr); // OldOp was NormalizedOp when it was inserted previously pair<pair<op_t, ea_t>, op_t> InsertValue(OldValue, NormalizedOp); pair<op_t, ea_t> InverseInsertValue(NormalizedOp, InstAddr); map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator OldIter = this->NormalizedStackOpsMap.begin(); pair<pair<op_t, ea_t>, map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator> InverseInsertTriple(InverseInsertValue, OldIter); map<pair<op_t, ea_t>, map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator>::iterator InverseIter; // If this function calls alloca(), stack operands could be normalized more than once. // Before we proceed, we update an old entry instead of inserting a new entry. if (this->CallsAlloca || this->HasPushAfterFrameAlloc()) { InverseIter = this->InverseNormalizedStackOpsMap.find(InverseValue); if (InverseIter != this->InverseNormalizedStackOpsMap.end()) { // We have our alloca() update case. We formerly mapped <A, InstAddr> to B. // Now B is being normalized to C. All we want to do is change the original // map entry so that we map <A, InstAddr> to C. In this manner, A is always the // original un-normalized stack op, available for lookup from an RTL. OldIter = InverseIter->second; // OldIter points at map of <A, InstAddr> to B. OldIter->second = NormalizedOp; // Change B to C // Now we want to erase the Inverse map entry and insert a new one that maps // <C, InstAddr> to OldIter instead of mapping <B, InstAddr> to OldIter. (void) this->InverseNormalizedStackOpsMap.erase(InverseIter); InverseInsertTriple.second = OldIter; InverseInsertResult = this->InverseNormalizedStackOpsMap.insert(InverseInsertTriple); assert(InverseInsertResult.second); return; } else { // We might have the final difficult case: We have a combination of CallsAlloca and the // DuplicateCase described below (e.g. an increment of a stack location produces a DEF // and a USE of the same location, causing duplicate mappings to be attempted). We need // to detect the duplicate case here. What will happen is that, on the first call to this // method, we will map <A, InstAddr> to B, and reverse-map <B, InstAddr> to A. On the second // call to this method, we will detect the duplicate case and exit. On the third call, caused // by CallsAlloca, we are asked to map <B, InstAddr> to C, and we will correctly hit the code // just above, in the if-clause, to fix the A->B mapping to be an A->C mapping, and we will // erase the reverse mapping of B->A and replace it with the C->A reverse mapping. On the // fourth call to this method, we will not find a reverse mapping B->A any more, so the if-clause // does not execute. We can only detect this case by finding an existing C->A reverse mapping // and an existing A->C mapping to confirm our inference. pair<op_t, ea_t> TestInverseValue(NormalizedOp, InstAddr); InverseIter = this->InverseNormalizedStackOpsMap.find(TestInverseValue); if (InverseIter != this->InverseNormalizedStackOpsMap.end()) { // Found existing C->A inverse mapping. Is there an A->C mapping to confirm // our interpretation of the situation? pair<op_t, ea_t> TestOldValue(InverseIter->second->first.first, InstAddr); map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator TestOldIter; TestOldIter = this->NormalizedStackOpsMap.find(TestOldValue); if (TestOldIter != this->NormalizedStackOpsMap.end()) { // We found a mapping from <A, InstAddr>. if (IsEqOp(NormalizedOp, TestOldIter->second)) { // The mapping is A->C as suspected. return; // duplication; nothing to do in either map. } } } } } // At this point, we have no inverse map entry to worry about, because we are // normalizing this operand for the first time. InsertResult = this->NormalizedStackOpsMap.insert(InsertValue); OldIter = InsertResult.first; if (!(InsertResult.second)) { // Already had an entry. That should mean a rare case such as "inc [esp+8]" which // produces a USE and a DEF of the same address. We can confirm that the map has // the same normalized operand we were trying to insert. Otherwise, the collision // is fatal. op_t OldOldOp = InsertResult.first->first.first; op_t OldNormalizedOp = InsertResult.first->second; assert(IsEqOp(OldOldOp, OldOp) && IsEqOp(OldNormalizedOp, NormalizedOp)); DuplicateCase = true; } if (this->CallsAlloca || this->HasPushAfterFrameAlloc()) { // We need to add an entry to the inverse map. InverseInsertTriple.second = OldIter; InverseInsertResult = this->InverseNormalizedStackOpsMap.insert(InverseInsertTriple); assert(InverseInsertResult.second || DuplicateCase); } if (DebugFlag) { map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator StackMapIter; SMP_msg("DEBUG: NormalizedStackOpsMap size: %zd\n", this->NormalizedStackOpsMap.size()); for (StackMapIter = this->NormalizedStackOpsMap.begin(); StackMapIter != this->NormalizedStackOpsMap.end(); ++ StackMapIter) { op_t OldOp = StackMapIter->first.first; ea_t InstAddr = StackMapIter->first.second; SMP_msg("DEBUG: NormalizedStackOps: "); PrintOperand(OldOp); SMP_msg(" addr: %lx\n", (unsigned long) InstAddr); } } return; } // SMPFunction::AddNormalizedStackOperand() // Insert SCCP value for global name; change old entry if already found. map<int, struct STARS_SCCP_Const_Struct>::iterator SMPFunction::InsertGlobalConstValue(int DefHashValue, struct STARS_SCCP_Const_Struct NewConstEntry) { map<int, struct STARS_SCCP_Const_Struct>::iterator MapIter = this->FindConstValue(DefHashValue); if (MapIter == this->GetLastConstValueIter()) { // no old entry; insert pair<int, struct STARS_SCCP_Const_Struct> InsertPair(DefHashValue, NewConstEntry); pair<map<int, struct STARS_SCCP_Const_Struct>::iterator, bool> InsertResult = this->ConstantDefs.insert(InsertPair); assert(InsertResult.second); MapIter = InsertResult.first; } else { // old entry found; update MapIter->second = NewConstEntry; } return MapIter; } // end of SMPFunction::InsertGlobalConstValue() // Return RTLop if not stack opnd; return normalized RTLop otherwise. op_t SMPFunction::GetNormalizedOperand(ea_t InstAddr, op_t RTLop) { op_t NormOp; bool DebugFlag = (0xb79b == InstAddr); if (DebugFlag) { map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator StackMapIter; SMP_msg("DEBUG: NormalizedStackOpsMap size: %zd\n", this->NormalizedStackOpsMap.size()); for (StackMapIter = this->NormalizedStackOpsMap.begin(); StackMapIter != this->NormalizedStackOpsMap.end(); ++ StackMapIter) { op_t OldOp = StackMapIter->first.first; ea_t InstAddr = StackMapIter->first.second; SMP_msg("DEBUG: NormalizedStackOps: "); PrintOperand(OldOp); SMP_msg(" addr: %lx\n", (unsigned long) InstAddr); } } if (MDIsStackAccessOpnd(RTLop, this->UsesFramePointer())) { pair<op_t, ea_t> OldDefn(RTLop, InstAddr); map<pair<op_t, ea_t>, op_t, LessDefinition>::iterator FindIter = this->NormalizedStackOpsMap.find(OldDefn); assert(this->NormalizedStackOpsMap.end() != FindIter); NormOp = FindIter->second; } else { NormOp = RTLop; } return NormOp; } // end of SMPFunction::GetNormalizedOperand() // Eight methods to set values into the maps of global reg/stack/SSA to FG info. // For local names, see corresponding methods in SMPBasicBlock. void SMPFunction::UpdateDefSignMiscInfo(int DefHashValue, unsigned short NewInfo) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter == this->GlobalDefFGInfoBySSA.end()) { // Not found; insert first. struct FineGrainedInfo NewFGInfo; NewFGInfo.SignMiscInfo = NewInfo; NewFGInfo.SizeInfo = 0; pair<int, struct FineGrainedInfo> MapItem(DefHashValue, NewFGInfo); MapResult = this->GlobalDefFGInfoBySSA.insert(MapItem); assert(MapResult.second); // Was not previously found, insertion must work. } else { // found; just OR in the new bits. MapIter->second.SignMiscInfo |= NewInfo; } return; } // end of SMPFunction::UpdateDefSignMiscInfo() void SMPFunction::UpdateStackDefSignMiscInfo(ea_t InstAddr, unsigned short NewInfo) { map<ea_t, struct FineGrainedInfo>::iterator MapIter; MapIter = this->StackDefFGInfo.find(InstAddr); assert(MapIter != this->StackDefFGInfo.end()); // found; just OR in the new bits. MapIter->second.SignMiscInfo |= NewInfo; return; } // end of SMPFunction::UpdateStackDefSignMiscInfo() void SMPFunction::UpdateUseSignMiscInfo(int UseHashValue, unsigned short NewInfo) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalUseFGInfoBySSA.find(UseHashValue); if (MapIter == this->GlobalUseFGInfoBySSA.end()) { // Not found; insert first. struct FineGrainedInfo NewFGInfo; NewFGInfo.SignMiscInfo = NewInfo; NewFGInfo.SizeInfo = 0; pair<int, struct FineGrainedInfo> MapItem(UseHashValue, NewFGInfo); MapResult = this->GlobalUseFGInfoBySSA.insert(MapItem); assert(MapResult.second); // Was not previously found, insertion must work. } else { // found; just OR in the new bits. MapIter->second.SignMiscInfo |= NewInfo; } return; } // end of SMPFunction::UpdateUseSignMiscInfo() void SMPFunction::UpdateStackUseSignMiscInfo(ea_t InstAddr, unsigned short NewInfo) { map<ea_t, struct FineGrainedInfo>::iterator MapIter; MapIter = this->StackUseFGInfo.find(InstAddr); assert(MapIter != this->StackUseFGInfo.end()); // found; just OR in the new bits. MapIter->second.SignMiscInfo |= NewInfo; return; } // end of SMPFunction::UpdateStackUseSignMiscInfo() void SMPFunction::UpdateDefWidthTypeInfo(int DefHashValue, unsigned short NewInfo) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter == this->GlobalDefFGInfoBySSA.end()) { // Not found; insert first. struct FineGrainedInfo NewFGInfo; NewFGInfo.SignMiscInfo = 0; NewFGInfo.SizeInfo = NewInfo; pair<int, struct FineGrainedInfo> MapItem(DefHashValue, NewFGInfo); MapResult = this->GlobalDefFGInfoBySSA.insert(MapItem); assert(MapResult.second); // Was not previously found, insertion must work. } else { // found; just OR in the new bits. MapIter->second.SizeInfo |= NewInfo; } return; } // end of SMPFunction::UpdateDefWidthTypeInfo() void SMPFunction::UpdateUseWidthTypeInfo(int UseHashValue, unsigned short NewInfo) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalUseFGInfoBySSA.find(UseHashValue); if (MapIter == this->GlobalUseFGInfoBySSA.end()) { // Not found; insert first. struct FineGrainedInfo NewFGInfo; NewFGInfo.SignMiscInfo = 0; NewFGInfo.SizeInfo = NewInfo; pair<int, struct FineGrainedInfo> MapItem(UseHashValue, NewFGInfo); MapResult = this->GlobalUseFGInfoBySSA.insert(MapItem); assert(MapResult.second); // Was not previously found, insertion must work. } else { // found; just OR in the new bits. MapIter->second.SizeInfo |= NewInfo; } return; } // end of SMPFunction::UpdateUseWidthTypeInfo() void SMPFunction::UpdateDefFGInfo(int DefHashValue, struct FineGrainedInfo NewFG) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter == this->GlobalDefFGInfoBySSA.end()) { // Not found; insert it. pair<int, struct FineGrainedInfo> MapItem(DefHashValue, NewFG); MapResult = this->GlobalDefFGInfoBySSA.insert(MapItem); assert(MapResult.second); // Was not previously found, insertion must work. } else { // found; just put in the new bits. MapIter->second.SignMiscInfo |= NewFG.SignMiscInfo; MapIter->second.SizeInfo |= NewFG.SizeInfo; } return; } // end of SMPFunction::UpdateDefFGInfo() void SMPFunction::UpdateUseFGInfo(int UseHashValue, struct FineGrainedInfo NewFG) { map<int, struct FineGrainedInfo>::iterator MapIter; pair<map<int, struct FineGrainedInfo>::iterator, bool> MapResult; MapIter = this->GlobalUseFGInfoBySSA.find(UseHashValue); if (MapIter == this->GlobalUseFGInfoBySSA.end()) { // Not found; insert it. pair<int, struct FineGrainedInfo> MapItem(UseHashValue, NewFG); MapResult = this->GlobalUseFGInfoBySSA.insert(MapItem); assert(MapResult.second); // Was not previously found, insertion must work. } else { // found; just put in the new bits. MapIter->second.SignMiscInfo |= NewFG.SignMiscInfo; MapIter->second.SizeInfo |= NewFG.SizeInfo; } return; } // end of SMPFunction::UpdateUseFGInfo() // Reset the signedness bits to zero for DEF. void SMPFunction::ClearDefSignedness(int DefHashValue) { map<int, struct FineGrainedInfo>::iterator MapIter; MapIter = this->GlobalDefFGInfoBySSA.find(DefHashValue); if (MapIter != this->GlobalDefFGInfoBySSA.end()) { MapIter->second.SignMiscInfo &= (~FG_MASK_SIGNEDNESS_BITS); } return; } // end of SMPFunction::ClearDefSignedness() // Erase a range of instructions from the Instrs list, usually corresponding // the the range of a basic block. void SMPFunction::EraseInstRange(ea_t FirstAddr, ea_t LastAddr) { list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); SMPInstr *CurrInst; ea_t InstAddr; while (InstIter != this->Instrs.end()) { CurrInst = (*InstIter); InstAddr = CurrInst->GetAddr(); if ((InstAddr >= FirstAddr) && (InstAddr <= LastAddr)) { InstIter = this->Instrs.erase(InstIter); } else { ++InstIter; } } } // end of SMPFunction::EraseInstRange() // For instruction address UseAddr, compute the reaching defs for operand TempOp, // placing them into the TempReachingDefs list. void SMPFunction::ComputeTempReachingDefs(op_t TempOp, ea_t UseAddr) { this->TempReachingDefs.clear(); SMPBasicBlock *CurrBlock = this->GetBlockFromInstAddr(UseAddr); assert(NULL != CurrBlock); set<pair<op_t, ea_t>, LessDefinition>::iterator ReachesInIter; pair<set<ea_t, LessAddr>::iterator, bool> InsertResult; // Start with the matching members of the ReachesIn set for the current basic block. for (ReachesInIter = CurrBlock->GetFirstReachesIn(); ReachesInIter != CurrBlock->GetLastReachesIn(); ++ReachesInIter) { pair<op_t, ea_t> ReachesInDef = *ReachesInIter; if (IsEqOp(TempOp, ReachesInDef.first)) { InsertResult = this->TempReachingDefs.insert(ReachesInDef.second); assert(InsertResult.second); } } // Now, see if any def in the block hides the ReachesIn defs before we get to UseAddr. vector<SMPInstr *>::iterator InstIter; for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { SMPInstr *CurrInst = *InstIter; ea_t InstAddr = CurrInst->GetAddr(); if (InstAddr >= UseAddr) break; set<DefOrUse, LessDefUse>::iterator DefIter = CurrInst->FindDef(TempOp); if (DefIter != CurrInst->GetLastDef()) { // Found a def. All previous defs are hidden from UseAddr by this def. this->TempReachingDefs.clear(); InsertResult = this->TempReachingDefs.insert(InstAddr); assert(InsertResult.second); } } return; } // end of SMPFunction::ComputeTempReachingDefs() // Find all the saved stack deltas (if any) for the def addrs in the TempReachesDefs list for TempOp. // Put the entries matching TempOp into TempStackDeltaReachesList. void SMPFunction::ComputeTempStackDeltaReachesList(op_t TempOp) { bool FoundOperand = false; set<pair<op_t, pair<ea_t, sval_t> >, LessStackDeltaCopy>::iterator CopyIter; this->TempStackDeltaReachesList.clear(); for (CopyIter = this->StackPtrCopySet.begin(); CopyIter != this->StackPtrCopySet.end(); ++CopyIter) { pair<op_t, pair<ea_t, sval_t> > CopyEntry = *CopyIter; if (IsEqOp(TempOp, CopyEntry.first)) { set<ea_t, LessAddr>::iterator FindReachDefIter; FoundOperand = true; // help us save time later by exiting loop // Match address at which stack ptr copy was made to a reaching def address for TempOp. FindReachDefIter = this->TempReachingDefs.find(CopyEntry.second.first); if (FindReachDefIter != this->TempReachingDefs.end()) { // Found a StackPtrCopySet entry for TempOp, AND we found the DefAddr // in the TempReachingDefs set. this->TempStackDeltaReachesList.push_back(CopyEntry.second); // push back a pair<ea_t, sval_t> } } else if (FoundOperand) { // We have found the operand, but have now moved past it in the iteration of StackPtrCopySet. // Save time by exiting the loop. break; } } return; } // end of SMPFunction::ComputeTempStackDeltaReachesList() // Find the largest stack delta in the TempStackDeltaReachesList. // Return true if only one value was found in the list. bool SMPFunction::FindReachingStackDelta(sval_t &StackDelta) { bool UniqueDelta = true; if (this->TempStackDeltaReachesList.empty()) { StackDelta = 0; return false; } else { StackDelta = this->TempStackDeltaReachesList.front().second; } list<pair<ea_t, sval_t> >::iterator DeltaIter; for (DeltaIter = this->TempStackDeltaReachesList.begin(); DeltaIter != this->TempStackDeltaReachesList.end(); ++DeltaIter) { sval_t NewDelta = DeltaIter->second; if (NewDelta != StackDelta) { UniqueDelta = false; if (NewDelta > StackDelta) { StackDelta = NewDelta; } } } return UniqueDelta; } // end of SMPFunction::FindReachingStackDelta() // Find any apparent stack adjustment after the call instruction at CallAddr, // confining our search to the basic block containing CallAddr. sval_t SMPFunction::GetStackAdjustmentForCallee(ea_t CallAddr) { sval_t CalleeAdjustment = 0; SMPBasicBlock *CallBlock = this->GetBlockFromInstAddr(CallAddr); assert(NULL != CallBlock); sval_t BlockAnalysisDelta = CallBlock->ComputeStackAdjustmentAfterCall(CallAddr); if (0 != BlockAnalysisDelta) { CalleeAdjustment = BlockAnalysisDelta; SMP_msg("INFO: Block analysis produced callee adjustment of %ld bytes after %lx\n", (long) CalleeAdjustment, (unsigned long) CallAddr); } return CalleeAdjustment; } // end of SMPFunction::GetStackAdjustmentForCallee() // Get stack delta from a callee function that is unable to provide the info from // its own analyses (e.g. analyses failed or have not been performed yet, due to // a mutually recursive clique in the call graph). We have three approaches in // this case: Use a default value, consult IDA Pro's analyses, or see if we can // detect a stack adjustment after the call instruction, from which we could infer // the stack delta of the callee. We choose the latter approach, and find the smallest // adjustment among all call sites for the callee. sval_t SMPFunction::GetStackDeltaForCallee(ea_t CallTargetAddr) { sval_t CalleeDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA; SMPFunction *CalleeFunc = this->GetProg()->FindFunction(CallTargetAddr); if (NULL != CalleeFunc) { sval_t GlobalAdjustment = CalleeFunc->ComputeGlobalStackAdjustment(); if (0 != GlobalAdjustment) { CalleeDelta -= GlobalAdjustment; SMP_msg("INFO: Global stack adjustment analysis produced callee delta of %ld bytes after %lx\n", (long) CalleeDelta, (unsigned long) CallTargetAddr); } } return CalleeDelta; } // end of SMPFunction::GetStackDeltaForCallee() // Compute a consistent (or smallest) stack adjustment seen program-wide after all calls to the current function. // Do not return a non-zero value unless more than one call site can be used as evidence. sval_t SMPFunction::ComputeGlobalStackAdjustment(void) { bool FoundZeroAdjustment = false; sval_t GlobalAdjustment = 0; sval_t NegativeAdjustment = -10000; // record negative adjustments detected sval_t PositiveAdjustment = 10000; // record positive adjustments detected size_t NumCallSites = this->AllCallSites.size(); // Use cached value if already computed. if (this->StackAdjustmentComputed) { return this->GlobalStackAdjustment; } if (1 < NumCallSites) { // if only one call site, it is dangerous to draw conclusions about seeming "adjustments." set<ea_t>::iterator CallSiteIter; for (CallSiteIter = this->AllCallSites.begin(); CallSiteIter != this->AllCallSites.end(); ++CallSiteIter) { ea_t CallSiteAddr = (*CallSiteIter); func_t *CurrFunc = SMP_get_func(CallSiteAddr); assert(NULL != CurrFunc); ea_t CallerFirstAddr = CurrFunc->startEA; SMPFunction *CallerFunc = this->GetProg()->FindFunction(CallerFirstAddr); assert(NULL != CallerFunc); sval_t CurrentAdjustment = CallerFunc->GetStackAdjustmentForCallee(CallSiteAddr); // See if CurrentAdjustment is a new, lowest positive value for GlobalAdjustment. if ((0 < CurrentAdjustment) && (CurrentAdjustment < PositiveAdjustment)) { PositiveAdjustment = CurrentAdjustment; } else if ((0 > CurrentAdjustment) && (CurrentAdjustment > NegativeAdjustment)) { NegativeAdjustment = CurrentAdjustment; } else if (0 == CurrentAdjustment) { FoundZeroAdjustment = true; break; // Any zero adjustment found invalidates non-zero inferences } } } // See if we consistently had positive or negative adjustments if (FoundZeroAdjustment) { GlobalAdjustment = 0; // cannot be a clear non-zero indication if we found any zeroes } else if (PositiveAdjustment < 10000) { // found at least one positive adjustment if (NegativeAdjustment > -10000) { // found at least one negative adjustment; bad GlobalAdjustment = 0; // inconsistent; reset to zero } else { GlobalAdjustment = PositiveAdjustment; } } else if (NegativeAdjustment > -10000) { // found negative but no positive adjustments GlobalAdjustment = NegativeAdjustment; } else { // did not find negative or positive adjustments GlobalAdjustment = 0; } this->StackAdjustmentComputed = true; // signal caching of the value for future speed this->GlobalStackAdjustment = GlobalAdjustment; // cache the value return GlobalAdjustment; } // end of SMPFunction::ComputeGlobalStackAdjustment() // Use IDA Pro stack pointer deltas instead of doing our own analysis. bool SMPFunction::UseIDAStackPointerDeltas(void) { list<SMPInstr *>::iterator InstIter; SMPInstr *CurrInst; #if SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS bool IDATraceFlag = (0 == strcmp("do_length", this->GetFuncName())); #endif InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker pseudo-instruction #endif while (InstIter != this->Instrs.end()) { CurrInst = *InstIter; sval_t IDAProDelta = get_spd(this->GetFuncInfo(), CurrInst->GetAddr()); CurrInst->SetStackPtrOffset(IDAProDelta); ++InstIter; if (IDATraceFlag) { SMP_msg("INFO: IDA Pro stack delta trace: %ld at %lx\n", (long) IDAProDelta, (unsigned long) CurrInst->GetAddr()); } } return true; } // end of SMPFunction::UseIDAStackPointerDeltas() // Analyze changes to the stack pointer over all instructions. bool SMPFunction::AnalyzeStackPointerDeltas(void) { list<pair<SMPBasicBlock *, sval_t> > WorkList; vector<SMPInstr *>::iterator InstIter; SMPInstr *CurrInst; sval_t CurrentDelta = 0; sval_t DeltaIncrement = 0; // change when reprocessing a block in alloca()-calling function bool ConsistentNetDelta = true; // Net change to stack pointer is consistent at all RETURN locations bool ConflictingValuesSeen = false; // At least one block was entered with multiple deltas bool StackPointerRestoreSeen = false; // Stack pointer restored; must become true if ConflictingValuesSeen bool ReturnSeen = false; bool IDAProSucceeded = this->AnalyzedSP; #if SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS bool DebugFlag = (0 == strcmp("_aesni_set_encrypt_key", this->GetFuncName())); bool TraceFlag = (0 == strcmp("_aesni_set_encrypt_key", this->GetFuncName())); #endif if (!this->HasGoodRTLs()) { SMP_msg("INFO: Using IDA Pro stack pointer deltas for BADRTLS function %s .\n", this->GetFuncName()); (void) this->UseIDAStackPointerDeltas(); this->AnalyzedSP = false; return false; // leave it unsolved } #if 0 // Temporarily pull the functions that call alloca out of the stack pointer delta computations, so // that we can focus on solving other problems. if (this->CallsAlloca || this->HasPushAfterFrameAlloc()) { if (!this->AnalyzedSP) { (void) this->UseIDAStackPointerDeltas(); return false; // leave it unsolved } else { SMP_msg("INFO: Using IDA Pro stack pointer deltas for alloca-calling function %s .\n", this->GetFuncName()); return this->UseIDAStackPointerDeltas(); } } #endif // In order to precisely track stack deltas, we need to deal with instruction sequences that save the stack pointer // and then restore it later. This requires a reaching definitions data flow analysis that includes, at a minimum, // all stack definitions (normalized by stack delta, so that we do not confuse [esp+20] and [esp+20] where the values // of esp are not the same). We also need to keep track of stack pointer saves in both registers and in stack locations. // In order for the information about saved stack pointer copies to be available as soon as we need them in the stack // delta analysis, we have to perform both stack delta analysis and reaching definitions analysis at the same time. Luckily, // both analyses are well suited to being performed as forward analyses starting from the entry basic block. // // Data structures for the reaching definitions analysis include a ReachesIn and a ReachesOut set for each basic block, a // VarKill set for each block, and a DownExposedDefs set for each block. The VarKill set is shared with the later Live Variable // Analysis (LVA), so we compute the VarKill and the UpExposed sets (UpExposed is only used by LVA) on the first pass through // each block. The VarKill and all other LVA sets are sets of operands. The ReachesIn, ReachesOut, and DownExposedDefs sets // are sets of definitions, where a definition is a pair<operand, instruction address>. The StackPtrCopySet is a triple of // <operand, instruction address, stack delta>, arranged as a pair of pairs <operand, <addr, delta> > // // Algorithm: We maintain a WorkList of pairs <basic block pointer, incoming stack delta to that block> // // All sets are empty at the beginning. // Add the entry basic block to the WorkList, with IncomingDelta of zero. // while (WorkList is not empty) do // de-queue first block from WorkList, obtain IncomingDelta // Compute ReachesIn as the union of the ReachesOut of all predecesssor blocks // if (block has not already been processed) then // mark block as processed // for each inst in block (forward iteration) do // for each USE in inst do // if USE operand not in VarKill set for block then // add USE operand to UpExposed set for block // endif // if USE operand is a stack pointer value AND it will produce DEF that is a stack pointer value then // if DEF is stack pointer register then { a stack pointer value that was saved is being restored } // retrieve new stack pointer delta from saved value in StackPtrCopySet, looking it up in // that set using the reaching definitions for the USE operand. If inconsistent ****** // else { stack pointer value is being saved somewhere besides the stack pointer register } // add stack delta to StackPtrCopySet for DEF that is receiving it in current inst // endif // endif // endfor { each USE } // for each DEF in inst do // if register or stack operand then // add to VarKill set // update DownExposedDefs set (insert, or replace current def for this operand) // endif // endfor { each DEF } // Store IncomingDelta for current instruction // Get change in delta for current instruction // Add current change to IncomingDelta // endfor { each inst } // At end of block, make ReachesOut set be (ReachesIn minus VarKill) union DownExposedDefs // For each successor block, add pairs <block pointer, IncomingDelta> to end of WorkList // else { block has already been processed at least once} // if IncomingDelta from WorkList is inconsistent with old IncomingDelta then // if function calls alloca() then // if new IncomingDelta makes stack frame look larger than old IncomingDelta then // ignore new IncomingDelta and just process reaching definitions sets below // else // use new IncomingDelta and re-process deltas in this block to converge to // smallest stack frame, which means we are basically ignoring alloca()'s as much as possible. // endif // else // Set AnalyzedSP to false, emit error message, clear WorkList and bail out of this function. // endif // endif { inconsistent IncomingDelta values } // Recompute ReachesIn as union of ReachesOut of all predecessor blocks // if ReachesIn set changed then // recompute ReachesOut without examining instructions unless alloca() case requires iterating through instructions // endif // if any change in deltas or reaching definitions sets, then add block to end of WorkList along with all successor blocks. // endif // end while // Mark all blocks as unprocessed this->ResetProcessedBlocks(); this->AnalyzedSP = true; // Put the entry block on the work list. assert(0 < this->Blocks.size()); pair<SMPBasicBlock *, sval_t> WorkingPair (this->Blocks.front(), CurrentDelta); WorkList.push_back(WorkingPair); // While blocks exist on the work list // if block already processed, confirm that we are re-entering // the block with the same stack pointer delta as previously, // and pop it off the work list // otherwise declare the stack pointer to be un-analyzeable; // else // iterate through all instructions in the block, analyzing // the stack pointer delta of each inst and accumulating current delta // At the end of the block, put the successor blocks on the work list. // For both cases, maintain and update reaching definitions sets, and the // UpExposed and VarKill sets that are used by LVA as well as reaching defs analysis. bool ReprocessingAllocaBlocks = false; bool ReachesInChanged; bool ReachesOutChanged = false; do { SMPBasicBlock *CurrBlock = WorkList.front().first; sval_t IncomingDelta = WorkList.front().second; if (0 < IncomingDelta) { SMP_msg("ERROR: Stack delta of %ld implies stack underflow in func at %lx\n", (long) IncomingDelta, (unsigned long) this->FirstEA); this->AnalyzedSP = false; WorkList.clear(); break; } if (CurrBlock->IsProcessed()) { // already processed ReachesOutChanged = false; #if 0 ReachesInChanged = CurrBlock->ComputeReachesInSet(); if (ReachesInChanged) { ReachesOutChanged = CurrBlock->ComputeReachesOutSet(); } #else if (CurrBlock->IsReachesOutStale()) { ReachesOutChanged = CurrBlock->ComputeReachesOutSet(); } #endif if (ReachesOutChanged) { // Push the successor blocks onto the work list sval_t SuccIncomingDelta = CurrBlock->GetOutgoingStackDelta(); list<SMPBasicBlock *>::iterator SuccIter; for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { pair<SMPBasicBlock *, sval_t> SuccPair (*SuccIter, SuccIncomingDelta); WorkList.push_back(SuccPair); } } InstIter = CurrBlock->GetFirstInst(); sval_t PrevIncomingDelta = (*InstIter)->GetStackPtrOffset(); if (IncomingDelta == PrevIncomingDelta) { // No error, already processed. WorkList.pop_front(); // discard already processed block. } #if 1 else if (this->CallsAlloca || this->HasPushAfterFrameAlloc()) { #else else { #endif ConflictingValuesSeen = true; // Calls to alloca() become additional stack allocations, which can produce // multiple possible stack deltas for an instruction if different paths // to the instruction do not hit the same alloca() calls, so it is not // an error to have conflicting deltas in the functions that call alloca(). // We want to converge to the smallest magnitude deltas, which are the greatest // values because the deltas are negative. This is the opposite of IDA Pro, which // seems to use the largest stack deltas it has seen. if (PrevIncomingDelta >= IncomingDelta) { // Old incoming delta should be retained. WorkList.pop_front(); // discard already processed block. } else { CurrBlock->SetProcessed(false); ReprocessingAllocaBlocks = true; DeltaIncrement = IncomingDelta - PrevIncomingDelta; continue; // Make the loop come around and process this block again, using // the new incoming delta. Because we do this only when it decreases // the stack size as seen by this block, no infinite loop is possible. } } #if 1 else { this->AnalyzedSP = false; SMP_msg("ERROR: Stack delta: PrevIncoming is %ld NewIncoming is %ld at %lx\n", (long) PrevIncomingDelta, (long) IncomingDelta, (unsigned long) (*InstIter)->GetAddr()); WorkList.clear(); } #endif } else { // not already processed // ReprocessingAllocaBlocks => Reaching definitions sets have already been computed; just need to do stack delta analysis ReachesOutChanged = false; #if 0 ReachesInChanged = CurrBlock->ComputeReachesInSet(); if (ReachesInChanged && ReprocessingAllocaBlocks) { // Because block is not truly being processed for the first time, the ReachesOut set can be // recomputed without processing instructions, as the DEDefs set and VarKill set will never // change after the first pass through the block. ReachesOutChanged = CurrBlock->ComputeReachesOutSet(); } if (CurrBlock->IsReachesOutStale()) { ReachesOutChanged = CurrBlock->ComputeReachesOutSet(); } #endif CurrBlock->SetProcessed(true); WorkList.pop_front(); for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { CurrInst = (*InstIter); if (CurrInst->IsFloatNop()) { continue; // skip marker instruction } ea_t InstAddr = CurrInst->GetAddr(); if (InstAddr == this->GetFirstFrameAllocInstAddr()) { // Record the reset point for frame deallocations this->PreAllocStackDelta = IncomingDelta; } CurrInst->SetStackPtrOffset(IncomingDelta); // Search for tail calls, defined strictly as having an incoming stack delta of zero and // being jumps to far chunks. if ((0 == IncomingDelta) && (CurrInst->IsBranchToFarChunk())) { CurrInst->SetTailCall(); #if 0 SMP_msg("Found tail call at %lx from %s: %s\n", (unsigned long) InstAddr, this->GetFuncName(), CurrInst->GetDisasm()); #endif } #if SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS if (DebugFlag && IDAProSucceeded && !(this->CallsAlloca || this->HasPushAfterFrameAlloc())) { sval_t IDAProDelta = get_spd(this->GetFuncInfo(), InstAddr); if ((IDAProDelta != IncomingDelta) && (!CurrInst->MDIsHaltInstr())) { // IDA Pro special-cases the HALT instruction to make it appear that the // incoming stack delta is zero. We do no such special case delta adjudstment, // so we suppress error messages, as our delta will be non-zero. SMP_msg("ERROR: At %lx IDA Pro has stack pointer delta of %ld and we compute %ld\n", (unsigned long) InstAddr, (long) IDAProDelta, (long) IncomingDelta); } } if (TraceFlag) { SMP_msg("INFO: Stack delta trace: %ld at %lx\n", (unsigned long) IncomingDelta, (unsigned long) InstAddr); } #endif // As soon as the stack ptr offset has been set for the current instruction, we can normalize // all of its stack DEFs and USEs. bool StackOpsChanged = CurrInst->MDNormalizeStackOps(UseFP, this->GetFramePtrStackDelta(), ReprocessingAllocaBlocks, DeltaIncrement); // Dataflow equation for upward exposed variables: If a variable has not been // killed yet in this block, starting from the top of the block, and it is used // in the current instruction, then it is upwardly exposed. set<DefOrUse, LessDefUse>::iterator CurrUse; if (!ReprocessingAllocaBlocks) { // Only compute on first pass through block for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) { op_t UseOp = CurrUse->GetOp(); CanonicalizeOpnd(UseOp); if (MDIsDataFlowOpnd(UseOp, this->UsesFramePointer())) { // We have a register or stack operand. If stack operand, it is normalized, i.e. EBP-4 might be ESP-8, // where the ESP-8 refers to the value of ESP upon entry to the function, not its current value. // This normalization makes each stack location uniquely named (no aliases at different code locations due // to different values of ESP at different code locations). // We only track certain kinds of operands in our data flow analyses. // Only add non-immediate operands that are not already killed in this block. // o_near and o_far operands are code addresses in immediate form, e.g. // call _printf might be call 0x8048040, with o_near = 0x8048040. if (!(CurrBlock->MDAlreadyKilled(UseOp))) { CurrBlock->AddUpExposed(UseOp); } } } } // Find stack pointer saves and restores. bool StackPtrSaved; sval_t SavedDelta; op_t CopyOperand; bool SavedDeltaHasNewValue = false; bool ErrorFlag = false; if (CurrInst->MDIsStackPtrSaveOrRestore(this->UsesFramePointer(), this->GetFramePtrStackDelta(), StackPtrSaved, SavedDelta, CopyOperand, ErrorFlag)) { // NOTE: If CopyOperand is a stack location, it is normalized. if (StackPtrSaved) { // Insert new entry into the StackPtrCopySet. For the ReprocessingAllocaBlocks case, this might be // just a tricky update of the delta for an existing item in the set. bool DeltaInserted = this->AddToStackPtrCopySet(CopyOperand, InstAddr, SavedDelta); if (TraceFlag) { SMP_msg("INFO: Stack delta saved: %ld at %lx\n", (long) SavedDelta, (unsigned long) InstAddr); } } else { // stack pointer was restored from saved value StackPointerRestoreSeen = true; SavedDeltaHasNewValue = true; // no need to compute effect of restore instruction later if (ReprocessingAllocaBlocks) { // Now that the stack pointer has been restored, the effect of the alloca() should // be undone. We no longer need to adjust delta values for the rest of the block. DeltaIncrement = 0; } } } // end if (CurrInst->MDIsStackPtrSaveOrRestore()) else if (ErrorFlag) { this->AnalyzedSP = false; WorkList.clear(); SMP_msg("ERROR: ErrorFlag=true from MDIsStackPtrSaveOrRestore() at %lx\n", (unsigned long) InstAddr); break; } else if (CurrInst->MDIsLeaveInstr()) { // LEAVE is a restoration of a stack pointer, not processed by CurrInst->MDIsStackPtrSaveOrRestore() StackPointerRestoreSeen = true; } // Update VarKill and DownExposedDefs sets for DEFs in current instruction. // Dataflow equation for killed variables: If a variable is defined in any // instruction in the block, it is killed by this block (i.e. prior definitions // of that variable will not make it through the block). if (!ReprocessingAllocaBlocks) { // Only compute on first pass through block set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) { op_t DefOp = CurrDef->GetOp(); if (MDIsDataFlowOpnd(DefOp, this->UsesFramePointer())) { // We have a register or stack operand. If stack operand, it is normalized, i.e. EBP-4 might be ESP-8, // where the ESP-8 refers to the value of ESP upon entry to the function, not its current value. // This normalization makes each stack location uniquely named (no aliases at different code locations due // to different values of ESP at different code locations). CurrBlock->AddVarKill(DefOp); CurrBlock->UpdateDownExposedDefs(DefOp, InstAddr); } } } if (SavedDeltaHasNewValue) { IncomingDelta = SavedDelta; // from restore instruction } else { CurrentDelta = CurrInst->AnalyzeStackPointerDelta(IncomingDelta, this->GetFramePtrStackDelta()); if (SMP_STACK_POINTER_BITWISE_AND_CODE == CurrentDelta) { // For now, we ignore instructions that AND a constant into the stack pointer. CurrentDelta = 0; SMP_msg("WARNING: Stack pointer bitwise AND ignored at %lx\n", (unsigned long) CurrInst->GetAddr()); } else if (SMP_STACK_DELTA_ERROR_CODE == CurrentDelta) { this->AnalyzedSP = false; SMP_msg("ERROR: Stack delta unanalyzeable at %lx\n", (unsigned long) InstAddr); WorkList.clear(); break; } SMPitype FlowType = CurrInst->GetDataFlowType(); IncomingDelta += CurrentDelta; if ((RETURN == FlowType) && (!CurrInst->IsCondTailCall()) && (!CurrInst->IsTailCall())) { // We hope to see a consistent outgoing delta from all RETURN points. // We special-case the conditional jump used as tail call, because it must be followed // by a real return instruction later. If the jump is taken, it acts as a return, but // it has not yet popped the stack. // Also, a regular tail call always has the stack delta at zero and does not match // the stack delta of actual return instructions elsewhere in the function. if (ReturnSeen) { // This is not the first RETURN seen. if (IncomingDelta != this->NetStackDelta) { // Inconsistent SMP_msg("ERROR: Inconsistent stack deltas at return instruction at %lx\n", (unsigned long) CurrInst->GetAddr()); ConsistentNetDelta = false; this->AnalyzedSP = false; WorkList.clear(); break; } } else { // First RETURN statement seen. ReturnSeen = true; this->NetStackDelta = IncomingDelta; #if SMP_AUDIT_STACK_POINTER_DELTAS if (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA != IncomingDelta) { SMP_msg("WARNING: Stack delta not %d after return instruction at %lx\n", CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA, (unsigned long) CurrInst->GetAddr()); } #endif } // If we permitted inconsistent stack deltas previously, then the stack pointer has to // have been restored, e.g. if we allocate a frame with sub esp,32 and then we later // have paths that pass through an alloca() call, a push, etc., then the alloca() or // push will not be undone by add esp,32. It must be undone by something like mov esp,ebp. if (ConflictingValuesSeen && !StackPointerRestoreSeen) { SMP_msg("ERROR: Inconsistent stack deltas seen, no stack pointer restore before return instruction at %lx\n", (unsigned long) CurrInst->GetAddr()); this->AnalyzedSP = false; WorkList.clear(); break; } } } // end if (SavedDeltaHasNewValue) ... else ... } // end for each instruction in WorkList block if (CurrBlock->IsReachesOutStale()) { ReachesOutChanged = CurrBlock->ComputeReachesOutSet(); } // Push the successor blocks onto the work list if anything changed if (this->AnalyzedSP) { // if we do not have an error already CurrBlock->SetOutgoingStackDelta(IncomingDelta); // record incoming delta for all successors if (ReachesOutChanged || (!ReprocessingAllocaBlocks)) { // if anything changed (deltas or reaching defs ReachOut set) list<SMPBasicBlock *>::iterator SuccIter; if (DebugFlag && (0 == IncomingDelta)) { SMP_msg("ERROR: Pushing WorkList items with IncomingDelta of zero. Dumping Block:\n"); CurrBlock->Dump(); } for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { pair<SMPBasicBlock *, sval_t> SuccPair (*SuccIter, IncomingDelta); WorkList.push_back(SuccPair); } } } } // end if block already processed ... else ... ReprocessingAllocaBlocks = false; // reset to default before processing next worklist element } while (!WorkList.empty()); this->STARSStackPtrAnalysisPerformed = true; if (this->AnalyzedSP) { if (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA != this->NetStackDelta) { SMP_msg("WARNING: Non-default stack ptr delta %ld for function: %s\n", (long) this->NetStackDelta, this->GetFuncName()); } if (this->StackAdjustmentComputed && (this->GlobalStackAdjustment != (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA - this->NetStackDelta))) { // Oops. When program graph cycles caused us to try to compute the GlobalStackAdjustment as our best guess // for this function's effect on the stack delta, we told our callers that these three values would cancel out. // They do not. Our callers have now been using a bad stack delta for their call instructions. Too late for // anything but a diagnostic message. SMP_msg("ERROR: Earlier GlobalStackAdjustment computation %ld does not agree with current NetStackDelta result for function: %s\n", (long) this->GlobalStackAdjustment, this->GetFuncName()); } } if (IDAProSucceeded) { if (!this->AnalyzedSP) { SMP_msg("ERROR: Stack Ptr Delta Analysis succeeded in IDA, failed in STARS for %lx : %s\n", (unsigned long) this->FirstEA, this->GetFuncName()); } } else { if (this->AnalyzedSP) { SMP_msg("SUCCESS: Stack Ptr Delta Analysis failed in IDA, succeeded in STARS for %lx : %s\n", (unsigned long) this->FirstEA, this->GetFuncName()); } } if (!this->AnalyzedSP) { (void) this->UseIDAStackPointerDeltas(); } // Cannot keep the reaching defs around on huge benchmarks, or we run out of memory. // Once we have SSA form, we can obtain reaching defs info on the fly if we want it. list<SMPBasicBlock *>::iterator BlockIter; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { (*BlockIter)->FreeReachingDefsMemory(); } return this->AnalyzedSP; } // end of SMPFunction::AnalyzeStackPointerDeltas() // Insert the arguments into the StackPtrCopySet; or, if a matching entry already exists // with a StackDelta of greater magnitude than StackDelta, update just the StackDelta. // Return true if StackDelta was inserted, false if it was used to update an old entry. bool SMPFunction::AddToStackPtrCopySet(op_t CopyOp, ea_t InstAddr, sval_t StackDelta) { bool NewInsertion; pair<ea_t, sval_t> InsertStackDefn(InstAddr, StackDelta); pair<op_t, pair<ea_t, sval_t> > InsertStackDefnOp(CopyOp, InsertStackDefn); set<pair<op_t, pair<ea_t, sval_t> >, LessStackDeltaCopy>::iterator FindIter; pair<set<pair<op_t, pair<ea_t, sval_t> >, LessStackDeltaCopy>::iterator, bool> InsertResult; FindIter = this->StackPtrCopySet.find(InsertStackDefnOp); if (FindIter == this->StackPtrCopySet.end()) { // Not already present; insert new triple. NewInsertion = true; InsertResult = this->StackPtrCopySet.insert(InsertStackDefnOp); assert(InsertResult.second); } else { // Already there; see if delta needs to be updated. NewInsertion = false; pair<op_t, pair<ea_t, sval_t> > OldStackDefnOp(*FindIter); // Favor a smaller stack frame for the alloca-calling functions, e.g. favor -24 over -32 as a delta. if (StackDelta > OldStackDefnOp.second.second) { // Replace the old entry with a new one. this->StackPtrCopySet.erase(FindIter); InsertResult = this->StackPtrCopySet.insert(InsertStackDefnOp); assert(InsertResult.second); } } return NewInsertion; } // end of SMPFunction::AddToStackPtrCopySet() void SMPFunction::FindAllAllocsAndDeallocs(void) { bool FoundAllocInstr = false; bool FoundDeallocInstr = false; bool DebugFlag = false; #if SMP_DEBUG_FRAMEFIXUP DebugFlag |= (0 == strcmp("frame_dummy", this->GetFuncName())); #endif // Now, if LocalVarsSize is not zero, we need to find the instruction // in the function prologue that allocates space on the stack for // local vars. This code could be made more robust in the future // by matching LocalVarsSize to the immediate value in the allocation // instruction. However, IDA Pro is sometimes a little off on this // number. **!!** if (0 < this->LocalVarsSize) { if (DebugFlag) SMP_msg("Searching for alloc and dealloc\n"); list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t addr = CurrInst->GetAddr(); // Keep the most recent instruction in the DeallocInstr // in case we reach the return without seeing a dealloc. if (!FoundDeallocInstr) { this->LocalVarsDeallocInstr = addr; } if (!FoundAllocInstr && CurrInst->MDIsFrameAllocInstr()) { #if SMP_DEBUG_CONTROLFLOW SMP_msg("Returned from MDIsFrameAllocInstr()\n"); #endif this->LocalVarsAllocInstr = addr; FoundAllocInstr = true; if (DebugFlag) SMP_msg("Found alloc: %s\n", CurrInst->GetDisasm()); // As soon as we have found the local vars allocation, // we can try to fix incorrect sets of UseFP by IDA. // NOTE: We might want to extend this in the future to // handle functions that have no locals. **!!** bool FixedUseFP = MDFixUseFP(); #if SMP_DEBUG_FRAMEFIXUP if (FixedUseFP) { SMP_msg("Fixed UseFP in %s\n", this->GetFuncName()); } #endif if (this->UsesFramePointer()) { // now that MDFixUseFP() has validated this flag ... this->FindFramePointerDelta(); // find stack delta that is saved in frame pointer in function prologue } } else if (FoundAllocInstr) { // We can now start searching for the DeallocInstr. if (CurrInst->MDIsFrameDeallocInstr(UseFP, this->LocalVarsSize)) { // Keep saving the most recent addr that looks // like the DeallocInstr until we reach the // end of the function. Last one to look like // it is used as the DeallocInstr. #if SMP_DEBUG_CONTROLFLOW SMP_msg("Returned from MDIsFrameDeallocInstr()\n"); #endif this->LocalVarsDeallocInstr = addr; FoundDeallocInstr = true; } else { if (DebugFlag) SMP_msg("Not dealloc: %s\n", CurrInst->GetDisasm()); } } } // end for (list<SMPInstr *>::iterator InstIter ... ) if (!FoundAllocInstr) { // Could not find the frame allocating instruction. Bad. // See if we can find the point at which the stack allocation reaches // a total of FuncInfo.frsize+frregs, regardless of whether it happened by push // instructions or some other means. this->LocalVarsAllocInstr = this->FindAllocPoint(this->FuncInfo.frsize + this->FuncInfo.frregs); #if SMP_DEBUG_CONTROLFLOW SMP_msg("Returned from FindAllocPoint()\n"); #endif #if SMP_DEBUG_FRAMEFIXUP if (BADADDR == this->LocalVarsAllocInstr) { SMP_msg("WARNING: Could not find stack frame allocation in %s\n", this->GetFuncName()); SMP_msg("LocalVarsSize: %lu SavedRegsSize: %u ArgsSize: %lu\n", (unsigned long) LocalVarsSize, CalleeSavedRegsSize, (unsigned long) IncomingArgsSize); } else { SMP_msg("FindAllocPoint found %lx for function %s\n", (unsigned long) this->LocalVarsAllocInstr, this->GetFuncName()); } #endif } #if SMP_DEBUG_FRAMEFIXUP if (!FoundDeallocInstr) { // Could not find the frame deallocating instruction. Bad. // Emit diagnostic and use the last instruction in the // function. SMP_msg("WARNING: Could not find stack frame deallocation in %s\n", this->GetFuncName()); } #endif } // else LocalVarsSize was zero, meaning that we need to search // for the end of the function prologue code and emit stack frame // annotations from that address (i.e. this method returns that // address). We will approximate this by finding the end of the // sequence of PUSH instructions at the beginning of the function. // The last PUSH instruction should be the last callee-save-reg // instruction. We can make this more robust in the future by // making sure that we do not count a PUSH of anything other than // a register. **!!** // NOTE: 2nd prologue instr is usually mov ebp,esp // THE ASSUMPTION THAT WE HAVE ONLY PUSH INSTRUCTIONS BEFORE // THE ALLOCATING INSTR IS ONLY TRUE WHEN LOCALVARSSIZE == 0; else { ea_t SaveAddr = this->FuncInfo.startEA; list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); insn_t CurrCmd = CurrInst->GetCmd(); ea_t addr = CurrInst->GetAddr(); if (CurrCmd.itype == NN_push) SaveAddr = addr; else break; } this->LocalVarsAllocInstr = SaveAddr; this->LocalVarsDeallocInstr = 0; // As soon as we have found the local vars allocation, // we can try to fix incorrect sets of UseFP by IDA. // NOTE: We might want to extend this in the future to // handle functions that have no locals. **!!** bool FixedUseFP = this->MDFixUseFP(); #if SMP_DEBUG_FRAMEFIXUP if (FixedUseFP) { SMP_msg("Fixed UseFP in %s\n", this->GetFuncName()); } #endif if (this->UsesFramePointer()) { // now that MDFixUseFP() has validated this flag ... this->FindFramePointerDelta(); // find stack delta that is saved in frame pointer in function prologue } } // end if (LocalVarsSize > 0) ... else ... return; } // end of SMPFunction::FindAllAllocsAndDeallocs() // Compute FramePointerStackDelta as soon as possible so that it is available for SyncAllRTs(). void SMPFunction::FindFramePointerDelta(void) { bool FirstBlockProcessed = false; bool FPSaved = false; // have seen push of frame pointer reg bool SPintoFP = false; // have seen copy of stack pointer into frame pointer sval_t IncomingDelta = 0; sval_t CurrentDelta; list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif while (!FirstBlockProcessed && (InstIter != this->Instrs.end())) { SMPInstr *CurrInst = (*InstIter); // Accumulate stack delta values. CurrentDelta = CurrInst->AnalyzeStackPointerDelta(IncomingDelta, this->GetFramePtrStackDelta()); if (SMP_STACK_POINTER_BITWISE_AND_CODE == CurrentDelta) { // For now, we ignore instructions that AND a constant into the stack pointer. CurrentDelta = 0; } else if (SMP_STACK_DELTA_ERROR_CODE == CurrentDelta) { this->AnalyzedSP = false; break; // error exit } // Look for initialization of frame pointer, record its stack delta FirstBlockProcessed = CurrInst->IsLastInBlock(); if (!FPSaved) { // still looking for "push <framepointerreg>" if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(MD_FRAME_POINTER_REG)) { FPSaved = true; } } else if (!SPintoFP) { // found "push <framepointerreg>", looking for "fp := sp" insn_t CurrCmd = CurrInst->GetCmd(); if ((CurrCmd.itype == MD_MOVE_INSTRUCTION) && (CurrInst->GetFirstDef()->GetOp().is_reg(MD_FRAME_POINTER_REG)) && (CurrInst->GetFirstUse()->GetOp().is_reg(MD_STACK_POINTER_REG))) { SPintoFP = true; this->FramePointerStackDelta = IncomingDelta; FirstBlockProcessed = true; // stop looking assert(this->UsesFramePointer()); } } IncomingDelta += CurrentDelta; ++InstIter; } return; } // end of SMPFunction::FindFramePointerDelta() // Figure out the different regions of the stack frame, and find the // instructions that allocate and deallocate the local variables space // on the stack frame. // The stack frame info will be used to emit stack // annotations when Analyze() reaches the stack allocation // instruction that sets aside space for local vars. // Set the address of the instruction at which these // annotations should be emitted. This should normally // be an instruction such as: sub esp,48 // However, for a function with no local variables at all, // we will need to determine which instruction should be // considered to be the final instruction of the function // prologue and return its address. // Likewise, we find the stack deallocating instruction in // the function epilogue. void SMPFunction::SetStackFrameInfo(void) { #if SMP_COMPUTE_STACK_GRANULARITY // Now, find the boundaries between local variables. this->BuildLocalVarTable(); #endif // Get callee-saved regs info for remediation use. if (BADADDR != this->GetFirstFrameAllocInstAddr()) { this->MDFindSavedRegs(); } return; } // end of SMPFunction::SetStackFrameInfo() // IDA Pro defines the sizes of regions in the stack frame in a way // that suits its purposes but not ours. The frsize field of the func_info_t // structure measures the distance between the stack pointer and the // frame pointer (ESP and EBP in the x86). This region includes some // of the callee-saved registers. So, the frregs field only includes // the callee-saved registers that are above the frame pointer. // x86 standard prologue on gcc/linux: // push ebp ; save old frame pointer // mov ebp,esp ; new frame pointer = current stack pointer // push esi ; callee save reg // push edi ; callee save reg // sub esp,34h ; allocate 52 bytes for local variables // // Notice that EBP acquires its final frame pointer value AFTER the // old EBP has been pushed. This means that, of the three callee saved // registers, one is above where EBP points and two are below. // IDA Pro is concerned with generating readable addressing expressions // for items on the stack. None of the callee-saved regs will ever // be addressed in the function; they will be dormant until they are popped // off the stack in the function epilogue. In order to create readable // disassembled code, IDA defines named constant offsets for locals. These // offsets are negative values (x86 stack grows downward from EBP toward // ESP). When ESP_relative addressing occurs, IDA converts a statement: // mov eax,[esp+12] // into the statement: // mov eax,[esp+3Ch+var_30] // Here, 3Ch == 60 decimal is the distance between ESP and EBP, and // var_30 is defined to have the value -30h == -48 decimal. So, the // "frame size" in IDA Pro is 60 bytes, and a certain local can be // addressed in ESP-relative manner as shown, or as [ebp+var_30] for // EBP-relative addressing. The interactive IDA user can then edit // the name var_30 to something mnemonic, such as "virus_size", and IDA // will replace all occurrences with the new name, so that code references // automatically become [ebp+virus_size]. As the user proceeds // interactively, he eventually produces very understandable code. // This all makes sense for producing readable assembly text. However, // our analyses have a compiler perspective as well as a memory access // defense perspective. SMP distinguishes between callee saved regs, // which should not be overwritten in the function body, and local // variables, which can be written. We view the stack frame in logical // pieces: here are the saved regs, here are the locals, here is the // return address, etc. We don't care which direction from EBP the // callee-saved registers lie; we don't want to lump them in with the // local variables. We also don't like the fact that IDA Pro will take // the function prologue code shown above and declare frregs=4 and // frsize=60, because frsize no longer matches the stack allocation // statement sub esp,34h == sub esp,52. We prefer frsize=52 and frregs=12. // So, the task of this function is to fix these stack sizes in our // private data members for the function, while leaving the IDA database // alone because IDA needs to maintain its own definitions of these // variables. // Fixing means we will update the data members LocalVarsSize and // CalleeSavedRegsSize. // NOTE: This function is both machine dependent and platform dependent. // The prologue and epilogue code generated by gcc-linux is as discussed // above, while on Visual Studio and other Windows x86 compilers, the // saving of registers other than EBP happens AFTER local stack allocation. // A Windows version of the function would expect to see the pushing // of ESI and EDI AFTER the sub esp,34h statement. bool SMPFunction::MDFixFrameInfo(void) { int SavedRegsSize = 0; int OtherPushesSize = 0; // besides callee-saved regs int NewLocalsSize = 0; int OldFrameTotal = this->CalleeSavedRegsSize + this->LocalVarsSize; bool Changed = false; bool DebugFlag = (0 == strcmp("__libc_csu_init", this->GetFuncName())); // Iterate through the first basic block in the function. If we find // a frame allocating Instr in it, then we have local vars. If not, // we don't, and LocalVarsSize should have been zero. Count the callee // register saves leading up to the local allocation. Set data members // according to what we found if the values of the data members would // change. SMPBasicBlock *CurrBlock = this->Blocks.front(); vector<SMPInstr *>::iterator CurrIter = CurrBlock->GetFirstInst(); #if SMP_USE_SSA_FNOP_MARKER ++CurrIter; // skip marker instruction #endif for ( ; CurrIter != CurrBlock->GetLastInst(); ++CurrIter) { SMPInstr *CurrInstr = (*CurrIter); if (CurrInstr->MDIsPushInstr()) { // We will make the gcc-linux assumption that a PUSH in // the first basic block, prior to the stack allocating // instruction, is a callee register save. To make this // more robust, we ensure that the register is from // the callee saved group of registers, and that it has // not been defined thus far in the function (else it might // be a push of an outgoing argument to a call that happens // in the first block when there are no locals). **!!!!** if (CurrInstr->MDUsesCalleeSavedReg() && !CurrInstr->HasSourceMemoryOperand()) { SavedRegsSize += 4; // **!!** should check the size if (DebugFlag) SMP_msg("libc_csu_init SavedRegsSize: %d %s\n", SavedRegsSize, CurrInstr->GetDisasm()); } else { // Pushes of outgoing args can be scheduled so that // they are mixed with the pushes of callee saved regs. OtherPushesSize += 4; if (DebugFlag) SMP_msg("libc_csu_init OtherPushesSize: %d %s\n", OtherPushesSize, CurrInstr->GetDisasm()); } } else if (CurrInstr->MDIsFrameAllocInstr()) { if (DebugFlag) SMP_msg("libc_csu_init allocinstr: %s\n", CurrInstr->GetDisasm()); SavedRegsSize += OtherPushesSize; // Get the size being allocated. set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = CurrInstr->GetFirstUse(); CurrUse != CurrInstr->GetLastUse(); ++CurrUse) { // Find the immediate operand. if (o_imm == CurrUse->GetOp().type) { // Get its value into LocalVarsSize. long AllocValue = (signed long) CurrUse->GetOp().value; // One compiler might have sub esp,24 and another // might have add esp,-24. Take the absolute value. if (0 > AllocValue) AllocValue = -AllocValue; if (AllocValue != (long) this->LocalVarsSize) { Changed = true; #if SMP_DEBUG_FRAMEFIXUP if (AllocValue + SavedRegsSize != OldFrameTotal) SMP_msg("Total frame size changed: %s OldTotal: %d NewTotal: %ld\n", this->GetFuncName(), OldFrameTotal, (AllocValue + SavedRegsSize)); #endif this->LocalVarsSize = (asize_t) AllocValue; this->CalleeSavedRegsSize = (ushort) SavedRegsSize; NewLocalsSize = this->LocalVarsSize; } else { // Old value was correct; no change. NewLocalsSize = this->LocalVarsSize; if (SavedRegsSize != this->CalleeSavedRegsSize) { this->CalleeSavedRegsSize = (ushort) SavedRegsSize; Changed = true; #if SMP_DEBUG_FRAMEFIXUP SMP_msg("Only callee regs size changed: %s\n", this->GetFuncName()); #endif } } } // end if (o_imm == ...) } // end for all uses break; // After frame allocation instr, we are done } // end if (push) .. elsif frame allocating instr } // end for all instructions in the first basic block // If we did not find an allocating instruction, see if it would keep // the total size the same to set LocalVarsSize to 0 and to set // CalleeSavedRegsSize to SavedRegsSize. If so, do it. If not, we // might be better off to leave the numbers alone. if (!Changed && (NewLocalsSize == 0)) { if (DebugFlag) SMP_msg("libc_csu_init OldFrameTotal: %d \n", OldFrameTotal); if (OldFrameTotal == SavedRegsSize) { this->CalleeSavedRegsSize = (ushort) SavedRegsSize; this->LocalVarsSize = 0; Changed = true; } #if SMP_DEBUG_FRAMEFIXUP else { SMP_msg("Could not update frame sizes: %s\n", this->GetFuncName()); } #endif } #if SMP_DEBUG_FRAMEFIXUP if ((0 < OtherPushesSize) && (0 < NewLocalsSize)) SMP_msg("Extra pushes found of size %d in %s\n", OtherPushesSize, this->GetFuncName()); #endif #if SMP_DEBUG_FRAMEFIXUP if (Changed) { SMP_msg("Fixed stack frame size info: %s\n", this->GetFuncName()); #if SMP_DEBUG_FRAMEFIXUP_VERBOSE SMPBasicBlock *CurrBlock = this->Blocks.front(); SMP_msg("First basic block:\n"); vector<SMPInstr *>::iterator InstIter = CurrBlock->GetFirstInst(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; #endif while (InstIter != CurrBlock->GetLastInst()) { SMP_msg("%s\n", (*InstIter)->GetDisasm()); ++InstIter; } #endif } #endif return Changed; } // end of SMPFunction::MDFixFrameInfo() // Some functions have difficult to find stack allocations. For example, in some // version of glibc, strpbrk() zeroes out register ECX and then pushes it more than // 100 times in order to allocate zero-ed out local vars space for a character translation // table. We will use the stack pointer analysis of IDA to find out if there is a point // in the first basic block at which the stack pointer reaches the allocation total // that IDA is expecting for the local vars region. // If so, we return the address of the instruction at which ESP reaches its value, else // we return BADADDR. ea_t SMPFunction::FindAllocPoint(asize_t OriginalLocSize) { sval_t TargetSize = - ((sval_t) OriginalLocSize); // negate; stack grows down #if SMP_DEBUG_FRAMEFIXUP bool DebugFlag = (0 == strcmp("_dl_runtime_resolve", this->GetFuncName())); if (DebugFlag) SMP_msg("%s OriginalLocSize: %lu\n", this->GetFuncName(), (unsigned long) OriginalLocSize); #endif if (this->AnalyzedSP) { // Limit our analysis to the first basic block in the function. list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t addr = CurrInst->GetAddr(); // get_spd() returns a cumulative delta of ESP sval_t sp_delta = get_spd(this->GetFuncInfo(), addr); #if SMP_DEBUG_FRAMEFIXUP if (DebugFlag) SMP_msg("%s delta: %ld at %lx\n", this->GetFuncName(), (long) sp_delta, (unsigned long) addr); #endif if (sp_delta == TargetSize) { // <= instead of == here? **!!** // Previous instruction hit the frame size. if (InstIter == this->Instrs.begin()) { return BADADDR; // cannot back up from first instruction } else { ea_t PrevAddr = (*(--InstIter))->GetAddr(); #if SMP_USE_SSA_FNOP_MARKER if ((*(this->Instrs.begin()))->GetAddr() == PrevAddr) return BADADDR; // don't return marker instruction else return PrevAddr; #else return PrevAddr; #endif } } if (CurrInst->IsLastInBlock()) { // It could be that the current instruction will cause the stack pointer // delta to reach the TargetSize. sp_delta is not updated until after the // current instruction, so we need to look ahead one instruction if the // current block falls through. On the other hand, if the current block // ends with a jump or return, we cannot hit TargetSize. if (CurrInst->IsBasicBlockTerminator()) return BADADDR; list<SMPInstr *>::iterator NextInstIter = InstIter; ++NextInstIter; if (NextInstIter == this->Instrs.end()) return BADADDR; sp_delta = get_spd(this->GetFuncInfo(), (*NextInstIter)->GetAddr()); if (sp_delta == TargetSize) { // CurrInst will cause stack pointer delta to hit TargetSize. return addr; } else { return BADADDR; } } // end if LastInBlock } // end for all instructions } // end if (this->AnalyzedSP) #if SMP_DEBUG_FRAMEFIXUP else { SMP_msg("AnalyzedSP is false for %s\n", this->GetFuncName()); } #endif return BADADDR; } // end of SMPFunction::FindAllocPoint() // IDA Pro is sometimes confused by a function that uses the frame pointer // register for other purposes. For the x86, a function that uses EBP // as a frame pointer would begin with: push ebp; mov ebp,esp to save // the old value of EBP and give it a new value as a frame pointer. The // allocation of local variable space would have to come AFTER the move // instruction. A function that begins: push ebp; push esi; sub esp,24 // is obviously not using EBP as a frame pointer. IDA is apparently // confused by the push ebp instruction being the first instruction // in the function. We will reset UseFP to false in this case. // The inverse problem happens with a function that begins with instructions // other than push ebp; mov ebp,esp; ... etc. but eventually has those // instructions in the first basic block. For example, a C compiler generates // for the first block of main(): // lea ecx,[esp+arg0] // and esp, 0xfffffff0 // push dword ptr [ecx-4] // push ebp // mov ebp,esp // push ecx // sub esp,<framesize> // // This function is obviously using EBP as a frame pointer, but IDA Pro marks // the function as not using a frame pointer. We will reset UseFP to true in // this case. // NOTE: This logic should work for both Linux and Windows x86 prologues. bool SMPFunction::MDFixUseFP(void) { bool OldUseFP = this->UsesFramePointer(); bool HasLocals = (0 < this->LocalVarsSize); list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); ea_t addr; #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif SMPInstr *CurrInst; #if 0 if (!(this->UseFP)) { #endif // See if we can detect the instruction "push ebp" followed by the instruction // "mov ebp,esp" in the first basic block. The instructions do not have to be // consecutive. If we find them, we will reset UseFP to true. bool FirstBlockProcessed = false; bool EBPSaved = false; bool ESPintoEBP = false; do { CurrInst = (*InstIter); addr = CurrInst->GetAddr(); FirstBlockProcessed = CurrInst->IsLastInBlock(); if (!EBPSaved) { // still looking for "push ebp" if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(MD_FRAME_POINTER_REG)) { EBPSaved = true; } } else if (!ESPintoEBP) { // found "push ebp", looking for "mov ebp,esp" insn_t CurrCmd = CurrInst->GetCmd(); if ((CurrCmd.itype == NN_mov) && (CurrInst->GetFirstDef()->GetOp().is_reg(MD_FRAME_POINTER_REG)) && (CurrInst->GetFirstUse()->GetOp().is_reg(MD_STACK_POINTER_REG))) { ESPintoEBP = true; FirstBlockProcessed = true; // exit loop } } // We must get EBP set to its frame pointer value before we reach the // local frame allocation instruction (i.e. the subtraction of locals space // from the stack pointer). if (HasLocals) { FirstBlockProcessed |= (addr >= this->LocalVarsAllocInstr); } ++InstIter; } while (!FirstBlockProcessed); // If we found ESPintoEBP, we also found EBPSaved first, and we need to change // this->UseFP to true and return true. Otherwise, return false. this->UseFP = ESPintoEBP; bool changed = (ESPintoEBP != OldUseFP); if (changed) SMP_msg("INFO: MDFixUseFP toggled UseFP for %s\n", this->GetFuncName()); return (changed); #if 0 } // end if (!(this->UseFP)) // At this point, this->UseFP must have been true on entry to this method and we will // check whether it should be reset to false. while (addr <= this->LocalVarsAllocInstr) { set<DefOrUse, LessDefUse>::iterator CurrDef = CurrInst->GetFirstDef(); while (CurrDef != CurrInst->GetLastDef()) { if (CurrDef->GetOp().is_reg(MD_FRAME_POINTER_REG)) return false; // EBP got set before locals were allocated ++CurrDef; } ++InstIter; CurrInst = (*InstIter); addr = CurrInst->GetAddr(); } // If we found no defs of the frame pointer before the local vars // allocation, then the frame pointer register is not being used // as a frame pointer, just as a general callee-saved register. this->UseFP = false; SMP_msg("INFO: MDFixUseFP reset UseFP to false for %s\n", this->GetFuncName()); return true; #endif } // end of SMPFunction::MDFixUseFP() // Find the callee-saved reg offsets (negative offset from return address) // for all registers pushed onto the stack before the stack frame allocation // instruction. void SMPFunction::MDFindSavedRegs(void) { list<SMPInstr *>::iterator InstIter; int RegIndex; func_t *CurrFunc = SMP_get_func(this->GetStartAddr()); assert(NULL != CurrFunc); for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); if (CurrInst->GetAddr() > this->LocalVarsAllocInstr) break; if (!(CurrInst->MDIsPushInstr())) continue; sval_t CurrOffset = get_spd(CurrFunc, CurrInst->GetAddr()); if (CurrInst->GetCmd().itype == NN_push) { op_t PushedReg = CurrInst->GetPushedOpnd(); if (o_reg == PushedReg.type) { RegIndex = (int) PushedReg.reg; if (RegIndex > R_di) { SMP_msg("WARNING: Skipping save of register %d\n", RegIndex); continue; } if (this->SavedRegLoc.at((size_t) RegIndex) == 0) { this->SavedRegLoc[(size_t) RegIndex] = CurrOffset - 4; } else { SMP_msg("WARNING: Multiple saves of register %d\n", RegIndex); } } // end if register push operand } // end if PUSH instruction else if (NN_pusha == CurrInst->GetCmd().itype) { // **!!** Handle pushes of all regs. this->SavedRegLoc[(size_t) R_ax] = CurrOffset - 4; this->SavedRegLoc[(size_t) R_cx] = CurrOffset - 8; this->SavedRegLoc[(size_t) R_dx] = CurrOffset - 12; this->SavedRegLoc[(size_t) R_bx] = CurrOffset - 16; this->SavedRegLoc[(size_t) R_sp] = CurrOffset - 20; this->SavedRegLoc[(size_t) R_bp] = CurrOffset - 24; this->SavedRegLoc[(size_t) R_si] = CurrOffset - 28; this->SavedRegLoc[(size_t) R_di] = CurrOffset - 32; break; // all regs accounted for } else if (CurrInst->MDIsEnterInstr()) { this->SavedRegLoc[(size_t) R_bp] = CurrOffset - 4; } } // end for all instructions return; } // end of SMPFunction::MDFindSavedRegs() // Compute the ReturnRegTypes[] as the meet over all register types // at all return instructions. void SMPFunction::MDFindReturnTypes(void) { list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; vector<SMPInstr *>::iterator InstIter; vector<SMPOperandType> RegTypes; SMPInstr *CurrInst; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); if (CurrBlock->HasReturn()) { // Get the types of all registers at the RETURN point. // Calculate the meet function over them. InstIter = CurrBlock->GetLastInst(); --InstIter; CurrInst = (*InstIter); assert(RETURN == CurrInst->GetDataFlowType()); set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) { op_t UseOp = CurrUse->GetOp(); if ((o_reg != UseOp.type) || (R_di < UseOp.reg)) continue; this->ReturnRegTypes[UseOp.reg] = SMPTypeMeet(this->ReturnRegTypes.at(UseOp.reg), CurrUse->GetType()); } // for all USEs in the RETURN instruction } // end if current block has a RETURN } // end for all blocks return; } // end of SMPFunction::MDFindReturnTypes() // Determine local variable boundaries in the stack frame. void SMPFunction::BuildLocalVarTable(void) { // Currently we just use the info that IDA Pro has inferred from the direct // addressing of stack locations. this->SemiNaiveLocalVarID(); return; } // end of SMPFunction::BuildLocalVarTable() // Limit damage from garbage stack offset values produced by IDA Pro. #define IDAPRO_KLUDGE_STACK_FRAME_SIZE_LIMIT 5000000 // Use the local variable offset list from IDA's stack frame structure to compute // the table of local variable boundaries. void SMPFunction::SemiNaiveLocalVarID(void) { // NOTE: We use IDA Pro's offsets from this->FuncInfo (e.g. frsize) and NOT // our own corrected values in our private data members. The offsets we // read from the stack frame structure returned by get_frame() are consistent // with other IDA Pro values, not with our corrected values. list<SMPInstr *>::iterator InstIter; bool DebugFlag = false; bool FoundReturnAddress = false; this->LocalVarOffsetLimit = -20000; #if SMP_DEBUG_STACK_GRANULARITY DebugFlag |= (0 == strcmp("qSort3", this->GetFuncName())); #endif func_t *FuncPtr = SMP_get_func(this->FuncInfo.startEA); if (NULL == FuncPtr) { SMP_msg("ERROR in SMPFunction::SemiNaiveLocalVarID; no func ptr\n"); } assert(NULL != FuncPtr); struc_t *StackFrame = get_frame(FuncPtr); if (NULL == StackFrame) { SMP_msg("WARNING: No stack frame info from get_frame for %s\n", this->GetFuncName()); return; } member_t *Member = StackFrame->members; for (size_t i = 0; i < StackFrame->memqty; ++i, ++Member) { long offset; char MemberName[MAXSMPVARSTR] = {'\0'}; if (NULL == Member) { SMP_msg("NULL stack frame member pointer in %s\n", this->GetFuncName()); break; } get_member_name(Member->id, MemberName, MAXSMPVARSTR - 1); if (MemberName == NULL) { #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("NULL stack frame member in %s\n", this->GetFuncName()); #endif continue; } if (Member->unimem()) { // Not a separate variable; name for member of a union. // The union itself should have a separate entry, so we skip this. SMP_msg("STACK INFO: Skipping union member %s frame member %zu in stack frame for %s\n", MemberName, i, this->GetFuncName()); continue; } offset = (long) Member->get_soff(); // Would be 0 for union member, so we skipped them above. if (DebugFlag) { SMP_msg("%s local var %s at offset %ld\n", this->GetFuncName(), MemberName, offset); } if (offset > IDAPRO_KLUDGE_STACK_FRAME_SIZE_LIMIT) { SMP_msg("ERROR: Rejected enormous stack offset %ld for var %s in func %s\n", offset, MemberName, this->GetFuncName()); continue; } if (!FoundReturnAddress && (2 == strlen(MemberName)) && (0 == strncmp(" r", MemberName, 2))) { FoundReturnAddress = true; this->IDAReturnAddressOffset = offset; } struct LocalVar TempLocal; TempLocal.offset = offset; TempLocal.size = Member->eoff - Member->soff; // audit later SMP_strncpy(TempLocal.VarName, MemberName, sizeof(TempLocal.VarName) - 1); this->LocalVarTable.push_back(TempLocal); if ((offset + (long) TempLocal.size) >= this->LocalVarOffsetLimit) { this->LocalVarOffsetLimit = (long) (TempLocal.offset + TempLocal.size); } } // end for all stack frame members // If AnalyzedSP is false, that is all we can do. if (!this->AnalyzedSP) { this->OutgoingArgsSize = 0; this->MinStackDelta = 0; this->AllocPointDelta = 0; return; } // Calculate min and max stack point deltas. this->MinStackDelta = 20000; // Final value should be negative or zero this->MaxStackDelta = -1000; // Final value should be zero. InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER if ((*InstIter)->IsFloatNop()) ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t addr = CurrInst->GetAddr(); sval_t sp_delta = CurrInst->GetStackPtrOffset(); if (sp_delta < this->MinStackDelta) this->MinStackDelta = sp_delta; if (sp_delta > this->MaxStackDelta) this->MaxStackDelta = sp_delta; if (addr == this->LocalVarsAllocInstr) { // Total stack pointer delta is sp_delta for the next instruction, // because IDA updates the sp delta AFTER each instruction. list<SMPInstr *>::iterator NextInstIter = InstIter; ++NextInstIter; if (NextInstIter != this->Instrs.end()) { sp_delta = (*NextInstIter)->GetStackPtrOffset(); this->AllocPointDelta = sp_delta; } } } // Calculate min and max stack operand offsets accessed. InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER if ((*InstIter)->IsFloatNop()) ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t addr = CurrInst->GetAddr(); // Find the min and max stack offsets in DEFs and USEs. op_t TempOp; if (CurrInst->HasDestMemoryOperand() || CurrInst->MDIsPushInstr() || CurrInst->MDIsEnterInstr()) { set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) { TempOp = CurrDef->GetOp(); if (TempOp.type != o_phrase && TempOp.type != o_displ) continue; this->UpdateMinMaxStackOffsets(CurrInst, TempOp); } // end for all DEFs } if (CurrInst->HasSourceMemoryOperand() || CurrInst->MDIsPopInstr() || CurrInst->MDIsLeaveInstr() || CurrInst->MDIsLoadEffectiveAddressInstr()) { if (CurrInst->MDIsLoadEffectiveAddressInstr()) { TempOp = CurrInst->GetLeaMemUseOp(); if (((TempOp.type == o_phrase) || (TempOp.type == o_displ)) && (!(CurrInst->IsRegClearIdiom() || CurrInst->IsNop()))) { this->UpdateMinMaxStackOffsets(CurrInst, TempOp); } } else { set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) { TempOp = CurrUse->GetOp(); if ((TempOp.type != o_phrase) && (TempOp.type != o_displ)) continue; this->UpdateMinMaxStackOffsets(CurrInst, TempOp); } // end for all USEs } } } if (0 == this->MaxStackAccessLimit) { // Never accessed any incoming args. However, we know the return address is on the stack, // and it is almost never accessed, so we want to record its presence. this->MaxStackAccessLimit = MD_DEFAULT_RETURN_ADDRESS_SIZE; } if (this->MinStackAccessOffset > this->MinStackDelta) { // Some functions allocate space that is not visibly accessed. We still want to make // our stack frame maps of maximum size, and MinStackDelta is used for normalizing offsets. this->MinStackAccessOffset = this->MinStackDelta; } else if (this->MinStackAccessOffset < this->MinStackDelta) { // x86-64 leaf functions are often optimized by not allocating a stack frame. Instead, // negative displacements from the stack pointer are used to access locals. So the // stack pointer (reflected in MinStackDelta) never goes down as far as the bottom of // the frame (reflected by MinStackAccessOffset). We need to record that such a function // has been detected so that we don't fail assertions unnecessarily later. this->SetStackFrameExtendsPastStackTop(); } // IDA Pro sometimes fails to add stack frame members for all incoming args, etc. // Find and correct these omissions by examining stack accesses in instructions // and extend the LocalVarTable to cover whatever is out of range. if (!this->AuditLocalVarTable()) { // Catastrophic error must have occurred, probably due to errors in IDA's // stack pointer analysis, despite AnalyzedSP being true. return; } if (!(this->LocalVarTable.empty())) { this->GoodLocalVarTable = true; // Sort the LocalVarTable so that we do not depend on IDA Pro // presenting the stack frame members in order. std::sort(this->LocalVarTable.begin(), this->LocalVarTable.end(), LocalVarCompare); } #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("Computing %d local var sizes\n", this->LocalVarTable.size()); #endif // Now we want to audit the size field for each local if (this->GoodLocalVarTable) { size_t VarLimit = this->LocalVarTable.size() - 1; assert(this->LocalVarTable.size() > 0); for (size_t VarIndex = 0; VarIndex < VarLimit; ++VarIndex) { struct LocalVar TempLocEntry = this->LocalVarTable[VarIndex]; bool AboveLocalsRegion = (TempLocEntry.offset >= this->LocalVarsSize); size_t TempSize = this->LocalVarTable[VarIndex + 1].offset - TempLocEntry.offset; int DiffSize = ((int) TempSize) - ((int) TempLocEntry.size); // We don't have IDA Pro stack frame members for callee saved registers. This // omission can make it seem that there is a gap between the uppermost local // variable and the return address or saved frame pointer. Avoid expanding the // last local variable into the callee saved registers region. if (DiffSize > 0) { // We are expanding the size. if (!AboveLocalsRegion && ((TempLocEntry.offset + TempLocEntry.size + DiffSize) > this->LocalVarsSize)) { // Current local does not start above the locals region, but its new size will // carry it above the locals region. if ((TempLocEntry.offset + TempLocEntry.size) > this->LocalVarsSize) { // Weird. It already overlapped the callee saved regs region. SMP_msg("WARNING: Local var at offset %ld size %zu in %s extends above local vars region.\n", TempLocEntry.offset, TempLocEntry.size, this->GetFuncName()); } // Limit DiffSize to avoid overlapping callee saved regs. DiffSize = this->LocalVarsSize - (TempLocEntry.offset + TempLocEntry.size); if (DiffSize < 0) DiffSize = 0; // started out positive, cap it at zero. } } if (DiffSize < 0) DiffSize = 0; // should not happen with sorted LocalVarTable unless duplicate entries. if (DiffSize != 0) { #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("STACK INFO: Adjusted size for stack frame member at %ld in %s\n", TempLocEntry.offset, this->GetFuncName()); #endif this->LocalVarTable[VarIndex].size += DiffSize; } } #if 0 // Using Member->eoff seems to be working for all members, including the last one. #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("Computing last local var size for frsize %d\n", this->FuncInfo.frsize); #endif // Size of last local is total frsize minus savedregs in frame minus offset of last local size_t SavedRegsSpace = 0; // portion of frsize that is saved regs, not locals. if (this->CalleeSavedRegsSize > this->FuncInfo.frregs) { // IDA Pro counts the save of EBP in frregs, but then EBP gets its new // value and callee saved regs other than the old EBP push get counted // in frsize rather than frregs. CalleeSavedRegsSize includes all saved // regs on the stack, both above and below the current EBP offset. // NOTE: For windows, this has to be done differently, as callee saved regs // happen at the bottom of the local frame, not the top. #if 0 SavedRegsSpace = this->CalleeSavedRegsSize - this->FuncInfo.frregs; #else SavedRegsSpace = this->FuncInfo.frsize - this->LocalVarsSize; #endif } this->LocalVarTable.back().size = this->FuncInfo.frsize - SavedRegsSpace - this->LocalVarTable.back().offset; this->LocalVarOffsetLimit = this->LocalVarTable.back().offset + (adiff_t) this->LocalVarTable.back().size; #endif } #if 0 // AboveLocalsSize is not a reliable number. // IDA Pro can have difficulty with some irregular functions such as are found // in the C startup code. The frsize value might be bogus. Just punt on the // local variable ID if that is the case. if ((this->LocalVarOffsetLimit - AboveLocalsSize) > (adiff_t) this->FuncInfo.frsize) { this->LocalVarTable.clear(); this->GoodLocalVarTable = false; SMP_msg("WARNING: Bad frsize %d for %s OffsetLimit: %d AboveLocalsSize: %d LocalVarsSize: %d ; abandoning SemiNaiveLocalVarID.\n", this->FuncInfo.frsize, this->GetFuncName(), this->LocalVarOffsetLimit, AboveLocalsSize, this->LocalVarsSize); return; } assert((this->LocalVarOffsetLimit - AboveLocalsSize) <= (adiff_t) this->FuncInfo.frsize); #endif // Find out how many of the locals are really outgoing args. if (this->AnalyzedSP && !this->CallsAlloca && (BADADDR != this->LocalVarsAllocInstr)) { this->FindOutgoingArgsSize(); } else { SMP_msg("FindOutgoingArgsSize not called for %s ", this->GetFuncName()); SMP_msg("AnalyzedSP: %d CallsAlloca: %d LocalVarsAllocInstr: %lx \n", this->AnalyzedSP, this->CallsAlloca, (unsigned long) this->LocalVarsAllocInstr); } return; } // end of SMPFunction::SemiNaiveLocalVarID() // Update MinStackAccessOffset and MaxStackAccessLimit if TempOp is stack access void SMPFunction::UpdateMinMaxStackOffsets(SMPInstr *CurrInst, op_t TempOp) { ea_t offset; size_t DataSize; bool UsedFramePointer; bool IndexedAccess; bool SignedMove; bool UnsignedMove; if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, this->MinStackDelta, offset, DataSize, UsedFramePointer, IndexedAccess, SignedMove, UnsignedMove)) { int SignedOffset = (int) offset + (int) this->MinStackDelta; // Don't want zero-based for min/max finding if (((sval_t) SignedOffset) < this->MinStackAccessOffset) { this->MinStackAccessOffset = (sval_t) SignedOffset; } if (((sval_t)(SignedOffset + (int) DataSize)) > this->MaxStackAccessLimit) { this->MaxStackAccessLimit = (sval_t)(SignedOffset + (int) DataSize); } } return; } // end of SMPFunction::UpdateMinMaxStackOffsets() // Check and correct the LocalVarTable derived from IDA Pro stack frame members. // Examine each instruction and see if any stack accesses are beyond the LocalVarTable // and create new entries in the LocalVarTable if so. bool SMPFunction::AuditLocalVarTable(void) { list<SMPInstr *>::iterator InstIter; // For some functions, IDA Pro does not base its stack frame at the MinStackDelta. This // is detected by noting that the offset field for the saved return address is not // the negation of the MinStackDelta, e.g. offset is 12 and MinStackDelta is -16 // for a function such as call_gmon_start, which has a temporary 4-byte decrease // in the stack delta for an internal thunk call that IDA Pro excludes from the // stack frame analysis, because it does not represent any local variable: // call next_instruction // pop ebx // Instead, IDA Pro typically bases its stack frame at the AllocPointDelta. For many // functions, the MinStackDelta and the AllocPointDelta are the same. For some, they // are not the same, and for some functions, IDA Pro has a stack frame that is not // even based at the AllocPointDelta, because IDA Pro makes a mistake in its analyses // when the first basic block is interrupted by odd code such as a function call // before it reaches the frame allocation instruction. // So, we need to align the local var table so that the base of the table is at the // AllocPointDelta and the saved return address falls at normalized address zero, i.e. // if AllocPointDelta is -28, then the LocalVarTable will start at offset zero as IDA // computes offsets, and the saved return address will fall at offset 28, the negation // of the AllocPointDelta. If the LocalVarTable does not conform to this pattern, we will // need to add 4-byte entries at the bottom of the table and adjust offsets until the return address // falls at the correct offset. long IDAFrameAdjustment = (0 - this->IDAReturnAddressOffset - this->AllocPointDelta); if (IDAFrameAdjustment != 0) { SMP_msg("WARNING: %ld bytes IDAFrameAdjustment needed: Func at: %lx RetAddrOffset: %ld AllocPointDelta: %lld\n", IDAFrameAdjustment, (unsigned long) this->FirstEA, this->IDAReturnAddressOffset, (int64) this->AllocPointDelta); // We need to subtract (IDAReturnAddressOffset + this->AllocPointDelta) from the local var table offsets. // this->AllocPointDelta is negative, e.g. -44 for libc_csu_init in toy.exe, and IDAReturnAddressOffset // should be its negation (44 in that example), but is a smaller number (20 in the toy.exe example), // so we are subtracting (20 + -44) from each offset, meaning we are adding 24. We also add 24 to the // value of this->LocalVarOffsetLimit, and create an entry at the bottom of the frame with a size of // 24 in this example. long LocalVarIncrement = (0 - (this->IDAReturnAddressOffset + this->AllocPointDelta)); if (LocalVarIncrement <= 0) { SMP_msg("SERIOUS WARNING: Unexpected non-positive value for LocalVarIncrement: %ld Func at: %lx\n", LocalVarIncrement, (unsigned long) this->FirstEA); } else { for (size_t i = 0; i < this->LocalVarTable.size(); ++i) { this->LocalVarTable[i].offset += LocalVarIncrement; } // Add dummy placeholders at bottom of LocalVarTable, four bytes each. size_t TotalFillerSize = 0; do { struct LocalVar TempLocal; char TempStr[20]; TempLocal.offset = (long) TotalFillerSize; TempLocal.size = 4; if (((long)(TempLocal.size + TotalFillerSize)) > LocalVarIncrement) { TempLocal.size = (size_t)(LocalVarIncrement - (long) TotalFillerSize); } TotalFillerSize += TempLocal.size; SMP_strncpy(TempLocal.VarName, "SMP_IDA_FixVar", sizeof(TempLocal.VarName) - 1); (void) SMP_snprintf(TempStr, 18, "%ld", TempLocal.offset); SMP_strncat(TempLocal.VarName, TempStr, sizeof(TempLocal.VarName) - 1); this->LocalVarTable.push_back(TempLocal); } while (((long)TotalFillerSize) < LocalVarIncrement); this->LocalVarOffsetLimit += LocalVarIncrement; this->FuncInfo.frsize += (asize_t) LocalVarIncrement; } } // We cannot depend on IDA Pro making Member // entries for everything that is accessed on the stack. // When an incoming arg is accessed but no Member is // created, then LocalVarOffsetLimit will be too small // and we will get ERROR messages. We already looped through the // instructions to find the MaxStackAccessLimit. If LocalVarOffsetLimit // is not big enough to reach from AllocPointDelta to MaxStackAccessLimit, // then add 4-byte incoming arg entries until it reaches. while (this->LocalVarOffsetLimit < (long) this->MaxStackAccessLimit) { // Extend LocalVarTable. struct LocalVar TempLocal; char TempStr[20]; TempLocal.offset = this->LocalVarOffsetLimit; TempLocal.size = STARS_ISA_Bytewidth; if ((TempLocal.size + TempLocal.offset) > ((long) this->MaxStackAccessLimit)) { TempLocal.size = ((long) this->MaxStackAccessLimit) - TempLocal.offset; } SMP_strncpy(TempLocal.VarName, "SMP_InArg", sizeof(TempLocal.VarName) - 1); (void) SMP_snprintf(TempStr, 18, "%ld", TempLocal.offset); SMP_strncat(TempLocal.VarName, TempStr, sizeof(TempLocal.VarName) - 1); this->LocalVarTable.push_back(TempLocal); this->LocalVarOffsetLimit += TempLocal.size; } // Fill in the gaps with new variables as well. SHOULD WE? WHY? return true; } // end of SMPFunction::AuditLocalVarTable() // Determine how many bytes at the bottom of the stack frame (i.e. at bottom of // this->LocalVarsSize) are used for outgoing args. This is the case when the cdecl // calling convention is used, e.g. gcc/linux allocates local var space + out args space // in a single allocation and then writes outarg values directly to ESP+0, ESP+4, etc. void SMPFunction::FindOutgoingArgsSize(void) { // Compute the lowest value reached by the stack pointer. list<SMPInstr *>::iterator InstIter; unsigned short BitWidthMask; bool DebugFlag = false; int SignedOffset; #if SMP_DEBUG_STACK_GRANULARITY DebugFlag = (0 == strcmp("BZ2_blockSort", this->GetFuncName())); #endif this->OutgoingArgsComputed = true; if (DebugFlag) { SMP_msg("DEBUG: Entered FindOutgoingArgsSize for %s\n", this->GetFuncName()); #if SMP_IDAPRO52_WORKAROUND this->OutgoingArgsSize = 16; return; #endif } #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("AllocPointDelta: %d MinStackDelta: %d\n", this->AllocPointDelta, this->MinStackDelta); #endif if ((0 <= this->MinStackDelta) || (0 <= this->AllocPointDelta)) { // No allocations; sometimes happens in library functions. this->OutgoingArgsSize = 0; this->AllocPointDelta = 0; if ((this->MinStackDelta > this->MaxStackDelta) || (0 < this->MinStackDelta)) { this->MinStackDelta = 0; } } assert(0 >= this->MinStackDelta); // Allocate a vector of stack frame entries, one for each byte of the stack frame. // This will be our memory map for analyzing stack usage. for (int i = this->MinStackAccessOffset; i < this->MaxStackAccessLimit; ++i) { struct StackFrameEntry TempEntry; TempEntry.VarPtr = NULL; TempEntry.offset = (long) i; TempEntry.Read = false; TempEntry.Written = false; TempEntry.AddressTaken = false; TempEntry.ESPRelativeAccess = false; TempEntry.EBPRelativeAccess = false; TempEntry.IndexedAccess = false; this->StackFrameMap.push_back(TempEntry); struct FineGrainedInfo TempFineGrained; TempFineGrained.SignMiscInfo = 0; TempFineGrained.SizeInfo = 0; this->FineGrainedStackTable.push_back(TempFineGrained); } #if 0 for (int i = 0; i < this->LocalVarOffsetLimit; ++i) { struct FineGrainedInfo TempFineGrained; TempFineGrained.SignMiscInfo = 0; TempFineGrained.SizeInfo = 0; this->FineGrainedStackTable.push_back(TempFineGrained); } #endif // Fill in the VarPtr fields for each StackFrameMap entry. if (0 < this->AllocPointDelta) { SMP_msg("FATAL ERROR: AllocPointDelta = %ld in %s\n", (long) this->AllocPointDelta, this->GetFuncName()); } assert(0 >= this->AllocPointDelta); // We were not able to adjust the LocalVarTable for a negative IDAFrameAdjustment back // in AuditLocalVarTable(), but we can use the negative adjustment value in this loop // to properly match the StackFrameMap entries to the LocalVarTable entries and avoid // an out of range error. long IDAFrameAdjustment = (0 - this->IDAReturnAddressOffset - this->AllocPointDelta); if (0 < IDAFrameAdjustment) { IDAFrameAdjustment = 0; // only handling the negative case; positive was handled in AuditLocalVarTable() } for (size_t i = 0; i < this->LocalVarTable.size(); ++i) { assert(this->LocalVarTable.at(i).offset >= 0); // Picture that AllocPointDelta is -200, MinStackAccessOffset is -210, and // the LocalVarTable[i].offset is +8 (i.e. 8 bytes above alloc point). // Then base = 8 + (-200 - -210) = 8 + 10 = 18, the proper offset into // the StackFrameMap. size_t base = (size_t) (this->LocalVarTable.at(i).offset + (this->AllocPointDelta - this->MinStackAccessOffset) + IDAFrameAdjustment); size_t limit = base + this->LocalVarTable.at(i).size; if (limit > this->StackFrameMap.size()) { SMP_msg("WARNING: FindOutArgsSize: Unaccessed IDA Pro local var or inarg %s base = %zu limit = %zu in %s\n", this->LocalVarTable.at(i).VarName, base, limit, this->GetFuncName()); } else { assert(limit <= this->StackFrameMap.size()); for (size_t MapIndex = base; MapIndex < limit; ++MapIndex) { this->StackFrameMap[MapIndex].VarPtr = &(this->LocalVarTable.at(i)); } } } // Iterate through all instructions and record stack frame accesses in the StackFrameMap. InstIter = this->Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER if ((*InstIter)->IsFloatNop()) ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t InstAddr = CurrInst->GetAddr(); sval_t sp_delta = CurrInst->GetStackPtrOffset(); if (0 < sp_delta) { // Stack underflow. SMP_msg("ERROR: Stack underflow at %lx %s sp_delta: %ld\n", (unsigned long) InstAddr, CurrInst->GetDisasm(), (long) sp_delta); this->OutgoingArgsComputed = false; this->OutgoingArgsSize = 0; return; } assert(0 >= sp_delta); ea_t offset; size_t DataSize; bool UsedFramePointer; bool IndexedAccess; bool SignedMove; bool UnsignedMove; if (CurrInst->HasDestMemoryOperand()) { set<DefOrUse, LessDefUse>::iterator CurrDef; for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) { op_t TempOp = CurrDef->GetOp(); if (TempOp.type != o_phrase && TempOp.type != o_displ) continue; if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, this->MinStackAccessOffset, offset, DataSize, UsedFramePointer, IndexedAccess, SignedMove, UnsignedMove)) { SignedOffset = (int) offset; if (IndexedAccess && ((0 > SignedOffset) || ((SignedOffset + DataSize) > this->StackFrameMap.size()))) { continue; // Indexed expressions can be within frame even when offset is outside frame } assert(0 <= SignedOffset); #if 0 if (offset >= this->FuncInfo.frsize) continue; // limit processing to outgoing args and locals #endif if ((offset + DataSize) > this->StackFrameMap.size()) { SMP_msg("ERROR: offset = %lu DataSize = %zu FrameMapSize = %zu\n", (unsigned long) offset, DataSize, this->StackFrameMap.size()); continue; } assert((offset + DataSize) <= this->StackFrameMap.size()); bool ESPRelative = (!(UsedFramePointer || CurrInst->HasFPNormalizedToSP())); for (int j = 0; j < (int) DataSize; ++j) { this->StackFrameMap[offset + j].Written = true; this->StackFrameMap[offset + j].IndexedAccess = IndexedAccess; if (ESPRelative) { this->StackFrameMap[offset + j].ESPRelativeAccess = true; } else { this->StackFrameMap[offset + j].EBPRelativeAccess = true; } } struct FineGrainedInfo StackDefFG; BitWidthMask = ComputeOperandBitWidthMask(TempOp, DataSize); this->FineGrainedStackTable.at(offset).SizeInfo |= BitWidthMask; StackDefFG.SizeInfo = BitWidthMask; this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_WRITTEN; StackDefFG.SignMiscInfo = FG_MASK_WRITTEN; if (IndexedAccess) { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_INDEXED_ACCESS; StackDefFG.SignMiscInfo |= FG_MASK_INDEXED_ACCESS; } if (ESPRelative) { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_SP_RELATIVE; StackDefFG.SignMiscInfo |= FG_MASK_SP_RELATIVE; } else { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_FP_RELATIVE; StackDefFG.SignMiscInfo |= FG_MASK_FP_RELATIVE; } // We will process the signedness of stores later, so that loads can take precedence // over stores in determining signedness in the table. We go ahead and process // signedness for the separate DEF and USE maps by InstAddr. if (SignedMove) { StackDefFG.SignMiscInfo |= FG_MASK_SIGNED; } else if (UnsignedMove) { StackDefFG.SignMiscInfo |= FG_MASK_UNSIGNED; } // Insert the StackDefFG into the map of InstAddr to DEF FG info. pair<map<ea_t, struct FineGrainedInfo>::iterator, bool> InsertResult; pair<ea_t, struct FineGrainedInfo> InsertValue(InstAddr, StackDefFG); InsertResult = this->StackDefFGInfo.insert(InsertValue); assert(InsertResult.second); } // end if MDGetStackOffsetAndSize() } // end for all DEFs } // end if DestMemoryOperand if (CurrInst->HasSourceMemoryOperand()) { set<DefOrUse, LessDefUse>::iterator CurrUse; for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) { op_t TempOp = CurrUse->GetOp(); if (TempOp.type != o_phrase && TempOp.type != o_displ) continue; if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, this->MinStackAccessOffset, offset, DataSize, UsedFramePointer, IndexedAccess, SignedMove, UnsignedMove)) { SignedOffset = (int) offset; if (IndexedAccess && ((0 > SignedOffset) || ((SignedOffset + DataSize) > this->StackFrameMap.size()))) { continue; // Indexed expressions can be within frame but offset is outside frame } assert(0 <= SignedOffset); #if 0 if (offset >= this->FuncInfo.frsize) continue; // limit processing to outgoing args and locals #endif if ((SignedOffset + DataSize) > this->StackFrameMap.size()) { SMP_msg("ERROR: offset = %lu DataSize = %zu FrameMapSize = %zu\n", (unsigned long) offset, DataSize, this->StackFrameMap.size()); continue; } assert((SignedOffset + DataSize) <= this->StackFrameMap.size()); bool ESPRelative = (!(UsedFramePointer || CurrInst->HasFPNormalizedToSP())); for (int j = 0; j < (int) DataSize; ++j) { this->StackFrameMap[offset + j].Read = true; this->StackFrameMap[offset + j].IndexedAccess |= IndexedAccess; if (ESPRelative) this->StackFrameMap[offset + j].ESPRelativeAccess = true; else this->StackFrameMap[offset + j].EBPRelativeAccess = true; } struct FineGrainedInfo StackUseFG; BitWidthMask = ComputeOperandBitWidthMask(TempOp, DataSize); this->FineGrainedStackTable.at(offset).SizeInfo |= BitWidthMask; StackUseFG.SizeInfo = BitWidthMask; this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_READ; StackUseFG.SignMiscInfo = FG_MASK_READ; if (IndexedAccess) { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_INDEXED_ACCESS; StackUseFG.SignMiscInfo |= FG_MASK_INDEXED_ACCESS; } if (ESPRelative) { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_SP_RELATIVE; StackUseFG.SignMiscInfo |= FG_MASK_SP_RELATIVE; } else { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_FP_RELATIVE; StackUseFG.SignMiscInfo |= FG_MASK_FP_RELATIVE; } if (SignedMove) { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_SIGNED; StackUseFG.SignMiscInfo |= FG_MASK_SIGNED; } else if (UnsignedMove) { this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_UNSIGNED; StackUseFG.SignMiscInfo |= FG_MASK_UNSIGNED; } // Insert the StackUseFG into the map of InstAddr to USE FG info. pair<map<ea_t, struct FineGrainedInfo>::iterator, bool> InsertResult; pair<ea_t, struct FineGrainedInfo> InsertValue(InstAddr, StackUseFG); InsertResult = this->StackUseFGInfo.insert(InsertValue); assert(InsertResult.second); } // end if MDGetStackOffsetAndSize() } // end for all USEs } // end if SourceMemoryOperand // NOTE: Detect taking the address of stack locations. **!!** } // end for all instructions // If function is a leaf function, set OutgoingArgsSize to zero and return. // If function has no local frame allocation, ditto. if ((this->IsLeaf() && !(this->IsDirectlyRecursive())) || (this->AllocPointDelta == 0)) { this->OutgoingArgsSize = 0; return; } // For non-leaf functions, set the OutgoingArgsSize to the write-only, ESP-relative // region of the bottom of the StackFrameMap. bool OutgoingArgsRegionFinished = false; bool IndexedOutgoingArgs = false; // Any indexed accesses to outgoing args? size_t FramePadSize = 0; size_t AlignmentPadSize = 0; // bottom of frame, unused space below outargs for (size_t MapIndex = 0; MapIndex < this->StackFrameMap.size(); ++MapIndex) { // Some of the bottom of the stack frame might be below the local frame allocation. // These are pushes that happened after allocation, etc. We skip over these // locations and define the outgoing args region to start strictly at the bottom // of the local frame allocation. struct StackFrameEntry TempEntry = this->StackFrameMap.at(MapIndex); if (DebugFlag) { SMP_msg("StackFrameMap entry %zu: offset: %ld Read: %d Written: %d ESP: %d EBP: %d\n", MapIndex, TempEntry.offset, TempEntry.Read, TempEntry.Written, TempEntry.ESPRelativeAccess, TempEntry.EBPRelativeAccess); } if (TempEntry.offset < this->AllocPointDelta) continue; if ((0 != TempEntry.VarPtr) && ((0 == strcmp(" s", TempEntry.VarPtr->VarName)) || (0 == strcmp(" r", TempEntry.VarPtr->VarName)))) { // We have reached saved regs or the return address. break; } if (OutgoingArgsRegionFinished) { // We are just processing the stack frame padding. if (!TempEntry.Read && !TempEntry.Written) { // Could be stack frame padding. ++FramePadSize; } else { break; // No more padding region } } else if ((this->OutgoingArgsSize == 0) && (!TempEntry.Read) && (!TempEntry.Written)) { // We have not started accumulating outgoing args bytes, we have reached the // AllocPointDelta, yet we find space that is neither written nor read. This // empty space at the bottom of the stack frame could just be for stack alignment // purposes, especially in the new x86-64 ABI, so it should not prevent us from // finding outgoing args space above it. ++AlignmentPadSize; } else if (TempEntry.Read || TempEntry.EBPRelativeAccess || !TempEntry.Written || !TempEntry.ESPRelativeAccess) { OutgoingArgsRegionFinished = true; if (!TempEntry.Read && !TempEntry.Written) { // Could be stack frame padding. ++FramePadSize; } else { break; // No padding region } } else { this->OutgoingArgsSize++; if (TempEntry.IndexedAccess) { IndexedOutgoingArgs = true; } } } // Add in the alignment padding below the written outargs region. if (this->OutgoingArgsSize > 0) { this->OutgoingArgsSize += AlignmentPadSize; } // If any outgoing arg was accessed using an index register, then we don't know how high // the index register value went. It could potentially consume the so-called padding // region, which might be just the region we did not detect direct accesses to because // the accesses were indirect. To be safe, we expand the outgoing args region to fill // the padding region above it in this indexed access case. if (IndexedOutgoingArgs) { this->OutgoingArgsSize += FramePadSize; } #if 0 // Sometimes we encounter unused stack space above the outgoing args. Lump this space // in with the outgoing args. We detect this by noting when the outgoing args space // has only partially used the space assigned to a local var. // NOTE: This is usually just stack padding to maintain stack alignment. It could // also be the case that the lowest local variable is accessed indirectly and we missed // seeing its address taken, in which case it would be unsound to lump it into the // outgoing args region. We might want to create a local var called STACKPAD // to occupy this space. if ((0 < this->OutgoingArgsSize) && (this->OutgoingArgsSize < this->FuncInfo.frsize)) { long MapIndex = (this->AllocPointDelta - this->MinStackDelta); assert(0 <= MapIndex); MapIndex += (((long) this->OutgoingArgsSize) - 1); struct StackFrameEntry TempEntry = this->StackFrameMap.at((size_t) MapIndex); if (NULL == TempEntry.VarPtr) { // Gap in stack frame; IDA 6.0 SMP_msg("Gap in stack frame: %s\n", this->GetFuncName()); } else if (this->OutgoingArgsSize < (TempEntry.VarPtr->offset + TempEntry.VarPtr->size)) { #if SMP_DEBUG_FRAMEFIXUP SMP_msg("OutGoingArgsSize = %d", this->OutgoingArgsSize); #endif this->OutgoingArgsSize = TempEntry.VarPtr->offset + TempEntry.VarPtr->size; #if SMP_DEBUG_FRAMEFIXUP SMP_msg(" adjusted to %d\n", this->OutgoingArgsSize); #endif } } #endif return; } // end of SMPFunction::FindOutgoingArgsSize() // If TempOp reads or writes to a stack location, return the offset (relative to the initial // stack pointer value) and the size in bytes of the data access. Also return whether the // access was frame-pointer-relative, and whether signedness can be inferred due to a load // from the stack being zero-extended or sign-extended. // NOTE: This function assumes that offsets are already normalized. i.e. the TempOp argument // should always come from a DEF or USE that has been normalized to the stack delta at function entry. // NOTE: TempOp must be of type o_displ or o_phrase, as no other operand type could be a // stack memory access. // BaseValue is either this->MinStackAccessOffset, or this->MinStackDelta (when this->MinStackAccessOffset is still // being computed). // Return true if a stack memory access was found in TempOp, false otherwise. bool SMPFunction::MDGetStackOffsetAndSize(SMPInstr *Instr, op_t TempOp, sval_t BaseValue, ea_t &offset, size_t &DataSize, bool &FP, bool &Indexed, bool &Signed, bool &Unsigned) { int BaseReg; int IndexReg; ushort ScaleFactor; int SignedOffset; sval_t sp_delta = Instr->GetStackPtrOffset(); ea_t InstAddr = Instr->GetAddr(); // helps debugging assert((o_displ == TempOp.type) || (o_phrase == TempOp.type)); MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset); if (TempOp.type == o_phrase) { assert(offset == 0); // implicit zero, as in [esp] ==> [esp+0] } SignedOffset = (int) offset; // avoid sign errors during adjustment arithmetic if ((BaseReg == R_sp) || (IndexReg == R_sp)) { // ESP-relative constant offset if (!Instr->AreDefsNormalized()) { SignedOffset += sp_delta; // base offsets from entry ESP value } SignedOffset -= BaseValue; // convert to StackFrameMap index offset = (ea_t) SignedOffset; // write back to outgoing argument // Get size of data written DataSize = GetOpDataSize(TempOp); FP = false; Indexed = ((BaseReg != R_none) && (IndexReg != R_none)); // two regs used unsigned short opcode = Instr->GetCmd().itype; Unsigned = (opcode == NN_movzx); Signed = (opcode == NN_movsx); if ((0 > SignedOffset) && (!Indexed) && (BaseValue == this->MinStackAccessOffset)) { // Consider asserting here. SMP_msg("ERROR: Negative offset in MDGetStackOffsetAndSize for inst dump: \n"); Instr->Dump(); } return true; } else if (this->UseFP && ((BaseReg == MD_FRAME_POINTER_REG) || (IndexReg == MD_FRAME_POINTER_REG))) { SignedOffset -= this->FuncInfo.frregs; // base offsets from entry ESP value SignedOffset -= BaseValue; // convert to StackFrameMap index offset = (ea_t) SignedOffset; DataSize = GetOpDataSize(TempOp); FP = true; Indexed = ((BaseReg != R_none) && (IndexReg != R_none)); // two regs used #if 0 assert(Indexed || (!this->StackPtrAnalysisSucceeded()) || !this->HasSTARSStackPtrAnalysisCompleted()); // Else we should never get here with unnormalized stack operands #else if (!(Indexed || (!this->StackPtrAnalysisSucceeded()) || !this->HasSTARSStackPtrAnalysisCompleted())) { SMP_msg("WARNING: Unnormalized FP-relative stack offset at %lx after stack analysis succeeded.\n", (unsigned long) Instr->GetAddr()); } #endif unsigned short opcode = Instr->GetCmd().itype; Unsigned = (opcode == NN_movzx); Signed = (opcode == NN_movsx); if ((0 > SignedOffset) && (!Indexed) && (BaseValue == this->MinStackAccessOffset)) { // Consider asserting here. SMP_msg("ERROR: Negative offset %d in MDGetStackOffsetAndSize: frregs: %d MinStackDelta: %ld Inst dump: \n", SignedOffset, this->FuncInfo.frregs, (long) this->MinStackDelta); Instr->Dump(); } return true; } else { return false; } } // end of SMPFunction::MDGetStackOffsetAndSize() // Return fine grained stack entry for stack op TempOp from instruction at InstAddr bool SMPFunction::MDGetFGStackLocInfo(ea_t InstAddr, op_t TempOp, struct FineGrainedInfo &FGEntry) { int BaseReg; int IndexReg; ushort ScaleFactor; ea_t offset; int SignedOffset; assert((o_displ == TempOp.type) || (o_phrase == TempOp.type)); MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset); SignedOffset = (int) offset; if (TempOp.type == o_phrase) { assert(SignedOffset == 0); // implicit zero, as in [esp] ==> [esp+0] } if ((BaseReg == MD_STACK_POINTER_REG) || (IndexReg == MD_STACK_POINTER_REG)) { // ESP-relative constant offset SignedOffset -= this->MinStackAccessOffset; // convert to StackFrameMap index } else if (this->UseFP && ((BaseReg == MD_FRAME_POINTER_REG) || (IndexReg == MD_FRAME_POINTER_REG))) { assert(false); // should never get here with unnormalized stack operand SignedOffset -= this->FuncInfo.frregs; // base offsets from entry ESP value SignedOffset -= this->MinStackAccessOffset; // convert to StackFrameMap index } else { return false; } // We did not return false, so we should have a good offset. Use it to // pass back the fine grained stack table entry for that offset. if ((0 > SignedOffset) || (SignedOffset >= (int) this->FineGrainedStackTable.size())) { if (this->OutgoingArgsComputed) { SMP_msg("ERROR: FG stack table index out of range in MDGetFGStackLocInfo at %lx\n", (unsigned long) InstAddr); } FGEntry.SignMiscInfo = 0; // We cannot figure out signedness info without an FG info stack table. FGEntry.SizeInfo = ComputeOperandBitWidthMask(TempOp, 0); // IDA can figure out width, anyway. } else { FGEntry = this->FineGrainedStackTable.at((size_t) SignedOffset); } return true; } // end of SMPFunction::MDGetFGStackLocInfo() // Return true if we update fine grained stack entry for stack op TempOp from instruction at InstAddr bool SMPFunction::MDUpdateFGStackLocInfo(ea_t InstAddr, op_t TempOp, struct FineGrainedInfo NewFG) { int BaseReg; int IndexReg; ushort ScaleFactor; ea_t offset; int SignedOffset; struct FineGrainedInfo OldFG, UnionFG; assert((o_displ == TempOp.type) || (o_phrase == TempOp.type)); MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset); SignedOffset = (int) offset; if (TempOp.type == o_phrase) { assert(SignedOffset == 0); // implicit zero, as in [esp] ==> [esp+0] } if ((BaseReg == MD_STACK_POINTER_REG) || (IndexReg == MD_STACK_POINTER_REG)) { // ESP-relative constant offset SignedOffset -= this->MinStackAccessOffset; // convert to StackFrameMap index } else if (this->UseFP && ((BaseReg == MD_FRAME_POINTER_REG) || (IndexReg == MD_FRAME_POINTER_REG))) { assert(false); // should never get here with unnormalized stack operands SignedOffset -= this->FuncInfo.frregs; // base offsets from entry ESP value SignedOffset -= this->MinStackAccessOffset; // convert to StackFrameMap index } else { return false; } // We did not return false, so we should have a good offset. Use it to // retrieve the fine grained stack table entry for that offset. if ((0 > SignedOffset) || (SignedOffset >= (int) this->FineGrainedStackTable.size())) { if (this->OutgoingArgsComputed) { SMP_msg("ERROR: FG stack table index out of range in MDGetFGStackLocInfo at %lx\n", (unsigned long) InstAddr); } return false; } else if (this->OutgoingArgsComputed && (((size_t)SignedOffset) < this->OutgoingArgsSize)) { // We don't want to update the outgoing args region, as it will not be consistent // over multiple function calls. NOTE: We could fine tune this by seeing if we // call mutliple target functions or not; if only one, then outgoing args region // would be consistent in the absence of varargs targets. return false; } else { OldFG = this->FineGrainedStackTable.at((size_t) SignedOffset); 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 maybe the sizes. this->FineGrainedStackTable.at(SignedOffset).SignMiscInfo |= NewFG.SignMiscInfo; this->FineGrainedStackTable.at(SignedOffset).SizeInfo |= NewFG.SizeInfo; } } return true; } // end of SMPFunction::MDUpdateFGStackLocInfo() // retrieve DEF addr from GlobalDefAddrBySSA or return BADADDR ea_t SMPFunction::GetGlobalDefAddr(op_t DefOp, int SSANum) { map<int, ea_t>::iterator DefAddrMapIter; map<int, ea_t>::iterator MapResult; ea_t DefAddr = BADADDR; // BADADDR means we did not find it bool RegDef = (o_reg == DefOp.type); if (RegDef) { int HashedName = HashGlobalNameAndSSA(DefOp, SSANum); MapResult = this->GlobalDefAddrBySSA.find(HashedName); if (MapResult != this->GlobalDefAddrBySSA.end()) { // Found it. DefAddr = (ea_t) MapResult->second; } } else if (MDIsStackAccessOpnd(DefOp, this->UsesFramePointer())) { // Until we get stack operands into the GlobalDefAddrBySSA map, // do a linear search. list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); set<DefOrUse, LessDefUse>::iterator DefIter; SMPInstr *CurrInst = (*InstIter); if (CurrInst->IsFloatNop()) { // marker inst if (0 == SSANum) { // Live-in-to-func stack locations get DEF at marker inst. DefIter = CurrInst->FindDef(DefOp); if (DefIter != CurrInst->GetLastDef()) { // Found it. Must be SSA 0. assert(0 == DefIter->GetSSANum()); DefAddr = CurrInst->GetAddr(); return DefAddr; } } ++InstIter; } for ( ; InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); if (CurrInst->HasDestMemoryOperand()) { op_t MemDefOp = CurrInst->GetMemDef(); if (IsEqOp(DefOp, MemDefOp)) { DefIter = CurrInst->FindDef(MemDefOp); assert(DefIter != CurrInst->GetLastDef()); int DefSSANum = DefIter->GetSSANum(); if (DefSSANum == SSANum) { // found it DefAddr = CurrInst->GetAddr(); break; } } } } } return DefAddr; } // end of SMPFunction::GetGlobalDefAddr() int SMPFunction::GetBlockNumForPhiDef(op_t DefOp, int SSANum) { size_t BlockIndex; for (BlockIndex = 0; BlockIndex < this->RPOBlocks.size(); ++BlockIndex) { SMPBasicBlock *CurrBlock = this->RPOBlocks.at(BlockIndex); set<SMPPhiFunction, LessPhi>::iterator PhiIter = CurrBlock->FindPhi(DefOp); if (PhiIter != CurrBlock->GetLastPhi()) { if (PhiIter->GetDefSSANum() == SSANum) { return CurrBlock->GetNumber(); } } } return (int) BADADDR; } // end of SMPFunction::GetBlockNumForPhiDef() // Retrieve block iterator for InstAddr from InstBlockMap; assert if failure SMPBasicBlock *SMPFunction::GetBlockFromInstAddr(ea_t InstAddr) { map<ea_t, SMPBasicBlock *>::iterator MapEntry; MapEntry = this->InstBlockMap.find(InstAddr); assert(MapEntry != this->InstBlockMap.end()); return MapEntry->second; } // Retrieve inst pointer for InstAddr; assert if failure on block find. SMPInstr *SMPFunction::GetInstFromAddr(ea_t InstAddr) { SMPBasicBlock *CurrBlock = this->GetBlockFromInstAddr(InstAddr); SMPInstr *CurrInst = CurrBlock->FindInstr(InstAddr); return CurrInst; } // Given block # and PhiDef op_t and SSANum, return the Phi iterator or assert. set<SMPPhiFunction, LessPhi>::iterator SMPFunction::GetPhiIterForPhiDef(size_t BlockNumber, op_t DefOp, int SSANum) { SMPBasicBlock *DefBlock = this->RPOBlocks.at(BlockNumber); set<SMPPhiFunction, LessPhi>::iterator PhiIter = DefBlock->FindPhi(DefOp); assert(PhiIter != DefBlock->GetLastPhi()); return PhiIter; } // Is DestOp within the outgoing args area? Assume it must be an ESP-relative // DEF operand in order to be a write to the outgoing args area. // NOTE: DestOp should be already normalized to the entry stack delta. bool SMPFunction::IsInOutgoingArgsRegion(op_t DestOp) { bool OutArgWrite = false; int BaseReg, IndexReg; ushort ScaleFactor; ea_t offset; if (this->IsLeaf()) return false; MDExtractAddressFields(DestOp, BaseReg, IndexReg, ScaleFactor, offset); if ((BaseReg != R_sp) && (IndexReg != R_sp)) return false; if (((BaseReg == R_sp) && (IndexReg != R_none)) || ((IndexReg == R_sp) && (BaseReg != R_none)) || (0 < ScaleFactor)) { #if 0 SMP_msg("WARNING: WritesToOutgoingArgs called with indexed write."); PrintOperand(DestOp); #endif return false; } if (!this->OutgoingArgsComputed) { OutArgWrite = true; // be conservative } else { int SignedOffset = (int) offset; SignedOffset -= this->MinStackDelta; // convert to zero-based from bottom of stack frame OutArgWrite = (((size_t) SignedOffset) < this->OutgoingArgsSize); } return OutArgWrite; } // end of SMPFunction::IsInOutgoingArgsRegion() // Is DestOp a direct memory access above the local vars frame? bool SMPFunction::WritesAboveLocalFrame(op_t DestOp, bool OpNormalized) { bool InArgWrite = false; int BaseReg, IndexReg; ushort ScaleFactor; ea_t offset; long SignedOffset; MDExtractAddressFields(DestOp, BaseReg, IndexReg, ScaleFactor, offset); SignedOffset = (long) offset; bool ESPrelative = (BaseReg == MD_STACK_POINTER_REG) || (IndexReg == MD_STACK_POINTER_REG); bool EBPrelative = this->UseFP && ((BaseReg == MD_FRAME_POINTER_REG) || (IndexReg == MD_FRAME_POINTER_REG)); assert(!EBPrelative || !OpNormalized); // stack operands should be normalized by now if (!(ESPrelative || EBPrelative)) return false; if (((IndexReg != R_none) && (BaseReg != R_none)) || (0 < ScaleFactor)) { SMP_msg("WARNING: WritesAboveLocalFrame called with indexed write."); PrintOperand(DestOp); return false; } // The next statement omits a complication: The possibility that OpNormalized is false, // and an ESPRelative access is above the stack frame. For the purposes of determining // whether a function is safe, this is irrelevant, because !OpNormalized would indicate // that AnalyzedSP is false, which will make the function unsafe anyway. Future uses for // other purposes need to fix this. InArgWrite = (ESPrelative && OpNormalized && (SignedOffset >= 0)) || (EBPrelative && (SignedOffset > 0)); if (InArgWrite && OpNormalized && (0 == SignedOffset)) { SMP_msg("DANGER: Write to saved return address detected in function that begins at %lx\n", (unsigned long) this->FirstEA); } return InArgWrite; }// end of SMPFunction::WritesAboveLocalFrame() // Is DestOp an indexed write above the local vars frame? bool SMPFunction::IndexedWritesAboveLocalFrame(op_t DestOp) { bool InArgWrite = false; int BaseReg, IndexReg; ushort ScaleFactor; ea_t offset; int SignedOffset; MDExtractAddressFields(DestOp, BaseReg, IndexReg, ScaleFactor, offset); bool ESPrelative = (BaseReg == MD_STACK_POINTER_REG) || (IndexReg == MD_STACK_POINTER_REG); bool EBPrelative = this->UseFP && ((BaseReg == MD_FRAME_POINTER_REG) || (IndexReg == MD_FRAME_POINTER_REG)); assert(!EBPrelative || !this->StackPtrAnalysisSucceeded() || !this->HasSTARSStackPtrAnalysisCompleted()); // stack operands should be normalized by now if (!(ESPrelative || EBPrelative)) return false; SignedOffset = (int) offset; InArgWrite = (ESPrelative && (SignedOffset > 0)) || (EBPrelative && (SignedOffset > 0)); return InArgWrite; } // end of SMPFunction::IndexedWritesAboveLocalFrame() // Is CurrOp found anywhere in the StackPtrCopySet, regardless of which address and stack delta // values are associated with it? bool SMPFunction::IsInStackPtrCopySet(op_t CurrOp) { bool found = false; // Set is composed of triples, so we have to iterate through it and compare operands. set<pair<op_t, pair<ea_t, sval_t> >, LessStackDeltaCopy>::iterator CopyIter; for (CopyIter = this->StackPtrCopySet.begin(); CopyIter != this->StackPtrCopySet.end(); ++CopyIter) { pair<op_t, pair<ea_t, sval_t> > CurrCopy = *CopyIter; op_t CopyOp = CurrCopy.first; if (IsEqOp(CopyOp, CurrOp)) { // Found it. found = true; break; } else if (CopyOp.type > CurrOp.type) { // already moved past its spot; not found break; } } return found; } // end of SMPFunction::IsInStackPtrCopySet() // Find evidence of calls to alloca(), which appear as stack space allocations (i.e. // subtractions [of unknown values(?)] from the stack pointer) AFTER the local frame allocation instruction // for this function. // Return true if such an allocation is found and false otherwise. bool SMPFunction::FindAlloca(void) { bool FoundAlloca = false; list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); SMPInstr *CurrInst; ea_t InstAddr; #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif for ( ; InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); InstAddr = CurrInst->GetAddr(); if (InstAddr > this->LocalVarsAllocInstr) { if (CurrInst->MDIsFrameAllocInstr()) { FoundAlloca = true; if (CurrInst->HasAllocaRTL()) { CurrInst->SetAllocaCall(); } } else if (CurrInst->MDIsPushInstr()) { this->PushAfterLocalVarAlloc = true; } } } return FoundAlloca; } // end of SMPFunction::FindAlloca() // Emit the annotations describing the regions of the stack frame. void SMPFunction::EmitStackFrameAnnotations(FILE *AnnotFile, SMPInstr *Instr) { ea_t addr = Instr->GetAddr(); #if 0 if (0 < IncomingArgsSize) { SMP_fprintf(AnnotFile, "%10lx %6d INARGS STACK esp + %ld %s \n", (unsigned long) addr, IncomingArgsSize, (long) (LocalVarsSize + CalleeSavedRegsSize + RetAddrSize), Instr->GetDisasm()); } #endif if (0 < this->RetAddrSize) { SMP_fprintf(AnnotFile, "%10lx %6d MEMORYHOLE STACK esp + %lu ReturnAddress \n", (unsigned long) addr, RetAddrSize, (unsigned long) (this->LocalVarsSize + this->CalleeSavedRegsSize)); } if (0 < this->CalleeSavedRegsSize) { SMP_fprintf(AnnotFile, "%10lx %6u MEMORYHOLE STACK esp + %lu CalleeSavedRegs \n", (unsigned long) addr, this->CalleeSavedRegsSize, (unsigned long) this->LocalVarsSize); } if ((0 < this->LocalVarsSize) && this->GoodLocalVarTable) { unsigned long ParentReferentID = DataReferentID++; SMP_fprintf(AnnotFile, "%10lx %6lu DATAREF STACK %ld esp + %d PARENT LocalFrame LOCALFRAME\n", (unsigned long) addr, (unsigned long) this->LocalVarsSize, ParentReferentID, 0); #if SMP_COMPUTE_STACK_GRANULARITY if (this->AnalyzedSP && !this->CallsAlloca && (BADADDR != this->LocalVarsAllocInstr)) { // We can only fine-grain the stack frame if we were able to analyze the stack if (this->OutgoingArgsSize > 0) { SMP_fprintf(AnnotFile, "%10lx %6zu DATAREF STACK %ld esp + %d CHILDOF %ld OFFSET %d OutArgsRegion OUTARGS\n", (unsigned long) addr, this->OutgoingArgsSize, DataReferentID, 0, ParentReferentID, 0); ++DataReferentID; } #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("LocalVarTable of size %d for function %s\n", this->LocalVarTable.size(), this->GetFuncName()); #endif for (size_t i = 0; i < this->LocalVarTable.size(); ++i) { #if SMP_DEBUG_STACK_GRANULARITY SMP_msg("Entry %d offset %ld size %d name %s\n", i, this->LocalVarTable[i].offset, this->LocalVarTable[i].size, this->LocalVarTable[i].VarName); #endif // Don't emit annotations for incoming or outgoing args or anything else // above or below the current local frame. if ((this->LocalVarTable[i].offset >= (long) this->FuncInfo.frsize) || (this->LocalVarTable[i].offset < (long) this->OutgoingArgsSize)) continue; SMP_fprintf(AnnotFile, "%10lx %6zu DATAREF STACK %ld esp + %ld CHILDOF %ld OFFSET %ld LOCALVAR %s \n", (unsigned long) addr, this->LocalVarTable[i].size, DataReferentID, this->LocalVarTable[i].offset, ParentReferentID, this->LocalVarTable[i].offset, this->LocalVarTable[i].VarName); ++DataReferentID; } } // end if (this->AnalyzedSP and not Alloca .... ) #endif } // end if (0 < LocalVarsSize) return; } // end of SMPFunction::EmitStackFrameAnnotations() // Audit and fix the IDA Pro code cross references for jumps and jump targets. void SMPFunction::MDAuditJumpXrefs(void) { func_tail_iterator_t FuncTail(this->GetFuncInfo()); enum cref_t NearJump = (cref_t)(fl_JN | XREF_USER); enum cref_t FarJump = (cref_t)(fl_JF | XREF_USER); for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) { const area_t &CurrChunk = FuncTail.chunk(); // Find the instructions for each chunk, audit the xrefs. for (ea_t addr = CurrChunk.startEA; addr < CurrChunk.endEA; addr = get_item_end(addr)) { flags_t InstrFlags = getFlags(addr); if (isHead(InstrFlags) && isCode(InstrFlags)) { // Fill cmd structure with disassembly of instr insn_t LocalCmd; ulong LocalFeatures; if (!SMPGetCmd(addr, LocalCmd, LocalFeatures)) { SMP_msg("ERROR: SMPGetCmd failed from MDAuditJumpXrefs at %lx\n", (unsigned long) addr); } else { unsigned short opcode = LocalCmd.itype; // 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, Distant_xrefs; for (bool ok = xrefs.SMP_first_to(addr, XREF_FAR); ok; ok = xrefs.SMP_next_to()) { ea_t DistantAddr = xrefs.GetFrom(); if ((DistantAddr != 0) && (xrefs.GetIscode())) { // Now we see if the distant instruction has an xref to this instruction. bool FoundDistantXref = false; for (bool ok2 = Distant_xrefs.SMP_first_from(DistantAddr, XREF_FAR); ok2; ok2 = Distant_xrefs.SMP_next_from()) { ea_t TargetAddr = Distant_xrefs.GetTo(); if (TargetAddr == addr) { FoundDistantXref = true; break; } } if (!FoundDistantXref) { SMP_msg("WARNING: Missing code Xref from %lx to %lx\n", (unsigned long) DistantAddr, (unsigned long) addr); long SignedAddrDiff = (long) (DistantAddr - addr); if ((SignedAddrDiff < -128) || (SignedAddrDiff > 127)) { add_cref(DistantAddr, addr, FarJump); } else { add_cref(DistantAddr, addr, NearJump); } } } } // end for all "to" xrefs // Now check the "from" xrefs to see if the target inst has the corresponding "to" xref. for (bool ok = xrefs.SMP_first_from(addr, XREF_FAR); ok; ok = xrefs.SMP_next_from()) { ea_t DistantAddr = xrefs.GetTo(); if ((DistantAddr != 0) && (xrefs.GetIscode())) { // Now we see if the distant instruction has an xref to this instruction. bool FoundDistantXref = false; for (bool ok2 = Distant_xrefs.SMP_first_to(DistantAddr, XREF_FAR); ok2; ok2 = Distant_xrefs.SMP_next_to()) { ea_t SourceAddr = Distant_xrefs.GetFrom(); if (SourceAddr == addr) { FoundDistantXref = true; break; } } if (!FoundDistantXref) { SMP_msg("WARNING: Missing code Xref to %lx from %lx\n", (unsigned long) DistantAddr, (unsigned long) addr); long SignedAddrDiff = (long) (DistantAddr - addr); if ((SignedAddrDiff < -128) || (SignedAddrDiff > 127)) { add_cref(DistantAddr, addr, FarJump); } else { add_cref(DistantAddr, addr, NearJump); } } } } // end for all "from" xrefs } // end if (!SMPGetCmd() ... else ... } // end if (IsHead() and IsCode()) } // end for all addrs in chunk } // end for all chunks return; } // end of SMPFunction::MDAuditJumpXrefs() // Main data flow analysis driver. Goes through the function and // fills all objects for instructions, basic blocks, and the function // itself. void SMPFunction::Analyze(void) { bool FoundAllCallers = false; list<SMPInstr *>::iterator FirstInBlock = this->Instrs.end(); // For starting a basic block list<SMPInstr *>::iterator LastInBlock = this->Instrs.end(); // Terminating a basic block sval_t CurrStackPointerOffset = 0; set<ea_t> FragmentWorkList; // Distant code fragments that belong to this function and need processing ea_t InstAddr; // grab address to help in debugging, conditional breakpoints, etc. ea_t PreviousIndirJumpAddr = BADADDR; enum cref_t NearJump = (cref_t)(fl_JN | XREF_USER); enum cref_t FarJump = (cref_t)(fl_JF | XREF_USER); #if SMP_DEBUG_CONTROLFLOW SMP_msg("Entering SMPFunction::Analyze.\n"); #endif // Get some basic info from the FuncInfo structure. this->Size = this->FuncInfo.endEA - this->FuncInfo.startEA; this->UseFP = (0 != (this->FuncInfo.flags & (FUNC_FRAME | FUNC_BOTTOMBP))); this->StaticFunc = (0 != (this->FuncInfo.flags & FUNC_STATIC)); this->LibFunc = (0 != (this->FuncInfo.flags & FUNC_LIB)); this->AnalyzedSP = this->FuncInfo.analyzed_sp(); #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: got basic info.\n"); #endif // Determine if we are dealing with shared chunks. size_t ChunkCounter = 0; func_tail_iterator_t FuncTail(this->GetFuncInfo()); ea_t FuncHeadLastAddr = 0; for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) { const area_t &CurrChunk = FuncTail.chunk(); ++ChunkCounter; if (1 == ChunkCounter) { // head chunk FuncHeadLastAddr = CurrChunk.endEA; } else { // a tail chunk #if STARS_FIND_UNSHARED_CHUNKS if (this->GetProg()->IsChunkUnshared(CurrChunk.startEA, this->FirstEA, FuncHeadLastAddr)) { this->UnsharedChunks = true; #if SMP_DEBUG_CHUNKS SMP_msg("INFO: Found unshared tail chunk for %s at %lx\n", this->GetFuncName(), (unsigned long) CurrChunk.startEA); #endif } else { #endif // STARS_FIND_UNSHARED_CHUNKS this->SharedChunks = true; #if SMP_DEBUG_CHUNKS SMP_msg("INFO: Found tail chunk for %s at %lx\n", this->GetFuncName(), (unsigned long) CurrChunk.startEA); #endif #if STARS_FIND_UNSHARED_CHUNKS } #endif // STARS_FIND_UNSHARED_CHUNKS } } #if STARS_AUDIT_JUMP_XREFS this->MDAuditJumpXrefs(); #endif // Cycle through all chunks that belong to the function. ChunkCounter = 0; bool GoodRTL; this->BuiltRTLs = true; for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) { const area_t &CurrChunk = FuncTail.chunk(); ++ChunkCounter; #if 0 if (CurrChunk.startEA < this->FirstEA) { this->FirstEA = CurrChunk.startEA; } #endif #if STARS_DEBUG_MEMORY_CORRUPTION bool DebugFlag = (0 == strcmp("sub_8063BE0", this->GetFuncName())); #endif // Build the instruction and block lists for the function. for (ea_t addr = CurrChunk.startEA; addr < CurrChunk.endEA; addr = get_item_end(addr)) { flags_t InstrFlags = getFlags(addr); if (!isHead(InstrFlags)) { continue; } if (!isCode(InstrFlags)) { //data // Check for code xrefs from the data. SMP_xref_t xrefs; for (bool ok = xrefs.SMP_first_from(addr, XREF_ALL); ok; ok = xrefs.SMP_next_from()) { if ((xrefs.GetTo() != 0) && (xrefs.GetIscode())) { // Found a code target, with its address in xrefs.to PrintDataToCodeXref(addr, xrefs.GetTo(), 0); } } } else { // code SMPInstr *CurrInst = new SMPInstr(addr); // Fill in the instruction data members. #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: calling CurrInst::Analyze.\n"); #endif CurrInst->Analyze(); if (SMPBinaryDebug) { SMP_msg("Disasm: %s \n", CurrInst->GetDisasm()); } #if SMP_COUNT_MEMORY_ALLOCATIONS SMPInstBytes += sizeof(*CurrInst); #endif #if SMP_USE_SSA_FNOP_MARKER if (this->Instrs.empty()) { // First instruction in function. We want to create a pseudo-instruction // at the top of the function that can hold SSA DEFs for LiveIn names // to the function. We use a floating point no-op as the pseudo-inst. // The code address is one less than the start address of the function. SMPInstr *MarkerInst = new SMPInstr(addr - 1); MarkerInst->AnalyzeMarker(); GoodRTL = MarkerInst->BuildRTL(); this->BuiltRTLs = (this->BuiltRTLs && GoodRTL); if (GoodRTL) { MarkerInst->SetGoodRTL(); } assert(FirstInBlock == this->Instrs.end()); this->Instrs.push_back(MarkerInst); #if SMP_COUNT_MEMORY_ALLOCATIONS SMPInstBytes += sizeof(*MarkerInst); #endif } #endif // Find all functions that call the current function. SMP_xref_t CurrXrefs; if (!FoundAllCallers) { for (bool ok = CurrXrefs.SMP_first_to(addr, XREF_ALL); ok; ok = CurrXrefs.SMP_next_to()) { ea_t FromAddr = CurrXrefs.GetFrom(); if ((FromAddr != 0) && (CurrXrefs.GetIscode())) { // Make sure it is not a fall-through. Must be a // control-flow instruction of some sort, including // direct or indirect calls or tail calls. SMPInstr CallInst(FromAddr); CallInst.Analyze(); SMPitype CallType = CallInst.GetDataFlowType(); if ((COND_BRANCH <= CallType) && (RETURN >= CallType)) { // Found a caller, with its call address in CurrXrefs.from this->AddCallSource(FromAddr); } } } FoundAllCallers = true; // only do this for first inst } SMPitype DataFlowType = CurrInst->GetDataFlowType(); if ((DataFlowType == INDIR_CALL) || (DataFlowType == CALL)) { // See if IDA has determined the target of the call. #if 0 CurrInst->AnalyzeCallInst(this->FirstEA, this->FuncInfo.endEA); #endif ea_t TargetAddr = CurrInst->GetCallTarget(); bool LinkedToTarget = (BADADDR != TargetAddr); if (LinkedToTarget) { if (0 == TargetAddr) { SMP_msg("WARNING: Ignoring NULL call target (unreachable) at %lx\n", (unsigned long) CurrInst->GetAddr()); } else { pair<set<ea_t>::iterator, bool> InsertResult; if (INDIR_CALL == DataFlowType) { InsertResult = this->IndirectCallTargets.insert(TargetAddr); } else { InsertResult = this->DirectCallTargets.insert(TargetAddr); } if (InsertResult.second) { this->AllCallTargets.push_back(TargetAddr); } } } if (DataFlowType == INDIR_CALL) { this->IndirectCalls = true; this->UnresolvedIndirectCalls = (!LinkedToTarget); } } // end if INDIR_CALL or CALL else if (DataFlowType == INDIR_JUMP) { this->IndirectJumps = true; #if STARS_AUDIT_INDIR_JUMP_XREFS PreviousIndirJumpAddr = addr; #endif } else if (DataFlowType == RETURN) { this->HasReturnInst = true; } // Add call targets for tail call jumps. else if (CurrInst->IsBranchToFarChunk()) { ea_t FarTargetAddr = CurrInst->GetFarBranchTarget(); if (BADADDR != FarTargetAddr) { assert((RETURN == DataFlowType) || (JUMP == DataFlowType) || (COND_BRANCH == DataFlowType)); // Optimized tail calls, where the stack frame is down to zero at the call point, // get RETURN as their DataFlowType. Might have to revisit that idea at some point. !!!!****!!!! if (this->FindDistantCodeFragment(FarTargetAddr)) { if (this->GetProg()->InsertUnsharedFragment(FarTargetAddr)) { // Fragment address was inserted in SMPProgram set, was not already there. pair<set<ea_t>::iterator, bool> InsertResult; InsertResult = FragmentWorkList.insert(FarTargetAddr); if (InsertResult.second) { SMP_msg("INFO: Found distant code fragment at %lx that can be added to func, reached from %lx\n", (unsigned long) FarTargetAddr, (unsigned long) addr); #if 0 if (FarTargetAddr < this->FirstEA) { this->FirstEA = FarTargetAddr; } #endif } else { // These kind of fragments are generally only jumped to from one place, // and jump back into the function that jumped into them. Very suspicious // to encounter such a fragment more than once, and even if it happens, // the insertion into the SMPProgram set should have failed due to already // being present. This message and assertion should never be reached. SMP_msg("FATAL ERROR: Distant fragment at %lx reached from %lx already reached from same function.\n", (unsigned long) FarTargetAddr, (unsigned long) addr); assert(InsertResult.second); // sanity lost; shut down } } else { // Fragment address was already in SMPProgram set ; // Probably added in loop at beginning that found unshared fragments. #if 0 // These kind of fragments are generally only jumped to from one place, // and jump back into the function that jumped into them. Very suspicious // to encounter such a fragment more than once. SMP_msg("WARNING: Distant fragment at %x reached from %x has already been processed.\n", FarTargetAddr, addr); #endif } } else if (!this->GetProg()->IsUnsharedFragment(FarTargetAddr)) { pair<set<ea_t>::iterator, bool> InsertResult; InsertResult = this->DirectCallTargets.insert(FarTargetAddr); if (InsertResult.second) { this->AllCallTargets.push_back(FarTargetAddr); } } } } #if STARS_AUDIT_INDIR_JUMP_XREFS if (FirstInBlock == this->Instrs.end()) { // CurrInst will start a block if (CurrInst->HasNoCodeXrefs() && (BADADDR != PreviousIndirJumpAddr) && (addr != PreviousIndirJumpAddr)) { // This block appears unreachable, but it can probably be reached by // the most recent indirect jump. IDA Pro sometimes thinks it has // resolved an indirect jump completely but has only done so partially. SMP_msg("WARNING: Adding possible missing indirect jump code Xref to %lx from %lx\n", (unsigned long) addr, (unsigned long) PreviousIndirJumpAddr); long SignedAddrDiff = (long) (addr - PreviousIndirJumpAddr); if ((SignedAddrDiff < -128) || (SignedAddrDiff > 127)) { add_cref(PreviousIndirJumpAddr, addr, FarJump); } else { add_cref(PreviousIndirJumpAddr, addr, NearJump); } CurrInst->SetJumpTarget(); } } #endif // Before we insert the instruction into the instruction // list, determine if it is a jump target that does not // follow a basic block terminator. This is the special case // of a CASE in a SWITCH that falls through into another // CASE, for example. The first sequence of statements // was not terminated by a C "break;" statement, so it // looks like straight line code, but there is an entry // point at the beginning of the second CASE sequence and // we have to split basic blocks at the entry point. if ((FirstInBlock != this->Instrs.end()) && CurrInst->IsJumpTarget()) { #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: hit special jump target case.\n"); #endif LastInBlock = --(this->Instrs.end()); SMPBasicBlock *NewBlock = new SMPBasicBlock(this, FirstInBlock, LastInBlock); // If not the first chunk in the function, it is a shared // tail chunk. if (ChunkCounter > 1) { NewBlock->SetShared(); } FirstInBlock = this->Instrs.end(); LastInBlock = this->Instrs.end(); this->Blocks.push_back(NewBlock); this->BlockCount += 1; } // Build tree RTLs for the instruction. GoodRTL = CurrInst->BuildRTL(); this->BuiltRTLs = (this->BuiltRTLs && GoodRTL); if (GoodRTL) { CurrInst->SetGoodRTL(); } #if SMP_DEBUG_BUILD_RTL else { SMP_msg("ERROR: Cannot build RTL at %lx for %s\n", (unsigned long) CurrInst->GetAddr(), CurrInst->GetDisasm()); } #endif #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: putting CurrInst on list.\n"); #endif // Insert instruction at end of list. this->Instrs.push_back(CurrInst); // Find basic block leaders and terminators. if (FirstInBlock == this->Instrs.end()) { #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: setting FirstInBlock.\n"); #endif #if SMP_USE_SSA_FNOP_MARKER if (2 == this->Instrs.size()) { // Just pushed first real instruction, after the fnop marker. FirstInBlock = this->Instrs.begin(); } else { FirstInBlock = --(this->Instrs.end()); } #else FirstInBlock = --(this->Instrs.end()); #endif } if (CurrInst->IsBasicBlockTerminator()) { #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: found block terminator.\n"); #endif LastInBlock = --(this->Instrs.end()); SMPBasicBlock *NewBlock = new SMPBasicBlock(this, FirstInBlock, LastInBlock); // If not the first chunk in the function, it is a shared // tail chunk. if (ChunkCounter > 1) { NewBlock->SetShared(); } FirstInBlock = this->Instrs.end(); LastInBlock = this->Instrs.end(); this->Blocks.push_back(NewBlock); this->BlockCount += 1; } } // end if (isCode(InstrFlags)) } // end for (ea_t addr = CurrChunk.startEA; ... ) // Handle the special case in which a function does not terminate // with a return instruction or any other basic block terminator. // Sometimes IDA Pro sees a call to a NORET function and decides // to not include the dead code after it in the function. That // dead code includes the return instruction, so the function no // longer includes a return instruction and terminates with a CALL. if (FirstInBlock != this->Instrs.end()) { LastInBlock = --(this->Instrs.end()); SMPBasicBlock *NewBlock = new SMPBasicBlock(this, FirstInBlock, LastInBlock); // If not the first chunk in the function, it is a shared // tail chunk. if (ChunkCounter > 1) { NewBlock->SetShared(); } FirstInBlock = this->Instrs.end(); LastInBlock = this->Instrs.end(); this->Blocks.push_back(NewBlock); this->BlockCount += 1; } } // end for (bool ChunkOK = ...) #if KLUDGE_VFPRINTF_FAMILY if (!this->SharedChunks && (0 != strstr(this->GetFuncName(), "printf"))) { this->SharedChunks = true; SMP_msg("INFO: Kludging function %s\n", this->GetFuncName()); } #endif #if SMP_IDAPRO52_WORKAROUND if (!this->SharedChunks && (0 == strcmp(this->GetFuncName(), "error_for_asm"))) { this->SharedChunks = true; SMP_msg("Kludging function %s\n", this->GetFuncName()); } #endif // Now that we have all instructions and basic blocks, link each instruction // to its basic block. list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; vector<SMPInstr *>::iterator InstIter; SMPInstr *CurrInst; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { CurrInst = (*InstIter); InstAddr = CurrInst->GetAddr(); CurrInst->SetBlock(CurrBlock->GetThisBlock()); #if 0 if (this->AnalyzedSP) { // Audit the IDA SP analysis. sval_t sp_delta = get_spd(this->GetFuncInfo(), InstAddr); // sp_delta is difference between current value of stack pointer // and value of the stack pointer coming into the function. It // is updated AFTER each instruction. Thus, it should not get back // above zero (e.g. to +4) until after a return instruction. if (sp_delta > 0) { // Stack pointer has underflowed, according to IDA's analysis, // which is probably incorrect. this->AnalyzedSP = false; SMP_msg("WARNING: Resetting AnalyzedSP to false for %s\n", this->GetFuncName()); SMP_msg("Underflowing instruction: %s sp_delta: %d\n", CurrInst->GetDisasm(), sp_delta); } else if (sp_delta == 0) { #if 0 // Search for tail calls. if (CurrInst->IsBranchToFarChunk()) { // After the stack has been restored to the point at which // we are ready to return, we instead find a jump to a // far chunk. This is the classic tail call optimization: // the return statement has been replaced with a jump to // another function, which will return not to this function, // but to the caller of this function. CurrInst->SetTailCall(); SMP_msg("Found tail call at %x from %s: %s\n", InstAddr, this->GetFuncName(), CurrInst->GetDisasm()); } #endif ; } else if (CurrInst->IsBranchToFarChunk() && (!this->HasSharedChunks())) { SMP_msg("WARNING: Found tail call branch with negative stack delta at %x\n", InstAddr); } } // end if (this->AnalyzedSP) #endif } // end for each inst CurrBlock->Analyze(); } // end for each block // Set up basic block links and map of instructions to blocks. this->SetLinks(); this->RPONumberBlocks(); FragmentWorkList.clear(); return; } // end of SMPFunction::Analyze() // Perform analyses that might need some info from other functions in the call graph. void SMPFunction::AdvancedAnalysis(void) { list<SMPInstr *>::iterator InstIter; SMPInstr *CurrInst; // IDA Pro has trouble with functions that do not have any local // variables. Unfortunately, the C library has plenty of these // functions. IDA usually claims that frregs is zero and frsize // is N, when the values should have been reversed. We can attempt // to detect this and fix it. IDA Pro also sometimes has trouble with // functions that allocate the stack frame, and then push registers // later before making a call, because it wants to include register // pushes below the stack frame as being part of the stack frame, // even when they are temporary saves and restores. __brk in the // Gnu stdclib is an example as of November of 2012. bool FrameInfoFixed = this->MDFixFrameInfo(); #if SMP_DEBUG_CONTROLFLOW SMP_msg("Returned from MDFixFrameInfo()\n"); #endif this->FindAllAllocsAndDeallocs(); this->CallsAlloca = this->FindAlloca(); #if 1 InstIter = this->Instrs.begin(); if ((*InstIter)->IsFloatNop()) { ++InstIter; // skip marker inst } for ( ; InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); // We can finally search for stack loads now that UseFP has been fixed by // MDFixUseFP(). Otherwise, we would do this in SMPInstr::Analyze(), // but the UseFP flag is not ready that early. CurrInst->MDFindLoadFromStack(this->UseFP); // Fix up machine dependent quirks in the def and use lists. // This used to be called from within SMPInstr.Analyze(), but info such as UseFP // is not available that early. CurrInst->MDFixupDefUseLists(); } #endif InstIter = this->Instrs.begin(); if ((*InstIter)->IsFloatNop()) { ++InstIter; // skip marker inst } for ( ; InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); ea_t InstAddr = CurrInst->GetAddr(); // for debugging breakpoints if (CurrInst->HasGoodRTL()) CurrInst->SyncAllRTs(this->UsesFramePointer(), this->GetFramePtrStackDelta()); // Detect indirect memory references. CurrInst->AnalyzeIndirectRefs(this->UseFP); #if 0 // Is the instruction a branch to a target outside the function? If // so, this function has shared tail chunks. if (CurrInst->IsBranchToFarChunk() && (!CurrInst->IsTailCall())) { this->SharedChunks = true; } #endif } // end for all instructions // Audit the call instructions and call targets. // !!!!****!!!! NOTE: Not sure the address range checks in this code are valid // for functions with scattered chunks. if ((!this->AllCallTargets.empty()) || this->UnresolvedIndirectCalls) { bool FoundInternalCallTarget = false; vector<ea_t>::iterator CurrTarget = this->AllCallTargets.begin(); set<ea_t>::iterator CurrDirectTarget, CurrIndirectTarget; while (CurrTarget != this->AllCallTargets.end()) { if ((this->FirstEA <= *CurrTarget) && (this->FuncInfo.endEA >= *CurrTarget)) { // Found a call target that is within the function. FoundInternalCallTarget = true; if (this->FirstEA == *CurrTarget) { // Direct recursion, not a pseudo-jump this->DirectlyRecursive = true; } CurrTarget = this->AllCallTargets.erase(CurrTarget); } else { ++CurrTarget; } } if (FoundInternalCallTarget) { // We have to mark the pseudo-call instructions and audit the direct and // indirect call target vectors. // Audit direct call targets. CurrDirectTarget = this->DirectCallTargets.begin(); set<ea_t>::iterator CopyOfIterator; while (CurrDirectTarget != this->DirectCallTargets.end()) { ea_t TargetAddr = (*CurrDirectTarget); if ((this->FirstEA <= TargetAddr) && (this->FuncInfo.endEA >= TargetAddr)) { // Found a call target that is within the function. CopyOfIterator = CurrDirectTarget; ++CopyOfIterator; // point to element after element that will be erased this->DirectCallTargets.erase(CurrDirectTarget); CurrDirectTarget = CopyOfIterator; } else { ++CurrDirectTarget; } } // Audit indirect call targets. CurrIndirectTarget = this->IndirectCallTargets.begin(); while (CurrIndirectTarget != this->IndirectCallTargets.end()) { ea_t TargetAddr = (*CurrIndirectTarget); if ((this->FirstEA <= TargetAddr) && (this->FuncInfo.endEA >= TargetAddr)) { // Found a call target that is within the function. CopyOfIterator = CurrIndirectTarget; ++CopyOfIterator; // point to element after element that will be erased this->IndirectCallTargets.erase(CurrIndirectTarget); CurrIndirectTarget = CopyOfIterator; } else { ++CurrIndirectTarget; } } #if 1 // Find calls used as jumps. list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); while (InstIter != this->Instrs.end()) { SMPInstr *CurrInst = (*InstIter); SMPitype InstFlow = CurrInst->GetDataFlowType(); if ((CALL == InstFlow) || (INDIR_CALL == InstFlow)) { CurrInst->AnalyzeCallInst(this->FirstEA, this->FuncInfo.endEA); } ++InstIter; } #endif } // end if (FoundInternalCallTarget) } // Figure out the stack frame and related info. #if SMP_ANALYZE_STACK_POINTER (void) this->AnalyzeStackPointerDeltas(); #else (void) this->UseIDAStackPointerDeltas(); #endif #if SMP_DEBUG_CONTROLFLOW SMP_msg("SMPFunction::Analyze: set stack frame info.\n"); #endif if (!(this->HasSharedChunks())) { this->SetStackFrameInfo(); } // end if not shared chunks else { // has shared chunks; still want to compute stack frame info #ifdef SMP_DEBUG_FUNC SMP_msg(" %s has shared chunks \n", this->GetFuncName()); #endif // Figure out the stack frame and related info. this->SetStackFrameInfo(); } this->MarkFunctionSafe(); #if SMP_COUNT_MEMORY_ALLOCATIONS SMPInstCount += ((unsigned long) this->Instrs.size()); SMPBlockCount += ((unsigned long) this->Blocks.size()); SMPLocalVarCount += ((unsigned long) this->LocalVarTable.size()); #endif } // end of SMPFunction::AdvancedAnalysis() // Count call targets that have not been processed. size_t SMPFunction::UnprocessedCalleesCount(void) { size_t UnprocessedTargetsCount = 0; size_t TargetIndex; for (TargetIndex = 0; TargetIndex < this->AllCallTargets.size(); ++TargetIndex) { SMPFunction *CurrTarget = this->GetProg()->FindFunction(this->AllCallTargets.at(TargetIndex)); if (NULL == CurrTarget) { #if 0 // Bad call targets are removed in AdvancedAnalysis(), which comes later. SMP_msg("ERROR: NULL CallTarget in UnprocessedCalleesCount() at TargetIndex %zu \n", TargetIndex); #endif } else if (!(CurrTarget->IsFuncProcessed())) { ++UnprocessedTargetsCount; } } return UnprocessedTargetsCount; } // end of SMPFunction::UnprocessedCalleesCount() ea_t SMPFunction::GetFirstUnprocessedCallee(void) { ea_t CalleeAddr = BADADDR; size_t TargetIndex; for (TargetIndex = 0; TargetIndex < this->AllCallTargets.size(); ++TargetIndex) { ea_t TargetAddr = this->AllCallTargets.at(TargetIndex); SMPFunction *CurrTarget = this->GetProg()->FindFunction(TargetAddr); if ((NULL != CurrTarget) && (!(CurrTarget->IsFuncProcessed()))) { CalleeAddr = TargetAddr; break; } } return CalleeAddr; } // end of SMPFunction::GetFirstUnprocessedCallee() // Is the code starting at TargetAddr a non-shared chunk that jumps back into our function? // If so, it can be incorporated into our function rather than treated as a separate function. // This method is called only when we see a jump outside our function, and it is looking for // code fragments that are not really functions (i.e. don't have a stack frame, jump straight back // into our function after executing a few instructions, not a chunk shared among other functions). // These code fragments are found in the locking and unlocking code of the gcc stdlib, for example. bool SMPFunction::FindDistantCodeFragment(ea_t TargetAddr) { bool PrivateFragment = false; func_t *TargetFunc = get_func(TargetAddr); if (TargetFunc) { // Determine if we are dealing with shared chunks. size_t ChunkCounter = 0; func_tail_iterator_t FuncTail(TargetFunc); for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) { ++ChunkCounter; } if (1 < ChunkCounter) { SMP_msg("INFO: Code fragment at %lx is shared chunk.\n", (unsigned long) TargetAddr); } else { bool JumpsBackIntoCurrentFunc = false; bool HasReturnInstruction = false; bool AllocatesStackFrame = false; for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) { const area_t &CurrChunk = FuncTail.chunk(); ++ChunkCounter; // Build the instruction and block lists for the function. for (ea_t addr = CurrChunk.startEA; addr < CurrChunk.endEA; addr = get_item_end(addr)) { flags_t InstrFlags = getFlags(addr); if (isHead(InstrFlags) && isCode(InstrFlags)) { SMPInstr *CurrInst = new SMPInstr(addr); // Fill in the instruction data members. CurrInst->Analyze(); // Search for two negative indicators (stack allocations and returns) // and one positive indicator (jump back into this function). if (CurrInst->MDIsReturnInstr()) { HasReturnInstruction = true; break; } else if (CurrInst->MDIsFrameAllocInstr()) { AllocatesStackFrame = true; break; } else { SMPitype FlowType = CurrInst->GetDataFlowType(); if ((JUMP == FlowType) || (INDIR_JUMP == FlowType)) { if (CurrInst->BuildRTL()) { ea_t FragmentJumpTarget = CurrInst->GetJumpTarget(); if ((FragmentJumpTarget >= this->FirstEA) && (FragmentJumpTarget <= this->FuncInfo.endEA)) { JumpsBackIntoCurrentFunc = true; } } } } } // end if isHead() and isCode() } // end for all addrs in chunk } // end for all chunks (there will only be one) PrivateFragment = (JumpsBackIntoCurrentFunc && (!HasReturnInstruction) && (!AllocatesStackFrame)); } // end if (1 < ChunkCounter) ... else ... } // end if (TargetFunc) return PrivateFragment; } // end of SMPFunction::FindDistantCodeFragment() // Free memory that is no longer needed after loop 2 of SMPProgram::Analyze(). void SMPFunction::FreeUnusedMemory2(void) { size_t UnusedElements; size_t CurrSize; #if 0 // Go through vector containers and resize to current capacity, if the vector // has been fully computed by the time SMPProgram:Analyze() loop 2 completes. CurrSize = this->DirectCallTargets.size(); UnusedElements = this->DirectCallTargets.capacity() - CurrSize; if (0 < UnusedElements) { UnusedIntCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<ea_t>(this->DirectCallTargets).swap(this->DirectCallTargets); #else this->DirectCallTargets.resize(CurrSize); #endif } CurrSize = this->IndirectCallTargets.size(); UnusedElements = this->IndirectCallTargets.capacity() - CurrSize; if (0 < UnusedElements) { UnusedIntCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<ea_t>(this->IndirectCallTargets).swap(this->IndirectCallTargets); #else this->IndirectCallTargets.resize(CurrSize); #endif } #endif CurrSize = this->AllCallTargets.size(); UnusedElements = this->AllCallTargets.capacity() - CurrSize; if (0 < UnusedElements) { UnusedIntCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<ea_t>(this->AllCallTargets).swap(this->AllCallTargets); #else this->AllCallTargets.resize(CurrSize); #endif } CurrSize = this->SavedRegLoc.size(); UnusedElements = this->SavedRegLoc.capacity() - CurrSize; if (0 < UnusedElements) { UnusedIntCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<int>(this->SavedRegLoc).swap(this->SavedRegLoc); #else this->SavedRegLoc.resize(CurrSize); #endif } CurrSize = this->RPOBlocks.size(); UnusedElements = this->RPOBlocks.capacity() - CurrSize; if (0 < UnusedElements) { list<SMPBasicBlock *>::iterator DummyIter = this->Blocks.end(); UnusedIntCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<SMPBasicBlock *>(this->RPOBlocks).swap(this->RPOBlocks); #else this->RPOBlocks.resize(CurrSize, DummyIter); #endif } CurrSize = this->LocalVarTable.size(); UnusedElements = this->LocalVarTable.capacity() - CurrSize; if (0 < UnusedElements) { struct LocalVar DummyVar; DummyVar.offset = 0; DummyVar.size = 0; UnusedStructCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<struct LocalVar>(this->LocalVarTable).swap(this->LocalVarTable); #else this->LocalVarTable.resize(CurrSize, DummyVar); #endif } CurrSize = this->StackFrameMap.size(); UnusedElements = this->StackFrameMap.capacity() - CurrSize; if (0 < UnusedElements) { struct StackFrameEntry DummyEntry; DummyEntry.offset = 0; DummyEntry.VarPtr = NULL; UnusedStructCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<struct StackFrameEntry>(this->StackFrameMap).swap(this->StackFrameMap); #else this->StackFrameMap.resize(CurrSize, DummyEntry); #endif } CurrSize = this->FineGrainedStackTable.size(); UnusedElements = this->FineGrainedStackTable.capacity() - CurrSize; if (0 < UnusedElements) { struct FineGrainedInfo DummyFG; DummyFG.SignMiscInfo = 0; DummyFG.SizeInfo = 0; UnusedStructCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<struct FineGrainedInfo>(this->FineGrainedStackTable).swap(this->FineGrainedStackTable); #else this->FineGrainedStackTable.resize(CurrSize, DummyFG); #endif } return; } // end of SMPFunction::FreeUnusedMemory2() // Free memory that is no longer needed after loop 3 of SMPProgram::Analyze(). void SMPFunction::FreeUnusedMemory3(void) { size_t UnusedElements; size_t CurrSize; // Go through vector containers and resize to current capacity, if the vector // has been fully computed by the time SMPProgram:Analyze() loop 2 completes. CurrSize = this->ReturnRegTypes.size(); UnusedElements = this->ReturnRegTypes.capacity() - CurrSize; if (0 < UnusedElements) { UnusedIntCount += (unsigned long) UnusedElements; #if SMP_SHRINK_TO_FIT std::vector<SMPOperandType>(this->ReturnRegTypes).swap(this->ReturnRegTypes); #else this->ReturnRegTypes.resize(CurrSize); #endif } return; } // end of SMPFunction::FreeUnusedMemory3() // Free memory that is no longer needed after type inference (loop 4 of SMPProgram::Analyze()). void SMPFunction::FreeUnusedMemory4(void) { this->KillSet.clear(); this->LiveOutSet.clear(); this->LiveInSet.clear(); this->StackFrameMap.clear(); this->BlocksDefinedIn.clear(); #if SMP_SHRINK_TO_FIT std::set<op_t, LessOp>(this->KillSet).swap(this->KillSet); std::set<op_t, LessOp>(this->LiveOutSet).swap(this->LiveOutSet); std::set<op_t, LessOp>(this->LiveInSet).swap(this->LiveInSet); #endif list<SMPBasicBlock *>::iterator BlockIter; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { (*BlockIter)->FreeUnusedMemory4(); } return; } // end of SMPFunction::FreeUnusedMemory4() // Free SSA data structures that are no longer needed when all SSA numbers have // been recorded in DEFs and USEs. void SMPFunction::FreeSSAMemory(void) { this->IDom.clear(); this->DomTree.clear(); this->BlocksDefinedIn.clear(); this->SSACounter.clear(); this->SSAStack.clear(); #if SMP_SHRINK_TO_FIT vector<int>(this->IDom).swap(this->IDom); vector<pair<int, list<int> > >(this->DomTree).swap(this->DomTree); vector<list<int> >(this->BlocksDefinedIn).swap(this->BlocksDefinedIn); vector<int>(this->SSACounter).swap(this->SSACounter); vector<list<int> >(this->SSAStack).swap(this->SSAStack); #endif list<SMPBasicBlock *>::iterator BlockIter; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { (*BlockIter)->FreeSSAMemory(); } return; } // end of SMPFunction::FreeSSAMemory() // For each instruction, mark the non-flags-reg DEFs as having live // metadata (mmStrata needs to fetch and track this metadata for this // instruction) or dead metadata (won't be used as addressing reg, won't // be stored to memory, won't be returned to caller). void SMPFunction::AnalyzeMetadataLiveness(void) { bool changed; int BaseReg; int IndexReg; ushort ScaleFactor; ea_t offset; op_t BaseOp = InitOp, IndexOp = InitOp, ReturnOp = InitOp; BaseOp.type = o_reg; IndexOp.type = o_reg; ReturnOp.type = o_reg; list<SMPInstr *>::iterator InstIter; list<SMPInstr *>::reverse_iterator RevInstIter; set<DefOrUse, LessDefUse>::iterator CurrDef; set<DefOrUse, LessDefUse>::iterator CurrUse; set<DefOrUse, LessDefUse>::iterator NextUse; bool DebugFlag = false; bool UseFP = this->UsesFramePointer(); int IterationCount = 0; #if SMP_DEBUG_DATAFLOW if (0 == strcmp("uw_frame_state_for", this->GetFuncName())) { DebugFlag = true; } #endif do { changed = false; ++IterationCount; bool SafeMemDest; if (DebugFlag) { SMP_msg("AnalyzeMetadataLiveness iteration count: %d \n", IterationCount); } for (RevInstIter = this->Instrs.rbegin(); RevInstIter != this->Instrs.rend(); ++RevInstIter) { SMPInstr *CurrInst = (*RevInstIter); ea_t InstAddr = CurrInst->GetAddr(); SafeMemDest = false; // true for some SafeFunc instructions // Skip the SSA marker instruction. if (NN_fnop == CurrInst->GetCmd().itype) continue; if (DebugFlag) { SMP_msg("Inst addr: %lx \n", (unsigned long) CurrInst->GetAddr()); } CurrDef = CurrInst->GetFirstDef(); while (CurrDef != CurrInst->GetLastDef()) { if (DEF_METADATA_UNANALYZED == CurrDef->GetMetadataStatus()) { op_t DefOp = CurrDef->GetOp(); // Handle special registers never used as address regs. if (DefOp.is_reg(X86_FLAGS_REG) || ((o_trreg <= DefOp.type) && (o_xmmreg >= DefOp.type))) { CurrDef = CurrInst->SetDefMetadata(DefOp, DEF_METADATA_UNUSED); changed = true; } else if (MDIsStackOrFramePointerReg(DefOp, UseFP)) { // Stack pointer register DEFs always have live // metadata, but we don't need to propagate back // through particular DEF-USE chains. CurrDef = CurrInst->SetDefMetadata(DefOp, DEF_METADATA_USED); changed = true; } else if ((o_mem <= DefOp.type) && (o_displ >= DefOp.type)) { // DEF is a memory operand. The addressing registers // therefore have live metadata, and the memory metadata is live. // EXCEPTION: If the function is Safe, then direct stack writes // to local variables (above the outgoing args area of the frame) // are not live metadata, and there will be no indirect local frame // writes, by definition of "safe." So, for safe funcs, only // the o_mem (globals) and indirect writes are live metadata. if (this->SafeFunc && MDIsStackAccessOpnd(DefOp, this->UseFP) && (!this->WritesAboveLocalFrame(DefOp, CurrInst->AreDefsNormalized())) && (!this->IsInOutgoingArgsRegion(DefOp))) { ++CurrDef; SafeMemDest = true; continue; } CurrDef = CurrInst->SetDefMetadata(DefOp, DEF_METADATA_USED); changed = true; MDExtractAddressFields(DefOp, BaseReg, IndexReg, ScaleFactor, offset); if (R_none != BaseReg) { BaseOp.reg = MDCanonicalizeSubReg((ushort) BaseReg); BaseOp.dtyp = CurrInst->GetOperandDtypField(); // canonical reg width if (MDIsStackOrFramePointerReg(BaseOp, UseFP)) { ; // do nothing; DEF handled by case above } else { CurrUse = CurrInst->FindUse(BaseOp); if (CurrUse == CurrInst->GetLastUse()) { SMP_msg("FATAL ERROR: BaseReg %d not in USE list at %lx for %s\n", BaseOp.reg, (unsigned long) CurrInst->GetAddr(), CurrInst->GetDisasm()); } assert(CurrUse != CurrInst->GetLastUse()); if (this->IsGlobalName(BaseOp)) { changed |= this->PropagateGlobalMetadata(BaseOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } else { changed |= CurrInst->GetBlock()->PropagateLocalMetadata(BaseOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } } } // end if R_none != BaseReg if (R_none != IndexReg) { IndexOp.reg = MDCanonicalizeSubReg((ushort) IndexReg); IndexOp.dtyp = CurrInst->GetOperandDtypField(); // canonical reg width if (MDIsStackOrFramePointerReg(IndexOp, UseFP)) { ; // do nothing; DEF handled by case above } else { CurrUse = CurrInst->FindUse(IndexOp); if (CurrUse == CurrInst->GetLastUse()) { SMP_msg("FATAL ERROR: IndexReg %d not in USE list at %lx for %s\n", IndexOp.reg, (unsigned long) CurrInst->GetAddr(), CurrInst->GetDisasm()); } assert(CurrUse != CurrInst->GetLastUse()); if (0 != ScaleFactor) { ; // mmStrata knows scaled reg is NUMERIC // ... its metadata is not fetched } else if (this->IsGlobalName(IndexOp)) { changed |= this->PropagateGlobalMetadata(IndexOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } else { changed |= CurrInst->GetBlock()->PropagateLocalMetadata(IndexOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } } } // end if R_none != IndexReg } // end if X86_FLAGS_REG .. else if stack ptr ... } // end if unanalyzed metadata usage ++CurrDef; } // end while processing DEFs if ((RETURN == CurrInst->GetDataFlowType()) || (CurrInst->IsTailCall()) // quasi-return || (CALL == CurrInst->GetDataFlowType()) || (INDIR_CALL == CurrInst->GetDataFlowType())) { // The EAX and EDX registers can be returned to the caller, // which might use their metadata. They show up as USEs // of the return instruction. Some library functions // pass return values in non-standard ways. e.g. through // EBX or EDI, so we treat all return regs the same. // For CALL instructions, values can be passed in caller-saved // registers, unfortunately, so the metadata is live-in. CurrUse = CurrInst->GetFirstUse(); while (CurrUse != CurrInst->GetLastUse()) { NextUse = CurrUse; ++NextUse; ReturnOp = CurrUse->GetOp(); if (DebugFlag) { SMP_msg("ReturnOp: "); PrintOperand(ReturnOp); SMP_msg("\n"); } if ((o_reg == ReturnOp.type) && (!MDIsStackOrFramePointerReg(ReturnOp, UseFP)) && (!ReturnOp.is_reg(X86_FLAGS_REG))) { if (this->IsGlobalName(ReturnOp)) { changed |= this->PropagateGlobalMetadata(ReturnOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } else { changed |= CurrInst->GetBlock()->PropagateLocalMetadata(ReturnOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } } CurrUse = NextUse; } // end while all USEs } // end if return or call else if (CurrInst->HasDestMemoryOperand() || CurrInst->MDIsPushInstr()) { // Memory writes cause a lot of metadata usage. // Addressing registers in the memory destination // have live metadata used in bounds checking. The // register being stored to memory could end up being // used in some other bounds checking, unless we // have precise memory tracking and know that it // won't. // We handled the addressing registers above, so we // handle the register written to memory here. // The same exception applies as above: If the destination // memory operand is not a stack write, then safe functions // do not need to track the metadata. // If we push a register and have callees, the metadata could // be live, if the callee gets its incoming args from our push // instructions. if (SafeMemDest && !(CurrInst->MDIsPushInstr() && !this->IsLeaf())) { continue; // go to next instruction } CurrUse = CurrInst->GetFirstUse(); while (CurrUse != CurrInst->GetLastUse()) { NextUse = CurrUse; ++NextUse; op_t UseOp = CurrUse->GetOp(); // NOTE: **!!** To be less conservative, we // should propagate less for exchange category // instructions. if ((UseOp.type == o_reg) && (!MDIsStackOrFramePointerReg(UseOp, UseFP)) && (!UseOp.is_reg(X86_FLAGS_REG))) { if (this->IsGlobalName(UseOp)) { changed |= this->PropagateGlobalMetadata(UseOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } else { changed |= CurrInst->GetBlock()->PropagateLocalMetadata(UseOp, DEF_METADATA_USED, CurrUse->GetSSANum(), InstAddr); } } // end if register CurrUse = NextUse; } // end while all USEs } // end if call or return else if memdest ... } // end for all instructions } while (changed); // All DEFs that still have status DEF_METADATA_UNANALYZED can now // be marked as DEF_METADATA_UNUSED. for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); if (NN_fnop == CurrInst->GetCmd().itype) continue; CurrDef = CurrInst->GetFirstDef(); while (CurrDef != CurrInst->GetLastDef()) { if (DEF_METADATA_UNANALYZED == CurrDef->GetMetadataStatus()) { CurrDef = CurrInst->SetDefMetadata(CurrDef->GetOp(), DEF_METADATA_UNUSED); assert(CurrDef != CurrInst->GetLastDef()); } ++CurrDef; } } return; } // end of SMPFunction::AnalyzeMetadataLiveness() // Propagate the metadata Status for UseOp/SSANum to its global DEF. // Return true if successful. bool SMPFunction::PropagateGlobalMetadata(op_t UseOp, SMPMetadataType Status, int SSANum, ea_t UseAddr) { bool changed = false; bool UseFP = this->UsesFramePointer(); if ((0 > SSANum) || (o_void == UseOp.type) || (!MDIsDataFlowOpnd(UseOp, UseFP)) || MDIsIndirectMemoryOpnd(UseOp, UseFP)) return false; // Find the DEF of UseOp with SSANum. ea_t DefAddr; SMPBasicBlock *UseBlock; SMPBasicBlock *DefBlock; if (UseAddr > this->GetNumBlocks()) { // UseAddr is an inst addr UseBlock = this->GetBlockFromInstAddr(UseAddr); } else { // UseAddr is a block number UseBlock = this->GetBlockByNum((size_t) UseAddr); } DefAddr = UseBlock->GetUltimateDefAddr(UseOp, UseAddr, SSANum, false); if (BADADDR == DefAddr) { return changed; } if (DefAddr > this->GetNumBlocks()) { // found a DEF inst. SMPInstr *CurrInst = this->GetInstFromAddr(DefAddr); ea_t InstAddr = DefAddr; DefBlock = this->GetBlockFromInstAddr(DefAddr); set<DefOrUse, LessDefUse>::iterator CurrDef; set<DefOrUse, LessDefUse>::iterator CurrUse; CurrDef = CurrInst->FindDef(UseOp); assert(CurrDef != CurrInst->GetLastDef()); assert(SSANum == CurrDef->GetSSANum()); if (Status != CurrDef->GetMetadataStatus()) { CurrDef = CurrInst->SetDefMetadata(UseOp, Status); changed = (CurrDef != CurrInst->GetLastDef()); bool PropThroughUses = changed; // If source operand was memory, we have two cases. // (1) The instruction could be a load, in which // case we should simply terminate the // propagation, because the prior DEF of a memory // location is always considered live metadata // already, and we do not want to propagate liveness // to the address regs in the USE list. // EXCEPTION: For safe funcs, we propagate liveness // for stack locations. // (2) We could have an arithmetic operation such // as reg := reg arithop memsrc. In this case, we // still do not want to propagate through the memsrc, // (with the same safe func EXCEPTION), // but the register is both DEF and USE and we need // to propagate through the register. if (CurrInst->HasSourceMemoryOperand()) { if (this->SafeFunc) { op_t MemSrcOp = CurrInst->MDGetMemUseOp(); assert(o_void != MemSrcOp.type); if (MDIsDirectStackAccessOpnd(MemSrcOp, this->UseFP)) { // We have a SafeFunc stack access. This is // the EXCEPTION case where we want to // propagate metadata liveness for a memory // location. CurrUse = CurrInst->FindUse(MemSrcOp); assert(CurrUse != CurrInst->GetLastUse()); if (this->IsGlobalName(MemSrcOp)) { changed |= this->PropagateGlobalMetadata(MemSrcOp, Status, CurrUse->GetSSANum(), InstAddr); } else { changed |= CurrInst->GetBlock()->PropagateLocalMetadata(MemSrcOp, Status, CurrUse->GetSSANum(), InstAddr); } } // end if stack access operand } // end if SafeFunc if (3 == CurrInst->GetOptType()) { // move inst PropThroughUses = false; // load address regs are not live metadata } else if ((5 == CurrInst->GetOptType()) || (NN_and == CurrInst->GetCmd().itype) || (NN_or == CurrInst->GetCmd().itype) || (NN_xor == CurrInst->GetCmd().itype)) { // add, subtract, and, or with memsrc // Find the DEF reg in the USE list. CurrUse = CurrInst->FindUse(UseOp); assert(CurrUse != CurrInst->GetLastUse()); changed |= this->PropagateGlobalMetadata(UseOp, Status, CurrUse->GetSSANum(), InstAddr); PropThroughUses = false; } } // end if memory source // Now, propagate the metadata status to all the // non-memory, non-flags-reg, non-special-reg // (i.e. regular registers) USEs. if (PropThroughUses) { CurrUse = CurrInst->GetFirstUse(); while (CurrUse != CurrInst->GetLastUse()) { op_t CurrUseOp = CurrUse->GetOp(); // NOTE: **!!** To be less conservative, we // should propagate less for exchange category // instructions. if ((CurrUseOp.type == o_reg) && (!MDIsStackOrFramePointerReg(CurrUseOp, UseFP)) && (!CurrUseOp.is_reg(X86_FLAGS_REG))) { if (this->IsGlobalName(CurrUseOp)) { changed |= this->PropagateGlobalMetadata(CurrUseOp, Status, CurrUse->GetSSANum(), InstAddr); } else { changed |= CurrInst->GetBlock()->PropagateLocalMetadata(CurrUseOp, Status, CurrUse->GetSSANum(), InstAddr); } } ++CurrUse; } // end while all USEs } } } else { // Found a DEF block number in DefAddr. // Check the Phi functions DefBlock = this->GetBlockByNum((size_t) DefAddr); set<SMPPhiFunction, LessPhi>::iterator DefPhi; DefPhi = DefBlock->FindPhi(UseOp); assert(DefPhi != DefBlock->GetLastPhi()); assert(SSANum == DefPhi->GetDefSSANum()); if (Status != DefPhi->GetDefMetadata()) { DefPhi = DefBlock->SetPhiDefMetadata(UseOp, Status); changed = true; // If the Phi DEF has live metadata, then the Phi // USEs each have live metadata. Propagate. int UseSSANum; for (size_t index = 0; index < DefPhi->GetPhiListSize(); ++index) { UseSSANum = DefPhi->GetUseSSANum(index); // UseSSANum can be -1 in some cases because // we conservatively make EAX and EDX be USEs // of all return instructions, when the function // might have a void return type, making it // appear as if an uninitialized EAX or EDX // could make it to the return block. if (0 <= UseSSANum) { changed |= this->PropagateGlobalMetadata(UseOp, Status, UseSSANum, DefAddr); } } } } // end if (DefAddr is inst addr) else ... [DefAddr is block number] return changed; } // end of SMPFunction::PropagateGlobalMetadata() // Find consecutive DEFs of the same type and mark the second one redundant. void SMPFunction::FindRedundantMetadata(void) { list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; bool changed = false; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); changed |= CurrBlock->FindRedundantLocalMetadata(this->SafeFunc); } return; } // end of SMPFunction::FindRedundantMetadata() // Perform SCCP to find constant values for DEFs, store in this->ConstantDefs void SMPFunction::SparseConditionalConstantPropagation(void) { // We perform the SCCP (Sparse Conditional Constant Propagation) algorithm // as found in Cooper & Torczon, "Engineering a Compiler." // CFGWorkList := { all edges from pseudo-entry block to entry blocks } // We do not have a pseudo-entry block, so we special case by starting processing at // the first basic block, block number 0, which is our entry block. list<pair<int, int> > CFGWorkList; // edges from pair.first = source block number to pair.second = dest block number pair<int, int> InitEdge(-1, 0); // -1 is pseudo-block-number CFGWorkList.push_back(InitEdge); size_t BlockNumLimit = this->Blocks.size(); vector<STARSBitSet> ExecutedEdgeBitSet(BlockNumLimit); // records which edges have been executed in SCCP; row = DestBlockNum, col (bit) = SrcBlockNum // for each edge e in the CFG // mark e as unexecuted for (size_t EdgeIndex = 0; EdgeIndex < BlockNumLimit; ++EdgeIndex) { ExecutedEdgeBitSet[EdgeIndex].AllocateBits(BlockNumLimit); // allocate and zero all bits } this->ResetSCCPVisitedBlocks(); // records which blocks have been visited in SCCP algorithm // SSAWorkList := { empty set } list<pair<int, int> > SSAWorkList; // pair.first = block number, pair.second = name+SSA hash SSAWorkList.clear(); // for each ref def x in the procedure // Value(x) = TOP // We currently implement this by having this->ConstantDefs contain no entry for defs with value TOP // while ((CFGWorkList is not empty) or (SSAWorkList is not empty)) while (!(CFGWorkList.empty() && SSAWorkList.empty())) { // if (CFGWorkList is not empty) then // remove an edge e = (m, n) from the CFGWorkList // if (e is marked as unexecuted) then // mark e as executed // EvaluateAllPhisInBlock(n) // if (e is only edge into n marked as executed) then // for (each instruction i in block n) do // if (i is an assignment) then // EvaluateAssign(i) // else if (i is a conditional branch) then // EvaluateConditional(i) // endif // endfor // Put block successors on CFGWorkList, based on conditional branch evaluation if any // endif // endif // endif // if (CFGWorkList is not empty) then if (!(CFGWorkList.empty())) { // remove an edge e = (m, n) from the CFGWorkList pair<int, int> CurrentEdge = CFGWorkList.front(); CFGWorkList.pop_front(); // if (e is marked as unexecuted) then int SrcBlockNum = CurrentEdge.first; int DestBlockNum = CurrentEdge.second; bool UnexecutedEdge = (0 > CurrentEdge.first); if (!UnexecutedEdge) { UnexecutedEdge = (!ExecutedEdgeBitSet.at((size_t) DestBlockNum).GetBit((size_t) SrcBlockNum)); } if (UnexecutedEdge) { // mark e as executed if (0 <= SrcBlockNum) { ExecutedEdgeBitSet.at((size_t) DestBlockNum).SetBit((size_t) SrcBlockNum); } // EvaluateAllPhisInBlock(n) this->EvaluateAllPhiConstants(DestBlockNum, ExecutedEdgeBitSet, SSAWorkList); // if (e is only edge into n marked as executed) then SMPBasicBlock *CurrBlock = this->GetBlockByNum(DestBlockNum); if (!(CurrBlock->IsSCCPVisited())) { // for (each instruction i in block n) do // if (i is an assignment) then // EvaluateAssign(i) // else if (i is a conditional branch) then // EvaluateConditional(i) // endif // endfor enum STARSBranchConst BranchEval = STARS_BRANCH_UNKNOWN; CurrBlock->SCCPEvaluateConstants(BranchEval, CFGWorkList, SSAWorkList); // also marks block as SCCP visited // endif } // endif } // endif } // // if (SSAWorkList is not empty) then // remove an edge e = (s, d) from SSAWorkList // c := CFG node that uses d // if (any edge entering c is marked as executable) then // if (d is a phi function argument) then // EvaluatePhi(d) // else // for (each instruction i in block c) do // if (i is an assignment that uses d) then // EvaluateAssign(i) // else if (i is a conditional branch that uses d) then // EvaluateConditional(i) // endif // endfor // endif // endif // endif // if (SSAWorkList is not empty) then if (!(SSAWorkList.empty())) { // remove an edge e = (s, d) from SSAWorkList pair<int, int> SSAEdge = SSAWorkList.front(); SSAWorkList.pop_front(); int BlockNum = SSAEdge.first; int DefHashValue = SSAEdge.second; // c := CFG node that uses d assert(0 <= BlockNum); SMPBasicBlock *CurrBlock = this->GetBlockByNum((size_t) BlockNum); op_t DefOp = InitOp; DefOp.type = o_reg; DefOp.reg = (DefHashValue & 0x0000ffff); int DefSSANum = ((DefHashValue & 0x7fff0000) >> 16); bool LocalName = CurrBlock->IsLocalName(DefOp); // if (any edge entering c is marked as executable) then // if (d is a phi function argument) then // EvaluatePhi(d) // else // for (each instruction i in block c) do // if (i is an assignment that uses d) then // EvaluateAssign(i) // else if (i is a conditional branch that uses d) then // EvaluateConditional(i) // endif // endfor // endif // endif assert(CurrBlock->IsSCCPVisited()); // a local name is only added to SSAWorkList from within the block vector<SMPInstr *>::iterator InstIter; bool FoundReDEF = false; for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); set<DefOrUse, LessDefUse>::iterator UseIter = CurrInst->FindUse(DefOp); if (UseIter != CurrInst->GetLastUse()) { // operand is USEd; check SSANum int UseSSANum = UseIter->GetSSANum(); if (UseSSANum == DefSSANum) { SMPitype DataFlowType = CurrInst->GetDataFlowType(); if (DEFAULT == DataFlowType) { CurrInst->SCCPEvaluateAssignment(SSAWorkList); } else if (COND_BRANCH == DataFlowType) { enum STARSBranchConst BranchEval; CurrInst->SCCPEvaluateCondBranch(BranchEval); CurrBlock->SCCPHandleSuccessors(BranchEval, CFGWorkList); } } } set<DefOrUse, LessDefUse>::iterator DefIter = CurrInst->FindDef(DefOp); if (DefIter != CurrInst->GetLastDef()) { // check for re-DEF int NewDefSSANum = DefIter->GetSSANum(); if (NewDefSSANum > DefSSANum) { // re-DEF; we can stop searching for USEs of DefOp/DefSSANum in this block FoundReDEF = true; break; } } } // end for all insts in block // See if we need to search for other blocks that use DefOp/DefSSANum if (!(LocalName || FoundReDEF)) { // we have global name that is not redefined list<SMPBasicBlock *>::iterator SuccIter; for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { SMPBasicBlock *SuccBlock = (*SuccIter); if (SuccBlock->IsSCCPVisited() && SuccBlock->IsLiveIn(DefOp)) { this->ResetProcessedBlocks(); SuccBlock->SCCPGlobalPropagationHelper(DefOp, DefSSANum, ExecutedEdgeBitSet, CFGWorkList, SSAWorkList); } } } // endif } #if 0 SSAWorkList.clear(); // temporary stub #endif // endwhile } // Go back over the basic blocks and detect never-visited blocks. Label these as unreachable. list<SMPBasicBlock *>::iterator BlockIter; bool UnreachableBlocksFound = false; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { SMPBasicBlock *CurrBlock = (*BlockIter); if (!(CurrBlock->IsSCCPVisited())) { UnreachableBlocksFound = true; CurrBlock->SetUnreachableBlock(true); #if STARS_DEBUG_SCCP ea_t BlockAddr = CurrBlock->GetFirstAddr(); SMP_msg("INFO: SCCP found unreachable block at %lx\n", (unsigned long) BlockAddr); #endif #if STARS_SCCP_CONVERT_UNREACHABLE_BLOCKS CurrBlock->SCCPNullifyUnreachableBlock(); #endif } #if STARS_SCCP_GATHER_STATISTICS else if (CurrBlock->HasCallInstruction()) { CurrBlock->SCCPGatherStatistics(); } #endif } return; } // end of SMPFunction::SparseConditionalConstantPropagation() // part of SCCP processing; propagate const DEFs into Phi USEs and Phi DEFs void SMPFunction::EvaluateAllPhiConstants(int BlockNum, vector<STARSBitSet> ExecutedEdgeBitSet, list<pair<int, int> > &SSAWorkList) { // For all Phi DEFs of the type we are tracking for const values: // If Phi DEF const value is not already the lattice bottom value: // Accumulate const DEF values only for Phi USEs that correspond to incoming // edges that are executed. // If we have a consistent const value, set Phi DEF const value to it; if // we have conflicting const values, set Phi DEF const value to type lattice bottom. // If we set the Phi DEF const value to a new value, then propagate along SSA edges. // For all Phi DEFs of the type we are tracking for const values: SMPBasicBlock *CurrBlock = this->GetBlockByNum(BlockNum); set<SMPPhiFunction, LessPhi>::iterator PhiIter; for (PhiIter = CurrBlock->GetFirstPhi(); PhiIter != CurrBlock->GetLastPhi(); ++PhiIter) { op_t PhiOp = PhiIter->GetAnyOp(); if (!(o_reg == PhiOp.type)) // !!!!****!!!! Add stack locations also in safe functions continue; int DefSSANum = PhiIter->GetDefSSANum(); int DefHashValue = HashGlobalNameAndSSA(PhiOp, DefSSANum); // If Phi DEF const value is not already the lattice bottom value: map<int, struct STARS_SCCP_Const_Struct>::iterator ConstIter = this->FindConstValue(DefHashValue); STARSConstantValueType DefConstValType = STARS_CONST_TOP; // default; no entry in map if (ConstIter != this->GetLastConstValueIter()) { // found entry in map DefConstValType = ConstIter->second.ConstType; } if (DefConstValType != STARS_CONST_BOTTOM) { // Accumulate const DEF values only for Phi USEs that correspond to incoming // edges that are executed. uval_t ConstUseVal; bool ConstUseValueSeen = false; list<SMPBasicBlock *>::iterator PredIter; size_t PredIndex = 0; STARSConstantValueType DefFinalConstValType = DefConstValType; for (PredIter = CurrBlock->GetFirstPred(); PredIter != CurrBlock->GetLastPred(); ++PredIter) { int IncomingBlockNum = (*PredIter)->GetNumber(); bool ExecutedIncomingEdge = ExecutedEdgeBitSet.at(BlockNum).GetBit(IncomingBlockNum); if (ExecutedIncomingEdge) { int UseSSANum = PhiIter->GetUseSSANum(PredIndex); int UseHashValue = HashGlobalNameAndSSA(PhiOp, UseSSANum); // If we have a consistent const value, set Phi DEF const value to it; if // we have conflicting const values, set Phi DEF const value to type lattice bottom. ConstIter = this->FindConstValue(UseHashValue); STARSConstantValueType UseConstValType = STARS_CONST_TOP; // default; no entry in map if (ConstIter != this->GetLastConstValueIter()) { // found entry in map UseConstValType = ConstIter->second.ConstType; } if (UseConstValType == STARS_CONST_HAS_VALUE) { if (ConstUseValueSeen) { // check for consistency of values if (ConstUseVal != ConstIter->second.ConstValue) { // inconsistent const values DefFinalConstValType = STARS_CONST_BOTTOM; break; // no need to see more Phi USEs } } else { // this is the first const value we have seen DefFinalConstValType = STARS_CONST_HAS_VALUE; // so far, anyway ConstUseValueSeen = true; ConstUseVal = ConstIter->second.ConstValue; // only value seen so far } } else if (UseConstValType == STARS_CONST_BOTTOM) { // Any BOTTOM value in a USE makes the DEF also become BOTTOM. DefFinalConstValType = STARS_CONST_BOTTOM; break; // no need to see more Phi USEs } // else must be STARS_CONST_TOP, which is a don't-care case } // end if (ExecutedIncomingEdge) ++PredIndex; // keep index in sync with PredIter } // end for PredIter iteration through predecessor blocks // If we set the Phi DEF const value to a new value, then propagate along SSA edges. if (DefFinalConstValType != DefConstValType) { // const entry is changing struct STARS_SCCP_Const_Struct NewConstEntry; NewConstEntry.ConstType = DefFinalConstValType; NewConstEntry.ConstValue = ConstUseVal; if (DefConstValType == STARS_CONST_TOP) { // there was no old map entry; insert new one pair<int, struct STARS_SCCP_Const_Struct> NewMapEntry(DefHashValue, NewConstEntry); pair<map<int, struct STARS_SCCP_Const_Struct>::iterator, bool> InsertResult = this->ConstantDefs.insert(NewMapEntry); assert(InsertResult.second); } else { // old map entry needs to be changed this->ConstantDefs[DefHashValue] = NewConstEntry; } // Propagate along SSA edges. pair<int, int> SSAEdge(BlockNum, DefHashValue); SSAWorkList.push_back(SSAEdge); } // end if entry is changing } // end if previous DEF value was not already BOTTOM } // end for all Phi functions return; } // end of SMPFunction::EvaluateAllPhiConstants() // Do we not care if DEF underflowed, due to how it is used? bool SMPFunction::IsBenignUnderflowDEF(op_t DefOp, int DefSSANum, size_t DefAddr, int &IdiomCode) { bool benign = false; list<SMPInstr *>::iterator InstIter; set<DefOrUse, LessDefUse>::iterator DefIter, UseIter; int UseSSANum; SMPOperandType DefType; // 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. // We know that DefOp/DefAddr/DefSSANum refer to a subtraction instruction that // produces a NEGATEDPTR result. We only need to find the paired addition instruction // that USEs the same SSA name to produce a PTROFFSET result to prove that we have // a case of benign underflow and overflow. If we find such a pair, we will mark // both of their DEF results as benign overflows to suppress overflow checks. // PAINFUL: Linear search of instructions. Need to address this in the future. // Perhaps we should have a map of UseHashValue to InstAddr, but that is more // memory consumption. Sure would be useful, though. for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); UseIter = CurrInst->FindUse(DefOp); UseSSANum = UseIter->GetSSANum(); if (UseSSANum == DefSSANum) { // Only remaining question: Do we produce a PTROFFSET in CurrInst? (If we do, // that implies we had an addition, so we don't need to check that.) DefIter = CurrInst->GetFirstNonFlagsDef(); DefType = DefIter->GetType(); // NOTE: Make this more general. What if we just move the NEGATEDPTR into a register // and then the next instruction, with different SSA name, produces the PTROFFSET? // !!!!!*****!!!!! if (IsEqType(DefType, PTROFFSET)) { // Found a pair. Mark both DEFs as benign and return true. benign = true; IdiomCode = 4; // Note that we have two possibilities for the addition. The NEGATEDPTR could be // both the DEF and a USE, e.g. add negptr,ptr1; or the NEGATEDPTR could be // just a USE, e.g. add reg,negptr, so that reg is overwritten and becomes a // PTROFFSET. It really does not matter. The point is that we want to ignore // overflow on this addition, and also on the subtraction that produced the // NEGATEDPTR, so we mark the DEF in each instruction as benignly overflowing. op_t UseInstDefOp = DefIter->GetOp(); CurrInst->SetDefNoOverflow(UseInstDefOp, true); SMPInstr *DefInst = this->GetInstFromAddr(DefAddr); DefInst->SetDefNoOverflow(DefOp, true); break; } } } return benign; } // end of SMPFunction::IsBenignUnderflowDEF() bool SMPFunction::HasIntErrorCallSink(op_t DefOp, int DefSSANum, size_t DefAddr, std::string &SinkString) { bool FoundSink = false; this->ResetProcessedBlocks(); // prepare for recursion through blocks SinkString.clear(); SMPBasicBlock *CurrBlock = this->GetBlockFromInstAddr(DefAddr); assert(CurrBlock != NULL); FoundSink = CurrBlock->IsCriticalSink(DefOp, DefSSANum, SinkString); return FoundSink; } // end of SMPFunction::HasIntErrorCallSink() // Compute SSA form data structures across the function. void SMPFunction::ComputeSSA(void) { bool DebugFlag = false; bool DumpFlag = false; #if SMP_DEBUG_DATAFLOW DumpFlag |= (0 == strcmp("uw_frame_state_for", this->GetFuncName())); DebugFlag |= (0 == strcmp("uw_frame_state_for", this->GetFuncName())); #endif #if 1 if (DumpFlag) this->Dump(); #endif if (DebugFlag) SMP_msg("Computing IDoms.\n"); this->ComputeIDoms(); if (DebugFlag) SMP_msg("Computing Dom frontiers.\n"); this->ComputeDomFrontiers(); if (DebugFlag) SMP_msg("Computing global names.\n"); this->ComputeGlobalNames(); if (DebugFlag) SMP_msg("Computing blocks defined in.\n"); this->ComputeBlocksDefinedIn(); if (DebugFlag) SMP_msg("Inserting Phi functions.\n"); this->InsertPhiFunctions(); if (DebugFlag) SMP_msg("Building dominator tree.\n"); this->BuildDominatorTree(); if (DebugFlag) SMP_msg("Computing SSA renumbering.\n"); this->SSARenumber(); list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); if (CurrBlock->FindLoopHeadsAndTails()) { ++this->LoopCount; } if (DumpFlag) CurrBlock->Dump(); if (DebugFlag) SMP_msg("Computing local names.\n"); CurrBlock->SetLocalNames(); if (DebugFlag) SMP_msg("Computing local SSA renumbering.\n"); CurrBlock->SSALocalRenumber(); if (DumpFlag) CurrBlock->Dump(); #if STARS_BUILD_DEF_USE_CHAINS if (DebugFlag) SMP_msg("Computing global chains.\n"); CurrBlock->CreateGlobalChains(); #endif #if 1 if (DebugFlag) SMP_msg("Marking dead registers.\n"); CurrBlock->MarkDeadRegs(); #endif } #if SMP_DEBUG_DATAFLOW if (DumpFlag) this->Dump(); #endif // Once SSA numbers have been set into all DEFs, USES, and DU-chains, then // the SSA numbering data structures will no longer be used and can be // de-allocated. this->FreeSSAMemory(); return; } // end of SMPFunction::ComputeSSA() // Detect which blocks are in which loops and populate FuncLoopsByBlock data structure. void SMPFunction::DetectLoops(void) { list<SMPBasicBlock *>::iterator BlockIter; size_t LoopNumber = 0; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { SMPBasicBlock *CurrBlock = (*BlockIter); if (CurrBlock->IsLoopTailBlock()) { // For each loop tail block, get its loop header block number (the target // block for its back edge). Then traverse the CFG upwards, recording all // of the basic block numbers that lie between the loop tail and loop head, // along with the tail and head. this->ResetProcessedBlocks(); int TailBlockNum = CurrBlock->GetNumber(); int HeadBlockNum = CurrBlock->GetLoopHeaderNumber(); assert((TailBlockNum != SMP_BLOCKNUM_UNINIT) && (HeadBlockNum != SMP_BLOCKNUM_UNINIT)); list<list<SMPBasicBlock *>::iterator> BlockWorkList; BlockWorkList.push_back(BlockIter); SMPBasicBlock *WorkBlock; SMPBasicBlock *PredBlock; list<SMPBasicBlock *>::iterator WorkIter; list<SMPBasicBlock *>::iterator PredIter; do { WorkIter = BlockWorkList.front(); BlockWorkList.pop_front(); WorkBlock = (*WorkIter); int WorkBlockNum = WorkBlock->GetNumber(); assert(WorkBlockNum != SMP_BLOCKNUM_UNINIT); if (!(WorkBlock->IsProcessed())) { this->FuncLoopsByBlock[(size_t) WorkBlockNum].SetBit(LoopNumber); WorkBlock->SetProcessed(true); // Add unprocessed predecessors to the work list until we reach the loop head. if (WorkBlockNum != HeadBlockNum) { for (PredIter = WorkBlock->GetFirstPred(); PredIter != WorkBlock->GetLastPred(); ++PredIter) { PredBlock = (*PredIter); bool AlreadyProcessed = PredBlock->IsProcessed(); if (!AlreadyProcessed) { BlockWorkList.push_back(PredIter); } } } } } while (!BlockWorkList.empty()); ++LoopNumber; } } return; } // end of SMPFunction::DetectLoops() // Find memory writes (DEFs) with possible aliases void SMPFunction::AliasAnalysis(void) { // First task: Mark which memory DEFs MIGHT be aliased because an // indirect memory write occurs somewhere in the DEF-USE chain. // Memory DEF-USE chains with no possible aliasing can be subjected // to type inference and type-based optimizing annotations, e.g. a // register spill to memory followed by retrieval from spill memory // followed by NUMERIC USEs should be typed as a continuous NUMERIC // chain if there is no possibility of aliasing. // Preparatory step: For each indirect write, mark all def-use chains // (maintained at the basic block level) that include the indirect // write instruction. If there are no indirect writes in the function, // leave all DEFs marked as unaliased and exit. if (!(this->HasIndirectWrites)) return; list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; vector<SMPInstr *>::iterator BlkInstIter; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); for (BlkInstIter = CurrBlock->GetFirstInst(); BlkInstIter != CurrBlock->GetLastInst(); ++BlkInstIter) { SMPInstr *CurrInst = (*BlkInstIter); if (CurrInst->HasIndirectMemoryWrite()) { #if STARS_BUILD_DEF_USE_CHAINS CurrBlock->MarkIndWriteChains(CurrInst->GetAddr()); #endif // Until we get true aliasing analysis, any indirect write // is classified as may-be-aliased. CurrBlock->SetMaybeAliased(true); } } // end for all insts in block } // end for all blocks in function // Step one: Find only the memory DEFs to start with. bool FoundIndWrite = false; list<SMPInstr *>::iterator InstIter; for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t InstAddr = CurrInst->GetAddr(); if (CurrInst->HasDestMemoryOperand()) { // Starting with the DEF instruction, traverse the control flow // until we run into (A) the re-definition of the operand, including // a re-definition of any of its addressing registers, or (B) an // indirect write. Return false if condition A terminates the // search, and true if condition B terminates the search. this->ResetProcessedBlocks(); op_t MemDefOp = CurrInst->MDGetMemDefOp(); assert(o_void != MemDefOp.type); set<DefOrUse, LessDefUse>::iterator CurrMemDef = CurrInst->FindDef(MemDefOp); assert(CurrMemDef != CurrInst->GetLastDef()); int SSANum = CurrMemDef->GetSSANum(); FoundIndWrite = this->FindPossibleChainAlias(CurrInst, MemDefOp, SSANum); if (FoundIndWrite) { // Mark the DEF as aliased. CurrMemDef = CurrInst->SetDefIndWrite(CurrMemDef->GetOp(), true); } } // end if inst has dest memory operand } // end for all instructions return; } // end of SMPFunction::AliasAnalysis() // Does the DefOp DEF_USE chain have an indirect mem write starting at CurrInst? bool SMPFunction::FindPossibleChainAlias(SMPInstr *CurrInst, op_t DefOp, int SSANum) { #if 0 bool DebugFlag = false; if (0 == strcmp("sdissect", this->GetFuncName())) { // Next line is just a good place to set a break point. DebugFlag = true; } #endif // Starting with the DEF instruction, traverse the control flow // until we run into (A) the re-definition of the operand, including // a re-definition of any of its addressing registers, or (B) an // indirect write. Return false if condition A terminates the // search, and true if condition B terminates the search. SMPBasicBlock *CurrBlock = CurrInst->GetBlock(); if (!(CurrBlock->IsProcessed())) { CurrBlock->SetProcessed(true); } else return false; // block already processed // Proceed by cases: ea_t DefAddr = CurrInst->GetAddr(); set<DefOrUse, LessDefUse>::iterator UseIter; vector<SMPInstr *>::iterator InstIter = CurrBlock->GetInstIterFromAddr(DefAddr); bool IndWriteFound, ReDef; ++InstIter; // Case 1: Local name. Return the IndWrite flag for the local Def-Use // chain begun by CurrInst. bool UseAfterIndirectWrite = CurrBlock->HasUseAfterIndWrite(DefOp, InstIter, IndWriteFound, ReDef); bool LiveOutFlag = CurrBlock->IsLiveOut(DefOp); if (CurrBlock->IsLocalName(DefOp)) { #if STARS_BUILD_DEF_USE_CHAINS return CurrBlock->GetLocalDUChainIndWrite(DefOp, SSANum); #else return (UseAfterIndirectWrite); #endif } // Case 2: Global name. #if 1 if (UseAfterIndirectWrite) { return true; } else if (IndWriteFound) { // We found an indirect write, but no USE of DefOp after it. // If DefOp is LiveOut, then there is a USE of DefOp in a // successor block, so DefOp can be aliased. return (LiveOutFlag && !ReDef); } #else // Case 2A: If Def-Use chain within this block for this memory operand // has its IndWrite flag set to true, then stop and return true. else if (CurrBlock->GetGlobalDUChainIndWrite(DefOp, DefAddr)) { return true; } // Case 2B: Else if Def-Use chain is not the last chain in this block // for this operand, then there must be a later redefinition of the // memory operand (with new SSA number assigned) later in this block. // Because we did not fall into case 2A, we know there is no IndWrite // within the current memory operand's chain, so we return false. else if (!CurrBlock->IsLastGlobalChain(DefOp, DefAddr)) { return false; } // Case 2C: Else if current memory operand is NOT LiveOut, even though // this is the last def-use chain in the block, then there is no more // traversing of the control flow graph to be done. The chain has ended // without encountering an IndWrite, so return false. else if (!(CurrBlock->IsLiveOut(DefOp))) { return false; } #endif // Case 2D: We have passed all previous checks, so we must have a memory // operand that reaches the end of the block without encountering an // IndWrite and is LiveOut. Its may-alias status will be determined by // following the control flow graph for all successor blocks and examining // the def-use chains in those blocks. list<SMPBasicBlock *>::iterator SuccBlock; SuccBlock = CurrBlock->GetFirstSucc(); bool FoundAliasedWrite = false; if (LiveOutFlag && !ReDef) { do { if ((*SuccBlock)->IsLiveIn(DefOp)) { FoundAliasedWrite = this->FindChainAliasHelper(SuccBlock, DefOp); } ++SuccBlock; } while (!FoundAliasedWrite && (SuccBlock != CurrBlock->GetLastSucc())); } return FoundAliasedWrite; } // end of SMPFunction::FindPossibleChainAlias() // recursive helper for global DU-chains that traverse CFG bool SMPFunction::FindChainAliasHelper(list<SMPBasicBlock *>::iterator BlockIter, op_t DefOp) { bool DebugFlag = false; SMPBasicBlock *CurrBlock = (*BlockIter); if (0 == strcmp("mem2chunk_check", this->GetFuncName())) { // Next line is just a good place to set a break point. DebugFlag = true; } if (!(CurrBlock->IsProcessed())) { CurrBlock->SetProcessed(true); } else return false; // block already processed // The LVA sets can be used to decide whether it is possible that // the incoming DU chain overlaps a may-alias write. We can express // the decision making in a truth table: // // Case # LiveIn? Killed? AliasedWrite in block? Action to take // ------- ------- ------- ---------------------- -------------- // 1 N N N return false // 2 N N Y return false // 3 N Y N return false // 4 N Y Y return false // 5 Y N N recurse into successors // 6 Y N Y return true // 7 Y Y N return false // 8 Y Y Y check location of aliased write // // In the last case, if there is an aliased write before the // incoming DEF is killed and after it is used, then the // incoming DU chain overlaps an aliased write, otherwise // it does not. // If not LiveIn, incoming DU chain does not run through this block // at all, so return false. if (!(CurrBlock->IsLiveIn(DefOp))) return false; // cases 1-4 bool killed = CurrBlock->IsVarKill(DefOp); bool BlockHasAliasedWrite = CurrBlock->MaybeAliasedWrite(); if (BlockHasAliasedWrite) { // If DefOp is LiveIn and is not killed, then any aliased // write in the block overlaps the incoming DU chain. if (!killed) { return true; // case 6 } // If DefOp is LiveIn and is killed, then the location // of the aliased write is the determining factor. else { // case 8; depends on finding indirect write, then USE, before re-DEF. vector<SMPInstr *>::iterator InstIter = CurrBlock->GetFirstInst(); bool IndWriteFound, ReDef; return CurrBlock->HasUseAfterIndWrite(DefOp, InstIter, IndWriteFound, ReDef); } } else { // If killed, no aliased write, then cannot overlap an aliased write. if (killed) return false; // case 7 else { // Need to recurse into all successors, because we passed through // the block without seeing an aliased write and without killing // the DefOp. list<SMPBasicBlock *>::iterator SuccBlock; SuccBlock = CurrBlock->GetFirstSucc(); bool FoundAliasedWrite = false; while (!FoundAliasedWrite && (SuccBlock != CurrBlock->GetLastSucc())) { FoundAliasedWrite = this->FindChainAliasHelper(SuccBlock, DefOp); ++SuccBlock; }; if (DebugFlag) { SMP_msg("FindChainAliasHelper is returning %d\n", FoundAliasedWrite); } return FoundAliasedWrite; } } assert(false); // statement should be unreachable return false; } // end of SMPFunction::FindChainAliasHelper() // Link basic blocks to their predecessors and successors, and build the map // of instruction addresses to basic blocks. void SMPFunction::SetLinks(void) { list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; list<SMPBasicBlock *> UnresolvedBranchWorkList; ea_t InstAddr; #if SMP_DEBUG_DATAFLOW_VERBOSE SMP_msg("SetLinks called for %s\n", this->GetFuncName()); #endif // First, set up the map of instructions to basic blocks. for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); vector<SMPInstr *>::iterator InstIter; for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { InstAddr = (*InstIter)->GetAddr(); pair<ea_t, SMPBasicBlock *> MapItem(InstAddr, CurrBlock); InstBlockMap.insert(MapItem); } } #if SMP_DEBUG_DATAFLOW_VERBOSE SMP_msg("SetLinks finished mapping: %s\n", this->GetFuncName()); #endif // Next, set successors of each basic block, also setting up the predecessors in the // process. for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); vector<SMPInstr *>::iterator InstIter = (--(CurrBlock->GetLastInst())); SMPInstr *CurrInst = (*InstIter); InstAddr = CurrInst->GetAddr(); bool CondTailCall = false; if (CurrBlock->HasReturn()) { if (!(CurrInst->IsCondTailCall())) { // We either have a return instruction or an unconditional // tail call instruction. We don't want to link to the // tail call target, and there is no link for a return continue; } else { // We have a conditional tail call. We don't want to // link to the tail call target, but we do want fall // through to the next instruction. CondTailCall = true; } } // Last instruction in block; set successors bool CallFlag = (CALL == CurrInst->GetDataFlowType()); bool IndirJumpFlag = (INDIR_JUMP == CurrInst->GetDataFlowType()); bool IndirCallFlag = (INDIR_CALL == CurrInst->GetDataFlowType()); // NOTE: Dues to phase re-ordering, we cannot yet identify tail calls, // so CondTailCall and TailCallFlag will always be false, which is harmless. // SMPInstr::SetTailCall() will do a little cleanup later. bool TailCallFlag = CondTailCall && CurrInst->IsCondTailCall(); SMP_xref_t CurrXrefs; bool LinkedToTarget = false; for (bool ok = CurrXrefs.SMP_first_from(CurrInst->GetAddr(), XREF_ALL); ok; ok = CurrXrefs.SMP_next_from()) { ea_t TargetAddr = CurrXrefs.GetTo(); if ((TargetAddr != 0) && (CurrXrefs.GetIscode())) { // Found a code target, with its address in CurrXrefs.to if ((CallFlag || IndirCallFlag || TailCallFlag) && (TargetAddr != (CurrInst->GetAddr() + CurrInst->GetCmd().size))) { // A call instruction will have two targets: the fall through to the // next instruction, and the called function. We want to link to the // fall-through instruction, but not to the called function. // Some blocks end with a call just because the fall-through instruction // is a jump target from elsewhere. continue; } map<ea_t, SMPBasicBlock *>::iterator MapEntry; MapEntry = this->InstBlockMap.find(TargetAddr); if (MapEntry == this->InstBlockMap.end()) { ; // do nothing; probably a tail call (not yet identified) #if 0 SMP_msg("WARNING: addr %x not found in map for %s\n", TargetAddr, this->GetFuncName()); SMP_msg(" Referenced from %s\n", CurrInst->GetDisasm()); #endif } else { SMPBasicBlock *Target = MapEntry->second; // Make target block a successor of current block. CurrBlock->LinkToSucc(Target); // Make current block a predecessor of target block. Target->LinkToPred(CurrBlock); LinkedToTarget = true; #if SMP_USE_SWITCH_TABLE_INFO if (IndirJumpFlag) { #if SMP_DEBUG_SWITCH_TABLE_INFO SMP_msg("Switch table link: jump at %x target at %x\n", CurrInst->GetAddr(), TargetAddr); #else ; #endif } #endif } } } // end for all xrefs if (IndirJumpFlag && (!LinkedToTarget)) { this->UnresolvedIndirectJumps = true; UnresolvedBranchWorkList.push_back(CurrBlock); SMP_msg("WARNING: Unresolved indirect jump at %lx\n", (unsigned long) CurrInst->GetAddr()); } else if (IndirCallFlag && (!LinkedToTarget)) { this->UnresolvedIndirectCalls = true; SMP_msg("WARNING: Unresolved indirect call at %lx\n", (unsigned long) CurrInst->GetAddr()); } } // end for all blocks // Mark all blocks that can be reached from the entry block, so we can find the unreachable ones. this->ResetProcessedBlocks(); this->Blocks.front()->DepthFirstMark(); // We have two cases: (1) Unresolved indirect branches could be targeting the unmarked blocks, making // these blocks reachable, in which case we should link the unresolved branches to the unmarked blocks; // or (2) there are no unresolved branches, in which case the unmarked blocks are unreachable within // the function. They might be reachable from outside the function using exception handling jumps, but // that still would not allow us to link them into the CFG of this function properly, so in any case we // are deleting those unreachable blocks and not emitting annotations for them. // NOTE: An odd new gcc recursion optimization uses indirect calls within the function, so // they can behave like indirect jumps. However, we don't want to link unresolved calls to unmarked blocks // at this time. bool HellNodeCase = (!UnresolvedBranchWorkList.empty() && (this->HasUnresolvedIndirectCalls() || this->HasUnresolvedIndirectJumps())); bool AddedMissingLinks = false; bool changed; do { changed = false; list<SMPBasicBlock *>::iterator BlockIter = this->Blocks.begin(); while (BlockIter != this->Blocks.end()) { CurrBlock = (*BlockIter); if (CurrBlock->IsProcessed()) { ++BlockIter; } else { // Block cannot be reached from entry node, even after we have added links // on previous loop iterations. if (!HellNodeCase) { if (CurrBlock->AllNops()) SMP_msg("INFO: Removing all nops block at %lx\n", (unsigned long) CurrBlock->GetFirstAddr()); else SMP_msg("INFO: Removing unreachable block at %lx\n", (unsigned long) CurrBlock->GetFirstAddr()); // Remove this block from the predecessors list of its successors. list<SMPBasicBlock *>::iterator SuccIter; ea_t TempAddr = CurrBlock->GetFirstAddr(); for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { (*SuccIter)->ErasePred(TempAddr); } // Remove the unreachable instructions from the function inst list. vector<SMPInstr *>::iterator InstIter; InstIter = CurrBlock->GetFirstInst(); ea_t FirstBadAddr = (*InstIter)->GetAddr(); InstIter = CurrBlock->GetLastInst(); --InstIter; // get last real instruction ea_t LastBadAddr = (*InstIter)->GetAddr(); this->EraseInstRange(FirstBadAddr, LastBadAddr); // Remove the block from the blocks list. BlockIter = this->Blocks.erase(BlockIter); this->BlockCount -= 1; #if 0 // Exception handling code requires something more delicate than this. Later checks for stack adjustment etc. can look at these blocks. // Finally, call destructors on the block and insts removed. InstIter = CurrBlock->GetFirstInst(); while (InstIter != CurrBlock->GetLastInst()) { SMPInstr *DeadInst = (*InstIter); ++InstIter; if (NULL != DeadInst) delete DeadInst; } delete CurrBlock; #endif } else { // HellNodeCase // Block must be reachable only through an unresolved indirect branch. // Make each unresolved indirect branch link to the block so it is reachable. list<SMPBasicBlock *>::iterator WorkIter; AddedMissingLinks = true; for (WorkIter = UnresolvedBranchWorkList.begin(); WorkIter != UnresolvedBranchWorkList.end(); ++ WorkIter) { SMPBasicBlock *WorkBlock = (*WorkIter); WorkBlock->LinkToSucc(CurrBlock); } // Mark CurrBlock as now being reachable, along with the blocks it dominates. CurrBlock->DepthFirstMark(); ++BlockIter; } changed = true; } // end if (processed) ... else ... } // end loop through blocks } while (changed); if (HellNodeCase && (!AddedMissingLinks)) { SMP_msg("SERIOUS WARNING: Function at %lx has unresolved indirect branches but no unreachable blocks.\n", (unsigned long) this->FirstEA); } #if 0 // If we have any blocks that are all no-ops and have no predecessors, remove those // blocks. They are dead and make the CFG no longer a lattice. Any blocks that have // no predecessors but are not all no-ops should also be removed with a different // log message. // NOTE: Prior to construction of hell nodes in functions with unresolved indirect jumps, // we cannot conclude that a block with no predecessors is unreachable. Also, the block // order might be such that removal of a block makes an already processed block // unreachable, so we have to iterate until there are no more changes. bool NoPredecessors; bool OnlyPredIsItself; list<SMPBasicBlock *>::iterator CurrPred; #if SMP_USE_SWITCH_TABLE_INFO if (!(this->HasUnresolvedIndirectJumps() || this->HasUnresolvedIndirectCalls())) { #else if (!(this->HasIndirectJumps() || this->HasIndirectCalls())) { #endif bool changed; do { changed = false; BlockIter = this->Blocks.begin(); ++BlockIter; // don't delete the top block, no matter what. while (BlockIter != this->Blocks.end()) { CurrBlock = (*BlockIter); OnlyPredIsItself = false; CurrPred = CurrBlock->GetFirstPred(); NoPredecessors = (CurrPred == CurrBlock->GetLastPred()); if (!NoPredecessors) { if ((*CurrPred)->GetFirstAddr() == CurrBlock->GetFirstAddr()) { // self-recursion ++CurrPred; // any more preds besides itself? OnlyPredIsItself = (CurrPred == CurrBlock->GetLastPred()); // Only predecessor was the self-recursion if no more preds } } if (NoPredecessors || OnlyPredIsItself) { if (CurrBlock->AllNops()) SMP_msg("Removing all nops block at %x\n", CurrBlock->GetFirstAddr()); else SMP_msg("Removing block with no predecessors at %x\n", CurrBlock->GetFirstAddr()); // Remove this block from the predecessors list of its successors. list<SMPBasicBlock *>::iterator SuccIter; ea_t TempAddr = CurrBlock->GetFirstAddr(); for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { (*SuccIter)->ErasePred(TempAddr); } // Remove the unreachable instructions from the function inst list. vector<SMPInstr *>::iterator InstIter; InstIter = CurrBlock->GetFirstInst(); ea_t FirstBadAddr = (*InstIter)->GetAddr(); InstIter = CurrBlock->GetLastInst(); --InstIter; // get last real instruction ea_t LastBadAddr = (*InstIter)->GetAddr(); this->EraseInstRange(FirstBadAddr, LastBadAddr); // Finally, remove the block from the blocks list. BlockIter = this->Blocks.erase(BlockIter); this->BlockCount -= 1; changed = true; } else { ++BlockIter; } } // end while all blocks after the first one } while (changed); } // end if not unresolved indirect jumps or indirect calls else if (this->UnresolvedIndirectJumps) { // Make each unresolved indirect branch have each block with no predecessor as a target, // so that the resulting CFG has a proper structure. BlockIter = this->Blocks.begin(); ++BlockIter; // The top block is expected to have no predecessors, which is not a CFG problem. bool AddedMissingLinks = false; while (BlockIter != this->Blocks.end()) { CurrBlock = (*BlockIter); OnlyPredIsItself = false; CurrPred = CurrBlock->GetFirstPred(); NoPredecessors = (CurrPred == CurrBlock->GetLastPred()); if (!NoPredecessors) { if ((*CurrPred)->GetFirstAddr() == CurrBlock->GetFirstAddr()) { // self-recursion ++CurrPred; // any more preds besides itself? OnlyPredIsItself = (CurrPred == CurrBlock->GetLastPred()); // Only predecessor was the self-recursion if no more preds } } if (NoPredecessors || OnlyPredIsItself) { // Block must be reachable only through an unresolved indirect branch. // Make each unresolved indirect branch link to the block so it is reachable. list<SMPBasicBlock *>::iterator WorkIter; AddedMissingLinks = true; for (WorkIter = UnresolvedBranchWorkList.begin(); WorkIter != UnresolvedBranchWorkList.end(); ++ WorkIter) { SMPBasicBlock *WorkBlock = (*WorkIter); WorkBlock->LinkToSucc(CurrBlock); } } ++BlockIter; } // end for all blocks if (!AddedMissingLinks) { SMP_msg("SERIOUS WARNING: Function at %x has unresolved indirect branches but no unreachable blocks.\n", this->FirstEA); } } #endif return; } // end of SMPFunction::SetLinks() // Number all basic blocks in reverse postorder (RPO) and set RPOBlocks vector to // access them. void SMPFunction::RPONumberBlocks(void) { #if SMP_DEBUG_DATAFLOW bool DebugFlag = false; DebugFlag = (0 == strcmp("uw_frame_state_for", this->GetFuncName())); if (DebugFlag) SMP_msg("Entered RPONumberBlocks\n"); #endif int CurrNum = 0; list<SMPBasicBlock *> WorkList; // Number the first block with 0. list<SMPBasicBlock *>::iterator BlockIter = this->Blocks.begin(); SMPBasicBlock *CurrBlock = (*BlockIter); #if 0 if (this->RPOBlocks.capacity() <= (size_t) this->BlockCount) { SMP_msg("Reserving %d RPOBlocks old value: %d\n", 2+this->BlockCount, this->RPOBlocks.capacity()); this->RPOBlocks.reserve(2 + this->BlockCount); this->RPOBlocks.assign(2 + this->BlockCount, this->Blocks.end()); } #endif CurrBlock->SetNumber(CurrNum); this->RPOBlocks.push_back(CurrBlock); ++CurrNum; // Push the first block's successors onto the work list. list<SMPBasicBlock *>::iterator CurrSucc = CurrBlock->GetFirstSucc(); while (CurrSucc != CurrBlock->GetLastSucc()) { WorkList.push_back(*CurrSucc); ++CurrSucc; } // Use the WorkList to iterate through all blocks in the function list<SMPBasicBlock *>::iterator CurrListItem = WorkList.begin(); bool change; while (!WorkList.empty()) { change = false; while (CurrListItem != WorkList.end()) { if ((*CurrListItem)->GetNumber() != SMP_BLOCKNUM_UNINIT) { // Duplicates get pushed onto the WorkList because a block // can be the successor of multiple other blocks. If it is // already numbered, it is a duplicate and can be removed // from the list. CurrListItem = WorkList.erase(CurrListItem); change = true; continue; } if ((*CurrListItem)->AllPredecessorsNumbered()) { // Ready to be numbered. (*CurrListItem)->SetNumber(CurrNum); #if 0 SMP_msg("Set RPO number %d\n", CurrNum); if (DebugFlag && (7 == CurrNum)) this->Dump(); #endif this->RPOBlocks.push_back(*CurrListItem); ++CurrNum; change = true; // Push its unnumbered successors onto the work list. CurrSucc = (*CurrListItem)->GetFirstSucc(); while (CurrSucc != (*CurrListItem)->GetLastSucc()) { if ((*CurrSucc)->GetNumber() == SMP_BLOCKNUM_UNINIT) WorkList.push_back(*CurrSucc); ++CurrSucc; } CurrListItem = WorkList.erase(CurrListItem); } else { ++CurrListItem; } } // end while (CurrListItem != WorkList.end()) if (change) { // Reset CurrListItem to beginning of work list for next iteration. CurrListItem = WorkList.begin(); } else { // Loops can cause us to not be able to find a WorkList item that has // all predecessors numbered. Take the WorkList item with the lowest address // and number it so we can proceed. CurrListItem = WorkList.begin(); ea_t LowAddr = (*CurrListItem)->GetFirstAddr(); list<SMPBasicBlock *>::iterator SaveItem = CurrListItem; ++CurrListItem; while (CurrListItem != WorkList.end()) { if (LowAddr > (*CurrListItem)->GetFirstAddr()) { SaveItem = CurrListItem; LowAddr = (*CurrListItem)->GetFirstAddr(); } ++CurrListItem; } // SaveItem should now be numbered. (*SaveItem)->SetNumber(CurrNum); #if SMP_DEBUG_DATAFLOW SMP_msg("Picked LowAddr %x and set RPO number %d\n", LowAddr, CurrNum); #endif this->RPOBlocks.push_back(*SaveItem); ++CurrNum; // Push its unnumbered successors onto the work list. CurrSucc = (*SaveItem)->GetFirstSucc(); while (CurrSucc != (*SaveItem)->GetLastSucc()) { if ((*CurrSucc)->GetNumber() == SMP_BLOCKNUM_UNINIT) WorkList.push_back(*CurrSucc); ++CurrSucc; } CurrListItem = WorkList.erase(SaveItem); CurrListItem = WorkList.begin(); } // end if (change) ... else ... } // end while work list is nonempty // Prior to construction of hell nodes for functions with indirect jumps, there // could still be unnumbered blocks because they appear to be unreachable // (no predecessors from SetLinks() because they are reached only via indirect // jumps). We need to number these and push them on the RPOBlocks vector so // that the vector contains all the blocks. // NOTE: Odd new gcc recursion optimization seems to use indirect calls to reach // some blocks within a recursive function, operating somewhat like an indirect // jump. if (this->HasIndirectJumps() || this->HasIndirectCalls()) { for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); if (SMP_BLOCKNUM_UNINIT == CurrBlock->GetNumber()) { SMP_msg("WARNING: Numbering indirectly reachable block at %lx\n", (unsigned long) CurrBlock->GetFirstAddr()); CurrBlock->SetNumber(CurrNum); this->RPOBlocks.push_back(CurrBlock); ++CurrNum; } } } // If we still have unnumbered blocks, it is not because of indirect jumps or calls. // We have some mysterious dead code. if (this->BlockCount > this->RPOBlocks.size()) { SMP_msg("SERIOUS WARNING: RPONumberBlocks method: Function %s has BlockCount %d and RPOBlocks size %zu\n", this->GetFuncName(), this->BlockCount, this->RPOBlocks.size()); for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); if (SMP_BLOCKNUM_UNINIT == CurrBlock->GetNumber()) { SMP_msg("WARNING: Numbering apparently unreachable block at %lx\n", (unsigned long) CurrBlock->GetFirstAddr()); CurrBlock->SetNumber(CurrNum); this->RPOBlocks.push_back(CurrBlock); ++CurrNum; } } } return; } // end of SMPFunction::RPONumberBlocks() // Perform live variable analysis on all blocks in the function. // See chapter 9 of Cooper/Torczon, Engineering a Compiler, for the algorithm. void SMPFunction::LiveVariableAnalysis(void) { list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; #if SMP_DEBUG_DATAFLOW bool DebugFlag = (0 == strcmp("uw_frame_state_for", this->GetFuncName())); #endif #if SMP_DEBUG_DATAFLOW_VERBOSE SMP_msg("LiveVariableAnalysis for %s\n", this->GetFuncName()); #endif #if SMP_ANALYZE_STACK_POINTER ; #else for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); // Initialize the Killed and UpwardExposed sets for each block. CurrBlock->InitKilledExposed(this->UsesFramePointer()); } #endif bool changed; // Iterate over each block, updating LiveOut sets until no more changes are made. // NOTE: LVA is more efficient when computed over a reverse post-order list of blocks // from the inverted CFG. We have an RPO list from the forward CFG, so it is just as // good to simply iterate through the blocks in layout order. #if 1 do { changed = false; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); changed |= CurrBlock->UpdateLiveOut(); } } while (changed); #else // Use reverse postorder do { changed = false; for (size_t index = 0; index < this->RPOBlocks.size(); ++index) { CurrBlock = this->RPOBlocks.at(index); changed |= CurrBlock->UpdateLiveOut(); } } while (changed); #endif #if SMP_USE_SSA_FNOP_MARKER // Create DEFs in the marker instruction for all names in the LiveInSet // of the first block. These are the names for the function that // would otherwise look like USEs of uninitialized variables later. // Note that the LiveVariableAnalysis work does not actually populate // a LiveInSet for the first block, so we simulate it with its // dataflow equation, UpExposed union (LiveOut minus VarKill). set<op_t, LessOp>::iterator UpExposedIter, LiveOutIter; list<SMPInstr *>::iterator MarkerInst = this->Instrs.begin(); SMPBasicBlock *FirstBlock = this->Blocks.front(); for (UpExposedIter = FirstBlock->GetFirstUpExposed(); UpExposedIter != FirstBlock->GetLastUpExposed(); ++UpExposedIter) { // Add DEF with SSANum of 0. (*MarkerInst)->AddDef(*UpExposedIter, UNINIT, 0); // Add to the VarKill and LiveIn sets. FirstBlock->AddVarKill(*UpExposedIter); // "killed" by marker inst def FirstBlock->AddLiveIn(*UpExposedIter); } for (LiveOutIter = FirstBlock->GetFirstLiveOut(); LiveOutIter != FirstBlock->GetLastLiveOut(); ++LiveOutIter) { if (!(FirstBlock->IsVarKill(*LiveOutIter))) { // Add DEF with SSANum of 0. (*MarkerInst)->AddDef(*LiveOutIter, UNINIT, 0); // Add to the VarKill and LiveIn sets. FirstBlock->AddVarKill(*LiveOutIter); // "killed" by marker inst def FirstBlock->AddLiveIn(*LiveOutIter); } } #endif #if SMP_DEBUG_DATAFLOW_VERBOSE if (DebugFlag) SMP_msg("Exiting LiveVariableAnalysis\n"); #endif return; } // end of SMPFunction::LiveVariableAnalysis() // Return the IDom index that is the end of the intersection prefix of the Dom sets of // the two blocks designated by the RPO numbers passed in. // See Cooper & Torczon, "Engineering a Compiler" 1st edition figure 9.8. int SMPFunction::IntersectDoms(int block1, int block2) const { int finger1 = block1; int finger2 = block2; while (finger1 != finger2) { while (finger1 > finger2) finger1 = this->IDom.at(finger1); while (finger2 > finger1) finger2 = this->IDom.at(finger2); } return finger1; } // end of SMPFunction::IntersectDoms() // Compute immediate dominators of all blocks into IDom[] vector. void SMPFunction::ComputeIDoms(void) { bool DebugFlag = false; #if SMP_DEBUG_DATAFLOW DebugFlag = (0 == strcmp("_ZN6soplex7NameSetC2Eiidd", this->GetFuncName())); if (DebugFlag) SMP_msg("Entered ComputeIDoms\n"); #endif // Initialize the IDom[] vector to uninitialized values for all blocks. this->IDom.reserve(this->BlockCount); this->IDom.assign(this->BlockCount, SMP_BLOCKNUM_UNINIT); if (DebugFlag) { SMP_msg("BlockCount = %d RPOBlocks size = %zu\n", this->BlockCount, this->RPOBlocks.size()); } if (this->BlockCount != this->RPOBlocks.size()) { SMP_msg("SERIOUS WARNING: Function %s has BlockCount of %d and RPOBlocks size of %zu\n", this->GetFuncName(), this->BlockCount, this->RPOBlocks.size()); } this->IDom[0] = 0; // First block is dominated only by itself bool changed; do { changed = false; for (size_t RPONum = 1; RPONum < (size_t) this->BlockCount; ++RPONum) { if (DebugFlag) SMP_msg("RPONum %zu\n", RPONum); #if 0 if (DebugFlag) { SMP_msg("RPOBlocks vector size: %d\n", this->RPOBlocks.size()); for (size_t index = 0; index < this->RPOBlocks.size(); ++index) { SMP_msg("RPOBlocks entry %d is %d\n", index, RPOBlocks[index]->GetNumber()); } } #endif // To avoid infinite loops on blocks that dominate themselves but otherwise have no // predecessors (probably reachable only through indirect jumps), we stop processing // the blocks once the IDom becomes the top (entry) block. This probably saves time // on other blocks as well. if (0 == this->IDom[RPONum]) continue; SMPBasicBlock *CurrBlock = this->RPOBlocks.at(RPONum); // if (DebugFlag) SMP_msg("CurrBlock: %x\n", CurrBlock._Ptr); list<SMPBasicBlock *>::iterator CurrPred; // Initialize NewIdom to the first processed predecessor of block RPONum. int NewIdom = SMP_BLOCKNUM_UNINIT; for (CurrPred = CurrBlock->GetFirstPred(); CurrPred != CurrBlock->GetLastPred(); ++CurrPred) { int PredNum = (*CurrPred)->GetNumber(); if (DebugFlag) SMP_msg("Pred: %d\n", PredNum); // **!!** See comment below about unreachable blocks. if (SMP_BLOCKNUM_UNINIT == PredNum) continue; int PredIDOM = this->IDom.at(PredNum); if (DebugFlag) SMP_msg("Pred IDom: %d\n", PredIDOM); if (SMP_BLOCKNUM_UNINIT != PredIDOM) { NewIdom = PredNum; break; } } if (NewIdom == SMP_BLOCKNUM_UNINIT) { SMP_msg("WARNING: Failure on NewIdom in ComputeIDoms for %s\n", this->GetFuncName()); if (this->HasIndirectJumps() || this->HasIndirectCalls()) { // Might be reachable only through indirect jumps. NewIdom = 0; // make it dominated by entry block SMP_msg("WARNING: Assuming block %d at address %lx is reachable indirectly.\n", CurrBlock->GetNumber(), (unsigned long) CurrBlock->GetFirstAddr()); } else { // Might be exception handling code, reachable only by call stack walking. NewIdom = 0; // make it be dominated by entry block SMP_msg("WARNING: Assuming block %d at address %lx is reachable by exception handling.\n", CurrBlock->GetNumber(), (unsigned long) CurrBlock->GetFirstAddr()); } } assert(NewIdom != SMP_BLOCKNUM_UNINIT); // Loop through all predecessors of block RPONum except block NewIdom. // Set NewIdom to the intersection of its Dom set and the Doms set of // each predecessor that has had its Doms set computed. for (CurrPred = CurrBlock->GetFirstPred(); CurrPred != CurrBlock->GetLastPred(); ++CurrPred) { int PredNum = (*CurrPred)->GetNumber(); if (DebugFlag) SMP_msg("PredNum: %d\n", PredNum); // **!!** We can avoid failure on unreachable basic blocks // by executing a continue statement if PredNum is -1. Long term solution // is to prune out unreachable basic blocks, or better yet, create hell nodes // if the function has indirect jumps. if (PredNum == SMP_BLOCKNUM_UNINIT) continue; int PredIDOM = this->IDom.at(PredNum); if (DebugFlag) SMP_msg("PredIDOM: %d\n", PredIDOM); if ((SMP_BLOCKNUM_UNINIT == PredIDOM) || (NewIdom == PredIDOM)) { // Skip predecessors that have uncomputed Dom sets, or are the // current NewIdom. continue; } if (DebugFlag) SMP_msg("Old NewIdom value: %d\n", NewIdom); NewIdom = this->IntersectDoms(PredNum, NewIdom); if (DebugFlag) SMP_msg("New NewIdom value: %d\n", NewIdom); } // If NewIdom is not the value currently in vector IDom[], update the // vector entry and set changed to true. if (NewIdom != this->IDom.at(RPONum)) { if (DebugFlag) SMP_msg("IDOM changed from %d to %d\n", this->IDom.at(RPONum), NewIdom); this->IDom[RPONum] = NewIdom; changed = true; } } } while (changed); return; } // end of SMPFunction::ComputeIDoms() // Compute dominance frontier sets for each block. void SMPFunction::ComputeDomFrontiers(void) { list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *RunnerBlock; SMPBasicBlock *CurrBlock; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); // We look only at join points in the CFG, as per Cooper/Torczon chapter 9. if (1 < CurrBlock->GetNumPreds()) { // join point; more than 1 predecessor int runner; list<SMPBasicBlock *>::iterator PredIter; SMPBasicBlock *CurrPred; for (PredIter = CurrBlock->GetFirstPred(); PredIter != CurrBlock->GetLastPred(); ++PredIter) { CurrPred = (*PredIter); // For each predecessor, we run up the IDom[] vector and add CurrBlock to the // DomFrontier for all blocks that are between CurrPred and IDom[CurrBlock], // not including IDom[CurrBlock] itself. runner = CurrPred->GetNumber(); while (runner != this->IDom.at(CurrBlock->GetNumber())) { // Cooper/Harvey/Kennedy paper does not quite agree with the later // text by Cooper/Torczon. Text says that the start node has no IDom // in the example on pages 462-463, but it shows an IDOM for the // root node in Figure 9.9 of value == itself. The first edition text // on p.463 seems correct, as the start node dominates every node and // thus should have no dominance frontier. if (SMP_TOP_BLOCK == runner) break; RunnerBlock = this->RPOBlocks.at(runner); RunnerBlock->AddToDomFrontier(CurrBlock->GetNumber()); runner = this->IDom.at(runner); } } // end for all predecessors } // end if join point } // end for all blocks return; } // end of SMPFunction::ComputeDomFrontiers() // Compute the GlobalNames set, which includes all operands that are used in more than // one basic block. It is the union of all UpExposedSets of all blocks. void SMPFunction::ComputeGlobalNames(void) { set<op_t, LessOp>::iterator SetIter; list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; unsigned int index = 0; if (this->Blocks.size() < 2) return; // cannot have global names if there is only one block bool DebugFlag = false; #if SMP_DEBUG_DATAFLOW DebugFlag = (0 == strcmp("uw_frame_state_for", this->GetFuncName())); #endif for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); for (SetIter = CurrBlock->GetFirstUpExposed(); SetIter != CurrBlock->GetLastUpExposed(); ++SetIter) { op_t TempOp = *SetIter; // The GlobalNames set will have the complete collection of operands that we are // going to number in our SSA computations. We now assign an operand number // within the op_t structure for each, so that we can index into the // BlocksUsedIn[] vector, for example. This operand number is not to be // confused with SSA numbers. // We use the operand number field op_t.n for the lower 8 bits, and the offset // fields op_t.offb:op_t.offo for the upper 16 bits. We are overwriting IDA // values here, but operands in the data flow analysis sets should never be // inserted back into the program anyway. SetGlobalIndex(&TempOp, index); #if SMP_DEBUG_DATAFLOW if (DebugFlag) { SMP_msg("Global Name: "); PrintListOperand(TempOp); } #endif set<op_t, LessOp>::iterator AlreadyInSet; pair<set<op_t, LessOp>::iterator, bool> InsertResult; InsertResult = this->GlobalNames.insert(TempOp); if (!InsertResult.second) { // Already in GlobalNames, so don't assign an index number. ; #if SMP_DEBUG_DATAFLOW if (DebugFlag) { SMP_msg(" already in GlobalNames.\n"); } #endif } else { ++index; #if SMP_DEBUG_DATAFLOW if (DebugFlag) { SMP_msg(" inserted as index %d\n", ExtractGlobalIndex(TempOp)); } #endif } } // for each upward exposed item in the current block } // for each basic block assert(16777215 >= this->GlobalNames.size()); // index fits in 24 bits return; } // end of SMPFunction::ComputeGlobalNames() // For each item in GlobalNames, record the blocks that DEF the item. void SMPFunction::ComputeBlocksDefinedIn(void) { // Loop through all basic blocks and examine all DEFs. For Global DEFs, record // the block number in BlocksDefinedIn. The VarKillSet records DEFs without // having to examine every instruction. list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; this->BlocksDefinedIn.clear(); for (size_t i = 0; i < this->GlobalNames.size(); ++i) { list<int> TempList; this->BlocksDefinedIn.push_back(TempList); } #if SMP_DEBUG_DATAFLOW_VERBOSE SMP_msg("Number of GlobalNames: %d\n", this->GlobalNames.size()); SMP_msg("Size of BlocksDefinedIn: %d\n", this->BlocksDefinedIn.size()); #endif for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { set<op_t, LessOp>::iterator KillIter; CurrBlock = (*BlockIter); for (KillIter = CurrBlock->GetFirstVarKill(); KillIter != CurrBlock->GetLastVarKill(); ++KillIter) { // If killed item is not a block-local item (it is global), record it. set<op_t, LessOp>::iterator NameIter = this->GlobalNames.find(*KillIter); if (NameIter != this->GlobalNames.end()) { // found in GlobalNames set // We have a kill of a global name. Get index from three 8-bit fields. unsigned int index = ExtractGlobalIndex(*NameIter); if (index >= this->GlobalNames.size()) { // We are about to assert false. SMP_msg("ComputeBlocksDefinedIn: Bad index: %d limit: %zu\n", index, this->GlobalNames.size()); SMP_msg("Block number %d\n", CurrBlock->GetNumber()); SMP_msg("Killed item: "); PrintListOperand(*KillIter); SMP_msg("\n"); SMP_msg("This is a fatal error.\n"); } assert(index < this->GlobalNames.size()); // index is a valid subscript for the BlocksDefinedIn vector. Push the // current block number onto the list of blocks that define this global name. this->BlocksDefinedIn[index].push_back(CurrBlock->GetNumber()); } } } return; } // end of SMPFunction::ComputeBlocksDefinedIn() // Compute the phi functions at the entry point of each basic block that is a join point. void SMPFunction::InsertPhiFunctions(void) { set<op_t, LessOp>::iterator NameIter; list<int> WorkList; // list of block numbers bool DebugFlag = false; #if SMP_DEBUG_DATAFLOW DebugFlag = (0 == strcmp("uw_frame_state_for", this->GetFuncName())); #endif if (DebugFlag) SMP_msg("GlobalNames size: %zu\n", this->GlobalNames.size()); for (NameIter = this->GlobalNames.begin(); NameIter != this->GlobalNames.end(); ++NameIter) { int CurrNameIndex = (int) (ExtractGlobalIndex(*NameIter)); if (DebugFlag) SMP_msg("CurrNameIndex: %d\n", CurrNameIndex); #if 0 DebugFlag = (DebugFlag && (6 == CurrNameIndex)); #endif // Initialize the work list to all blocks that define the current name. WorkList.clear(); list<int>::iterator WorkIter; for (WorkIter = this->BlocksDefinedIn.at((size_t) CurrNameIndex).begin(); WorkIter != this->BlocksDefinedIn.at((size_t) CurrNameIndex).end(); ++WorkIter) { WorkList.push_back(*WorkIter); } // Iterate through the work list, inserting phi functions for the current name // into all the blocks in the dominance frontier of each work list block. // Insert into the work list each block that had a phi function added. while (!WorkList.empty()) { #if SMP_DEBUG_DATAFLOW_VERBOSE if (DebugFlag) SMP_msg("WorkList size: %d\n", WorkList.size()); #endif list<int>::iterator WorkIter = WorkList.begin(); while (WorkIter != WorkList.end()) { set<int>::iterator DomFrontIter; #if SMP_DEBUG_DATAFLOW_VERBOSE if (DebugFlag) SMP_msg("WorkIter: %d\n", *WorkIter); #endif if (DebugFlag && (*WorkIter > this->BlockCount)) { SMP_msg("ERROR: WorkList block # %d out of range.\n", *WorkIter); } SMPBasicBlock *WorkBlock = this->RPOBlocks[*WorkIter]; for (DomFrontIter = WorkBlock->GetFirstDomFrontier(); DomFrontIter != WorkBlock->GetLastDomFrontier(); ++DomFrontIter) { #if SMP_DEBUG_DATAFLOW_VERBOSE if (DebugFlag) SMP_msg("DomFront: %d\n", *DomFrontIter); #endif if (DebugFlag && (*DomFrontIter > this->BlockCount)) { SMP_msg("ERROR: DomFront block # %d out of range.\n", *DomFrontIter); } SMPBasicBlock *PhiBlock = this->RPOBlocks[*DomFrontIter]; // Before inserting a phi function for the current name in *PhiBlock, // see if the current name is LiveIn for *PhiBlock. If not, there // is no need for the phi function. This check is what makes the SSA // a fully pruned SSA. if (PhiBlock->IsLiveIn(*NameIter)) { size_t NumPreds = PhiBlock->GetNumPreds(); DefOrUse CurrRef(*NameIter); SMPPhiFunction CurrPhi(CurrNameIndex, CurrRef); for (size_t NumCopies = 0; NumCopies < NumPreds; ++NumCopies) { CurrPhi.PushBack(CurrRef); // inputs to phi } if (PhiBlock->AddPhi(CurrPhi)) { // If not already in Phi set, new phi function was inserted. WorkList.push_back(PhiBlock->GetNumber()); #if SMP_DEBUG_DATAFLOW_VERBOSE if (DebugFlag) SMP_msg("Added phi for name %d at top of block %d\n", CurrNameIndex, PhiBlock->GetNumber()); #endif } } else { if (DebugFlag) { SMP_msg("Global %d not LiveIn for block %d\n", CurrNameIndex, PhiBlock->GetNumber()); } } } // end for all blocks in the dominance frontier // Remove current block number from the work list if (DebugFlag) { SMP_msg("Removing block %d from work list.\n", *WorkIter); } WorkIter = WorkList.erase(WorkIter); } // end for all block numbers in the work list } // end while the work list is not empty if (DebugFlag) SMP_msg("WorkList empty.\n"); } // end for all elements of the GlobalNames set return; } // end of SMPFunction::InsertPhiFunctions() // Build the dominator tree. void SMPFunction::BuildDominatorTree(void) { size_t index; // First, fill the DomTree vector with the parent numbers filled in and the child lists // left empty. for (index = 0; index < this->IDom.size(); ++index) { pair<int, list<int> > DomTreeEntry; DomTreeEntry.first = this->IDom.at(index); DomTreeEntry.second.clear(); this->DomTree.push_back(DomTreeEntry); } // Now, push the children onto the appropriate lists. for (index = 0; index < this->IDom.size(); ++index) { // E.g. if block 5 has block 3 as a parent, then we fetch the number 3 // using the expression this->DomTree.at(index).first, which was just // initialized in the previous loop. Then we go to DomTree entry 3 and push // the number 5 on its child list. int parent = this->DomTree.at(index).first; if (parent != (int) index) // block can dominate itself, but not in DomTree! this->DomTree.at(parent).second.push_back((int) index); } return; } // end of SMPFunction::BuildDominatorTree() // Does basic block HeadBlockNum dominate basic block TailBlockNum? bool SMPFunction::DoesBlockDominateBlock(int HeadBlockNum, int TailBlockNum) { if (HeadBlockNum == TailBlockNum) return true; else if ((HeadBlockNum == SMP_BLOCKNUM_UNINIT) || (TailBlockNum == SMP_BLOCKNUM_UNINIT)) { return false; } else { // Recurse downward from HeadBlockNum in the dominator tree until we find TailBlockNum // or return false if we never find it. bool FoundIt = false; for (list<int>::iterator ChildIter = this->DomTree.at(HeadBlockNum).second.begin(); ChildIter != this->DomTree.at(HeadBlockNum).second.end(); ++ChildIter) { int ChildBlockNum = (*ChildIter); if (this->DoesBlockDominateBlock(ChildBlockNum, TailBlockNum)) { // recurse depth-first FoundIt = true; break; } } return FoundIt; } } // end of SMPFunction::DoesBlockDominateBlock() // Is block (with block # BlockNum) inside any loop? bool SMPFunction::IsBlockInAnyLoop(int BlockNum) { bool FoundInsideLoop = this->FuncLoopsByBlock.at((size_t)BlockNum).IsAnyBitSet(); return FoundInsideLoop; } // end of SMPFunction::IsBlockInAnyLoop() // Is block (with block # BlockNum) inside loop # LoopNum? bool SMPFunction::IsBlockInLoop(int BlockNum, size_t LoopNum) { return ((LoopNum < this->LoopCount) && (this->FuncLoopsByBlock.at((size_t) BlockNum).GetBit(LoopNum))); } // end of SMPFunction::IsBlockInLoop() // build list of loop numbers that BlockNum is part of. void SMPFunction::BuildLoopList(int BlockNum, list<size_t> &LoopList) { size_t LoopIndex; for (LoopIndex = 0; LoopIndex < this->LoopCount; ++LoopIndex) { if (this->FuncLoopsByBlock.at((size_t) BlockNum).GetBit(LoopIndex)) { LoopList.push_back(LoopIndex); } } return; } // end of SMPFunction::BuildLoopList() // Helper for SSA subscript renumbering: return the next SSA number for the global name // and increment the SSACounter to prepare the next number. Push the returned number onto // the SSAStack for the global name. int SMPFunction::SSANewNumber(size_t GlobNameIndex) { int Subscript = this->SSACounter.at(GlobNameIndex); ++(this->SSACounter[GlobNameIndex]); this->SSAStack[GlobNameIndex].push_back(Subscript); return Subscript; } // end of SMPFunction::SSANewNumber() // Main helper for SSA subscript renumbering. Renumber within block throughout its phi // functions, then its DEFs and USEs, then its phi successors. Recurse then on all // successors in the dominator tree. void SMPFunction::SSARename(int BlockNumber) { assert(0 <= BlockNumber); assert(BlockNumber < this->BlockCount); SMPBasicBlock *CurrBlock = this->RPOBlocks.at((size_t) BlockNumber); bool DumpFlag = false; #if SMP_DEBUG_DATAFLOW_VERBOSE DumpFlag |= (0 == strcmp("main", this->GetFuncName())); DumpFlag |= (0 == strcmp("dohanoi", this->GetFuncName())); DumpFlag |= (0 == strcmp("uw_frame_state_for", this->GetFuncName())); DumpFlag |= (0 == strcmp("_IO_sputbackc", this->GetFuncName())); #endif if (DumpFlag) SMP_msg("Entered SSARename for block number %d\n", BlockNumber); // For each phi function at the top of the block, rename the DEF of the phi function // using SSANewNumber() on the global name index. set<SMPPhiFunction, LessPhi>::iterator CurrPhi; list<SMPPhiFunction> TempPhiList; int GlobalNameIndex; for (CurrPhi = CurrBlock->GetFirstPhi(); CurrPhi != CurrBlock->GetLastPhi(); ++CurrPhi) { op_t PhiDefOp = CurrPhi->GetAnyOp(); GlobalNameIndex = CurrPhi->GetIndex(); assert(0 <= GlobalNameIndex); int NewSSANum = this->SSANewNumber((size_t) GlobalNameIndex); // Cannot change the C++ STL set item directly, as sets might become unordered. SMPPhiFunction TempPhi = (*CurrPhi); TempPhi.SetSSADef(NewSSANum); TempPhiList.push_back(TempPhi); if (o_reg == PhiDefOp.type) { if (DumpFlag && PhiDefOp.is_reg(R_ax)) { SMP_msg("New EAX Phi Def SSANum: %d Block %d\n", NewSSANum, BlockNumber); } // Map the final SSA number to the block number. int DefHashValue = HashGlobalNameAndSSA(PhiDefOp, NewSSANum); pair<int, ea_t> DefMapEntry(DefHashValue, CurrBlock->GetNumber()); pair<map<int, ea_t>::iterator, bool> MapReturnValue; MapReturnValue = this->GlobalDefAddrBySSA.insert(DefMapEntry); assert(MapReturnValue.second); } } // Go back through the Phi function set and replace the items that need to be updated. list<SMPPhiFunction>::iterator TempIter; for (TempIter = TempPhiList.begin(); TempIter != TempPhiList.end(); ++TempIter) { // Use the op_t from the first phi use, because they are all the same. bool Erased = CurrBlock->ErasePhi(TempIter->GetPhiRef(0).GetOp()); assert(Erased); // Now we can add back the phi function that had the DEF SSA number changed. bool Added = CurrBlock->AddPhi(*TempIter); assert(Added); } TempPhiList.clear(); if (DumpFlag) SMP_msg("Processed phi functions at top.\n"); // For each instruction in the block, rename all global USEs and then all global DEFs. vector<SMPInstr *>::iterator InstIter; SMPInstr *CurrInst; for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { CurrInst = (*InstIter); set<DefOrUse, LessDefUse>::iterator CurrUse = CurrInst->GetFirstUse(); ea_t InstAddr = CurrInst->GetAddr(); // for debugging break points while (CurrUse != CurrInst->GetLastUse()) { // See if Use is a global name. op_t UseOp = CurrUse->GetOp(); set<op_t, LessOp>::iterator GlobIter = this->GlobalNames.find(UseOp); if (GlobIter != this->GlobalNames.end()) { // found it unsigned int GlobIndex = ExtractGlobalIndex(*GlobIter); if (GlobIndex > this->SSAStack.size()) { // Get some debug info out to the log file before we crash. SMP_msg("FATAL ERROR: Bad GlobIndex: %d at %lx in %s\n", GlobIndex, (unsigned long) InstAddr, this->GetFuncName()); exit(EXIT_FAILURE); } // Set the SSA number for this use to the top of stack SSA # (back()) int NewSSANum; if (this->SSAStack.at(GlobIndex).empty()) { // No top of stack entry to read. #if SMP_DEBUG_UNINITIALIZED_SSA_NAMES if (!CurrInst->MDIsPopInstr() && (o_reg == UseOp.type)) { // POP uses the stack offset and generates spurious // uninitialized variable messages for [esp+0]. SMP_msg("WARNING: function %s : Use of uninitialized variable: ", this->GetFuncName()); SMP_msg(" Variable: "); PrintListOperand(*GlobIter); SMP_msg(" Block number: %d Address: %lx Instruction: %s\n", BlockNumber, (unsigned long) CurrInst->GetAddr(), CurrInst->GetDisasm()); } #endif NewSSANum = SMP_SSA_UNINIT; } else { NewSSANum = this->SSAStack.at(GlobIndex).back(); } CurrUse = CurrInst->SetUseSSA(UseOp, NewSSANum); if (DumpFlag && (o_reg == UseOp.type) && UseOp.is_reg(R_ax)) { SMP_msg("New EAX Use SSANum: %d at %lx\n", NewSSANum, (unsigned long) CurrInst->GetAddr()); } } ++CurrUse; } // end for all USEs set<DefOrUse, LessDefUse>::iterator CurrDef = CurrInst->GetFirstDef(); while (CurrDef != CurrInst->GetLastDef()) { // See if Def is a global name. op_t DefOp = CurrDef->GetOp(); set<op_t, LessOp>::iterator GlobIter = this->GlobalNames.find(DefOp); if (GlobIter != this->GlobalNames.end()) { // found it unsigned int GlobIndex = ExtractGlobalIndex(*GlobIter); // Set the SSA number for this DEF to the SSANewNumber top of stack int NewSSANum = this->SSANewNumber(GlobIndex); CurrDef = CurrInst->SetDefSSA(DefOp, NewSSANum); if (o_reg == DefOp.type) { ea_t DefAddr = InstAddr; if (DumpFlag && DefOp.is_reg(R_ax)) { SMP_msg("New EAX Def SSANum: %d at %lx\n", NewSSANum, (unsigned long) DefAddr); } // Map the final SSA number to the DEF address. int DefHashValue = HashGlobalNameAndSSA(DefOp, NewSSANum); pair<int, ea_t> DefMapEntry(DefHashValue, DefAddr); pair<map<int, ea_t>::iterator, bool> MapReturnValue; MapReturnValue = this->GlobalDefAddrBySSA.insert(DefMapEntry); assert(MapReturnValue.second); } } ++CurrDef; } // end for all DEFs } // end for all instructions if (DumpFlag) SMP_msg("Processed all instructions.\n"); // For all control flow graph (not dominator tree) successors, fill in the current // (outgoing) SSA number in the corresponding USE slot in the phi function, for all // global names appearing in phi functions. list<SMPBasicBlock *>::iterator SuccIter; for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { // What position in the Preds list of this successor is CurrBlock? int ListPos = (*SuccIter)->GetPredPosition(BlockNumber); assert(0 <= ListPos); // Go through all phi functions in this successor. At ListPos position in the // incoming arguments for that phi function, set the SSA number to the SSA number // in the top of stack entry for the global name associated with that phi function. set<SMPPhiFunction, LessPhi>::iterator CurrPhi; for (CurrPhi = (*SuccIter)->GetFirstPhi(); CurrPhi != (*SuccIter)->GetLastPhi(); ++CurrPhi) { int GlobIndex = CurrPhi->GetIndex(); int CurrSSA; if (this->SSAStack.at(GlobIndex).empty()) { // No top of stack entry to read. #if SMP_DEBUG_UNINITIALIZED_SSA_NAMES SMP_msg("WARNING: function %s : Path to use of uninitialized variable: ", this->GetFuncName()); SMP_msg(" Variable: "); PrintListOperand(CurrPhi->GetAnyOp()); SMP_msg(" Block number: %d Successor block number: %d\n", BlockNumber, (*SuccIter)->GetNumber()); #endif CurrSSA = SMP_SSA_UNINIT; } else { CurrSSA = this->SSAStack.at(GlobIndex).back(); // fetch from top of stack } SMPPhiFunction TempPhi = (*CurrPhi); TempPhi.SetSSARef(ListPos, CurrSSA); TempPhiList.push_back(TempPhi); if (DumpFlag && (BlockNumber >= 3) && (BlockNumber <= 4)) { SMP_msg("BlockNumber: %d ListPos: %d\n", BlockNumber, ListPos); } } // end for all phi functions in successor // Go back through the Phi function set and replace the items that need to be updated. for (TempIter = TempPhiList.begin(); TempIter != TempPhiList.end(); ++TempIter) { #if 0 if (DumpFlag && (BlockNumber >= 3) && (BlockNumber <= 4)) { SMP_msg("Special before phi dump:\n"); set<SMPPhiFunction, LessPhi>::iterator FoundPhi; FoundPhi = (*SuccIter)->FindPhi(TempIter->GetAnyOp()); FoundPhi->Dump(); } #endif // Use the op_t from the first phi use, because they are all the same. bool Erased = (*SuccIter)->ErasePhi(TempIter->GetPhiRef(0).GetOp()); assert(Erased); // Now we can add back the phi function that had one SSA number changed. bool Added = (*SuccIter)->AddPhi(*TempIter); assert(Added); if (DumpFlag && (BlockNumber >= 3) && (BlockNumber <= 4)) { SMP_msg("Special after phi dump:\n"); set<SMPPhiFunction, LessPhi>::iterator FoundPhi; FoundPhi = (*SuccIter)->FindPhi(TempIter->GetAnyOp()); FoundPhi->Dump(); } } TempPhiList.clear(); } // end for all successors of CurrBlock if (DumpFlag) SMP_msg("Processed successor phi functions.\n"); // For each successor in the dominator tree, recurse. list<int>::iterator ChildIter; for (ChildIter = this->DomTree[BlockNumber].second.begin(); ChildIter != this->DomTree[BlockNumber].second.end(); ++ChildIter) { this->SSARename(*ChildIter); } if (DumpFlag) SMP_msg("Finished recursion.\n"); // Pop off all SSAStack entries pushed during this block. I.e. for each global name, // pop its SSAStack once per DEF and once per phi function in this block. for (CurrPhi = CurrBlock->GetFirstPhi(); CurrPhi != CurrBlock->GetLastPhi(); ++CurrPhi) { GlobalNameIndex = CurrPhi->GetIndex(); this->SSAStack.at((size_t) GlobalNameIndex).pop_back(); } if (DumpFlag) SMP_msg("Popped off entries due to phi functions.\n"); for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++InstIter) { set<DefOrUse, LessDefUse>::iterator CurrDef; CurrInst = (*InstIter); for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) { // See if DEF is a global name. set<op_t, LessOp>::iterator GlobIter = this->GlobalNames.find(CurrDef->GetOp()); if (GlobIter != this->GlobalNames.end()) { // found it unsigned int GlobIndex = ExtractGlobalIndex(*GlobIter); this->SSAStack.at((size_t) GlobIndex).pop_back(); } } // end for all DEFs } // end for all instructions if (DumpFlag) { SMP_msg("Popped off entries due to instructions.\n"); } return; } // end of SMPFunction::SSARename() // Main driver of SSA subscript renumbering. void SMPFunction::SSARenumber(void) { bool DumpFlag = false; #if 0 DumpFlag |= (0 == strcmp("_IO_sputbackc", this->GetFuncName())); #endif if (0 >= this->GlobalNames.size()) return; // no names to renumber // Initialize stacks and counters of SSA numbers. size_t GlobIndex; assert(0 == this->SSACounter.size()); for (GlobIndex = 0; GlobIndex < this->GlobalNames.size(); ++GlobIndex) { list<int> DummyList; this->SSACounter.push_back(0); this->SSAStack.push_back(DummyList); } // Recurse through the dominator tree starting with node 0. this->SSARename(0); if (DumpFlag) this->Dump(); return; } // end of SMPFunction::SSARenumber() // Emit debugging output for analyzing time spent in InferTypes() ? #define SMP_ANALYZE_INFER_TYPES_TIME 0 // Main driver for the type inference system. void SMPFunction::InferTypes(bool FirstIter) { // The type inference system is an iteration over four analysis steps, until // a fixed point is reached: // 1) Within an instruction, set types of operators based on the operator type, // the operand types, and the instruction type category, and propagate the // type of the SMP_ASSIGN operator to its DEF. // 2) Propagate the type of a DEF along its SSA chain to all USEs of that SSA name. // 3) If all USEs of an SSA name have the same type, but the DEF has no type, // then infer that the DEF must have the same type. // 4) If all references to a memory location have the same type, mark that memory // location as having that type, if no aliasing occurs. // // The type inference system will mark DEFs and USEs in each instruction's DEF and USE // sets with an inferred type. This inference on USEs is not conclusive for other USEs // outside of that instruction. For example, a pointer could be read in from memory // and used as a pointer, then hashed using an arithmetic operation. If the arithmetic // operation always treats its source operands as NUMERIC and produces a NUMERIC // result, e.g. SMP_BITWISE_XOR, then the USE of that pointer is NUMERIC within // this xor instruction. If the DEF at the beginning of the SSA chain for the pointer // is eventually marked as POINTER, then all USEs in the chain will be marked POINTER // as well (see step 2 above). This inconsistency along the USE chain is perfectly // acceptable in our type system. It is important to mark the USEs according to how // we observe them being used, because consistent USEs will propagate back up to // the DEF in step 3 above. bool changed; bool NewChange = false; #if SMP_ANALYZE_INFER_TYPES_TIME bool DebugFlag2 = false; DebugFlag2 |= (0 == strcmp("Option", this->GetFuncName())); long NewChangeCount; long IterationCount = 0; #endif #if SMP_DEBUG_TYPE_INFERENCE bool DebugFlag = false; DebugFlag |= (0 == strcmp("__libc_csu_init", this->GetFuncName())); #endif list<SMPInstr *>::iterator InstIter; SMPInstr *CurrInst; set<DefOrUse, LessDefUse>::iterator CurrDef; set<DefOrUse, LessDefUse>::iterator NextDef; list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; ea_t InstAddr; #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) { this->Dump(); } #endif // One time only: Set the types of immediate values, flags register, stack and frame // pointers, and floating point registers. if (FirstIter) { for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) { SMP_msg("SetImmedTypes for inst at %x: %s\n", CurrInst->GetAddr(), CurrInst->GetDisasm()); } #endif CurrInst->SetImmedTypes(this->UseFP); // Infer signedness, bit width, and other info from the nature of the instruction // (e.g. loads from stack locations whose signedness has been inferred earlier // in FindOutGoingArgSize(), or inherently signed arithmetic opcodes like signed // or unsigned multiplies and divides). CurrInst->MDSetWidthSignInfo(this->UseFP); } // Check for signedness inferences from conditional branches at the end of blocks. for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); CurrBlock->MarkBranchSignedness(); } // Find counter variables (e.g. init to zero or small constant, then just add or subtract small // constant values. These cannot be POINTER and can be marked as NUMERIC. this->FindCounterVariables(); } // Iterate until no more changes: set types in DEF and USE lists based on RTL // operators and the instruction category, SSA DEF-USE chains, etc. do { #if SMP_ANALYZE_INFER_TYPES_TIME if (DebugFlag2) ++IterationCount; #endif #if 0 do { #endif changed = false; #if SMP_ANALYZE_INFER_TYPES_TIME if (DebugFlag2) NewChangeCount = 0; #endif // Step one: Infer types within instructions, context free. // Step two, propagating DEF types to all USEs, happens within step one // whenever a DEF type is set for the first time. for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) SMP_msg("Inferring types for %s\n", CurrInst->GetDisasm()); #endif NewChange = CurrInst->InferTypes(); changed = (changed || NewChange); #if SMP_ANALYZE_INFER_TYPES_TIME if (DebugFlag2 && NewChange) { ea_t InstAddr = CurrInst->GetAddr(); ++NewChangeCount; } #endif } #if SMP_ANALYZE_INFER_TYPES_TIME if (DebugFlag2) { SMP_msg(" InferTypes iteration: %ld NewChangeCount: %ld \n", IterationCount, NewChangeCount); } #endif #if 0 } while (changed); #endif #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) SMP_msg("Finished type inference steps 1 and 2.\n"); #endif // Step three: If all USEs of an SSA name have the same type, but the DEF has no // type, then infer that the DEF must have the same type. this->TypedDefs = 0; this->UntypedDefs = 0; this->TypedPhiDefs = 0; this->UntypedPhiDefs = 0; // This step of the type inference might converge faster if we used a reverse iterator // to go through the instructions, because we could infer a DEF, propagate it to // the right hand side by making SMPInstr::InferOperatorType() public and calling it // on the SMP_ASSIGN operator after we set the type of the left hand side (DEF). Any // additional DEF inferences would be triggered mostly in the upwards direction by // setting the type of one or more USEs in the current instruction. How much time gain // could be achieved by doing this sequence is questionable. !!!!****!!!!**** for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { CurrInst = (*InstIter); InstAddr = CurrInst->GetAddr(); // Find any DEF that still has type UNINIT. CurrDef = CurrInst->GetFirstDef(); while (CurrDef != CurrInst->GetLastDef()) { // Set erase() and insert() are needed to change types of DEFs, so // get hold of the next iterator value now. NextDef = CurrDef; ++NextDef; NewChange = false; if (UNINIT != CurrDef->GetType()) { ++(this->TypedDefs); } else { op_t DefOp = CurrDef->GetOp(); bool MemDef = (DefOp.type != o_reg); bool AliasedMemWrite = (MemDef && CurrDef->HasIndirectWrite()); ++(this->UntypedDefs); if (MDIsIndirectMemoryOpnd(DefOp, this->UseFP) // relax this? #if 0 || (o_mem == DefOp.type) #endif || AliasedMemWrite) { // Don't want to infer along DEF-USE chains for indirect // memory accesses until we have alias analysis. ++CurrDef; continue; } ea_t DefAddr = InstAddr; // Call inference method based on whether it is a block-local // name or a global name. CurrBlock = CurrInst->GetBlock(); if (CurrBlock->IsLocalName(DefOp)) { set<op_t, LessOp>::iterator NameIter; NameIter = CurrBlock->FindLocalName(DefOp); assert(CurrBlock->GetLastLocalName() != NameIter); unsigned int LocIndex = ExtractGlobalIndex(*NameIter); NewChange = CurrBlock->InferLocalDefType(DefOp, LocIndex, DefAddr); if (NewChange) { --(this->UntypedDefs); ++(this->TypedDefs); } changed = (changed || NewChange); } else { // global name bool CallInst = ((CALL == CurrInst->GetDataFlowType()) || (INDIR_CALL == CurrInst->GetDataFlowType())); int DefSSANum = CurrDef->GetSSANum(); SMPOperandType DefType = UNINIT; DefType = this->InferGlobalDefType(DefOp, DefSSANum, CurrBlock, CallInst, DefAddr); if (IsNotEqType(UNINIT, DefType)) { CurrDef = CurrInst->SetDefType(DefOp, DefType); --(this->UntypedDefs); ++(this->TypedDefs); NewChange = true; // If we have one or more USEs of type POINTER and the // other USEs are UNINIT, then InferGlobalDefType() will // infer that it is a POINTER. We want to propagate POINTER // to all the USEs now. if (IsDataPtr(DefType)) { this->ResetProcessedBlocks(); CurrInst->GetBlock()->PropagateGlobalDefType(DefOp, DefType, DefSSANum, IsMemOperand(DefOp)); } } changed = (changed || NewChange); } // end if local name ... else ... } // end if (UNINIT != CurrDef->GetType()) .. else ... CurrDef = NextDef; } // end while all DEFs in the DEF set } // end for all instructions #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) SMP_msg("Finished type inference step 3.\n"); #endif for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); changed |= CurrBlock->InferAllPhiDefTypes(); } #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) SMP_msg("Finished unconditional phi type inference.\n"); #endif #if SMP_CONDITIONAL_TYPE_PROPAGATION if (!changed) { // Try conditional type propagation changed |= this->ConditionalTypePropagation(); #if SMP_DEBUG_TYPE_INFERENCE if (DebugFlag) { SMP_msg("changed = %d after conditional type propagation.\n", changed); } #endif } #endif } while (changed); // With type inference finished, infer signedness from the types, e.g. // POINTER and CODEPOINTER types must be UNSIGNED. if (FirstIter) { // Don't want profiler-dependent signedness in the system yet. for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { (*InstIter)->InferSignednessFromSMPTypes(this->UsesFramePointer()); } } // Record the meet of all register types that reach RETURN instructions. this->MDFindReturnTypes(); return; } // end of SMPFunction::InferTypes() // determine signedness and width info for all operands void SMPFunction::InferFGInfo(void) { bool changed, NewChange; unsigned short IterCount = 0; list<SMPInstr *>::iterator InstIter; list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; do { changed = false; ++IterCount; for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); NewChange = CurrInst->InferFGInfo(IterCount); changed = (changed || NewChange); } if (changed) { for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); CurrBlock->PropagatePhiFGInfo(); } } #if STARS_AGGRESSIVE_SIGNEDNESS_PROPAGATION if (!changed) { changed = this->PropagateSignedness(); } #endif } while (changed); return; } // end of SMPFunction::InferFGInfo() // Apply the profiler information to this function once we've inferred everything we can about it. void SMPFunction::ApplyProfilerInformation(ProfilerInformation* pi) { assert(pi); // If no profiler annotations are available, save time. if (0 == pi->GetProfilerAnnotationCount()) return; SetIsSpeculative(true); list<SMPInstr *>::iterator InstIter; set<DefOrUse, LessDefUse>::iterator CurrDef, NextDef; bool DebugFlag = false; #if SMP_DEBUG_PROFILED_TYPE_INFERENCE DebugFlag |= (0 == strcmp("dohanoi", this->GetFuncName())); #endif // for each instruction in this function for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); // lookup whether a load at this instruction was profiled as always numeric InstructionInformation* ii = pi->GetInfo(CurrInst->GetAddr()); if (ii && DebugFlag) SMP_msg("Found instruction information for %lx\n", (unsigned long) CurrInst->GetAddr()); if (ii && ii->isNumeric()) { #if SMP_DEBUG_PROFILED_TYPE_INFERENCE SMP_msg("Found instruction information for %lx and it's numeric!\n", (unsigned long) CurrInst->GetAddr()); #endif CurrInst->UpdateMemLoadTypes((SMPOperandType)(NUMERIC|PROF_BASE)); } // lookup whether this instruction has been profiled as an indirect call set<ea_t> indirect_call_targets = pi->GetIndirectCallTargets(CurrInst->GetAddr()); for (set<ea_t>::iterator ict_iter = indirect_call_targets.begin(); ict_iter != indirect_call_targets.end(); ++ict_iter) { ea_t target = *ict_iter; pair<set<ea_t>::iterator, bool> InsertResult; InsertResult = this->IndirectCallTargets.insert(target); if (InsertResult.second && (!vector_exists(target, AllCallTargets))) AllCallTargets.push_back(target); } } return; } // end of SMPFunction::ApplyProfilerInformation // For the UNINIT type DEF DefOp, see if all its USEs have a single type. // If so, set the DEF to that type and return type, // else return UNINIT. // If DefAddr == BADADDR, then the DEF is in a Phi function, not an instruction. SMPOperandType SMPFunction::InferGlobalDefType(op_t DefOp, int SSANum, SMPBasicBlock *DefBlock, bool CallInst, ea_t DefAddr) { bool DebugFlag = false; bool FoundNumeric = false; bool FoundPointer = false; bool FoundUnknown = false; bool FoundUninit = false; bool FoundDEF; bool DefEscapes = true; #if SMP_DEBUG_TYPE_INFERENCE DebugFlag |= (0 == strcmp("mem_init", this->GetFuncName())); #endif if (DebugFlag) { SMP_msg("InferGlobalDefType for SSANum %d of ", SSANum); PrintOperand(DefOp); SMP_msg("\n"); } vector<SMPInstr *>::iterator InstIter; assert(0 <= SSANum); set<DefOrUse, LessDefUse>::iterator CurrUse, CurrDef; // Go through all instructions in the block and find the instructions // that have USEs of DefOp with SSANum. If all USEs in the chain have // a single type (other than UNINIT), change the DEF type to match the // USE type and set changed to true. SMPOperandType UseType = UNINIT; SMPOperandType PtrType = UNINIT; if (BADADDR == DefAddr) { // DEF is in a Phi function FoundDEF = true; } else { // DEF is in an instruction FoundDEF = false; // need to see the DefAddr first } for (InstIter = DefBlock->GetFirstInst(); InstIter != DefBlock->GetLastInst(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); if ((!FoundDEF) && (DefAddr == CurrInst->GetAddr())) { FoundDEF = true; } else if (FoundDEF) { CurrDef = CurrInst->FindDef(DefOp); if (CurrDef != CurrInst->GetLastDef()) { // Found re-DEF of DefOp. DefEscapes = false; } } // NOTE: Following instructions should be inside if (FoundDEF) condition. CurrUse = CurrInst->FindUse(DefOp); if (CurrUse != CurrInst->GetLastUse()) { // found a USE of DefOp if (CurrUse->GetSSANum() == SSANum) { // matched SSA number UseType = CurrUse->GetType(); FoundNumeric |= (IsNumeric(UseType)); FoundUnknown |= (IsUnknown(UseType)); FoundUninit |= (IsEqType(UNINIT, UseType)); if (IsDataPtr(UseType)) { if (FoundPointer) { if (IsNotEqType(PtrType, UseType)) { #if SMP_DEBUG_TYPE_INFERENCE SMP_msg("WARNING: Differing ptr types in global chain:"); SMP_msg(" Prev: %d Current: %d %s\n", PtrType, UseType, CurrInst->GetDisasm()); #endif PtrType = POINTER; } } else { FoundPointer = true; PtrType = UseType; } } } // end if matched SSA # } // end if found a USE of DefOp } // end for all instructions if (DefEscapes) { // did not find re-def DefEscapes = DefBlock->IsLiveOut(DefOp); } if (DefEscapes) { // Need to recurse into successor blocks list<SMPBasicBlock *>::iterator SuccIter; ea_t TempAddr; this->ResetProcessedBlocks(); // set up recursion for (SuccIter = DefBlock->GetFirstSucc(); SuccIter != DefBlock->GetLastSucc(); ++SuccIter) { SMPBasicBlock *CurrBlock = (*SuccIter); set<SMPPhiFunction, LessPhi>::iterator PhiIter = CurrBlock->FindPhi(DefOp); TempAddr = DefAddr; if (PhiIter != CurrBlock->GetLastPhi()) { TempAddr = BADADDR; // signals that DefOp will get re-DEFed in a Phi function. } else if (BADADDR == TempAddr) { // was BADADDR coming in to this function // We don't want to pass BADADDR down the recursion chain, because it will be interpreted // by each successor block to mean that DefOp was a Phi USE that got re-DEFed in a Phi function // within itself. Pass the dummy address that indicates LiveIn to the block. TempAddr = CurrBlock->GetFirstAddr() - 1; } // Should we screen the recursive call below using CurrBlock->IsLiveIn(DefOp) for speed? !!!!****!!!! CurrBlock->InferGlobalDefType(DefOp, SSANum, TempAddr, FoundNumeric, FoundPointer, FoundUnknown, FoundUninit, PtrType); } } // Do we have a consistent type? // If we see any definite POINTER uses, we must set the DEF // to type POINTER or a refinement of it. if (FoundPointer) UseType = PtrType; else if (FoundNumeric && !FoundUninit && !FoundUnknown) UseType = NUMERIC; else return UNINIT; // no POINTER, but no consistent type assert(UNINIT != UseType); if (DebugFlag) SMP_msg("Inferring global DEF of type %d\n", UseType); return UseType; } // end of SMPFunction::InferGlobalDefType() // Mark NUMERIC (and propagate) any DEF that starts at small immed. value and gets only small inc/dec operations. void SMPFunction::FindCounterVariables(void) { // We define a counter variable as one that starts out as a small immediate value and then is only updated // via small additions and subtractions. This cannot produce a POINTER, so it must be NUMERIC. This routine // helps get the NUMERIC inference past the phi function barrier, e.g.: // // mov eax,0 ; might be NULL POINTER or might be NUMERIC // eax2 := phi(eax1, eax0) ; still don't know type // label1: ; top of loop // : // add eax,4 ; increment induction variable; eax1 := eax0 + 4 // cmp eax,looplimit // jl label1 // // Viewed in isolation, adding 4 to EAX could be a pointer operation or a numeric operation, and // the same is true for initializing to zero. Viewed together, these statements obviously cannot be // producing a POINTER value, as 0,4,8, etc. are not a sequence of POINTER values. list<SMPInstr *>::iterator InstIter; for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); bool ValueFound; uval_t ConstValue; if (CurrInst->MDIsSimpleAssignment(ValueFound, ConstValue)) { if (ValueFound && (0 == ConstValue)) { // Start small: Find init to zero, then track it. Init to small values after we test. set<DefOrUse, LessDefUse>::iterator DefIter = CurrInst->GetFirstNonFlagsDef(); if (DefIter == CurrInst->GetLastDef()) { // Must have been a simple assignment to a flag, e.g. clc (clear the carry flag). continue; } op_t DefOp = DefIter->GetOp(); if (o_reg == DefOp.type) { list<pair<int, ea_t> > CounterSSANums; // SSA numbers that are definitely counters for DefOp int DefSSANum = DefIter->GetSSANum(); ea_t DefAddr = CurrInst->GetAddr(); pair<int, ea_t> ListItem(DefSSANum, DefAddr); CounterSSANums.push_back(ListItem); SMPBasicBlock *CurrBlock = CurrInst->GetBlock(); int BlockNum = CurrBlock->GetNumber(); bool LocalName = CurrBlock->IsLocalName(DefOp); if (this->CounterVarHelper(DefOp, DefSSANum, BlockNum, LocalName, CounterSSANums)) { while (!CounterSSANums.empty()) { int CurrSSANum = CounterSSANums.front().first; ea_t CurrDefAddr = CounterSSANums.front().second; bool Propagated; if (LocalName) { Propagated = CurrBlock->PropagateLocalDefType(DefOp, NUMERIC, CurrDefAddr, CurrSSANum, false); } else { this->ResetProcessedBlocks(); Propagated = CurrBlock->PropagateGlobalDefType(DefOp, NUMERIC, CurrSSANum, false); } CounterSSANums.pop_front(); } } } // end if o_reg type } // end if const value of 0 } // end if simple assignment } // end for all instructions return; } // end of SMPFunction::FindCounterVariables() // recursive helper for FindCounterVariables() // Return true if we added to the DefSSANums list. bool SMPFunction::CounterVarHelper(op_t DefOp, int DefSSANum, int BlockNum, bool LocalName, list<pair<int, ea_t> > CounterSSANums) { bool ListExpanded = false; size_t IncomingListSize = CounterSSANums.size(); set<int> NonEscapingRegisterHashes; // First, examine the Phi list to find uses of DefOp/DefSSANum. // Next, examine instructions to find uses of DefOp/DefSSANum. They must be counter operations if DefOp is re-defed. // As a first cut, we will just find the following pattern: // 1. Counter-style DEF reaches the end of the current block. // 2. Successor block is a single-block loop with counter DEF appearing as a USE in a phi function. // 3. Within the single-block loop, Phi DEF is used in a counter-style operation, with new DEF becoming a Phi USE at top of block. // We will expand this to loops that are not in a single block later. SMPBasicBlock *CurrBlock = this->GetBlockByNum((size_t) BlockNum); assert(NULL != CurrBlock); ea_t DefAddr = CounterSSANums.front().second; if (CurrBlock->DoesDefReachBlockEnd(DefAddr, DefOp, DefSSANum, NonEscapingRegisterHashes)) { NonEscapingRegisterHashes.clear(); // Not memoizing for this use of DoesDefReachBlockEnd() bool LoopSuccFound = false; list<SMPBasicBlock *>::iterator SuccIter; SMPBasicBlock *SuccBlock; int PhiDefSSANum; set<SMPPhiFunction, LessPhi>::iterator PhiIter; for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) { SuccBlock = (*SuccIter); if (SuccBlock->IsSelfLoop()) { PhiIter = SuccBlock->FindPhi(DefOp); if (PhiIter != SuccBlock->GetLastPhi()) { // Found a Phi function that could match size_t PhiSize = PhiIter->GetPhiListSize(); for (size_t index = 0; index < PhiSize; ++index) { int PhiUseSSANum = PhiIter->GetUseSSANum(index); if (PhiUseSSANum == DefSSANum) { // Our DEF is a USE in the phi function at the top of SuccBlock. Success. PhiDefSSANum = PhiIter->GetDefSSANum(); LoopSuccFound = true; break; } } } if (LoopSuccFound) { break; } } } if (LoopSuccFound) { // SuccBlock points to a one-block loop with PhiIter pointing to a phi function that has // DefOp/DefSSANum as a phi use, and DefOp/PhiDefSSANum as its phi def. Are the uses of // DefOp/PhiDefSSANum within SuccBlock merely counter redefinitions? vector<SMPInstr *>::iterator InstIter; for (InstIter = SuccBlock->GetFirstInst(); InstIter != SuccBlock->GetLastInst(); ++InstIter) { set<DefOrUse, LessDefUse>::iterator DefIter, UseIter; SMPInstr *CurrInst = (*InstIter); DefIter = CurrInst->FindDef(DefOp); if (DefIter != CurrInst->GetLastDef()) { // Found a redefinition of DefOp. Is it just a counter operation that redefines DefOp? if (CurrInst->IsCounterOperation()) { // We will add the new DEF SSA # to the list of counter SSAs. pair<int, ea_t> CounterPair(DefIter->GetSSANum(), CurrInst->GetAddr()); CounterSSANums.push_back(CounterPair); // We don't need to push the PhiDefSSANum discovered earlier, because if // it follows the simple pattern of only using two counter DEFs as its USEs, // one from before the loop and one from within the loop, then both of its USEs // will get set to NUMERIC and propagation will occur naturally. If it does not // fit this simple pattern, we don't want to force it to be NUMERIC yet. } else { // Problem: we redefined DefOp with a non-counter operation. We want to terminate // the chain of detection of counter variables. break; } } else { UseIter = CurrInst->FindUse(DefOp); if (UseIter != CurrInst->GetLastUse()) { // Found USE of DefOp. See if it is a POINTER use, which would // invalidate the hypothesis that DefOp is a counter. SMPOperandType UseType = UseIter->GetType(); if (IsDataPtr(UseType) || IsEqType(UseType, CODEPTR)) { // Any apparent counter operations so far have really been pointer arithmetic. // We need to restore the list to its incoming state. while (IncomingListSize < CounterSSANums.size()) { CounterSSANums.pop_back(); } break; // terminate search } } } } // end for all insts in SuccBlock } // end if LoopSuccFound } // end if original pre-loop DEF reaches the end of its block ListExpanded = (CounterSSANums.size() > IncomingListSize); return ListExpanded; } // end of SMPFunction::CounterVarHelper() #define SMP_SIMPLE_CONDITIONAL_TYPE_PROPAGATION 1 #if SMP_SIMPLE_CONDITIONAL_TYPE_PROPAGATION // The simple form of conditional type propagation observes that we // simply need to apply the meet operator over Phi function USEs and // then propagate any DEF type changes using PropagateGlobalDefType(). // The outermost iteration over all type inference methods in InferTypes() // will take care of all the propagation that is handled by the work list // processing in the textbook algorithm. // Iteration convergence might be slower in the simple approach, but the code // is much simpler to debug. bool SMPFunction::ConditionalTypePropagation(void) { bool changed = false; SMPBasicBlock *CurrBlock; vector<SMPBasicBlock *>::iterator CurrRPO; set<SMPPhiFunction, LessPhi>::iterator CurrPhi; for (CurrRPO = this->RPOBlocks.begin(); CurrRPO != this->RPOBlocks.end(); ++CurrRPO) { CurrBlock = *CurrRPO; SMPOperandType MeetType; for (CurrPhi = CurrBlock->GetFirstPhi(); CurrPhi != CurrBlock->GetLastPhi(); ++CurrPhi) { MeetType = CurrPhi->ConditionalMeetType(); // Here we use a straight equality test, not our macros, // because we consider it a change if the MeetType is // profiler derived and the DEFType is not. if (MeetType == CurrPhi->GetDefType()) continue; // Change the DEF type to the MeetType and propagate. op_t DefOp = CurrPhi->GetAnyOp(); bool IsMemOp = (o_reg != DefOp.type); CurrPhi = CurrBlock->SetPhiDefType(DefOp, MeetType); changed = true; this->ResetProcessedBlocks(); changed |= CurrBlock->PropagateGlobalDefType(DefOp, MeetType, CurrPhi->GetDefSSANum(), IsMemOp); } // end for all phi functions in the current block } // end for all blocks return changed; } // end of SMPFunction::ConditionalTypePropagation() #else // not SMP_SIMPLE_CONDITIONAL_TYPE_PROPAGATION // Apply the SCC (Sparse Conditional Constant) propagation algorithm to // propagate types starting from unresolved Phi DEFs. bool SMPFunction::ConditionalTypePropagation(void) { bool changed = false; // Collections of Phi functions and instructions that have a DEF // with type UNINIT for the current global name. map<int, set<SMPPhiFunction, LessPhi>::iterator> UninitDEFPhis; vector<list<SMPInstr>::iterator> UninitDEFInsts; // Work lists of Phi functions and instructions that need to be processed // according to the SCC algorithm. list<map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator> PhiWorkList; list<vector<list<SMPInstr>::iterator>::iterator> InstWorkList; // Iterate through all global names that are either (1) registers // or (2) stack locations in SAFE functions. set<op_t, LessOp>::iterator CurrGlob; for (CurrGlob = this->GetFirstGlobalName(); CurrGlob != this->GetLastGlobalName(); ++CurrGlob) { op_t GlobalOp = *CurrGlob; list<SMPBasicBlock>::iterator CurrBlock; vector<list<SMPBasicBlock>::iterator>::iterator CurrRPO; if (MDIsIndirectMemoryOpnd(GlobalOp, this->UseFP)) continue; // need alias analysis to process indirect accesses if ((GlobalOp.type != o_reg) && (!((this->GetReturnAddressStatus() == FUNC_SAFE) && MDIsStackAccessOpnd(GlobalOp, this->UseFP)))) continue; // not register, not safe stack access // Set up a map (indexed by SSANum) of iterators to Phi functions // for the current global name that have UNINIT as the Phi DEF type. UninitDEFPhis.clear(); UninitDEFInsts.clear(); for (CurrRPO = this->RPOBlocks.begin(); CurrRPO != this->RPOBlocks.end(); ++CurrRPO) { CurrBlock = *CurrRPO; set<SMPPhiFunction, LessPhi>::iterator CurrPhi; CurrPhi = CurrBlock->FindPhi(GlobalOp); if (CurrPhi != CurrBlock->GetLastPhi()) { // Found Phi function for current global name. if (IsEqType(CurrPhi->GetDefType(), UNINIT)) { // Phi DEF is UNINIT; add Phi to the map. pair<int, set<SMPPhiFunction, LessPhi>::iterator> TempPair(CurrPhi->GetDefSSANum(), CurrPhi); bool Inserted = false; map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator WhereIns; pair<map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator, bool> Result(WhereIns, Inserted); Result = UninitDEFPhis.insert(TempPair); assert(Result.second == true); } } } // end for all blocks // If any Phi DEF had UNINIT as its type, set up a vector of // iterators to instructions that have UNINIT as the DEF type // for the current global name. if (UninitDEFPhis.empty()) continue; list<SMPInstr *>::iterator InstIter; for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); set<DefOrUse, LessDefUse>::iterator CurrDef = CurrInst->FindDef(GlobalOp); if (CurrDef != CurrInst->GetLastDef()) { // Found DEF of current global name. if (IsEqType(UNINIT, CurrDef->GetType())) { UninitDEFInsts.push_back(CurrInst); } } } // end for all instructions // Put all UNINIT Phi DEFs that have at least one USE // that is not UNINIT onto the PhiWorkList. map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator CurrUnPhi; for (CurrUnPhi = UninitDEFPhis.begin(); CurrUnPhi != UninitDEFPhis.end(); ++CurrUnPhi) { pair<int, set<SMPPhiFunction, LessPhi>::iterator> PhiDefPair(*CurrUnPhi); if (PhiDefPair.second->HasTypedUses()) { PhiWorkList.push_back(CurrUnPhi); } } // Iterate until both work lists are empty: while (!(PhiWorkList.empty() && InstWorkList.empty())) { // Process Phi items first. while (!PhiWorkList.empty()) { // If applying the meet operator over the Phi USE types // would produce a new DEF type, change the DEF type and // propagate it, adding Phi functions and instructions that // received the propagated type to their respective work lists. map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator MapIter; MapIter = PhiWorkList.front(); PhiWorkList.pop_front(); // remove from work list pair<int, set<SMPPhiFunction, LessPhi>::iterator> PhiDefPair; PhiDefPair.first = MapIter->first; PhiDefPair.second = MapIter->second; set<SMPPhiFunction, LessPhi>::iterator CurrPhi = PhiDefPair.second; SMPOperandType MeetType = CurrPhi->ConditionalMeetType(); // Here we use a straight equality test, not our macros, // because we consider it a change if the MeetType is // profiler derived and the DEFType is not. if (MeetType == CurrPhi->GetDefType()) continue; // At this point, we need to set the DEFType to the MeetType // and propagate the change. We have a map of all the // critical Phi functions for this global name, as well // as a vector of the relevant instructions for this name. CurrPhi->SetDefType(MeetType); changed = true; int DefSSANum = CurrPhi->GetDefSSANum(); map<int, set<SMPPhiFunction, LessPhi>::iterator>::iterator PhiIter; vector<list<SMPInstr>::iterator>::iterator InstIter; // Propagate to Phi functions first. for (PhiIter = UninitDEFPhis.begin(); PhiIter != UninitDEFPhis.end(); ++PhiIter) { if (DefSSANum == PhiIter->first) continue; // Skip the Phi that we just changed for (size_t index = 0; index < PhiIter->second->GetPhiListSize(); ++index) { if (DefSSANum == PhiIter->second->GetUseSSANum(index)) { // Matched SSA # to USE. Propagate new type. PhiIter->second->SetRefType(index, MeetType); // Add this phi function to the work list. PhiWorkList.push_back(PhiIter); } } } #define SMP_COND_TYPE_PROP_TO_INSTS 0 #if SMP_COND_TYPE_PROP_TO_INSTS // Propagate to instructions with uninit DEFs of global name. // The idea is that the instructions that hold up type propagation // are the ones that USE and then DEF the same global name. // For example, "increment EAX" has to know the type of // the USE of EAX in order to set the type of the DEF. #endif } // end while the PhiWorkList is not empty #if SMP_COND_TYPE_PROP_TO_INSTS // The PhiWorkList is empty at this point, so process // instructions on the InstWorkList. #endif } // end while both work lists are not empty } // end for all global names return changed; } // end of SMPFunction::ConditionalTypePropagation() #endif // end if SMP_SIMPLE_CONDITIONAL_TYPE_PROPAGATION else ... // Propagate signedness FG info from DEFs to USEs whenever there is no USE sign info. bool SMPFunction::PropagateSignedness(void) { bool changed = false; #if STARS_AGGRESSIVE_SIGNEDNESS_PROPAGATION map<int, struct FineGrainedInfo>::iterator UseFGIter, DefFGIter; list<SMPBasicBlock *>::iterator BlockIter; for (UseFGIter = this->GlobalUseFGInfoBySSA.begin(); UseFGIter != this->GlobalUseFGInfoBySSA.end(); ++UseFGIter) { struct FineGrainedInfo UseFG = UseFGIter->second; if (0 == (UseFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS)) { // No signedness info. Propagate any signedness info from DEF. int UseHashValue = UseFGIter->first; unsigned short DefSignMask = this->GetDefSignMiscInfo(UseHashValue); DefSignMask &= FG_MASK_SIGNEDNESS_BITS; if (0 != DefSignMask) { // DEF has signedness info. UseFGIter->second.SignMiscInfo |= DefSignMask; changed = true; } } } // See if we have DEF signedness info for DEFs with no corresponding USE map entries. for (DefFGIter = this->GlobalDefFGInfoBySSA.begin(); DefFGIter != this->GlobalDefFGInfoBySSA.end(); ++DefFGIter) { struct FineGrainedInfo DefFG = DefFGIter->second; unsigned short DefSignMask = (DefFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS); if (0 != DefSignMask) { // Has signedness info. See if USE has no entry. int DefHashValue = DefFGIter->first; UseFGIter = this->GlobalUseFGInfoBySSA.find(DefHashValue); if (UseFGIter == this->GlobalUseFGInfoBySSA.end()) { this->UpdateUseSignMiscInfo(DefHashValue, DefSignMask); changed = true; } } } // Do the same processsing for block-local registers. for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { bool NewChange = (*BlockIter)->PropagateDEFSignedness(); changed = changed || NewChange; } #endif return changed; } // end of SMPFunction::PropagateSignedness() // Detect and mark special cases before emitting numeric error annotations. void SMPFunction::MarkSpecialNumericErrorCases(void) { list<SMPBasicBlock *>::iterator BlockIter; vector<SMPInstr *>::reverse_iterator InstIter; SMPBasicBlock *CurrBlock; SMPInstr *CurrInst; bool DebugFlag = (0 == strcmp("sub_8063BE0", this->GetFuncName())); set<int> NonEscapingRegisterHashes; // memoization optimization: set of register/SSA# hashes that do not reach end of block #if STARS_BUILD_LOOP_BITSET // Now that we know how many loops we have, we can allocate the loops data structure. this->FuncLoopsByBlock.resize(this->BlockCount); for (size_t BlockIndex = 0; BlockIndex < this->BlockCount; ++BlockIndex) { this->FuncLoopsByBlock.at(BlockIndex).AllocateBits(this->LoopCount); } if (this->LoopCount > 0) { this->DetectLoops(); } #endif // Special-case preparatory analyses. for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); CurrBlock->AnalyzePrepForNumericAnnotations(); } if (this->LoopCount == 0) { return; } // Loop through blocks and detect tight loops of hashing arithmetic. for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); int BlockNum = CurrBlock->GetNumber(); #if 0 if (CurrBlock->IsLoopTailBlock() && CurrBlock->IsLoopHeaderBlock()) { #else if (this->IsBlockInAnyLoop(BlockNum)) { #endif // We have a one-block loop to itself. This is the simple case we want // to start with, as hash functions we have observed are tight loops of arithmetic computations. // The next question is whether we can find the kind of shift/rotate that is common to hashing, plus // at least one addition. bool ShiftFound = false; bool AddFound = false; op_t DefOp = InitOp, AddDefOp = InitOp; set<DefOrUse, LessDefUse>::iterator DefIter; NonEscapingRegisterHashes.clear(); for (InstIter = CurrBlock->GetRevInstBegin(); InstIter != CurrBlock->GetRevInstEnd(); ++InstIter) { CurrInst = (*InstIter); if ((!ShiftFound) && CurrInst->MDIsHashingArithmetic()) { // If the operand being shifted is never used in any assignment or arithmetic // except as an address register computation within a memory operand, then the // shifted value does not reach the top of the loop and get shifts accumulated. // In that case, we are not dealing with a shift-and-add type of hash function. // So, do not claim success unless the later addition DEF reaches the end // of the block. DefIter = CurrInst->GetFirstNonFlagsDef(); ea_t DefAddr = CurrInst->GetAddr(); DefOp = DefIter->GetOp(); ea_t AdditionAddr = BADADDR; ShiftFound = CurrBlock->IsDefInvolvedInAddition(DefAddr, DefOp, AdditionAddr); if (ShiftFound) { SMPInstr *AdditionInst = this->GetInstFromAddr(AdditionAddr); DefIter = AdditionInst->GetFirstNonFlagsDef(); AddDefOp = DefIter->GetOp(); AddFound = CurrBlock->DoesDefReachBlockEnd(AdditionAddr, AddDefOp, DefIter->GetSSANum(), NonEscapingRegisterHashes); if (AddFound) { break; } else { // Reset ShiftFound and look for a different shift. ShiftFound = false; } } } } if (ShiftFound && AddFound) { // We found a tight hashing loop. Mark all the overflowing and underflowing opcodes as benign. // NOTE: We could do loop-variant analysis to ensure that the shifted and added values are actually // changing within the loop, but if they are not, they are probably not exploitable overflows anyway, // and the loop-invariant overflow would happen on every loop iteration based on initial values, which // is a pattern we have never seen for this kind of code. vector<SMPInstr *>::iterator ForwardInstIter; for (ForwardInstIter = CurrBlock->GetFirstInst(); ForwardInstIter != CurrBlock->GetLastInst(); ++ForwardInstIter) { CurrInst = (*ForwardInstIter); if (CurrInst->MDIsOverflowingOpcode() || CurrInst->MDIsUnderflowingOpcode() || CurrInst->MDIsLoadEffectiveAddressInstr()) { CurrInst->SetHashOperation(); } } } } // end if loop header and loop tail } // end for all blocks NonEscapingRegisterHashes.clear(); return; } // end of SMPFunction::MarkSpecialNumericErrorCases() // Emit all annotations for the function, including all per-instruction // annotations. void SMPFunction::EmitAnnotations(FILE *AnnotFile, FILE *InfoAnnotFile) { // Emit annotation for the function as a whole. list<SMPBasicBlock *>::iterator BlockIter; SMPBasicBlock *CurrBlock; bool FuncHasProblems = ((!this->AnalyzedSP) || (!this->HasGoodRTLs()) || (this->HasUnresolvedIndirectCalls()) || (this->HasUnresolvedIndirectJumps()) || (this->HasSharedChunks())); if (this->StaticFunc) { SMP_fprintf(AnnotFile, "%10lx %6zu FUNC LOCAL %s ", (unsigned long) this->FuncInfo.startEA, this->Size, this->GetFuncName()); } else { SMP_fprintf(AnnotFile, "%10lx %6zu FUNC GLOBAL %s ", (unsigned long) this->FuncInfo.startEA, this->Size, this->GetFuncName()); } switch (this->GetReturnAddressStatus()) { case FUNC_UNKNOWN: { SMP_fprintf(AnnotFile, "FUNC_UNKNOWN "); break; } case FUNC_SAFE: { SMP_fprintf(AnnotFile, "FUNC_SAFE "); break; } case FUNC_UNSAFE: { SMP_fprintf(AnnotFile, "FUNC_UNSAFE "); break; } default: assert(0); } if (this->UseFP) { SMP_fprintf(AnnotFile, "USEFP "); } else { SMP_fprintf(AnnotFile, "NOFP "); } if (this->FuncInfo.does_return()) { SMP_fprintf(AnnotFile, "RET "); } else { SMP_fprintf(AnnotFile, "NORET "); } if (this->IsLeaf()) SMP_fprintf(AnnotFile, "FUNC_LEAF "); // store the return address SMP_fprintf(AnnotFile,"%10lx ", (unsigned long) (this->FuncInfo.endEA - 1)); if (this->IsLibFunc()) SMP_fprintf(AnnotFile, "LIBRARY "); SMP_fprintf(AnnotFile, "\n"); // Emit annotations about how to restore register values SMP_fprintf(AnnotFile, "%10lx %6d FUNC FRAMERESTORE ", (unsigned long) this->FuncInfo.startEA, 0); for(int i = R_ax; i <= R_di; i++) { SMP_fprintf(AnnotFile, "%d %d %d ", i, this->SavedRegLoc[i], this->ReturnRegTypes[i]); } SMP_fprintf(AnnotFile, "ZZ\n"); SMP_fprintf(AnnotFile, "%10lx %6d FUNC MMSAFENESS ", (unsigned long) this->FuncInfo.startEA, 0); if (!IsSpecSafe()) SMP_fprintf(AnnotFile, "UNSAFE\n"); else if (!IsSafe()) SMP_fprintf(AnnotFile, "SPECSAFE\n"); else { assert(IsSafe()); SMP_fprintf(AnnotFile, "SAFE\n"); } // If function has problems that limited our analyses, emit an information annotation so that // other tools can be aware of which analyses will be sound. if (FuncHasProblems) { SMP_fprintf(InfoAnnotFile, "%10lx %6zu FUNC PROBLEM %s ", (unsigned long) this->FuncInfo.startEA, this->Size, this->GetFuncName()); if (!this->AnalyzedSP) { SMP_fprintf(InfoAnnotFile, "STACKANALYSIS "); } if (this->HasSharedChunks()) { SMP_fprintf(InfoAnnotFile, "CHUNKS "); } if (this->HasUnresolvedIndirectJumps()) { SMP_fprintf(InfoAnnotFile, "JUMPUNRESOLVED "); } if (this->HasUnresolvedIndirectCalls()) { SMP_fprintf(InfoAnnotFile, "CALLUNRESOLVED "); } if (!this->HasGoodRTLs()) { SMP_fprintf(InfoAnnotFile, "BADRTLS "); } SMP_fprintf(InfoAnnotFile, "\n"); } // Find and mark special cases that will affect the integer error annotations. this->MarkSpecialNumericErrorCases(); // Loop through all instructions in the function. // Output optimization annotations for those // instructions that do not require full computation // of their memory metadata by the Memory Monitor SDT. list<size_t> LoopList; // for current block int CurrBlockNum = SMP_BLOCKNUM_UNINIT; list<SMPInstr *>::iterator InstIter = Instrs.begin(); #if SMP_USE_SSA_FNOP_MARKER ++InstIter; // skip marker instruction #endif bool AllocSeen = false; // Reached LocalVarsAllocInstr yet? bool DeallocTrigger = false; for ( ; InstIter != Instrs.end(); ++InstIter) { SMPInstr *CurrInst = (*InstIter); ea_t addr = CurrInst->GetAddr(); CurrBlock = CurrInst->GetBlock(); int BlockNum = CurrBlock->GetNumber(); if (BlockNum != CurrBlockNum) { CurrBlockNum = BlockNum; if (0 < this->LoopCount) { LoopList.clear(); this->BuildLoopList(BlockNum, LoopList); } } SMP_fprintf(AnnotFile, "%10lx %6zu INSTR BELONGTO %lx \n", (unsigned long) addr, CurrInst->GetSize(), (unsigned long) GetStartAddr()); SMPitype CurrDataFlow = CurrInst->GetDataFlowType(); if ((CurrDataFlow == INDIR_JUMP) || (CurrDataFlow == INDIR_CALL)) { SMP_xref_t xrefs; for (bool ok = xrefs.SMP_first_from(addr, XREF_ALL); ok; ok = xrefs.SMP_next_from()) { if (xrefs.GetTo() != 0) { if (xrefs.GetIscode() && (xrefs.GetType() != fl_F)) { // Found a code target, with its address in xrefs.to PrintCodeToCodeXref(addr, xrefs.GetTo(), CurrInst->GetSize()); } } } } if (this->LocalVarsAllocInstr == addr) { AllocSeen = true; if (this->NeedsStackReferent) this->EmitStackFrameAnnotations(AnnotFile, CurrInst); else { int OptType = CurrInst->GetOptType(); if (5 == OptType) { // ADD or SUB // Prevent mmStrata from extending the caller's stack frame // to include the new allocation. SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL SafeFrameAlloc %s \n", (unsigned long) addr, -1, CurrInst->GetDisasm()); } else if (CurrInst->MDIsPushInstr()) { SMP_fprintf(AnnotFile, "%10lx %6d INSTR LOCAL NoWarn %s \n", (unsigned long) addr, -3, CurrInst->GetDisasm()); } // mmStrata ignores the DATAREF annotations anyway, so even though // they are not needed, emit them for use by Strata and other tools // in other projects besides MEDS. this->EmitStackFrameAnnotations(AnnotFile, CurrInst); } } // If this is the instruction which deallocated space // for local variables, we set a flag to remind us to // emit an annotation on the next instruction. // mmStrata wants the instruction AFTER the // deallocating instruction, so that it processes // the deallocation after it happens. It inserts // instrumentation before an instruction, not // after, so it will insert the deallocating // instrumentation before the first POP of callee-saved regs, // if there are any, or before the return, otherwise. if (addr == this->LocalVarsDeallocInstr) { DeallocTrigger = true; } else if (DeallocTrigger) { // Time for annotation SMP_fprintf(AnnotFile, "%10lx %6lu DEALLOC STACK esp - %lu %s\n", (unsigned long) addr, (unsigned long) this->LocalVarsSize, (unsigned long) this->LocalVarsSize, CurrInst->GetDisasm()); DeallocTrigger = false; } #ifndef SMP_REDUCED_ANALYSIS if (this->StackPtrAnalysisSucceeded() && this->HasGoodRTLs() && !this->HasUnresolvedIndirectJumps() && !this->HasSharedChunks()) { CurrInst->EmitTypeAnnotations(this->UseFP, AllocSeen, this->NeedsStackReferent, AnnotFile, InfoAnnotFile); CurrInst->EmitIntegerErrorAnnotations(InfoAnnotFile, LoopList); } else #endif CurrInst->EmitAnnotations(this->UseFP, AllocSeen, this->NeedsStackReferent, AnnotFile, InfoAnnotFile); if (CurrInst->MDIsReturnInstr() && this->GetReturnAddressStatus() == FUNC_SAFE) CurrInst->EmitSafeReturn(AnnotFile); } // end for all instructions // Loop through all basic blocks and emit profiling request annotations // for those blocks that have unsafe memory writes in them. this->SafeBlocks = 0; this->UnsafeBlocks = 0; for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) { CurrBlock = (*BlockIter); if (CurrBlock->MaybeAliasedWrite()) { ++(this->UnsafeBlocks); #if SMP_OPTIMIZE_BLOCK_PROFILING vector<SMPInstr *>::iterator CurrInst; CurrInst = CurrBlock->GetFirstInst(); ea_t addr = (*CurrInst)->GetAddr(); SMP_fprintf(AnnotFile, "%10x %6d BLOCK PROFILECOUNT %s\n", addr, (*CurrInst)->GetCmd().size, (*CurrInst)->GetDisasm()); #endif } else { ++(this->SafeBlocks); } } // Free loop memory. LoopList.clear(); this->FuncLoopsByBlock.clear(); #if SMP_SHRINK_TO_FIT vector<STARSBitSet>(this->FuncLoopsByBlock).swap(this->FuncLoopsByBlock); #endif return; } // end of SMPFunction::EmitAnnotations() // Debug output dump. void SMPFunction::Dump(void) { list<SMPBasicBlock *>::iterator CurrBlock; SMP_msg("Debug dump for function: %s\n", this->GetFuncName()); SMP_msg("UseFP: %d LocalVarsAllocInstr: %lx\n", this->UseFP, (unsigned long) this->LocalVarsAllocInstr); for (size_t index = 0; index < this->IDom.size(); ++index) { SMP_msg("IDOM for %zu: %d\n", index, this->IDom.at(index)); } for (size_t index = 0; index < this->DomTree.size(); ++index) { SMP_msg("DomTree for %zu: ", index); list<int>::iterator DomIter; for (DomIter = this->DomTree.at(index).second.begin(); DomIter != this->DomTree.at(index).second.end(); ++DomIter) { SMP_msg("%d ", *DomIter); } SMP_msg("\n"); } SMP_msg("Global names: \n"); set<op_t, LessOp>::iterator NameIter; for (NameIter = this->GlobalNames.begin(); NameIter != this->GlobalNames.end(); ++NameIter) { SMP_msg("index: %d ", ExtractGlobalIndex(*NameIter)); PrintListOperand(*NameIter); SMP_msg("\n"); } SMP_msg("Blocks each name is defined in: \n"); for (size_t index = 0; index < this->BlocksDefinedIn.size(); ++index) { SMP_msg("Name index: %zu Blocks: ", index); list<int>::iterator BlockIter; for (BlockIter = this->BlocksDefinedIn.at(index).begin(); BlockIter != this->BlocksDefinedIn.at(index).end(); ++BlockIter) { SMP_msg("%d ", *BlockIter); } SMP_msg("\n"); } for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { // Dump out the function number and data flow sets before the instructions. (*CurrBlock)->Dump(); } SMP_msg("End of debug dump for function: %s\n", this->GetFuncName()); return; } // end of SMPFunction::Dump() // Analyzes the function to see if the return address can be marked as safe void SMPFunction::MarkFunctionSafe() { #if SMP_DEBUG_FUNC SMP_msg(" Analyzing function %s and isLeaf = %d \n ", this->GetFuncName(), this->IsLeaf()); #endif bool HasCallTargets = false; bool HasStackPointerCopy = false; bool HasStackPointerPush = false; bool HasIndirectGlobalWrite = false; bool WritesAboveLocalFrame = false; // Direct writes above local frame bool WritesAboveLocalFrameIndirect = false; // Indirect writes above local frame bool HasIndexedStackWrite = false; bool HasIndirectWrite = false; bool IsIndirectCallTarget = false; // could be called indirectly bool IsTailCallTarget = false; // could be called by jump instruction used as tail call bool HasNoCallers = this->AllCallSources.empty(); this->ReturnAddrStatus = FUNC_SAFE; this->SafeFunc = true; if (!this->AllCallTargets.empty()) { HasCallTargets = true; } #if SMP_USE_SWITCH_TABLE_INFO if (this->UnresolvedIndirectJumps) { #else if (this->IndirectJumps) { #endif #if SMP_DEBUG_FUNC SMP_msg("Function %s marked as unsafe due to indirect jumps\n", this->GetFuncName()); #endif } #if SMP_DECLARE_INDIRECT_TARGETS_UNSAFE ea_t FirstAddr = this->FirstEA; SMP_xref_t xrefs; for (bool ok = xrefs.SMP_first_to(FirstAddr, XREF_ALL); ok; ok = xrefs.SMP_next_to()) { ea_t FromAddr = xrefs.GetFrom(); if (FromAddr != 0) { if (!xrefs.GetIscode()) { // found data xref IsIndirectCallTarget = true; // addr of func appears in data; assume indirect calls to func } else { // found code xref; see if it is a jump used as a tail call // These tail calls could be a problem for fast returns if they go from unsafe to safe functions. insn_t TempCmd; ulong TempFeatures; bool CmdOK = SMPGetCmd(FromAddr, TempCmd, TempFeatures); if (!CmdOK) { // Better be conservative and assume it could be a tail call. IsTailCallTarget = true; SMP_msg("ERROR: Could not decode instruction at %lx from within MarkFunctionSafe(); assuming tail call\n", (unsigned long) FromAddr); } else if (TempCmd.itype != MD_CALL_INSTRUCTION) { // not a call instruction; must be jump of some sort IsTailCallTarget = true; } } } } this->PossibleIndirectCallTarget = IsIndirectCallTarget; this->PossibleTailCallTarget = IsTailCallTarget; #endif list<SMPInstr *>::iterator Instructions = Instrs.begin(); SMPInstr *CurrInst; #if SMP_USE_SSA_FNOP_MARKER ++Instructions; // skip marker instruction #endif // While processing the stack pointer writes, the prologue code for // saving the frame register and allocating local variables needs to be // handled. bool SaveEBP = false; bool XferESPtoEBP = false; for ( ; Instructions != Instrs.end(); ++Instructions) { CurrInst = (*Instructions); #if SMP_VERBOSE_DEBUG_FUNC SMP_msg(" Total number of defs for this instruction %d\n", CurrInst->NumDefs()); #endif if (!SaveEBP) { // still looking for "push ebp" if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(MD_FRAME_POINTER_REG)) { SaveEBP = true; continue; } } else if (!XferESPtoEBP) { // found "push ebp", looking for "mov ebp,esp" insn_t CurrCmd = CurrInst->GetCmd(); if ((CurrCmd.itype == NN_mov) && (CurrInst->GetFirstDef()->GetOp().is_reg(MD_FRAME_POINTER_REG)) && (CurrInst->GetFirstUse()->GetOp().is_reg(MD_STACK_POINTER_REG))) { XferESPtoEBP = true; continue; } } ea_t address = CurrInst->GetAddr(); if (address == this->LocalVarsAllocInstr || address == this->LocalVarsDeallocInstr) continue; if (CurrInst->MDIsStackPointerCopy(this->UseFP)) { HasStackPointerCopy = true; if (CurrInst->MDIsLoadEffectiveAddressInstr()) { // If an lea instruction loads an address above // the stack frame, we must assume that writes // above the stack frame could occur. op_t TempOp = CurrInst->GetLeaMemUseOp(); if (this->WritesAboveLocalFrame(TempOp, CurrInst->AreDefsNormalized())) WritesAboveLocalFrameIndirect = true; } #if SMP_DEBUG_FUNC SMP_msg(" Function %s marked as unsafe due to stack pointer copy \n ", this->GetFuncName()); SMP_msg("%s %x \n", CurrInst->GetDisasm(), CurrInst->GetAddr()); #endif } if (CurrInst->MDIsPushInstr()) { // not exactly sure how to handle this instruction // for the moment if its a push on a esp or usefp & ebp // mark as unsafe if (CurrInst->GetCmd().Operands[0].is_reg(MD_STACK_POINTER_REG) || (this->UseFP && CurrInst->GetCmd().Operands[0].is_reg(MD_FRAME_POINTER_REG))) { HasStackPointerPush = true; #if SMP_DEBUG_FUNC SMP_msg(" Function %s marked as unsafe due to push on ebp or esp outside of function header \n", this->GetFuncName()); SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr()); #endif } continue; } if (CurrInst->MDIsPopInstr() || CurrInst->MDIsReturnInstr()) { // ignore pops and returns for the moment continue; } set<DefOrUse, LessDefUse>::iterator setIterator; for (setIterator = CurrInst->GetFirstDef(); setIterator != CurrInst->GetLastDef(); ++setIterator) { op_t Operand = setIterator->GetOp(); int BaseReg; int IndexReg; ushort ScaleFactor; ea_t offset; if (Operand.type == o_mem) { // now o_mem can have sib byte as well, as // reported by IDA. Check if the base reg is R_none // and index reg is R_none. If they are, then this is // a direct global write and can be marked safe. MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset); if ((BaseReg == R_none) && (IndexReg == R_none)) { // go onto next def continue; } else { HasIndirectGlobalWrite = true; } } else if (Operand.type == o_displ) { MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset); bool FramePointerRelative = (this->UseFP && (BaseReg == MD_FRAME_POINTER_REG)); bool StackPointerRelative = (BaseReg == MD_STACK_POINTER_REG); if (StackPointerRelative || FramePointerRelative) { if (IndexReg == R_none) { bool tempWritesAboveLocalFrame = this->WritesAboveLocalFrame(Operand, CurrInst->AreDefsNormalized()); WritesAboveLocalFrame |= tempWritesAboveLocalFrame; #if SMP_DEBUG_FUNC if (tempWritesAboveLocalFrame) { SMP_msg(" Function %s marked as unsafe due to direct write above loc " "variables offset=%x loc=%x\n ", this->GetFuncName(), offset, this->LocalVarsSize); SMP_msg("Write above local frame in %s : offset: %d ", this->GetFuncName(), offset); SMP_msg("LocalVarsSize: %d OutgoingArgsSize: %d frsize: %d frregs: %d", this->LocalVarsSize, this->OutgoingArgsSize, this->FuncInfo.frsize, this->FuncInfo.frregs); Instructions->Dump(); } #endif } else { bool tempWritesAboveLocalFrameIndirect = this->IndexedWritesAboveLocalFrame(Operand); /* separate indirect writes to this frame from indirect writes to another frame */ if (tempWritesAboveLocalFrameIndirect) { WritesAboveLocalFrameIndirect = true; #if SMP_DEBUG_FUNC SMP_msg(" Function %s marked as unsafe due to indexed stack write above " "loc variable offset\n", this->GetFuncName()); SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr()); #endif } else { HasIndexedStackWrite = true; #if SMP_DEBUG_FUNC SMP_msg(" Function %s marked as unsafe due to indexed stack write\n", this->GetFuncName()); SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr()); #endif } } } else { /* check whether there is profiler information for this indirect reference */ HasIndirectWrite = true; } } else if (Operand.type == o_phrase) { // so phrase is of the form [BASE_REG + IND ] // if the index register is missing just make sure that // the displacement is below stack frame top MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset); // check the base reg // if index reg is used mark as unsafe if (MDIsStackPtrReg(BaseReg, this->UseFP)) { if (IndexReg == R_none) { /* addressing mode is *esp or *ebp */ continue; } else { HasIndexedStackWrite = true; #if SMP_DEBUG_FUNC SMP_msg(" Function %s marked as unsafe due to indexed stack write\n", this->GetFuncName()); SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr()); #endif } } else { /* check whether there is profiler information for this indirect reference */ HasIndirectWrite = true; } } // else not memory, and we don't care. } // end for all DEFs in current instruction } // end for all instructions // For mmStrata bounds checking of the stack frame, we don't care // about indirect writes unless they are to the stack. bool SpecUnsafe = (HasStackPointerCopy || HasStackPointerPush || HasIndexedStackWrite || this->SharedChunks || this->UnresolvedIndirectJumps); bool Unsafe = SpecUnsafe || this->UnresolvedIndirectCalls; this->SafeFunc = (!Unsafe); this->SpecSafeFunc = (!SpecUnsafe); this->WritesAboveRA = WritesAboveLocalFrameIndirect; this->SafeCallee = (!Unsafe) && (!WritesAboveLocalFrameIndirect) && this->AnalyzedSP; this->SpecSafeCallee = (!SpecUnsafe) && (!WritesAboveLocalFrameIndirect) && this->AnalyzedSP; this->NeedsStackReferent = Unsafe; this->SpecNeedsStackReferent = SpecUnsafe; this->HasIndirectWrites = (HasIndexedStackWrite || HasIndirectWrite || WritesAboveLocalFrameIndirect || HasIndirectGlobalWrite); bool UnsafeReturnAddr = (Unsafe || WritesAboveLocalFrame || WritesAboveLocalFrameIndirect || HasIndirectGlobalWrite || HasIndirectWrite || (!this->AnalyzedSP)); #if SMP_DECLARE_INDIRECT_TARGETS_UNSAFE if (!UnsafeReturnAddr && this->PossibleIndirectCallTarget) { SMP_msg("INFO: Function at %lx becoming UNSAFE because it is indirect call target.\n", (unsigned long) this->FirstEA); UnsafeReturnAddr = true; } else if (!UnsafeReturnAddr && this->PossibleTailCallTarget) { SMP_msg("INFO: Function at %lx becoming UNSAFE because it is tail call target.\n", (unsigned long) this->FirstEA); UnsafeReturnAddr = true; } else if (!UnsafeReturnAddr && HasNoCallers) { SMP_msg("INFO: Function at %lx becoming UNSAFE because it has no callers.\n", (unsigned long) this->FirstEA); UnsafeReturnAddr = true; } #endif if (UnsafeReturnAddr) { this->SetReturnAddressStatus(FUNC_UNSAFE); #if SMP_DEBUG_FUNC_SAFETY SMP_msg("UNSAFE function %s ", this->GetFuncName()); SMP_msg("StackPtrCopy: %d StackPtrPush: %d IndirectGlobal: %d ", HasStackPointerCopy, HasStackPointerPush, HasIndirectGlobalWrite); SMP_msg("WritesAboveFrame: %d IndirectStack: %d IndirectWrite: %d ", WritesAboveLocalFrame, HasIndexedStackWrite, HasIndirectWrite); SMP_msg("AnalyzedSP: %d UnresolvedCalls: %d UnresolvedJumps: %d SharedChunks: %d IsLeaf: %d ", this->AnalyzedSP, this->UnresolvedIndirectCalls, this->UnresolvedIndirectJumps, this->SharedChunks, this->IsLeaf()); SMP_msg("IndirCallTarget: %d TailCallTarget: %d HasNoCallers: %d\n", this->PossibleIndirectCallTarget, this->PossibleTailCallTarget, HasNoCallers); #endif } else if (HasCallTargets) { this->SetReturnAddressStatus(FUNC_SAFE_IF_CALLEES_ARE_SAFE); } #if SMP_DEBUG_FUNC if (this->GetReturnAddressStatus() == FUNC_SAFE) SMP_msg("Function %s is SAFE\n", GetFuncName()); else if (this->GetReturnAddressStatus() == FUNC_UNSAFE) SMP_msg("Function %s is UNSAFE\n", GetFuncName()); else if (this->GetReturnAddressStatus() == FUNC_SAFE_IF_CALLEES_ARE_SAFE) SMP_msg("Function %s is SAFE_IF_CALLEES_ARE_SAFE\n", GetFuncName()); if (!Unsafe) SMP_msg("Function %s is mmSAFE\n", GetFuncName()); else SMP_msg("Function %s is mmUNSAFE\n", GetFuncName()); if (!SpecUnsafe) SMP_msg("Function %s is Speculatively mmSAFE\n", GetFuncName()); else SMP_msg("Function %s is Speculatively mmUNSAFE\n", GetFuncName()); #endif return; } // end of SMPFunction::MarkFunctionSafe()