Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • opensrc/SMPStaticAnalyzer
1 result
Show changes
Commits on Source (1)
......@@ -560,7 +560,7 @@ public:
std::size_t UnprocessedCalleesCount(void); // Count of callees that have FuncProcessed == false
STARS_sval_t GetStackAdjustmentForCallee(STARS_ea_t CallAddr); // Get stack pointer adjustment in basic block, after CallAddr
STARS_sval_t GetStackDeltaForCallee(STARS_ea_t CallTargetAddr); // Get stack pointer delta for callee function, which starts at CallTargetAddr
STARS_sval_t ComputeGlobalStackAdjustment(void); // Find consistent or smallest stack adjustment after all calls to this function, program-wide
STARS_sval_t ComputeGlobalStackAdjustment(bool AllowSingleCaller); // Find consistent or smallest stack adjustment after all calls to this function, program-wide
void ComputeTempReachingDefs(const STARSOpndTypePtr &TempOp, STARS_ea_t UseAddr); // Compute the TempReachingDefs set that reaches UseAddr for TempOp
void ComputeTempStackDeltaReachesList(const STARSOpndTypePtr &TempOp); // Compute the TempStackDeltaReachesList for TempOp for all DefAddrs in TempReachingDefs
bool FindReachingStackDelta(STARS_sval_t &StackDelta); // Find maximum stack delta in TempStackDeltaReachesList; return true if one consistent delta is in the list
......
......@@ -7276,7 +7276,10 @@ STARS_sval_t SMPBasicBlock::ComputeStackAdjustmentAfterCall(STARS_ea_t CallAddr)
vector<SMPInstr *>::iterator InstIter;
STARS_ea_t InstAddr; // for debugging, breakpoints, etc.
bool FoundCallInst = false;
bool FirstBlock = (this->GetFirstAddr() == this->GetFunc()->GetFirstFuncAddr());
bool PriorCallFound = false;
bool JustFoundCallInst = false; // current or prior instruction
bool EndedWithTailCall = false;
bool FirstBlock = (this->GetFirstNonMarkerAddr() == this->GetFunc()->GetFirstFuncAddr());
for (InstIter = this->GetFirstInst(); InstIter != this->GetLastInst(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
......@@ -7289,59 +7292,130 @@ STARS_sval_t SMPBasicBlock::ComputeStackAdjustmentAfterCall(STARS_ea_t CallAddr)
if (!FoundCallInst && !FirstBlock) {
PriorAdjustmentBytes += CurrentDelta;
if (JUMP <= DataFlowType) {
// Reset PriorAdjustmentBytes and exit. Basic block is too confusing
// to analyze if multiple calls happen. We cannot tell if stack adjustments
// between the first call and the second call are related to the first call
// or the second call.
// Reset PriorAdjustmentBytes. The Nth call can still be analyzeable
// if there are no adjustments between the (N-1)st call and the Nth call.
PriorCallFound = true;
PriorAdjustmentBytes = 0;
}
}
else if (FoundCallInst) { // just found it.
JustFoundCallInst = true;
EndedWithTailCall = (RETURN == DataFlowType); // no successor blocks in this case
if (PriorCallFound && (0 < PriorAdjustmentBytes)) {
// We had adjustments between the prior call and the call we are analyzing.
// We cannot safely determine whether the adjustments were post-first-call
// (unusual to be positive) or pre-second-call (almost never positive).
PriorAdjustmentBytes = 0;
SMP_msg("INFO: Stack adjustment set to zero due to multi-call block, current call at %llx\n",
(uint64_t)InstAddr);
JustFoundCallInst = false;
break;
}
}
}
else {
if (JUMP > DataFlowType) {
JustFoundCallInst = false;
if (SMP_STACK_DELTA_ERROR_CODE == ((STARS_uval_t) CurrentDelta)) {
// An error will soon occur, no need for further computations.
AdjustmentBytes = 0;
SMP_msg("INFO: Stack adjustment set to zero due to error code at %llx\n", (unsigned long long) InstAddr);
SMP_msg("INFO: Stack adjustment set to zero due to error code at %llx\n", (uint64_t) InstAddr);
break;
}
else if (SMP_STACK_POINTER_BITWISE_AND_CODE == ((STARS_uval_t) CurrentDelta)) {
; // Ignore AND with stack pointer for now
}
else if (0 != CurrentDelta) {
SMP_msg("INFO: Found stack adjustment of %ld bytes at %llx\n",
(long) CurrentDelta, (unsigned long long) InstAddr);
AdjustmentBytes += CurrentDelta;
}
// NOTE: ****!!!!****!!!! We want to find changes in stack direction and terminate
// in the future, in case we see code such as:
//
// call foo
// pop eax ; adjust stack after call to foo, grab return arg and put in eax
// push ebx ; caller-save before call to bar
// call bar
// If we see a pop in a return block, don't count as adjustment.
// Function is preparing to return, and we are done.
if (this->HasReturn() && CurrInst->MDIsPopInstr()) {
break;
}
// We want to find changes in stack direction and terminate
// in case we see code such as:
//
// call foo
// pop eax ; adjust stack after call to foo, grab return arg and put in eax
// push ebx ; caller-save before call to bar
// call bar
if ((0 < CurrentDelta) && (0 >= AdjustmentBytes)) {
SMP_msg("INFO: Found stack adjustment direction change at %llx\n",
(uint64_t) InstAddr);
break;
}
else {
SMP_msg("INFO: Found stack adjustment of %ld bytes at %llx\n",
(long) CurrentDelta, (uint64_t) InstAddr);
AdjustmentBytes += CurrentDelta;
}
}
}
else if ((RETURN == DataFlowType)
|| ((!this->GetFunc()->HasExplicitReturnInstruction()) && ((INDIR_JUMP == DataFlowType) || (JUMP == DataFlowType)))) {
// We have been counting up adjustment bytes that precede a RETURN. Those
// adjustments are expected before a RETURN and have nothing to do with
// the function call at CallAddr. If the function has no explicit return instructions, we will
// conservatively treat jumps as probable tail calls, which will later be given the RETURN
// data flow type but have not yet been analyzed and marked.
AdjustmentBytes = 0;
SMP_msg("INFO: Resetting stack adjustment to zero at return inst at %llx\n",
(unsigned long long) InstAddr);
break;
if (0 < AdjustmentBytes) {
// We have been counting up positive adjustment bytes that precede a RETURN. Those
// adjustments are expected before a RETURN and have nothing to do with
// the function call at CallAddr. If the function has no explicit return instructions, we will
// conservatively treat jumps as probable tail calls, which will later be given the RETURN
// data flow type but have not yet been analyzed and marked.
AdjustmentBytes = 0;
SMP_msg("INFO: Resetting stack adjustment to zero at return inst at %llx\n",
(uint64_t) InstAddr);
break;
}
}
else {
// We want to break at calls after CallAddr, and other instructions that
// hit this break would terminate the block anyway (e.g. jumps, branches).
if (JUMP != DataFlowType) {
JustFoundCallInst = false;
}
break;
}
}
}
if (JustFoundCallInst) {
// We either ended the block with the call, or had the call followed by a jump.
// In either case, we should have only one successor block to look at.
if (EndedWithTailCall) {
AdjustmentBytes = 0;
SMP_msg("INFO: Resetting stack adjustment to zero at tail call at %llx\n",
(uint64_t) InstAddr);
}
else {
assert(this->GetNumSuccessors() <= 1);
if (1 == this->GetNumSuccessors()) { // might be zero for call to NORET function
SMPBasicBlock *SuccBlock = (*(this->GetFirstSucc()));
assert(nullptr != SuccBlock);
for (vector<SMPInstr *>::iterator SuccInstIter = SuccBlock->GetFirstInst();
SuccInstIter != SuccBlock->GetLastInst();
++SuccInstIter) {
SMPInstr *SuccInst = (*SuccInstIter);
assert(nullptr != SuccInst);
STARS_sval_t CurrentDelta = SuccInst->FindStackAdjustment();
InstAddr = SuccInst->GetAddr();
SMPitype DataFlowType = SuccInst->GetDataFlowType();
if ((JUMP > DataFlowType) && (0 > CurrentDelta)) {
// Looks like an adjustment instruction.
AdjustmentBytes += CurrentDelta;
SMP_msg("INFO: Finding stack adjustment at %llx for call in another block at %llx\n",
(uint64_t)InstAddr, (uint64_t)CallAddr);
break; // stop at one adjustment, for simplicity in an already complicated case.
}
else if (SuccInst->MDIsMoveInstr()) {
// Instruction scheduling might put moves before adjustment instrs.
;
}
else {
break; // Anything other than move or adjustment ends the search.
}
} // end for all SuccInsts in SuccBlock
}
}
}
if (AdjustmentBytes != 0) {
// PriorAdjustmentBytes is usually negative (making stack space before the call)
// and AdjustmentBytes is usually positive (returning stack space after the call).
......
......@@ -1932,7 +1932,7 @@ STARS_sval_t SMPFunction::GetStackDeltaForCallee(STARS_ea_t CallTargetAddr) {
 
SMPFunction *CalleeFunc = this->GetProg()->FindFunction(CallTargetAddr);
if (nullptr != CalleeFunc) {
STARS_sval_t GlobalAdjustment = CalleeFunc->ComputeGlobalStackAdjustment();
STARS_sval_t GlobalAdjustment = CalleeFunc->ComputeGlobalStackAdjustment(true);
if (0 != GlobalAdjustment) {
CalleeDelta -= GlobalAdjustment;
SMP_msg("INFO: Global stack adjustment analysis produced callee delta of %ld bytes after %llx\n",
......@@ -1945,19 +1945,24 @@ STARS_sval_t SMPFunction::GetStackDeltaForCallee(STARS_ea_t CallTargetAddr) {
 
// Compute a consistent (or smallest) stack adjustment seen program-wide after all calls to the current function.
// Do not return a non-zero value unless more than one call site can be used as evidence.
STARS_sval_t SMPFunction::ComputeGlobalStackAdjustment(void) {
STARS_sval_t SMPFunction::ComputeGlobalStackAdjustment(bool AllowSingleCaller) {
bool FoundZeroAdjustment = false;
STARS_sval_t GlobalAdjustment = 0;
STARS_sval_t NegativeAdjustment = -10000; // record negative adjustments detected
STARS_sval_t PositiveAdjustment = 10000; // record positive adjustments detected
std::size_t NumCallSites = this->AllCallSites.size();
 
if (!this->FuncInfo->HasReturnPoints()) { // never returns to caller for adjustments to be possible
this->GlobalStackAdjustment = 0;
this->StackAdjustmentComputed = true;
}
// Use cached value if already computed.
if (this->StackAdjustmentComputed) {
return this->GlobalStackAdjustment;
}
 
if (1 < NumCallSites) { // if only one call site, it is dangerous to draw conclusions about seeming "adjustments."
if (AllowSingleCaller || (1 < NumCallSites)) { // if only one call site, it is dangerous to draw conclusions about seeming "adjustments."
set<STARS_ea_t>::iterator CallSiteIter;
for (CallSiteIter = this->AllCallSites.begin(); CallSiteIter != this->AllCallSites.end(); ++CallSiteIter) {
STARS_ea_t CallSiteAddr = (*CallSiteIter);
......@@ -2047,8 +2052,8 @@ bool SMPFunction::AnalyzeStackPointerDeltas(void) {
bool IDATraceFlag = false;
 
#if SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS
DebugFlag = (0 == strcmp("sub_41F320", this->GetFuncName()));
TraceFlag = (0 == strcmp("sub_41F320", this->GetFuncName()));
DebugFlag = (0 == strcmp("_dl_profile_fixup", this->GetFuncName()));
TraceFlag = (0 == strcmp("_dl_profile_fixup", this->GetFuncName()));
#endif
 
if (!this->HasGoodRTLs()) {
......@@ -2073,6 +2078,10 @@ bool SMPFunction::AnalyzeStackPointerDeltas(void) {
}
#endif
 
// NOTE: The below comment assumes that we cannot have SSA form yet, because we need stack pointer
// deltas in order to incorporate stack locations into SSA form. Dealing with this phase ordering
// problem is the motivation for the reaching definitions analysis discussed below.
// In order to precisely track stack deltas, we need to deal with instruction sequences that save the stack pointer
// and then restore it later. This requires a reaching definitions data flow analysis that includes, at a minimum,
// all stack definitions (normalized by stack delta, so that we do not confuse [esp+20] and [esp+20] where the values
......@@ -2173,7 +2182,7 @@ bool SMPFunction::AnalyzeStackPointerDeltas(void) {
STARS_sval_t IncomingDelta = WorkList.front().second;
 
if (TraceFlag) {
SMP_msg("TRACE: Starting block with IncomingDelta of %lld\n", (long long) IncomingDelta);
SMP_msg("TRACE: Starting block %d with IncomingDelta of %lld\n", CurrBlock->GetNumber(), (long long) IncomingDelta);
}
#if SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS
if (DebugFlag && IDAProSucceeded) {
......@@ -2244,7 +2253,7 @@ bool SMPFunction::AnalyzeStackPointerDeltas(void) {
// an error to have conflicting deltas in the functions that call alloca().
// We want to converge to the smallest magnitude deltas, which are the greatest
// values because the deltas are negative. This is the opposite of IDA Pro, which
// seems to use the largest stack deltas it has seen.
// seems to use the largest magnitude stack deltas it has seen.
if (PrevIncomingDelta >= IncomingDelta) {
// Old incoming delta should be retained.
WorkList.pop_front(); // discard already processed block.
......@@ -2518,8 +2527,29 @@ bool SMPFunction::AnalyzeStackPointerDeltas(void) {
 
this->STARSStackPtrAnalysisPerformed = true;
if (this->AnalyzedSP) {
// Don't let net effect of a function on the stack pointer be greater than this value.
// Anything greater indicates some sort of alloca() analyzeability failure, or similar failure.
#define STARS_NET_STACK_DELTA_ERROR_THRESHOLD 16
if (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA != ((size_t) this->NetStackDelta)) {
SMP_msg("WARNING: Non-default stack ptr delta %ld for function: %s\n", (long) this->NetStackDelta, this->GetFuncName());
// See if all of our callers agree that we have a non-standard net effect on the stack
// pointer, which is indicated by all of them making a stack pointer adjustment after
// we return to them.
STARS_sval_t StackAdjustment = this->ComputeGlobalStackAdjustment(true);
STARS_sval_t FinalNetDelta = this->NetStackDelta + StackAdjustment;
if (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA != ((size_t)FinalNetDelta)) {
if (STARS_NET_STACK_DELTA_ERROR_THRESHOLD < this->NetStackDelta) {
SMP_msg("ERROR: Func cannot have NetStackDelta of %lld \n", (int64_t) this->NetStackDelta);
this->AnalyzedSP = false;
}
else if (this->CallsAlloca && ((STARS_sval_t)CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA < FinalNetDelta)) {
if (this->GetNumCallers() != 0) { // should have good stack adjustment
SMP_msg("ERROR: Func cannot have NetStackDelta of %lld and FinalNetDelta of %lld for func that calls alloca\n",
(int64_t) this->NetStackDelta, (int64_t)FinalNetDelta);
this->AnalyzedSP = false;
}
}
}
}
if (this->StackAdjustmentComputed
&& (this->GlobalStackAdjustment != (((STARS_sval_t) CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA) - this->NetStackDelta))) {
......@@ -2710,7 +2740,7 @@ void SMPFunction::FindAllAllocsAndDeallocs(void) {
bool FoundAlloca = false;
bool DebugFlag = false;
#if SMP_DEBUG_FRAMEFIXUP
DebugFlag |= (0 == strcmp("frame_dummy", this->GetFuncName()));
DebugFlag |= (0 == strcmp("_dl_profile_fixup", this->GetFuncName()));
#endif
 
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
......@@ -2745,7 +2775,11 @@ void SMPFunction::FindAllAllocsAndDeallocs(void) {
}
else if (CurrInst->MDIsFrameAllocInstr()) {
FoundAlloca = true;
if (DebugFlag)
SMP_msg("INFO: FoundAlloca set to true at %llx\n", (uint64_t)addr);
if (CurrInst->HasAllocaRTL()) {
if (DebugFlag)
SMP_msg("INFO: Alloca RTL found at %llx\n", (uint64_t)addr);
CurrInst->SetAllocaCall();
}
}
......@@ -3570,8 +3604,19 @@ void SMPFunction::BuildStackAccessTables(void) {
 
bool ESPRelative = (!(UsedFramePointer || CurrInst->HasFPNormalizedToSP()));
if (SignedOffset < 0) {
if (IndexedAccess && ((offset + DataSize - 1) >= this->NegativeOffsetStackFrameMap.size())) {
continue; // Indexed expressions can be within frame even when offset is outside frame
bool OutOfRange = ((offset + DataSize - 1) >= this->NegativeOffsetStackFrameMap.size());
if (OutOfRange) {
if (IndexedAccess)
continue; // Indexed expressions can be within frame even when offset is outside frame
else if (this->CallsAlloca) {
SMP_msg("ERROR: Stack access out of range at %llx due to alloca issues.\n",
(uint64_t)InstAddr);
this->AnalyzedSP = false;
this->OutgoingArgsSize = 0;
this->MinStackDelta = 0;
this->AllocPointDelta = 0;
return;
}
}
assert((offset + DataSize - 1) < this->NegativeOffsetStackFrameMap.size());
for (int j = 0; j < (int) DataSize; ++j) { // offset has zero-based index into negative offset vectors
......@@ -3695,8 +3740,19 @@ void SMPFunction::BuildStackAccessTables(void) {
 
bool ESPRelative = (!(UsedFramePointer || CurrInst->HasFPNormalizedToSP()));
if (SignedOffset < 0) { // offset has zero-based index into negative offset vectors
if (IndexedAccess && ((offset + DataSize - 1) >= this->NegativeOffsetStackFrameMap.size())) {
continue; // Indexed expressions can be within frame even when offset is outside frame
bool OutOfRange = ((offset + DataSize - 1) >= this->NegativeOffsetStackFrameMap.size());
if (OutOfRange) {
if (IndexedAccess)
continue; // Indexed expressions can be within frame even when offset is outside frame
else if (this->CallsAlloca) {
SMP_msg("ERROR: Stack access out of range at %llx due to alloca issues.\n",
(uint64_t)InstAddr);
this->AnalyzedSP = false;
this->OutgoingArgsSize = 0;
this->MinStackDelta = 0;
this->AllocPointDelta = 0;
return;
}
}
assert((offset + DataSize - 1) < this->NegativeOffsetStackFrameMap.size());
for (int j = 0; j < (int) DataSize; ++j) {
......@@ -5420,15 +5476,17 @@ void SMPFunction::AdvancedAnalysis3(void) {
#if SMP_DEBUG_CONTROLFLOW
SMP_msg("SMPFunction::AdvancedAnalysis3: set stack frame info.\n");
#endif
if (!(this->HasSharedChunks())) {
this->SetStackFrameInfo();
} // end if not shared chunks
else { // has shared chunks; still want to compute stack frame info
if (this->StackPtrAnalysisSucceeded()) {
if (!(this->HasSharedChunks())) {
this->SetStackFrameInfo();
} // end if not shared chunks
else { // has shared chunks; still want to compute stack frame info
#ifdef SMP_DEBUG_FUNC
SMP_msg("INFO: %s has shared chunks \n", this->GetFuncName());
SMP_msg("INFO: %s has shared chunks \n", this->GetFuncName());
#endif
// Figure out the stack frame and related info.
this->SetStackFrameInfo();
// Figure out the stack frame and related info.
this->SetStackFrameInfo();
}
}
 
#if SMP_COUNT_MEMORY_ALLOCATIONS
......
......@@ -8499,6 +8499,14 @@ bool SMPInstr::MDIsStackPtrSaveOrRestore(bool UseFP, STARS_sval_t FPDelta, bool
if (!RightRightOp->IsImmedOp()) {
// Complex RTL such as lea esp,[ebx+ecx] ; cannot analyze
StackPointerSaveOrRestore = false;
// If RTL is RSP := RSP - RAX and we cannot analyze RAX's value, then
// we cannot analyze stack pointer values for this function.
// TODO: Use reaching definitions to get a value for RAX in this case.
if (RightLeftOp->MatchesReg(MD_STACK_POINTER_REG)) {
SMP_msg("ERROR: Unanalyzeable add/sub operation on stack pointer at %llx\n",
(uint64_t) this->GetAddr());
Error = true;
}
}
else {
TempOp = RightLeftOp;
......@@ -9211,11 +9219,11 @@ STARS_sval_t SMPInstr::AnalyzeStackPointerDelta(STARS_sval_t IncomingDelta, STAR
// A real call instruction, which pushes a return address on the stack,
// not a call used as a branch within the function. A return instruction
// will usually cancel out the stack push that is implicit in the call, which
// means that the function will have a net stack ptr delta of +4, which will
// cancel out the -4 value of the call instruction and set the delta to zero.
// means that the function will have a net stack ptr delta of +4 or +8, which will
// cancel out the -4 or -8 value of the call instruction and set the delta to zero.
// However, this is not true in all cases, so we get the net stack ptr delta
// directly from the called function unless it is an unresolved indirect call,
// in which case we assume +4. !!!!****!!!! In the future, we could analyze
// in which case we assume +4 or +8. !!!!****!!!! In the future, we could analyze
// the code around an unresolved indirect call to see if it seems to be
// removing items left on the stack by the callee.
#if 0