Newer
Older
ea_t DefAddr = BADADDR; // BADADDR means we did not find it
int HashedName = HashGlobalNameAndSSA(DefOp, SSANum);
MapResult = this->GlobalDefAddrBySSA.find(HashedName);
if (MapResult != this->GlobalDefAddrBySSA.end()) { // Found it.
DefAddr = (ea_t) MapResult->second;
}
return DefAddr;
} // end of SMPFunction::GetGlobalDefAddr()
// 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;
}
clc5q
committed
// 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;
clc5q
committed
}
// 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)) {
clc5q
committed
SMP_msg("WARNING: WritesToOutgoingArgs called with indexed write.");
PrintOperand(DestOp);
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;
MDExtractAddressFields(DestOp, BaseReg, IndexReg, ScaleFactor, offset);
bool ESPrelative = (BaseReg == R_sp) || (IndexReg == R_sp);
bool EBPrelative = this->UseFP && ((BaseReg == R_bp) || (IndexReg == R_bp));
assert(!EBPrelative || !OpNormalized); // stack operands should be normalized by now
if (!(ESPrelative || EBPrelative))
return false;
if (((IndexReg != R_none) && (BaseReg != R_none))
|| (0 < ScaleFactor)) {
clc5q
committed
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 %x\n",
this->FirstEA);
}
return InArgWrite;
}// end of SMPFunction::WritesAboveLocalFrame()
// Is DestOp an indexed write above the local vars frame?
clc5q
committed
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 == R_sp) || (IndexReg == R_sp);
bool EBPrelative = this->UseFP && ((BaseReg == R_bp) || (IndexReg == R_bp));
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));
} // end of SMPFunction::IndexedWritesAboveLocalFrame()
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
// 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
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) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INARGS STACK esp + %d %s \n",
addr, IncomingArgsSize,
(LocalVarsSize + CalleeSavedRegsSize + RetAddrSize),
Instr->GetDisasm());
}
#endif
if (0 < this->RetAddrSize) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d MEMORYHOLE STACK esp + %d ReturnAddress \n",
addr, RetAddrSize, (this->LocalVarsSize + this->CalleeSavedRegsSize));
if (0 < this->CalleeSavedRegsSize) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6u MEMORYHOLE STACK esp + %d CalleeSavedRegs \n",
addr, this->CalleeSavedRegsSize, this->LocalVarsSize);
if ((0 < this->LocalVarsSize) && this->GoodLocalVarTable) {
unsigned long ParentReferentID = DataReferentID++;
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6u DATAREF STACK %ld esp + %d PARENT LocalFrame LOCALFRAME\n",
addr, 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) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6zu DATAREF STACK %ld esp + %d CHILDOF %ld OFFSET %d OutArgsRegion OUTARGS\n",
addr, this->OutgoingArgsSize, DataReferentID, 0, ParentReferentID, 0);
++DataReferentID;
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("LocalVarTable of size %d for function %s\n", this->LocalVarTable.size(),
for (size_t i = 0; i < this->LocalVarTable.size(); ++i) {
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("Entry %d offset %ld size %d name %s\n", i, this->LocalVarTable[i].offset,
this->LocalVarTable[i].size, this->LocalVarTable[i].VarName);
// 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;
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6zu DATAREF STACK %ld esp + %ld CHILDOF %ld OFFSET %ld LOCALVAR %s \n",
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 .... )
} // end if (0 < LocalVarsSize)
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) {
clc5q
committed
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.
#if SMP_DEBUG_CONTROLFLOW
clc5q
committed
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
clc5q
committed
SMP_msg("SMPFunction::Analyze: got basic info.\n");
// Determine if we are dealing with shared chunks.
size_t ChunkCounter = 0;
func_tail_iterator_t FuncTail(this->GetFuncInfo());
for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) {
const area_t &CurrChunk = FuncTail.chunk();
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 %x\n", this->GetFuncName(), CurrChunk.startEA);
#endif
}
else {
#endif // STARS_FIND_UNSHARED_CHUNKS
this->SharedChunks = true;
SMP_msg("INFO: Found tail chunk for %s at %x\n", this->GetFuncName(), CurrChunk.startEA);
#if STARS_FIND_UNSHARED_CHUNKS
}
#endif // STARS_FIND_UNSHARED_CHUNKS
}
// Cycle through all chunks that belong to the function.
ChunkCounter = 0;
bool GoodRTL;
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.
#if SMP_DEBUG_CONTROLFLOW
clc5q
committed
SMP_msg("SMPFunction::Analyze: calling CurrInst::Analyze.\n");
CurrInst->Analyze();
clc5q
committed
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);
clc5q
committed
// Find all functions that call the current function.
clc5q
committed
SMP_xref_t CurrXrefs;
clc5q
committed
if (!FoundAllCallers) {
clc5q
committed
for (bool ok = CurrXrefs.SMP_first_to(CurrInst->GetAddr(), XREF_ALL);
clc5q
committed
ok;
clc5q
committed
ok = CurrXrefs.SMP_next_to()) {
ea_t FromAddr = CurrXrefs.GetFrom();
if ((FromAddr != 0) && (CurrXrefs.GetIscode())) {
clc5q
committed
// 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.
clc5q
committed
CallInst.Analyze();
SMPitype CallType = CallInst.GetDataFlowType();
if ((COND_BRANCH <= CallType) && (RETURN >= CallType)) {
// Found a caller, with its call address in CurrXrefs.from
clc5q
committed
}
}
}
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) {
clc5q
committed
SMP_msg("WARNING: Ignoring NULL call target (unreachable) at %x\n", CurrInst->GetAddr());
}
else {
this->AllCallTargets.push_back(TargetAddr);
if (INDIR_CALL == DataFlowType) {
this->IndirectCallTargets.push_back(TargetAddr);
}
else {
this->DirectCallTargets.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;
}
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. !!!!****!!!!
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
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 %x that can be added to func, reached from %x\n",
FarTargetAddr, addr);
}
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 %x reached from %x already reached from same function.\n",
FarTargetAddr, 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)) {
this->AllCallTargets.push_back(FarTargetAddr);
this->DirectCallTargets.push_back(FarTargetAddr);
}
// 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
clc5q
committed
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) {
}
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
if (!GoodRTL) {
SMP_msg("ERROR: Cannot build RTL at %x for %s\n", CurrInst->GetAddr(),
CurrInst->GetDisasm());
#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
clc5q
committed
SMP_msg("SMPFunction::Analyze: setting FirstInBlock.\n");
#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());
if (CurrInst->IsBasicBlockTerminator()) {
#if SMP_DEBUG_CONTROLFLOW
clc5q
committed
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) {
}
FirstInBlock = this->Instrs.end();
LastInBlock = this->Instrs.end();
this->Blocks.push_back(NewBlock);
this->BlockCount += 1;
}
} // end if (isHead(InstrFlags) && 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) {
}
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
clc5q
committed
// to its basic block.
list<SMPBasicBlock *>::iterator BlockIter;
SMPBasicBlock *CurrBlock;
list<SMPInstr *>::iterator InstIter;
clc5q
committed
SMPInstr *CurrInst;
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
for (InstIter = CurrBlock->GetFirstInstr(); InstIter != CurrBlock->GetLastInstr(); ++InstIter) {
clc5q
committed
CurrInst = (*InstIter);
CurrInst->SetBlock(CurrBlock->GetThisBlock());
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) {
// 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());
}
;
}
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)
CurrBlock->Analyze();
// 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<SMPBasicBlock *>::iterator BlockIter;
SMPBasicBlock *CurrBlock;
list<SMPInstr *>::iterator InstIter;
SMPInstr *CurrInst;
clc5q
committed
// 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");
clc5q
committed
this->FindAllAllocsAndDeallocs();
this->CallsAlloca = this->FindAlloca();
#if 1
for (InstIter = this->Instrs.begin(); 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();
for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) {
CurrInst = (*InstIter);
ea_t InstAddr = CurrInst->GetAddr(); // for debugging breakpoints
if (CurrInst->HasGoodRTL())
CurrInst->SyncAllRTs(this->UsesFramePointer(), this->GetFramePtrStackDelta());
clc5q
committed
// Detect indirect memory references.
CurrInst->AnalyzeIndirectRefs(this->UseFP);
clc5q
committed
// 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;
}
// 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();
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) {
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
// We have to mark the pseudo-call instructions and audit the direct and
// indirect call target vectors.
// Audit direct call targets.
CurrTarget = this->DirectCallTargets.begin();
while (CurrTarget != this->DirectCallTargets.end()) {
if ((this->FirstEA <= *CurrTarget) && (this->FuncInfo.endEA >= *CurrTarget)) {
// Found a call target that is within the function.
CurrTarget = this->DirectCallTargets.erase(CurrTarget);
}
else {
++CurrTarget;
}
}
// Audit indirect call targets.
CurrTarget = this->IndirectCallTargets.begin();
while (CurrTarget != this->IndirectCallTargets.end()) {
if ((this->FirstEA <= *CurrTarget) && (this->FuncInfo.endEA >= *CurrTarget)) {
// Found a call target that is within the function.
CurrTarget = this->IndirectCallTargets.erase(CurrTarget);
}
else {
++CurrTarget;
}
}
// 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;
}
} // end if (FoundInternalCallTarget)
}
// Figure out the stack frame and related info.
clc5q
committed
#if SMP_ANALYZE_STACK_POINTER
(void) this->AnalyzeStackPointerDeltas();
(void) this->UseIDAStackPointerDeltas();
clc5q
committed
#endif
#if SMP_DEBUG_CONTROLFLOW
SMP_msg("SMPFunction::Analyze: set stack frame info.\n");
#endif
if (!(this->HasSharedChunks())) {
clc5q
committed
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();
}
#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()
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
// 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 %x is shared chunk.\n", 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;
// 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);
}
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);
}
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);
}
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);
}
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);
this->RPOBlocks.resize(CurrSize, DummyIter);
}
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);
}
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);
}
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);
}
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