From 1b5391953b64cb9da26e8f865917e62edf934bb5 Mon Sep 17 00:00:00 2001
From: clc5q <clc5q@git.zephyr-software.com>
Date: Thu, 13 Dec 2007 22:31:53 +0000
Subject: [PATCH] Add SMPFunction::FindAllocPoint method to find end of stack
 frame allocation when it is done by pushes, e.g. in strpbrk().

---
 SMPDataFlowAnalysis.cpp | 110 ++++++++++++++++++++++++++++++++++++----
 SMPDataFlowAnalysis.h   |   1 +
 2 files changed, 101 insertions(+), 10 deletions(-)

diff --git a/SMPDataFlowAnalysis.cpp b/SMPDataFlowAnalysis.cpp
index 91aab70e..b9be5af9 100644
--- a/SMPDataFlowAnalysis.cpp
+++ b/SMPDataFlowAnalysis.cpp
@@ -867,10 +867,7 @@ void SMPFunction::SetStackFrameInfo(void) {
 	//  local vars. This code could be made more robust in the future
 	//  by matching LocalVarsSize to the immediate value in the allocation
 	//  instruction. However, IDA Pro is sometimes a little off on this
-	//  number, claiming 4 bytes for functions with no locals at all,
-	//  assigning the 4 bytes used for a callee-saved register to the
-	//  local vars space and then claiming 0 bytes for callee-saved
-	//  registers. **!!**
+	//  number. **!!**
 	if (0 < this->LocalVarsSize) {
 		for (list<SMPInstr>::iterator CurrInstr = this->Instrs.begin();
 			CurrInstr != this->Instrs.end();
@@ -913,13 +910,23 @@ void SMPFunction::SetStackFrameInfo(void) {
 		if (!FoundAllocInstr) {
 			// Could not find the frame allocating instruction.  Bad.
 			// Emit diagnostic and use the first instruction in the
-			// function.
-			msg("ERROR: Could not find stack frame allocation in %s\n",
-				FuncName);
-			msg("LocalVarsSize: %d  SavedRegsSize: %d ArgsSize: %d\n",
-				LocalVarsSize, CalleeSavedRegsSize, IncomingArgsSize);
-			this->LocalVarsAllocInstr = this->FuncInfo->startEA;
+			//  function as a pseudo-allocation instruction to emit
+			//  some stack frame info (return address, etc.)
+			this->LocalVarsAllocInstr = this->FindAllocPoint(this->FuncInfo->frsize);
+#if SMP_DEBUG_FRAMEFIXUP
+			if (BADADDR == this->LocalVarsAllocInstr) {
+				msg("ERROR: Could not find stack frame allocation in %s\n",
+					FuncName);
+				msg("LocalVarsSize: %d  SavedRegsSize: %d ArgsSize: %d\n",
+					LocalVarsSize, CalleeSavedRegsSize, IncomingArgsSize);
+			}
+			else {
+				msg("FindAllocPoint found %x for function %s\n",
+					this->LocalVarsAllocInstr, this->GetFuncName());
+			}
+#endif
 		}
+#if SMP_DEBUG_FIX_FRAMEINFO
 		if (!FoundDeallocInstr) {
 			// Could not find the frame deallocating instruction.  Bad.
 			// Emit diagnostic and use the last instruction in the
@@ -927,6 +934,7 @@ void SMPFunction::SetStackFrameInfo(void) {
 			msg("ERROR: Could not find stack frame deallocation in %s\n",
 				FuncName);
 		}
+#endif
 	}
 	// else LocalVarsSize was zero, meaning that we need to search 
 	//  for the end of the function prologue code and emit stack frame
@@ -1166,6 +1174,88 @@ bool SMPFunction::MDFixFrameInfo(void) {
 	return Changed;
 } // end of SMPFunction::MDFixFrameInfo()
 
+// Some functions have difficult to find stack allocations. For example, in some
+//  version of glibc, strpbrk() zeroes out register ECX and then pushes it more than
+//  100 times in order to allocate zero-ed out local vars space for a character translation
+//  table. We will use the stack pointer analysis of IDA to find out if there is a point
+//  in the first basic block at which the stack pointer reaches the allocation total
+//  that IDA is expecting for the local vars region.
+// If so, we return the address of the instruction at which ESP reaches its value, else
+//  we return BADADDR.
+ea_t SMPFunction::FindAllocPoint(asize_t OriginalLocSize) {
+	bool DebugFlag = (0 == strncmp("strpbrk", this->GetFuncName(), 7));
+	sval_t TargetSize = - ((sval_t) OriginalLocSize);  // negate; stack grows down 
+
+#if SMP_DEBUG_FRAMEFIXUP
+	if (DebugFlag)
+		msg("strpbrk OriginalLocSize: %d\n", OriginalLocSize);
+#endif
+
+	if (this->FuncInfo->analyzed_sp()) {
+		// Limit our analysis to the first basic block in the function.
+		ea_t AddrLimit = this->Blocks.front().GetLastInstr()->GetAddr();
+		for (list<SMPInstr>::iterator CurrInstr = this->Blocks.front().GetFirstInstr();
+			CurrInstr != this->Blocks.front().GetLastInstr();
+			++CurrInstr) {
+				ea_t addr = CurrInstr->GetAddr();
+				// get_spd() returns a cumulative delta of ESP
+				sval_t sp_delta = get_spd(this->FuncInfo, addr);
+#if SMP_DEBUG_FRAMEFIXUP
+				if (DebugFlag)
+					msg("strpbrk delta: %d at %x\n", sp_delta, addr);
+#endif
+				if (sp_delta == TargetSize) {
+					// Previous instruction hit the frame size.
+					if (CurrInstr == this->Blocks.front().GetFirstInstr()) {
+						return BADADDR;  // cannot back up from first instruction
+					}
+					else {
+						return (--CurrInstr)->GetAddr();
+					}
+				}
+		}
+		// SP delta is marked at the beginning of an instruction to show the SP
+		//  after the effects of the previous instruction. Maybe the last instruction
+		//  is the first time the SP shows its desired value.
+		list<SMPInstr>::iterator FinalInstr = this->Blocks.front().GetLastInstr();
+		ea_t FinalAddr = FinalInstr->GetAddr();
+		sval_t FinalDelta = get_spd(this->FuncInfo, FinalAddr);
+#if SMP_DEBUG_FRAMEFIXUP
+		if (DebugFlag)
+			msg("strpbrk FinalDelta: %d\n", FinalDelta);
+#endif
+		if (TargetSize == FinalDelta) {
+			// Back up one instruction
+			if (FinalAddr == this->Blocks.front().GetFirstInstr()->GetAddr()) {
+				return BADADDR;  // cannot back up from first instruction
+			}
+			else {
+				return (--FinalInstr)->GetAddr();
+			}
+		}
+		else if (!FinalInstr->IsBasicBlockTerminator()) {
+			// Special case. The basic block does not terminate with a branch or
+			//  return, but falls through to the start of a loop, most likely.
+			//  Thus, the last instruction CAN increase the sp_delta, unlike
+			//  a jump or branch, and the sp_delta would not hit the target until
+			//  the first instruction in the second block. We can examine the 
+			//  effect on the stack pointer of this last instruction to see if it
+			//  causes the SP delta to hit the OriginalLocSize.
+			sval_t LastInstrDelta = get_sp_delta(this->FuncInfo, FinalAddr);
+			if (TargetSize == (FinalDelta + LastInstrDelta)) {
+				// Return very last instruction (don't back up 1 here)
+				return FinalAddr;
+			}
+		}
+	} // end if (this->FuncInfo->analyzed_sp())
+#if SMP_DEBUG_FRAMEFIXUP
+	else {
+		msg("analyzed_sp() is false for %s\n", this->GetFuncName());
+	}
+#endif
+	return BADADDR;
+} // end of SMPFunction::FindAllocPoint()
+
 // IDA Pro is sometimes confused by a function that uses the frame pointer
 //  register for other purposes. For the x86, a function that uses EBP
 //  as a frame pointer would begin with: push ebp; mov ebp,esp to save
diff --git a/SMPDataFlowAnalysis.h b/SMPDataFlowAnalysis.h
index f78e7b66..a6267bd9 100644
--- a/SMPDataFlowAnalysis.h
+++ b/SMPDataFlowAnalysis.h
@@ -174,6 +174,7 @@ private:
 	ea_t LocalVarsAllocInstr; // address of instr that allocates stack frame
 	ea_t LocalVarsDeallocInstr; // address of epilogue instr that deallocs frame
 	void SetStackFrameInfo(void);
+	ea_t FindAllocPoint(asize_t); // Deal with difficult to find stack frame allocations
 	bool MDFixFrameInfo(void); // Redefine stack regions for our needs
 	bool MDFixUseFP(void);  // Fix IDA errors affecting UseFP
 	void EmitStackFrameAnnotations(FILE *AnnotFile, list<SMPInstr>::iterator Instr);
-- 
GitLab