// // SMPFunction.cpp // // This module performs the fundamental data flow analyses needed for the // SMP project (Software Memory Protection) at the function level. // #include <list> #include <set> #include <vector> #include <algorithm> #include <cstring> #include <pro.h> #include <assert.h> #include <ida.hpp> #include <idp.hpp> #include <auto.hpp> #include <bytes.hpp> #include <funcs.hpp> #include <allins.hpp> #include <intel.hpp> #include <name.hpp> #include "SMPDataFlowAnalysis.h" #include "SMPStaticAnalyzer.h" #include "SMPFunction.h" #include "SMPBasicBlock.h" #include "SMPInstr.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 0 #define SMP_DEBUG_DATAFLOW 0 // Compute LVA/SSA or not? Turn it off for NICECAP demo on 31-JAN-2008 #define SMP_COMPUTE_LVA_SSA 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; // ***************************************************************** // Class SMPFunction // ***************************************************************** // Constructor SMPFunction::SMPFunction(func_t *Info) { this->FuncInfo = *Info; this->IndirectCalls = false; this->SharedChunks = false; return; } // 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) { bool FoundAllocInstr = false; bool FoundDeallocInstr = false; // 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->FuncInfo)); // 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. bool FrameInfoFixed = this->MDFixFrameInfo(); #if SMP_DEBUG_FRAMEFIXUP if (FrameInfoFixed) { msg("Fixed stack frame size info: %s\n", this->FuncName); SMPBasicBlock CurrBlock = this->Blocks.front(); msg("First basic block:\n"); for (list<list<SMPInstr>::iterator>::iterator CurrInstr = CurrBlock.GetFirstInstr(); CurrInstr != CurrBlock.GetLastInstr(); ++CurrInstr) { msg("%s\n", (*CurrInstr)->GetDisasm()); } } #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) { for (list<SMPInstr>::iterator CurrInstr = this->Instrs.begin(); CurrInstr != this->Instrs.end(); ++CurrInstr) { ea_t addr = CurrInstr->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 && CurrInstr->MDIsFrameAllocInstr()) { this->LocalVarsAllocInstr = addr; FoundAllocInstr = true; // 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) { msg("Fixed UseFP in %s\n", this->FuncName); } #endif } else if (FoundAllocInstr) { // We can now start searching for the DeallocInstr. if (CurrInstr->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. this->LocalVarsDeallocInstr = addr; FoundDeallocInstr = true; } } } // end for (list<SMPInstr>::iterator CurrInstr ... ) if (!FoundAllocInstr) { // Could not find the frame allocating instruction. Bad. // Emit diagnostic and use the first instruction in the // function as a pseudo-allocation instruction to emit // some stack frame info (return address, etc.) this->LocalVarsAllocInstr = this->FindAllocPoint(this->FuncInfo.frsize); #if SMP_DEBUG_FRAMEFIXUP if (BADADDR == this->LocalVarsAllocInstr) { msg("ERROR: Could not find stack frame allocation in %s\n", FuncName); msg("LocalVarsSize: %d SavedRegsSize: %d ArgsSize: %d\n", LocalVarsSize, CalleeSavedRegsSize, IncomingArgsSize); } else { msg("FindAllocPoint found %x for function %s\n", this->LocalVarsAllocInstr, this->GetFuncName()); } #endif } #if SMP_DEBUG_FIX_FRAMEINFO if (!FoundDeallocInstr) { // Could not find the frame deallocating instruction. Bad. // Emit diagnostic and use the last instruction in the // function. msg("ERROR: Could not find stack frame deallocation in %s\n", FuncName); } #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; for (list<SMPInstr>::iterator CurrInstr = this->Instrs.begin(); CurrInstr != this->Instrs.end(); ++CurrInstr) { insn_t CurrCmd = CurrInstr->GetCmd(); ea_t addr = CurrInstr->GetAddr(); if (CurrCmd.itype == NN_push) SaveAddr = addr; else break; } this->LocalVarsAllocInstr = SaveAddr; this->LocalVarsDeallocInstr = 0; } // end if (LocalVarsSize > 0) ... else ... #if 0 // Now we need to do the corresponding operations from the // end of the function to find the DeallocInstr in the // function epilogue. Because there is no addition to the // stack pointer to deallocate the local vars region, the // function epilogue will consist of (optional) pops of // callee-saved regs, followed by the return instruction. // Working backwards, we should find a return and then // stop when we do not find any more pops. if (0 >= LocalVarsSize) { this->LocalVarsDeallocInstr = NULL; } else { SaveAddr = this->FuncInfo.endEA - 1; bool FoundRet = false; do { ea_t addr = get_item_head(SaveAddr); flags_t InstrFlags = getFlags(addr); if (isCode(addr) && isHead(addr)) { ua_ana0(addr); if (!FoundRet) { // Just starting out. if (MDIsReturnInstr(cmd)) { FoundRet = true; SaveAddr = addr - 1; } else { msg("ERROR: Last instruction not a return.\n"); } } else { // Should be 0 or more POPs before the return. if (MDIsPopInstr(cmd)) { SaveAddr = addr - 1; } else if (FrameAllocInstr(cmd, this->LocalVarsSize)) { this->LocalVarsDeallocInstr = addr; } else { msg("ERROR: Frame deallocation not prior to POPs.\n"); this->LocalVarsDeallocInstr = SaveAddr + 1; } } // end if (!FoundRet) ... else ... } else { --SaveAddr; } // end if (isCode(addr) && isHead(addr)) } while (NULL == this->LocalVarsDeallocInstr); } // end if (0 >= this->LocalVarsSize) #endif // 0 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 ahve 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; // 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(); for (list<list<SMPInstr>::iterator>::iterator CurrIter = CurrBlock.GetFirstInstr(); CurrIter != CurrBlock.GetLastInstr(); ++CurrIter) { list<SMPInstr>::iterator 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 } else { // Pushes of outgoing args can be scheduled so that // they are mixed with the pushes of callee saved regs. OtherPushesSize += 4; } } else if (CurrInstr->MDIsFrameAllocInstr()) { SavedRegsSize += OtherPushesSize; // Get the size being allocated. for (size_t index = 0; index < CurrInstr->NumUses(); ++index) { // Find the immediate operand. if (o_imm == CurrInstr->GetUse(index).GetOp().type) { // Get its value into LocalVarsSize. long AllocValue = (signed long) CurrInstr->GetUse(index).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) msg("Total frame size changed: %s\n", this->FuncName); #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 msg("Only callee regs size changed: %s\n", this->FuncName); #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 (OldFrameTotal == SavedRegsSize) { this->CalleeSavedRegsSize = SavedRegsSize; this->LocalVarsSize = 0; Changed = true; } #if SMP_DEBUG_FRAMEFIXUP else { msg("Could not update frame sizes: %s\n", this->FuncName); } #endif } #if SMP_DEBUG_FRAMEFIXUP if ((0 < OtherPushesSize) && (0 < NewLocalsSize)) msg("Extra pushes found of size %d in %s\n", OtherPushesSize, this->FuncName); #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) { bool DebugFlag = (0 == strncmp("strpbrk", this->GetFuncName(), 7)); sval_t TargetSize = - ((sval_t) OriginalLocSize); // negate; stack grows down #if SMP_DEBUG_FRAMEFIXUP if (DebugFlag) msg("strpbrk OriginalLocSize: %d\n", OriginalLocSize); #endif if (this->FuncInfo.analyzed_sp()) { // Limit our analysis to the first basic block in the function. list<SMPInstr>::iterator TempIter = *(--(this->Blocks.front().GetLastInstr())); ea_t AddrLimit = TempIter->GetAddr(); for (list<list<SMPInstr>::iterator>::iterator CurrIter = this->Blocks.front().GetFirstInstr(); CurrIter != this->Blocks.front().GetLastInstr(); ++CurrIter) { list<SMPInstr>::iterator CurrInstr = *CurrIter; ea_t addr = CurrInstr->GetAddr(); // get_spd() returns a cumulative delta of ESP sval_t sp_delta = get_spd(&(this->FuncInfo), addr); #if SMP_DEBUG_FRAMEFIXUP if (DebugFlag) msg("strpbrk delta: %d at %x\n", sp_delta, addr); #endif if (sp_delta == TargetSize) { // Previous instruction hit the frame size. if (CurrInstr == *(this->Blocks.front().GetFirstInstr())) { return BADADDR; // cannot back up from first instruction } else { return (--CurrInstr)->GetAddr(); } } } // SP delta is marked at the beginning of an instruction to show the SP // after the effects of the previous instruction. Maybe the last instruction // is the first time the SP achieves its desired value, which will not be shown // until the first instruction of the next basic block if it just falls through. // We can compute the delta AFTER the last instruction using get_spd+get_sp_delta. list<SMPInstr>::iterator FinalInstr = *(--(this->Blocks.front().GetLastInstr())); ea_t FinalAddr = FinalInstr->GetAddr(); sval_t FinalDelta = get_spd(&(this->FuncInfo), FinalAddr); if (!FinalInstr->IsBasicBlockTerminator()) { // Special case. The basic block does not terminate with a branch or // return, but falls through to the start of a loop, most likely. // Thus, the last instruction CAN increase the sp_delta, unlike // a jump or branch, and the sp_delta would not hit the target until // the first instruction in the second block. We can examine the // effect on the stack pointer of this last instruction to see if it // causes the SP delta to hit the OriginalLocSize. sval_t LastInstrDelta = get_sp_delta(&(this->FuncInfo), FinalAddr); if (TargetSize == (FinalDelta + LastInstrDelta)) { // Return very last instruction (don't back up 1 here) return FinalAddr; } } } // end if (this->FuncInfo.analyzed_sp()) #if SMP_DEBUG_FRAMEFIXUP else { msg("analyzed_sp() 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. // NOTE: This logic should work for both Linux and Windows x86 prologues. bool SMPFunction::MDFixUseFP(void) { list<SMPInstr>::iterator CurrInstr = this->Instrs.begin(); ea_t addr = CurrInstr->GetAddr(); if (!UseFP) return false; // Only looking to reset true to false. while (addr < this->LocalVarsAllocInstr) { size_t DefIndex = 0; while (DefIndex < CurrInstr->NumDefs()) { if (CurrInstr->GetDef(DefIndex).GetOp().is_reg(R_bp)) return false; // EBP got set before locals were allocated ++DefIndex; } ++CurrInstr; addr = CurrInstr->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; return true; } // end of SMPFunction::MDFixUseFP() // Emit the annotations describing the regions of the stack frame. void SMPFunction::EmitStackFrameAnnotations(FILE *AnnotFile, list<SMPInstr>::iterator Instr) { ea_t addr = Instr->GetAddr(); if (0 < IncomingArgsSize) qfprintf(AnnotFile, "%x %d INARGS STACK esp + %d %s \n", addr, IncomingArgsSize, (LocalVarsSize + CalleeSavedRegsSize + RetAddrSize), Instr->GetDisasm()); if (0 < RetAddrSize) qfprintf(AnnotFile, "%x %d MEMORYHOLE STACK esp + %d ReturnAddress \n", addr, RetAddrSize, (LocalVarsSize + CalleeSavedRegsSize)); if (0 < CalleeSavedRegsSize) qfprintf(AnnotFile, "%x %d MEMORYHOLE STACK esp + %d CalleeSavedRegs \n", addr, CalleeSavedRegsSize, LocalVarsSize); if (0 < LocalVarsSize) qfprintf(AnnotFile, "%x %d LOCALFRAME STACK esp + %d LocalVars \n", addr, LocalVarsSize, 0); return; } // end of SMPFunction::EmitStackFrameAnnotations() // 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) { list<SMPInstr>::iterator FirstInBlock = this->Instrs.end(); // For starting a basic block list<SMPInstr>::iterator LastInBlock = this->Instrs.end(); // Terminating a basic block #if SMP_DEBUG_CONTROLFLOW 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)); get_func_name(this->FuncInfo.startEA, this->FuncName, sizeof(this->FuncName) - 1); this->BlockCount = 0; #if SMP_DEBUG_CONTROLFLOW msg("SMPFunction::Analyze: got basic info.\n"); #endif // Cycle through all chunks that belong to the function. func_tail_iterator_t FuncTail(&(this->FuncInfo)); size_t ChunkCounter = 0; for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) { const area_t &CurrChunk = FuncTail.chunk(); ++ChunkCounter; if (1 < ChunkCounter) { this->SharedChunks = true; #if SMP_DEBUG_CHUNKS msg("Found tail chunk for %s at %x\n", this->FuncName, CurrChunk.startEA); #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) && isCode(InstrFlags)) { SMPInstr CurrInst = SMPInstr(addr); // Fill in the instruction data members. #if SMP_DEBUG_CONTROLFLOW msg("SMPFunction::Analyze: calling CurrInst::Analyze.\n"); #endif CurrInst.Analyze(); if (SMPBinaryDebug) { msg("Disasm: %s \n", CurrInst.GetDisasm()); } if (CurrInst.GetDataFlowType() == INDIR_CALL) this->IndirectCalls = true; // 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 msg("SMPFunction::Analyze: hit special jump target case.\n"); #endif LastInBlock = --(this->Instrs.end()); SMPBasicBlock CurrBlock = SMPBasicBlock(FirstInBlock, LastInBlock); CurrBlock.Analyze(); // If not the first chunk in the function, it is a shared // tail chunk. if (ChunkCounter > 1) { CurrBlock.SetShared(); } FirstInBlock = this->Instrs.end(); LastInBlock = this->Instrs.end(); this->Blocks.push_back(CurrBlock); this->BlockCount += 1; } #if SMP_DEBUG_CONTROLFLOW 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 msg("SMPFunction::Analyze: setting FirstInBlock.\n"); #endif FirstInBlock = --(this->Instrs.end()); } if (CurrInst.IsBasicBlockTerminator()) { #if SMP_DEBUG_CONTROLFLOW msg("SMPFunction::Analyze: found block terminator.\n"); #endif LastInBlock = --(this->Instrs.end()); SMPBasicBlock CurrBlock = SMPBasicBlock(FirstInBlock, LastInBlock); CurrBlock.Analyze(); // If not the first chunk in the function, it is a shared // tail chunk. if (ChunkCounter > 1) { CurrBlock.SetShared(); } FirstInBlock = this->Instrs.end(); LastInBlock = this->Instrs.end(); this->Blocks.push_back(CurrBlock); this->BlockCount += 1; // Is the instruction a branch to a target outside the function? If // so, this function has shared tail chunks. if (CurrInst.IsBranchToFarChunk()) { this->SharedChunks = true; } } } // end if (isHead(InstrFlags) && isCode(InstrFlags) } // end for (ea_t addr = FuncInfo.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 CurrBlock = SMPBasicBlock(FirstInBlock, LastInBlock); CurrBlock.Analyze(); // If not the first chunk in the function, it is a shared // tail chunk. if (ChunkCounter > 1) { CurrBlock.SetShared(); } FirstInBlock = this->Instrs.end(); LastInBlock = this->Instrs.end(); this->Blocks.push_back(CurrBlock); this->BlockCount += 1; } } // end for (bool ChunkOK = ...) #if KLUDGE_VFPRINTF_FAMILY if (0 != strstr(this->GetFuncName(), "printf")) { this->SharedChunks = true; msg("Kludging function %s\n", this->GetFuncName()); } #endif // Set up basic block links and map of instructions to blocks. if (!(this->HasSharedChunks())) { this->SetLinks(); #if SMP_COMPUTE_LVA_SSA this->RPONumberBlocks(); this->LiveVariableAnalysis(); this->ComputeSSA(); bool DumpFlag = (0 == strcmp("main", this->GetFuncName())); DumpFlag |= (0 == strcmp("dohanoi", this->GetFuncName())); DumpFlag |= (0 == strcmp(".init_proc", this->GetFuncName())); #if 0 DumpFlag = true; #endif if (DumpFlag) this->Dump(); #endif } #if SMP_DEBUG_CONTROLFLOW msg("SMPFunction::Analyze: set stack frame info.\n"); #endif // Figure out the stack frame and related info. this->SetStackFrameInfo(); return; } // end of SMPFunction::Analyze() // Compute SSA form data structures across the function. void SMPFunction::ComputeSSA(void) { #if 1 this->ComputeIDoms(); this->ComputeDomFrontiers(); this->ComputeGlobalNames(); this->ComputeBlocksDefinedIn(); this->InsertPhiFunctions(); this->SSARenumber(); #endif return; } // end of SMPFunction::ComputeSSA() // 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 CurrBlock; #if SMP_DEBUG_DATAFLOW msg("SetLinks called for %s\n", this->GetFuncName()); #endif // First, set up the map of instructions to basic blocks. for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { list<list<SMPInstr>::iterator>::iterator CurrInst; for (CurrInst = CurrBlock->GetFirstInstr(); CurrInst != CurrBlock->GetLastInstr(); ++CurrInst) { pair<ea_t, list<SMPBasicBlock>::iterator> MapItem((*CurrInst)->GetAddr(),CurrBlock); InstBlockMap.insert(MapItem); } } #if SMP_DEBUG_DATAFLOW 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 (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { list<SMPInstr>::iterator CurrInst = *(--(CurrBlock->GetLastInstr())); // Last instruction in block; set successors bool CallFlag = (CALL == CurrInst->GetDataFlowType()); xrefblk_t CurrXrefs; for (bool ok = CurrXrefs.first_from(CurrInst->GetAddr(), XREF_ALL); ok; ok = CurrXrefs.next_from()) { if ((CurrXrefs.to != 0) && (CurrXrefs.iscode)) { // Found a code target, with its address in CurrXrefs.to if (CallFlag && (CurrXrefs.to != (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, list<SMPBasicBlock>::iterator>::iterator MapEntry; MapEntry = this->InstBlockMap.find(CurrXrefs.to); if (MapEntry == this->InstBlockMap.end()) { msg("WARNING: addr %x not found in map for %s\n", CurrXrefs.to, this->GetFuncName()); msg(" Referenced from %s\n", CurrInst->GetDisasm()); } else { list<SMPBasicBlock>::iterator 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); } } } // end for all xrefs } // end for all blocks // 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. CurrBlock = this->Blocks.begin(); while (CurrBlock != this->Blocks.end()) { if (CurrBlock->AllNops() && (CurrBlock->GetFirstPred() == CurrBlock->GetLastPred())) { msg("Removing all nops block at %x\n", CurrBlock->GetFirstAddr()); CurrBlock = this->Blocks.erase(CurrBlock); this->BlockCount -= 1; } else ++CurrBlock; } return; } // end of SMPFunction::SetLinks() // Number all basic blocks in reverse postorder (RPO) and set RPOBlocks vector to // access them. void SMPFunction::RPONumberBlocks(void) { bool DebugFlag = (0 == strcmp("vfprintf", this->GetFuncName())); int CurrNum = 0; list<list<SMPBasicBlock>::iterator> WorkList; // Number the first block with 0. list<SMPBasicBlock>::iterator CurrBlock = this->Blocks.begin(); #if 0 if (this->RPOBlocks.capacity() <= (size_t) this->BlockCount) { 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<list<SMPBasicBlock>::iterator>::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<list<SMPBasicBlock>::iterator>::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 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<list<SMPBasicBlock>::iterator>::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); msg("Picked LowAddr %x and set RPO number %d\n", LowAddr, CurrNum); 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 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 CurrBlock; msg("LiveVariableAnalysis for %s\n", this->GetFuncName()); for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { // Initialize the Killed and UpwardExposed sets for each block. CurrBlock->InitKilledExposed(); } 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. #if 1 do { changed = false; for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { 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[index]; changed |= CurrBlock->UpdateLiveOut(); } } while (changed); #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 = (0 == strcmp("vfprintf", this->GetFuncName())); // 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) msg("BlockCount = %d\n", this->BlockCount); this->IDom[0] = 0; // Start block dominated only by itself bool changed; do { changed = false; for (size_t RPONum = 1; RPONum < (size_t) this->BlockCount; ++RPONum) { if (DebugFlag) msg("RPONum %d\n", RPONum); if (DebugFlag) { msg("RPOBlocks vector size: %d\n", this->RPOBlocks.size()); for (size_t index = 0; index < this->RPOBlocks.size(); ++index) { msg("RPOBlocks entry %d is %d\n", index, RPOBlocks[index]->GetNumber()); } } list<SMPBasicBlock>::iterator CurrBlock = this->RPOBlocks.at(RPONum); // if (DebugFlag) msg("CurrBlock: %x\n", CurrBlock._Ptr); list<list<SMPBasicBlock>::iterator>::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) { if (DebugFlag) msg("Pred: %d\n", (*CurrPred)->GetNumber()); int PredIDOM = this->IDom.at((*CurrPred)->GetNumber()); if (DebugFlag) msg("Pred IDom: %d\n", PredIDOM); if (SMP_BLOCKNUM_UNINIT != PredIDOM) { NewIdom = (*CurrPred)->GetNumber(); break; } } if (NewIdom == SMP_BLOCKNUM_UNINIT) msg("Failure on NewIdom in ComputeIDoms for %s\n", this->GetFuncName()); 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) msg("PredNum: %d\n", PredNum); int PredIDOM = this->IDom.at(PredNum); if (DebugFlag) 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) msg("Old NewIdom value: %d\n", NewIdom); NewIdom = this->IntersectDoms(PredNum, NewIdom); if (DebugFlag) 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) 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 CurrBlock; for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { // 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<list<SMPBasicBlock>::iterator>::iterator CurrPred; for (CurrPred = CurrBlock->GetFirstPred(); CurrPred != CurrBlock->GetLastPred(); ++CurrPred) { // 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; (*CurrPred)->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 CurrBlock; unsigned int index = 0; if (this->Blocks.size() < 2) return; // cannot have global names if there is only one block for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { for (SetIter = CurrBlock->GetFirstUpExposed(); SetIter != CurrBlock->GetLastUpExposed(); ++SetIter) { op_t TempOp = *SetIter; msg("Global Name: "); PrintOneOperand(TempOp, 0, -1); set<op_t, LessOp>::iterator AlreadyInSet = this->GlobalNames.find(TempOp); if (AlreadyInSet != this->GlobalNames.end()) { // Already in GlobalNames, so don't assign an index number or call insert. msg(" already in GlobalNames.\n"); continue; } // 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. TempOp.n = (char) (index & 0x000000ff); TempOp.offb = (char) ((index & 0x0000ff00) >> 8); TempOp.offo = (char) ((index & 0x00ff0000) >> 16); ++index; this->GlobalNames.insert(TempOp); msg(" inserted as index %d\n", ExtractGlobalIndex(TempOp)); } } 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 CurrBlock; this->BlocksDefinedIn.clear(); for (size_t i = 0; i < this->GlobalNames.size(); ++i) { list<int> TempList; this->BlocksDefinedIn.push_back(TempList); } msg("Number of GlobalNames: %d\n", this->GlobalNames.size()); for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) { set<op_t, LessOp>::iterator KillIter; 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 0 msg("VarKill item offo: %d offb: %d n: %d index: %d\n", NameIter->offo, NameIter->offb, NameIter->n, index); #endif 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 for (NameIter = this->GlobalNames.begin(); NameIter != this->GlobalNames.end(); ++NameIter) { int CurrNameIndex = (int) (ExtractGlobalIndex(*NameIter)); // 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()) { msg("WorkList size: %d\n", WorkList.size()); list<int>::iterator WorkIter = WorkList.begin(); while (WorkIter != WorkList.end()) { set<int>::iterator DomFrontIter; list<SMPBasicBlock>::iterator WorkBlock = this->RPOBlocks[*WorkIter]; for (DomFrontIter = WorkBlock->GetFirstDomFrontier(); DomFrontIter != WorkBlock->GetLastDomFrontier(); ++DomFrontIter) { list<SMPBasicBlock>::iterator 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(); SMPPhiFunction CurrPhi(CurrNameIndex); DefOrUse CurrRef(*NameIter); for (size_t NumCopies = 0; NumCopies < NumPreds; ++NumCopies) { CurrPhi.PushBack(CurrRef); } if (PhiBlock->AddPhi(CurrPhi)) { // If not already in Phi set, new phi function was inserted. WorkList.push_back(PhiBlock->GetNumber()); msg("Added phi for name %d at top of block %d\n", CurrNameIndex, PhiBlock->GetNumber()); } } } // end for all blocks in the dominance frontier // Remove current block number from the work list WorkIter = WorkList.erase(WorkIter); } // end for all block numbers in the work list } // end while the work list is not empty } // end for all elements of the GlobalNames set return; } // end of SMPFunction::InsertPhiFunctions() void SMPFunction::SSARenumber(void) { // **!!** Get this into CVS and patch in the code later after final debugging return; } // Emit all annotations for the function, including all per-instruction // annotations. void SMPFunction::EmitAnnotations(FILE *AnnotFile) { // Emit annotation for the function as a whole. if (this->StaticFunc) { qfprintf(AnnotFile, "%x %d FUNC LOCAL %s ", this->FuncInfo.startEA, this->Size, this->FuncName); } else { qfprintf(AnnotFile, "%x %d FUNC GLOBAL %s ", this->FuncInfo.startEA, this->Size, this->FuncName); } if (this->UseFP) { qfprintf(AnnotFile, "USEFP "); } else { qfprintf(AnnotFile, "NOFP "); } if (this->FuncInfo.does_return()) { qfprintf(AnnotFile, "\n"); } else { qfprintf(AnnotFile, "NORET \n"); } // 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<SMPInstr>::iterator CurrInst; bool AllocSeen = false; // Reached LocalVarsAllocInstr yet? bool DeallocTrigger = false; for (CurrInst = Instrs.begin(); CurrInst != Instrs.end(); ++CurrInst) { ea_t addr = CurrInst->GetAddr(); if (this->LocalVarsAllocInstr == addr) { AllocSeen = true; 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 == LocalVarsDeallocInstr) { DeallocTrigger = true; } else if (DeallocTrigger) { // Time for annotation qfprintf(AnnotFile, "%x %d DEALLOC STACK esp - %d %s\n", addr, LocalVarsSize, LocalVarsSize, CurrInst->GetDisasm()); DeallocTrigger = false; } CurrInst->EmitAnnotations(this->UseFP, AllocSeen, AnnotFile); } // end for (ea_t addr = FuncInfo.startEA; ...) return; } // end of SMPFunction::EmitAnnotations() // Debug output dump. void SMPFunction::Dump(void) { list<SMPBasicBlock>::iterator CurrBlock; msg("Debug dump for function: %s\n", this->GetFuncName()); for (size_t index = 0; index < this->IDom.size(); ++index) { msg("IDOM for %d: %d\n", index, this->IDom.at(index)); } msg("Global names: \n"); set<op_t, LessOp>::iterator NameIter; for (NameIter = this->GlobalNames.begin(); NameIter != this->GlobalNames.end(); ++NameIter) { msg("index: %d ", ExtractGlobalIndex(*NameIter)); PrintOneOperand(*NameIter, 0, -1); msg("\n"); } msg("Blocks each name is defined in: \n"); for (size_t index = 0; index < this->BlocksDefinedIn.size(); ++index) { msg("Name index: %d Blocks: ", index); list<int>::iterator BlockIter; for (BlockIter = this->BlocksDefinedIn.at(index).begin(); BlockIter != this->BlocksDefinedIn.at(index).end(); ++BlockIter) { msg("%d ", *BlockIter); } 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(); } return; } // end of SMPFunction::Dump()