Newer
Older
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())) {
clc5q
committed
SMP_msg("ERROR: FG stack table index out of range in MDGetFGStackLocInfo at %x\n", 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) {
clc5q
committed
SMP_msg("ERROR: FG stack table index out of range in MDGetFGStackLocInfo at %x\n", InstAddr);
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
}
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 %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 == 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()
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
// 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()
// Audit and fix the IDA Pro code cross references for jumps and jump targets.
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
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
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
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 %x\n", 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 %x to %x\n", DistantAddr, addr);
int SignedAddrDiff = ((int) DistantAddr) - ((int) addr);
if ((SignedAddrDiff < -128) || (SignedAddrDiff > 127)) {
add_cref(DistantAddr, addr, FarJump);
}
else {
add_cref(DistantAddr, addr, NearJump);
}
}
}
} // end for all "to" xrefs
// Now check the "from" xrefs to see if the target inst has the corresponding "to" xref.
for (bool ok = xrefs.SMP_first_from(addr, XREF_FAR); ok; ok = xrefs.SMP_next_from()) {
ea_t DistantAddr = xrefs.GetTo();
if ((DistantAddr != 0) && (xrefs.GetIscode())) {
// Now we see if the distant instruction has an xref to this instruction.
bool FoundDistantXref = false;
for (bool ok2 = Distant_xrefs.SMP_first_to(DistantAddr, XREF_FAR); ok2; ok2 = Distant_xrefs.SMP_next_to()) {
ea_t SourceAddr = Distant_xrefs.GetFrom();
if (SourceAddr == addr) {
FoundDistantXref = true;
break;
}
}
if (!FoundDistantXref) {
SMP_msg("WARNING: Missing code Xref to %x from %x\n", DistantAddr, addr);
int SignedAddrDiff = ((int) DistantAddr) - ((int) 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 %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
}
#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) {
clc5q
committed
SMP_msg("WARNING: Ignoring NULL call target (unreachable) at %x\n", 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 %x that can be added to func, reached from %x\n",
FarTargetAddr, addr);
#if 0
if (FarTargetAddr < this->FirstEA) {
this->FirstEA = FarTargetAddr;
}
#endif
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
}
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)) {
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 %x from %x\n", addr, PreviousIndirJumpAddr);
int SignedAddrDiff = ((int) addr) - ((int) 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
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 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();
#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();
set<ea_t>::iterator CurrDirectTarget, CurrIndirectTarget;
while (CurrTarget != this->AllCallTargets.end()) {
if ((this->FirstEA <= *CurrTarget) && (this->FuncInfo.endEA >= *CurrTarget)) {
// Found a call target that is within the function.
FoundInternalCallTarget = true;
if (this->FirstEA == *CurrTarget) { // Direct recursion, not a pseudo-jump
this->DirectlyRecursive = true;
}
CurrTarget = this->AllCallTargets.erase(CurrTarget);
}
else {
++CurrTarget;
}
}
if (FoundInternalCallTarget) {
// We have to mark the pseudo-call instructions and audit the direct and
// indirect call target vectors.
// Audit direct call targets.
CurrDirectTarget = this->DirectCallTargets.begin();
set<ea_t>::iterator CopyOfIterator;
while (CurrDirectTarget != this->DirectCallTargets.end()) {
ea_t TargetAddr = (*CurrDirectTarget);
if ((this->FirstEA <= TargetAddr) && (this->FuncInfo.endEA >= TargetAddr)) {
// Found a call target that is within the function.
CopyOfIterator = CurrDirectTarget;
++CopyOfIterator; // point to element after element that will be erased
this->DirectCallTargets.erase(CurrDirectTarget);
CurrDirectTarget = CopyOfIterator;
}
else {
++CurrDirectTarget;