diff --git a/include/base/SMPFunction.h b/include/base/SMPFunction.h index 80786f0329d8e5f9537694b0fc9f53a1c2ed6b16..76518fa1f961677e54595967f09197eb74dc4d55 100644 --- a/include/base/SMPFunction.h +++ b/include/base/SMPFunction.h @@ -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 diff --git a/src/base/SMPBasicBlock.cpp b/src/base/SMPBasicBlock.cpp index 923866d98fa9d3611da18c9d08b52bdf151bc090..d64950c1c16f0ada007b9369b0519dd8dbee704f 100644 --- a/src/base/SMPBasicBlock.cpp +++ b/src/base/SMPBasicBlock.cpp @@ -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). diff --git a/src/base/SMPFunction.cpp b/src/base/SMPFunction.cpp index 1e22c24848318bc380b2a1318a7a62653d2de638..7cbdb5c2337fa28f3be07d5c7edbac86b5dd1bd0 100644 --- a/src/base/SMPFunction.cpp +++ b/src/base/SMPFunction.cpp @@ -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 diff --git a/src/base/SMPInstr.cpp b/src/base/SMPInstr.cpp index 3c65b553cede940310fc874ff844d32663bf76c4..eb11d4186fdc0e37b0cb8b377ae521c0c4a07e46 100644 --- a/src/base/SMPInstr.cpp +++ b/src/base/SMPInstr.cpp @@ -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