Newer
Older
return;
} // end of SMPFunction::MarkSpecialNumericErrorCases()
// Emit all annotations for the function, including all per-instruction
// annotations.
void SMPFunction::EmitAnnotations(FILE *AnnotFile, FILE *InfoAnnotFile) {
// Emit annotation for the function as a whole.
list<SMPBasicBlock *>::iterator BlockIter;
SMPBasicBlock *CurrBlock;
bool FuncHasProblems = ((!this->AnalyzedSP) || (!this->HasGoodRTLs()) || (this->HasUnresolvedIndirectCalls())
|| (this->HasUnresolvedIndirectJumps()) || (this->HasSharedChunks()));
if (this->StaticFunc) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6zu FUNC LOCAL %s ", this->FuncInfo.startEA,
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6zu FUNC GLOBAL %s ", this->FuncInfo.startEA,
switch (this->GetReturnAddressStatus())
{
case FUNC_UNKNOWN:
{
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_UNKNOWN ");
break;
}
case FUNC_SAFE:
{
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_SAFE ");
break;
}
case FUNC_UNSAFE:
{
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_UNSAFE ");
break;
}
default:
assert(0);
}
clc5q
committed
SMP_fprintf(AnnotFile, "USEFP ");
clc5q
committed
SMP_fprintf(AnnotFile, "NOFP ");
}
if (this->FuncInfo.does_return()) {
clc5q
committed
SMP_fprintf(AnnotFile, "RET ");
clc5q
committed
SMP_fprintf(AnnotFile, "NORET ");
clc5q
committed
SMP_fprintf(AnnotFile, "FUNC_LEAF ");
clc5q
committed
SMP_fprintf(AnnotFile,"%10x ", this->FuncInfo.endEA - 1);
clc5q
committed
SMP_fprintf(AnnotFile, "LIBRARY ");
SMP_fprintf(AnnotFile, "\n");
// Emit annotations about how to restore register values
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d FUNC FRAMERESTORE ", this->FuncInfo.startEA, 0);
clc5q
committed
for(int i = R_ax; i <= R_di; i++)
clc5q
committed
SMP_fprintf(AnnotFile, "%d %d %d ", i, this->SavedRegLoc[i], this->ReturnRegTypes[i]);
clc5q
committed
SMP_fprintf(AnnotFile, "ZZ\n");
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d FUNC MMSAFENESS ", this->FuncInfo.startEA, 0);
clc5q
committed
SMP_fprintf(AnnotFile, "UNSAFE\n");
clc5q
committed
SMP_fprintf(AnnotFile, "SPECSAFE\n");
clc5q
committed
SMP_fprintf(AnnotFile, "SAFE\n");
// If function has problems that limited our analyses, emit an information annotation so that
// other tools can be aware of which analyses will be sound.
if (FuncHasProblems) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "%10x %6zu FUNC PROBLEM %s ", this->FuncInfo.startEA,
if (!this->AnalyzedSP) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "STACKANALYSIS ");
}
if (this->HasSharedChunks()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "CHUNKS ");
}
if (this->HasUnresolvedIndirectJumps()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "JUMPUNRESOLVED ");
}
if (this->HasUnresolvedIndirectCalls()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "CALLUNRESOLVED ");
}
if (!this->HasGoodRTLs()) {
clc5q
committed
SMP_fprintf(InfoAnnotFile, "BADRTLS ");
clc5q
committed
SMP_fprintf(InfoAnnotFile, "\n");
// Find and mark special cases that will affect the integer error annotations.
this->MarkSpecialNumericErrorCases();
// Loop through all instructions in the function.
// Output optimization annotations for those
// instructions that do not require full computation
// of their memory metadata by the Memory Monitor SDT.
list<SMPInstr *>::iterator InstIter = Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
#endif
bool AllocSeen = false; // Reached LocalVarsAllocInstr yet?
bool DeallocTrigger = false;
for ( ; InstIter != Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t addr = CurrInst->GetAddr();
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR BELONGTO %x \n", addr, 0, GetStartAddr());
if (this->LocalVarsAllocInstr == addr) {
AllocSeen = true;
clc5q
committed
this->EmitStackFrameAnnotations(AnnotFile, CurrInst);
clc5q
committed
int OptType = CurrInst->GetOptType();
if (5 == OptType) { // ADD or SUB
// Prevent mmStrata from extending the caller's stack frame
// to include the new allocation.
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL SafeFrameAlloc %s \n",
clc5q
committed
addr, -1, CurrInst->GetDisasm());
}
else if (CurrInst->MDIsPushInstr()) {
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
clc5q
committed
addr, -3, CurrInst->GetDisasm());
}
clc5q
committed
// mmStrata ignores the DATAREF annotations anyway, so even though
// they are not needed, emit them for use by Strata and other tools
// in other projects besides MEDS.
this->EmitStackFrameAnnotations(AnnotFile, CurrInst);
}
// If this is the instruction which deallocated space
// for local variables, we set a flag to remind us to
// emit an annotation on the next instruction.
// mmStrata wants the instruction AFTER the
// deallocating instruction, so that it processes
// the deallocation after it happens. It inserts
// instrumentation before an instruction, not
// after, so it will insert the deallocating
// instrumentation before the first POP of callee-saved regs,
// if there are any, or before the return, otherwise.
if (addr == this->LocalVarsDeallocInstr) {
DeallocTrigger = true;
}
else if (DeallocTrigger) { // Time for annotation
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d DEALLOC STACK esp - %d %s\n", addr,
this->LocalVarsSize, this->LocalVarsSize, CurrInst->GetDisasm());
DeallocTrigger = false;
}
#ifndef SMP_REDUCED_ANALYSIS
if (this->StackPtrAnalysisSucceeded() && this->HasGoodRTLs() && !this->HasUnresolvedIndirectJumps() && !this->HasSharedChunks()) {
CurrInst->EmitTypeAnnotations(this->UseFP, AllocSeen, this->NeedsStackReferent, AnnotFile, InfoAnnotFile);
CurrInst->EmitIntegerErrorAnnotations(InfoAnnotFile);
else
#endif
CurrInst->EmitAnnotations(this->UseFP, AllocSeen, this->NeedsStackReferent, AnnotFile, InfoAnnotFile);
if (CurrInst->MDIsReturnInstr() && this->GetReturnAddressStatus() == FUNC_SAFE)
CurrInst->EmitSafeReturn(AnnotFile);
} // end for all instructions
// Loop through all basic blocks and emit profiling request annotations
// for those blocks that have unsafe memory writes in them.
this->SafeBlocks = 0;
this->UnsafeBlocks = 0;
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
if (CurrBlock->MaybeAliasedWrite()) {
++(this->UnsafeBlocks);
clc5q
committed
#if SMP_OPTIMIZE_BLOCK_PROFILING
list<SMPInstr *>::iterator CurrInst;
CurrInst = CurrBlock->GetFirstInstr();
ea_t addr = (*CurrInst)->GetAddr();
clc5q
committed
SMP_fprintf(AnnotFile, "%10x %6d BLOCK PROFILECOUNT %s\n", addr,
(*CurrInst)->GetCmd().size, (*CurrInst)->GetDisasm());
clc5q
committed
#endif
}
else {
++(this->SafeBlocks);
}
}
// Free loop memory.
this->FuncLoopsByBlock.clear();
#if SMP_SHRINK_TO_FIT
vector<STARSBitSet>(this->FuncLoopsByBlock).swap(this->FuncLoopsByBlock);
#endif
return;
} // end of SMPFunction::EmitAnnotations()
// Debug output dump.
void SMPFunction::Dump(void) {
list<SMPBasicBlock *>::iterator CurrBlock;
clc5q
committed
SMP_msg("Debug dump for function: %s\n", this->GetFuncName());
SMP_msg("UseFP: %d LocalVarsAllocInstr: %x\n", this->UseFP,
this->LocalVarsAllocInstr);
for (size_t index = 0; index < this->IDom.size(); ++index) {
clc5q
committed
SMP_msg("IDOM for %zu: %d\n", index, this->IDom.at(index));
for (size_t index = 0; index < this->DomTree.size(); ++index) {
clc5q
committed
SMP_msg("DomTree for %zu: ", index);
list<int>::iterator DomIter;
for (DomIter = this->DomTree.at(index).second.begin();
DomIter != this->DomTree.at(index).second.end();
++DomIter) {
clc5q
committed
SMP_msg("%d ", *DomIter);
clc5q
committed
SMP_msg("\n");
clc5q
committed
SMP_msg("Global names: \n");
set<op_t, LessOp>::iterator NameIter;
for (NameIter = this->GlobalNames.begin(); NameIter != this->GlobalNames.end(); ++NameIter) {
clc5q
committed
SMP_msg("index: %d ", ExtractGlobalIndex(*NameIter));
PrintListOperand(*NameIter);
clc5q
committed
SMP_msg("\n");
clc5q
committed
SMP_msg("Blocks each name is defined in: \n");
for (size_t index = 0; index < this->BlocksDefinedIn.size(); ++index) {
clc5q
committed
SMP_msg("Name index: %zu Blocks: ", index);
list<int>::iterator BlockIter;
for (BlockIter = this->BlocksDefinedIn.at(index).begin();
BlockIter != this->BlocksDefinedIn.at(index).end();
++BlockIter) {
clc5q
committed
SMP_msg("%d ", *BlockIter);
clc5q
committed
SMP_msg("\n");
}
for (CurrBlock = this->Blocks.begin(); CurrBlock != this->Blocks.end(); ++CurrBlock) {
// Dump out the function number and data flow sets before the instructions.
clc5q
committed
SMP_msg("End of debug dump for function: %s\n", this->GetFuncName());
return;
} // end of SMPFunction::Dump()
// Analyzes the function to see if the return address can be marked as safe
void SMPFunction::MarkFunctionSafe() {
clc5q
committed
SMP_msg(" Analyzing function %s and isLeaf = %d \n ", this->GetFuncName(), this->IsLeaf());
bool HasCallTargets = false;
clc5q
committed
bool HasStackPointerCopy = false;
bool HasStackPointerPush = false;
bool HasIndirectGlobalWrite = false;
bool WritesAboveLocalFrame = false; // Direct writes above local frame
bool WritesAboveLocalFrameIndirect = false; // Indirect writes above local frame
clc5q
committed
bool HasIndexedStackWrite = false;
bool HasIndirectWrite = false;
bool IsIndirectCallTarget = false; // could be called indirectly
bool IsTailCallTarget = false; // could be called by jump instruction used as tail call
bool HasNoCallers = this->AllCallSources.empty();
clc5q
committed
this->ReturnAddrStatus = FUNC_SAFE;
this->SafeFunc = true;
if (!this->AllCallTargets.empty()) {
HasCallTargets = true;
#if SMP_USE_SWITCH_TABLE_INFO
if (this->UnresolvedIndirectJumps) {
#else
clc5q
committed
SMP_msg("Function %s marked as unsafe due to indirect jumps\n", this->GetFuncName());
#if SMP_DECLARE_INDIRECT_TARGETS_UNSAFE
ea_t FirstAddr = this->FirstEA;
SMP_xref_t xrefs;
for (bool ok = xrefs.SMP_first_to(FirstAddr, XREF_ALL); ok; ok = xrefs.SMP_next_to()) {
ea_t FromAddr = xrefs.GetFrom();
if (FromAddr != 0) {
if (!xrefs.GetIscode()) { // found data xref
IsIndirectCallTarget = true; // addr of func appears in data; assume indirect calls to func
}
else { // found code xref; see if it is a jump used as a tail call
// These tail calls could be a problem for fast returns if they go from unsafe to safe functions.
insn_t TempCmd;
ulong TempFeatures;
bool CmdOK = SMPGetCmd(FromAddr, TempCmd, TempFeatures);
if (!CmdOK) {
// Better be conservative and assume it could be a tail call.
IsTailCallTarget = true;
SMP_msg("ERROR: Could not decode instruction at %x from within MarkFunctionSafe(); assuming tail call\n", FromAddr);
}
else if (TempCmd.itype != MD_CALL_INSTRUCTION) { // not a call instruction; must be jump of some sort
IsTailCallTarget = true;
}
}
}
}
this->PossibleIndirectCallTarget = IsIndirectCallTarget;
this->PossibleTailCallTarget = IsTailCallTarget;
#endif
list<SMPInstr *>::iterator Instructions = Instrs.begin();
SMPInstr *CurrInst;
#if SMP_USE_SSA_FNOP_MARKER
++Instructions; // skip marker instruction
#endif
// While processing the stack pointer writes, the prologue code for
clc5q
committed
// saving the frame register and allocating local variables needs to be
// handled.
bool SaveEBP = false;
bool XferESPtoEBP = false;
for ( ; Instructions != Instrs.end(); ++Instructions) {
CurrInst = (*Instructions);
clc5q
committed
SMP_msg(" Total number of defs for this instruction %d\n", CurrInst->NumDefs());
#endif
if (!SaveEBP) { // still looking for "push ebp"
if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(R_bp)) {
SaveEBP = true;
continue;
}
}
else if (!XferESPtoEBP) { // found "push ebp", looking for "mov ebp,esp"
insn_t CurrCmd = CurrInst->GetCmd();
if ((CurrCmd.itype == NN_mov)
&& (CurrInst->GetFirstDef()->GetOp().is_reg(R_bp))
&& (CurrInst->GetFirstUse()->GetOp().is_reg(R_sp))) {
XferESPtoEBP = true;
continue;
}
}
ea_t address = CurrInst->GetAddr();
if (address == this->LocalVarsAllocInstr ||
address == this->LocalVarsDeallocInstr)
continue;
if (CurrInst->MDIsStackPointerCopy(this->UseFP)) {
clc5q
committed
HasStackPointerCopy = true;
if (CurrInst->MDIsLoadEffectiveAddressInstr()) {
// If an lea instruction loads an address above
// the stack frame, we must assume that writes
// above the stack frame could occur.
op_t TempOp = CurrInst->GetLeaMemUseOp();
if (this->WritesAboveLocalFrame(TempOp, CurrInst->AreDefsNormalized()))
WritesAboveLocalFrameIndirect = true;
}
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to stack pointer copy \n ", this->GetFuncName());
SMP_msg("%s %x \n", CurrInst->GetDisasm(), CurrInst->GetAddr());
if (CurrInst->MDIsPushInstr()) {
// not exactly sure how to handle this instruction
// for the moment if its a push on a esp or usefp & ebp
// mark as unsafe
if (CurrInst->GetCmd().Operands[0].is_reg(R_sp) ||
(this->UseFP && CurrInst->GetCmd().Operands[0].is_reg(R_bp))) {
clc5q
committed
HasStackPointerPush = true;
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to push on ebp or esp outside of function header \n", this->GetFuncName());
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
#endif
}
continue;
}
if (CurrInst->MDIsPopInstr() || CurrInst->MDIsReturnInstr()) {
// ignore pops and returns for the moment
continue;
}
set<DefOrUse, LessDefUse>::iterator setIterator;
for (setIterator = CurrInst->GetFirstDef(); setIterator != CurrInst->GetLastDef(); ++setIterator) {
op_t Operand = setIterator->GetOp();
clc5q
committed
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t offset;
// now o_mem can have sib byte as well, as
clc5q
committed
// reported by IDA. Check if the base reg is R_none
// and index reg is R_none. If they are, then this is
clc5q
committed
// a direct global write and can be marked safe.
clc5q
committed
MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset);
clc5q
committed
// go onto next def
continue;
HasIndirectGlobalWrite = true;
}
clc5q
committed
MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset);
bool FramePointerRelative = (this->UseFP && (BaseReg == R_bp));
bool StackPointerRelative = (BaseReg == R_sp);
if (StackPointerRelative || FramePointerRelative) {
clc5q
committed
if (IndexReg == R_none) {
bool tempWritesAboveLocalFrame = this->WritesAboveLocalFrame(Operand, CurrInst->AreDefsNormalized());
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to direct write above loc "
"variables offset=%x loc=%x\n ", this->GetFuncName(),
offset, this->LocalVarsSize);
clc5q
committed
SMP_msg("Write above local frame in %s : offset: %d ",
clc5q
committed
SMP_msg("LocalVarsSize: %d OutgoingArgsSize: %d frsize: %d frregs: %d",
this->LocalVarsSize, this->OutgoingArgsSize,
this->FuncInfo.frsize, this->FuncInfo.frregs);
Instructions->Dump();
}
#endif
bool tempWritesAboveLocalFrameIndirect = this->IndexedWritesAboveLocalFrame(Operand);
/* separate indirect writes to this frame from indirect writes to another frame */
WritesAboveLocalFrameIndirect = true;
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to indexed stack write above "
"loc variable offset\n", this->GetFuncName());
clc5q
committed
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to indexed stack write\n",
clc5q
committed
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
/* check whether there is profiler information for this indirect reference */
clc5q
committed
HasIndirectWrite = true;
}
else if (Operand.type == o_phrase) {
// so phrase is of the form [BASE_REG + IND ]
// if the index register is missing just make sure that
// the displacement is below stack frame top
clc5q
committed
MDExtractAddressFields(Operand, BaseReg, IndexReg, ScaleFactor, offset);
// check the base reg
// if index reg is used mark as unsafe
if ((BaseReg == R_sp || (this->UseFP && BaseReg == R_bp))) {
if (IndexReg == R_none) {
/* addressing mode is *esp or *ebp */
clc5q
committed
continue;
}
else {
HasIndexedStackWrite = true;
#if SMP_DEBUG_FUNC
clc5q
committed
SMP_msg(" Function %s marked as unsafe due to indexed stack write\n", this->GetFuncName());
SMP_msg("%s %x\n", CurrInst->GetDisasm(), CurrInst->GetAddr());
clc5q
committed
#endif
}
/* check whether there is profiler information for this indirect reference */
clc5q
committed
HasIndirectWrite = true;
// else not memory, and we don't care.
} // end for all DEFs in current instruction
} // end for all instructions
clc5q
committed
// For mmStrata bounds checking of the stack frame, we don't care
// about indirect writes unless they are to the stack.
bool SpecUnsafe = (HasStackPointerCopy || HasStackPointerPush
|| HasIndexedStackWrite || this->SharedChunks
|| this->UnresolvedIndirectJumps);
bool Unsafe = SpecUnsafe || this->UnresolvedIndirectCalls;
this->SafeFunc = (!Unsafe);
this->SpecSafeFunc = (!SpecUnsafe);
this->WritesAboveRA = WritesAboveLocalFrameIndirect;
this->SafeCallee = (!Unsafe) && (!WritesAboveLocalFrameIndirect) && this->AnalyzedSP;
this->SpecSafeCallee = (!SpecUnsafe) && (!WritesAboveLocalFrameIndirect) && this->AnalyzedSP;
this->SpecNeedsStackReferent = SpecUnsafe;
this->HasIndirectWrites = (HasIndexedStackWrite || HasIndirectWrite
|| WritesAboveLocalFrameIndirect || HasIndirectGlobalWrite);
bool UnsafeReturnAddr = (Unsafe || WritesAboveLocalFrame || WritesAboveLocalFrameIndirect || HasIndirectGlobalWrite
|| HasIndirectWrite || (!this->AnalyzedSP));
#if SMP_DECLARE_INDIRECT_TARGETS_UNSAFE
if (!UnsafeReturnAddr && this->PossibleIndirectCallTarget) {
SMP_msg("INFO: Function at %x becoming UNSAFE because it is indirect call target.\n", this->FirstEA);
UnsafeReturnAddr = true;
}
else if (!UnsafeReturnAddr && this->PossibleTailCallTarget) {
SMP_msg("INFO: Function at %x becoming UNSAFE because it is tail call target.\n", this->FirstEA);
UnsafeReturnAddr = true;
}
else if (!UnsafeReturnAddr && HasNoCallers) {
SMP_msg("INFO: Function at %x becoming UNSAFE because it has no callers.\n", this->FirstEA);
UnsafeReturnAddr = true;
}
#endif
if (UnsafeReturnAddr) {
this->SetReturnAddressStatus(FUNC_UNSAFE);
clc5q
committed
SMP_msg("UNSAFE function %s ", this->GetFuncName());
SMP_msg("StackPtrCopy: %d StackPtrPush: %d IndirectGlobal: %d ",
clc5q
committed
HasStackPointerCopy, HasStackPointerPush, HasIndirectGlobalWrite);
clc5q
committed
SMP_msg("WritesAboveFrame: %d IndirectStack: %d IndirectWrite: %d ",
clc5q
committed
WritesAboveLocalFrame, HasIndexedStackWrite, HasIndirectWrite);
SMP_msg("AnalyzedSP: %d UnresolvedCalls: %d UnresolvedJumps: %d SharedChunks: %d IsLeaf: %d",
this->AnalyzedSP, this->UnresolvedIndirectCalls, this->UnresolvedIndirectJumps,
this->SharedChunks, this->IsLeaf());
SMP_msg("IndirCallTarget: %d TailCallTarget: %d HasNoCallers: %d\n", this->PossibleIndirectCallTarget,
this->PossibleTailCallTarget, HasNoCallers);
clc5q
committed
}
else if (HasCallTargets) {
this->SetReturnAddressStatus(FUNC_UNKNOWN);
clc5q
committed
}
if (this->GetReturnAddressStatus() == FUNC_SAFE)
clc5q
committed
SMP_msg("Function %s is SAFE\n", GetFuncName());
else if (this->GetReturnAddressStatus() == FUNC_SAFE)
clc5q
committed
SMP_msg("Function %s is UNSAFE\n", GetFuncName());
else if (this->GetReturnAddressStatus() == FUNC_SAFE)
clc5q
committed
SMP_msg("Function %s is UNKNOWN\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is mmSAFE\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is mmUNSAFE\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is Speculatively mmSAFE\n", GetFuncName());
clc5q
committed
SMP_msg("Function %s is Speculatively mmUNSAFE\n", GetFuncName());
clc5q
committed
return;
} // end of SMPFunction::MarkFunctionSafe()