Newer
Older
}
#endif
unsigned short opcode = Instr->GetCmd().itype;
Unsigned = (opcode == NN_movzx);
Signed = (opcode == NN_movsx);
if ((0 > SignedOffset) && (!Indexed) && (BaseValue == this->MinStackAccessOffset)) {
SMP_msg("ERROR: Negative offset %d in MDGetStackOffsetAndSize: frregs: %d MinStackDelta: %ld Inst dump: \n",
SignedOffset, this->FuncInfo.frregs, (long) this->MinStackDelta);
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())) {
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);
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
}
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
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;
}
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 == 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)) {
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 %lx\n",
(unsigned long) 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 == 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));
} // end of SMPFunction::IndexedWritesAboveLocalFrame()
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
// 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) {
SMP_fprintf(AnnotFile, "%10lx %6d INARGS STACK esp + %ld %s \n",
(unsigned long) addr, IncomingArgsSize,
(long) (LocalVarsSize + CalleeSavedRegsSize + RetAddrSize),
}
#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
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;
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 .... )
} // 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);
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
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) {
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.
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
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 %lx\n",
this->GetFuncName(), (unsigned long) CurrChunk.startEA);
#endif
}
else {
#endif // STARS_FIND_UNSHARED_CHUNKS
this->SharedChunks = true;
SMP_msg("INFO: Found tail chunk for %s at %lx\n",
this->GetFuncName(), (unsigned long) CurrChunk.startEA);
#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;
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()));
// 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
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) {
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())) {
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) {
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
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
SMP_msg("ERROR: Cannot build RTL at %lx for %s\n",
(unsigned long) 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 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;
vector<SMPInstr *>::iterator InstIter;
clc5q
committed
SMPInstr *CurrInst;
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
for (InstIter = CurrBlock->GetFirstInst(); InstIter != CurrBlock->GetLastInst(); ++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<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();
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();
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())