Newer
Older
// the block with the same stack pointer delta as previously,
// and pop it off the work list
// otherwise declare the stack pointer to be un-analyzeable;
// else
// iterate through all instructions in the block, analyzing
// the stack pointer delta of each inst and accumulating current delta
// At the end of the block, put the successor blocks on the work list.
// For both cases, maintain and update reaching definitions sets, and the
// UpExposed and VarKill sets that are used by LVA as well as reaching defs analysis.
bool ReprocessingAllocaBlocks = false;
bool ReachesInChanged;
bool ReachesOutChanged = false;
do {
SMPBasicBlock *CurrBlock = WorkList.front().first;
sval_t IncomingDelta = WorkList.front().second;
if (CurrBlock->IsProcessed()) { // already processed
ReachesOutChanged = false;
#if 0
ReachesInChanged = CurrBlock->ComputeReachesInSet();
if (ReachesInChanged) {
ReachesOutChanged = CurrBlock->ComputeReachesOutSet();
}
#else
if (CurrBlock->IsReachesOutStale()) {
ReachesOutChanged = CurrBlock->ComputeReachesOutSet();
}
#endif
if (ReachesOutChanged) {
// Push the successor blocks onto the work list
sval_t SuccIncomingDelta = CurrBlock->GetOutgoingStackDelta();
list<SMPBasicBlock *>::iterator SuccIter;
for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) {
pair<SMPBasicBlock *, sval_t> SuccPair (*SuccIter, SuccIncomingDelta);
WorkList.push_back(SuccPair);
}
}
InstIter = CurrBlock->GetFirstInstr();
sval_t PrevIncomingDelta = (*InstIter)->GetStackPtrOffset();
if (IncomingDelta == PrevIncomingDelta) {
// No error, already processed.
WorkList.pop_front(); // discard already processed block.
}
#if 1
else if (this->CallsAlloca || this->HasPushAfterFrameAlloc()) {
#else
else {
#endif
ConflictingValuesSeen = true;
clc5q
committed
// Calls to alloca() become additional stack allocations, which can produce
// multiple possible stack deltas for an instruction if different paths
// to the instruction do not hit the same alloca() calls, so it is not
// 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.
if (PrevIncomingDelta >= IncomingDelta) {
// Old incoming delta should be retained.
WorkList.pop_front(); // discard already processed block.
}
else {
CurrBlock->SetProcessed(false);
ReprocessingAllocaBlocks = true;
DeltaIncrement = IncomingDelta - PrevIncomingDelta;
clc5q
committed
continue; // Make the loop come around and process this block again, using
// the new incoming delta. Because we do this only when it decreases
// the stack size as seen by this block, no infinite loop is possible.
}
}
else {
this->AnalyzedSP = false;
SMP_msg("ERROR: Stack delta: PrevIncoming is %d NewIncoming is %d at %x\n",
PrevIncomingDelta, IncomingDelta, (*InstIter)->GetAddr());
WorkList.clear();
}
}
else { // not already processed
// ReprocessingAllocaBlocks => Reaching definitions sets have already been computed; just need to do stack delta analysis
ReachesOutChanged = false;
ReachesInChanged = CurrBlock->ComputeReachesInSet();
if (ReachesInChanged && ReprocessingAllocaBlocks) {
// Because block is not truly being processed for the first time, the ReachesOut set can be
// recomputed without processing instructions, as the DEDefs set and VarKill set will never
// change after the first pass through the block.
ReachesOutChanged = CurrBlock->ComputeReachesOutSet();
}
if (CurrBlock->IsReachesOutStale()) {
ReachesOutChanged = CurrBlock->ComputeReachesOutSet();
}
#endif
CurrBlock->SetProcessed(true);
WorkList.pop_front();
for (InstIter = CurrBlock->GetFirstInstr(); InstIter != CurrBlock->GetLastInstr(); ++InstIter) {
clc5q
committed
CurrInst = (*InstIter);
if (CurrInst->IsFloatNop()) {
continue; // skip marker instruction
}
ea_t InstAddr = CurrInst->GetAddr();
if (InstAddr == this->GetFirstFrameAllocInstAddr()) {
clc5q
committed
// Record the reset point for frame deallocations
this->PreAllocStackDelta = IncomingDelta;
}
CurrInst->SetStackPtrOffset(IncomingDelta);
// Search for tail calls, defined strictly as having an incoming stack delta of zero and
// being jumps to far chunks.
if ((0 == IncomingDelta) && (CurrInst->IsBranchToFarChunk())) {
CurrInst->SetTailCall();
clc5q
committed
#if 0
SMP_msg("Found tail call at %x from %s: %s\n", InstAddr, this->GetFuncName(),
CurrInst->GetDisasm());
clc5q
committed
}
clc5q
committed
#if SMP_COMPARE_IDA_STARS_STACK_POINTER_DELTAS
if (DebugFlag && IDAProSucceeded && !(this->CallsAlloca || this->HasPushAfterFrameAlloc())) {
sval_t IDAProDelta = get_spd(this->GetFuncInfo(), InstAddr);
clc5q
committed
if ((IDAProDelta != IncomingDelta) && (!CurrInst->MDIsHaltInstr())) {
// IDA Pro special-cases the HALT instruction to make it appear that the
// incoming stack delta is zero. We do no such special case delta adjudstment,
// so we suppress error messages, as our delta will be non-zero.
SMP_msg("ERROR: At %x IDA Pro has stack pointer delta of %d and we compute %d\n", InstAddr,
clc5q
committed
IDAProDelta, IncomingDelta);
}
}
if (TraceFlag) {
SMP_msg("INFO: Stack delta trace: %d at %x\n", IncomingDelta, InstAddr);
clc5q
committed
}
#endif
// As soon as the stack ptr offset has been set for the current instruction, we can normalize
// all of its stack DEFs and USEs.
bool StackOpsChanged = CurrInst->MDNormalizeStackOps(UseFP, this->GetFramePtrStackDelta(), ReprocessingAllocaBlocks, DeltaIncrement);
clc5q
committed
// Dataflow equation for upward exposed variables: If a variable has not been
// killed yet in this block, starting from the top of the block, and it is used
// in the current instruction, then it is upwardly exposed.
set<DefOrUse, LessDefUse>::iterator CurrUse;
if (!ReprocessingAllocaBlocks) { // Only compute on first pass through block
for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) {
op_t UseOp = CurrUse->GetOp();
CanonicalizeOpnd(UseOp);
if (MDIsDataFlowOpnd(UseOp, this->UsesFramePointer())) {
// We have a register or stack operand. If stack operand, it is normalized, i.e. EBP-4 might be ESP-8,
// where the ESP-8 refers to the value of ESP upon entry to the function, not its current value.
// This normalization makes each stack location uniquely named (no aliases at different code locations due
// to different values of ESP at different code locations).
// We only track certain kinds of operands in our data flow analyses.
// Only add non-immediate operands that are not already killed in this block.
// o_near and o_far operands are code addresses in immediate form, e.g.
// call _printf might be call 0x8048040, with o_near = 0x8048040.
if (!(CurrBlock->MDAlreadyKilled(UseOp))) {
CurrBlock->AddUpExposed(UseOp);
}
}
}
// Find stack pointer saves and restores.
bool StackPtrSaved;
sval_t SavedDelta;
op_t CopyOperand;
bool SavedDeltaHasNewValue = false;
bool ErrorFlag = false;
if (CurrInst->MDIsStackPtrSaveOrRestore(this->UsesFramePointer(), this->GetFramePtrStackDelta(), StackPtrSaved, SavedDelta, CopyOperand, ErrorFlag)) {
// NOTE: If CopyOperand is a stack location, it is normalized.
if (StackPtrSaved) {
// Insert new entry into the StackPtrCopySet. For the ReprocessingAllocaBlocks case, this might be
// just a tricky update of the delta for an existing item in the set.
bool DeltaInserted = this->AddToStackPtrCopySet(CopyOperand, InstAddr, SavedDelta);
if (TraceFlag) {
SMP_msg("INFO: Stack delta saved: %d at %x\n", SavedDelta, InstAddr);
}
}
else { // stack pointer was restored from saved value
StackPointerRestoreSeen = true;
SavedDeltaHasNewValue = true; // no need to compute effect of restore instruction later
if (ReprocessingAllocaBlocks) {
// Now that the stack pointer has been restored, the effect of the alloca() should
// be undone. We no longer need to adjust delta values for the rest of the block.
DeltaIncrement = 0;
}
}
} // end if (CurrInst->MDIsStackPtrSaveOrRestore())
else if (ErrorFlag) {
this->AnalyzedSP = false;
WorkList.clear();
SMP_msg("ERROR: ErrorFlag=true from MDIsStackPtrSaveOrRestore() at %x\n", InstAddr);
// Update VarKill and DownExposedDefs sets for DEFs in current instruction.
// Dataflow equation for killed variables: If a variable is defined in any
// instruction in the block, it is killed by this block (i.e. prior definitions
// of that variable will not make it through the block).
if (!ReprocessingAllocaBlocks) { // Only compute on first pass through block
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) {
op_t DefOp = CurrDef->GetOp();
if (MDIsDataFlowOpnd(DefOp, this->UsesFramePointer())) {
// We have a register or stack operand. If stack operand, it is normalized, i.e. EBP-4 might be ESP-8,
// where the ESP-8 refers to the value of ESP upon entry to the function, not its current value.
// This normalization makes each stack location uniquely named (no aliases at different code locations due
// to different values of ESP at different code locations).
CurrBlock->AddVarKill(DefOp);
CurrBlock->UpdateDownExposedDefs(DefOp, InstAddr);
clc5q
committed
}
}
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
}
if (SavedDeltaHasNewValue) {
IncomingDelta = SavedDelta; // from restore instruction
}
else {
CurrentDelta = CurrInst->AnalyzeStackPointerDelta(IncomingDelta, this->GetFramePtrStackDelta());
if (SMP_STACK_POINTER_BITWISE_AND_CODE == CurrentDelta) {
// For now, we ignore instructions that AND a constant into the stack pointer.
CurrentDelta = 0;
SMP_msg("WARNING: Stack pointer bitwise AND ignored at %x\n", CurrInst->GetAddr());
}
else if (SMP_STACK_DELTA_ERROR_CODE == CurrentDelta) {
this->AnalyzedSP = false;
SMP_msg("ERROR: Stack delta unanalyzeable at %x\n", InstAddr);
WorkList.clear();
break;
}
SMPitype FlowType = CurrInst->GetDataFlowType();
IncomingDelta += CurrentDelta;
if ((RETURN == FlowType) && (!CurrInst->IsCondTailCall())) {
// We hope to see a consistent outgoing delta from all RETURN points.
// We special-case the conditional jump used as tail call, because it must be followed
// by a real return instruction later. If the jump is taken, it acts as a return, but
// it has not yet popped the stack.
if (ReturnSeen) { // This is not the first RETURN seen.
if (IncomingDelta != this->NetStackDelta) { // Inconsistent
SMP_msg("ERROR: Inconsistent stack deltas at return instruction at %x\n", CurrInst->GetAddr());
ConsistentNetDelta = false;
this->AnalyzedSP = false;
WorkList.clear();
break;
}
}
else { // First RETURN statement seen.
ReturnSeen = true;
this->NetStackDelta = IncomingDelta;
#if SMP_AUDIT_STACK_POINTER_DELTAS
if (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA != IncomingDelta) {
SMP_msg("WARNING: Stack delta not %d after return instruction at %x\n",
CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA, CurrInst->GetAddr());
#endif
}
// If we permitted inconsistent stack deltas previously, then the stack pointer has to
// have been restored, e.g. if we allocate a frame with sub esp,32 and then we later
// have paths that pass through an alloca() call, a push, etc., then the alloca() or
// push will not be undone by add esp,32. It must be undone by something like mov esp,ebp.
if (ConflictingValuesSeen && !StackPointerRestoreSeen) {
SMP_msg("ERROR: Inconsistent stack deltas seen, no stack pointer restore before return instruction at %x\n", CurrInst->GetAddr());
this->AnalyzedSP = false;
WorkList.clear();
break;
clc5q
committed
}
}
} // end if (SavedDeltaHasNewValue) ... else ...
} // end for each instruction in WorkList block
if (CurrBlock->IsReachesOutStale()) {
ReachesOutChanged = CurrBlock->ComputeReachesOutSet();
// Push the successor blocks onto the work list if anything changed
clc5q
committed
if (this->AnalyzedSP) { // if we do not have an error already
CurrBlock->SetOutgoingStackDelta(IncomingDelta); // record incoming delta for all successors
if (ReachesOutChanged || (!ReprocessingAllocaBlocks)) { // if anything changed (deltas or reaching defs ReachOut set)
list<SMPBasicBlock *>::iterator SuccIter;
if (DebugFlag && (0 == IncomingDelta)) {
SMP_msg("ERROR: Pushing WorkList items with IncomingDelta of zero. Dumping Block:\n");
CurrBlock->Dump();
}
for (SuccIter = CurrBlock->GetFirstSucc(); SuccIter != CurrBlock->GetLastSucc(); ++SuccIter) {
pair<SMPBasicBlock *, sval_t> SuccPair (*SuccIter, IncomingDelta);
WorkList.push_back(SuccPair);
}
clc5q
committed
}
} // end if block already processed ... else ...
ReprocessingAllocaBlocks = false; // reset to default before processing next worklist element
} while (!WorkList.empty());
clc5q
committed
this->STARSStackPtrAnalysisPerformed = true;
if (this->AnalyzedSP) {
if (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA != this->NetStackDelta) {
SMP_msg("WARNING: Non-default stack ptr delta %d for function: %s\n", this->NetStackDelta, this->GetFuncName());
}
if (this->StackAdjustmentComputed
&& (this->GlobalStackAdjustment != (CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA - this->NetStackDelta))) {
// Oops. When program graph cycles caused us to try to compute the GlobalStackAdjustment as our best guess
// for this function's effect on the stack delta, we told our callers that these three values would cancel out.
// They do not. Our callers have now been using a bad stack delta for their call instructions. Too late for
// anything but a diagnostic message.
SMP_msg("ERROR: Earlier GlobalStackAdjustment computation %d does not agree with current NetStackDelta result for function: %s\n",
this->GlobalStackAdjustment, this->GetFuncName());
}
clc5q
committed
}
clc5q
committed
if (IDAProSucceeded) {
if (!this->AnalyzedSP) {
SMP_msg("ERROR: Stack Ptr Delta Analysis succeeded in IDA, failed in STARS for %x : %s\n", this->FirstEA,
this->GetFuncName());
}
}
else {
if (this->AnalyzedSP) {
SMP_msg("SUCCESS: Stack Ptr Delta Analysis failed in IDA, succeeded in STARS for %x : %s\n", this->FirstEA,
this->GetFuncName());
}
}
if (!this->AnalyzedSP) {
(void) this->UseIDAStackPointerDeltas();
}
// Cannot keep the reaching defs around on huge benchmarks, or we run out of memory.
// Once we have SSA form, we can obtain reaching defs info on the fly if we want it.
list<SMPBasicBlock *>::iterator BlockIter;
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
(*BlockIter)->FreeReachingDefsMemory();
}
return this->AnalyzedSP;
} // end of SMPFunction::AnalyzeStackPointerDeltas()
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
// Insert the arguments into the StackPtrCopySet; or, if a matching entry already exists
// with a StackDelta of greater magnitude than StackDelta, update just the StackDelta.
// Return true if StackDelta was inserted, false if it was used to update an old entry.
bool SMPFunction::AddToStackPtrCopySet(op_t CopyOp, ea_t InstAddr, sval_t StackDelta) {
bool NewInsertion;
pair<ea_t, sval_t> InsertStackDefn(InstAddr, StackDelta);
pair<op_t, pair<ea_t, sval_t> > InsertStackDefnOp(CopyOp, InsertStackDefn);
set<pair<op_t, pair<ea_t, sval_t> >, LessStackDeltaCopy>::iterator FindIter;
pair<set<pair<op_t, pair<ea_t, sval_t> >, LessStackDeltaCopy>::iterator, bool> InsertResult;
FindIter = this->StackPtrCopySet.find(InsertStackDefnOp);
if (FindIter == this->StackPtrCopySet.end()) {
// Not already present; insert new triple.
NewInsertion = true;
InsertResult = this->StackPtrCopySet.insert(InsertStackDefnOp);
assert(InsertResult.second);
}
else {
// Already there; see if delta needs to be updated.
NewInsertion = false;
pair<op_t, pair<ea_t, sval_t> > OldStackDefnOp(*FindIter);
// Favor a smaller stack frame for the alloca-calling functions, e.g. favor -24 over -32 as a delta.
if (StackDelta > OldStackDefnOp.second.second) {
// Replace the old entry with a new one.
this->StackPtrCopySet.erase(FindIter);
InsertResult = this->StackPtrCopySet.insert(InsertStackDefnOp);
assert(InsertResult.second);
}
}
return NewInsertion;
} // end of SMPFunction::AddToStackPtrCopySet()
clc5q
committed
void SMPFunction::FindAllAllocsAndDeallocs(void) {
bool FoundAllocInstr = false;
bool FoundDeallocInstr = false;
clc5q
committed
DebugFlag |= (0 == strcmp("frame_dummy", this->GetFuncName()));
#endif
// Now, if LocalVarsSize is not zero, we need to find the instruction
// in the function prologue that allocates space on the stack for
// 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. **!!**
if (0 < this->LocalVarsSize) {
clc5q
committed
if (DebugFlag) SMP_msg("Searching for alloc and dealloc\n");
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
for ( ; InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t addr = CurrInst->GetAddr();
// Keep the most recent instruction in the DeallocInstr
// in case we reach the return without seeing a dealloc.
if (!FoundDeallocInstr) {
this->LocalVarsDeallocInstr = addr;
}
if (!FoundAllocInstr
&& CurrInst->MDIsFrameAllocInstr()) {
#if SMP_DEBUG_CONTROLFLOW
clc5q
committed
SMP_msg("Returned from MDIsFrameAllocInstr()\n");
this->LocalVarsAllocInstr = addr;
FoundAllocInstr = true;
clc5q
committed
if (DebugFlag) SMP_msg("Found alloc: %s\n", CurrInst->GetDisasm());
// As soon as we have found the local vars allocation,
// we can try to fix incorrect sets of UseFP by IDA.
// NOTE: We might want to extend this in the future to
// handle functions that have no locals. **!!**
bool FixedUseFP = MDFixUseFP();
#if SMP_DEBUG_FRAMEFIXUP
if (FixedUseFP) {
clc5q
committed
SMP_msg("Fixed UseFP in %s\n", this->GetFuncName());
if (this->UsesFramePointer()) { // now that MDFixUseFP() has validated this flag ...
this->FindFramePointerDelta(); // find stack delta that is saved in frame pointer in function prologue
}
}
else if (FoundAllocInstr) {
// We can now start searching for the DeallocInstr.
if (CurrInst->MDIsFrameDeallocInstr(UseFP, this->LocalVarsSize)) {
// Keep saving the most recent addr that looks
// like the DeallocInstr until we reach the
// end of the function. Last one to look like
// it is used as the DeallocInstr.
#if SMP_DEBUG_CONTROLFLOW
clc5q
committed
SMP_msg("Returned from MDIsFrameDeallocInstr()\n");
this->LocalVarsDeallocInstr = addr;
FoundDeallocInstr = true;
}
clc5q
committed
if (DebugFlag) SMP_msg("Not dealloc: %s\n", CurrInst->GetDisasm());
} // end for (list<SMPInstr *>::iterator InstIter ... )
if (!FoundAllocInstr) {
// Could not find the frame allocating instruction. Bad.
// See if we can find the point at which the stack allocation reaches
// a total of FuncInfo.frsize+frregs, regardless of whether it happened by push
// instructions or some other means.
this->LocalVarsAllocInstr = this->FindAllocPoint(this->FuncInfo.frsize + this->FuncInfo.frregs);
#if SMP_DEBUG_CONTROLFLOW
clc5q
committed
SMP_msg("Returned from FindAllocPoint()\n");
#if SMP_DEBUG_FRAMEFIXUP
if (BADADDR == this->LocalVarsAllocInstr) {
SMP_msg("WARNING: Could not find stack frame allocation in %s\n",
clc5q
committed
SMP_msg("LocalVarsSize: %d SavedRegsSize: %d ArgsSize: %d\n",
LocalVarsSize, CalleeSavedRegsSize, IncomingArgsSize);
}
else {
clc5q
committed
SMP_msg("FindAllocPoint found %x for function %s\n",
this->LocalVarsAllocInstr, this->GetFuncName());
}
#endif
}
if (!FoundDeallocInstr) {
// Could not find the frame deallocating instruction. Bad.
// Emit diagnostic and use the last instruction in the
// function.
SMP_msg("WARNING: Could not find stack frame deallocation in %s\n",
}
#endif
}
// else LocalVarsSize was zero, meaning that we need to search
// for the end of the function prologue code and emit stack frame
// annotations from that address (i.e. this method returns that
// address). We will approximate this by finding the end of the
// sequence of PUSH instructions at the beginning of the function.
// The last PUSH instruction should be the last callee-save-reg
// instruction. We can make this more robust in the future by
// making sure that we do not count a PUSH of anything other than
// a register. **!!**
// NOTE: 2nd prologue instr is usually mov ebp,esp
// THE ASSUMPTION THAT WE HAVE ONLY PUSH INSTRUCTIONS BEFORE
// THE ALLOCATING INSTR IS ONLY TRUE WHEN LOCALVARSSIZE == 0;
else {
ea_t SaveAddr = this->FuncInfo.startEA;
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
for ( ; InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
insn_t CurrCmd = CurrInst->GetCmd();
ea_t addr = CurrInst->GetAddr();
if (CurrCmd.itype == NN_push)
SaveAddr = addr;
else
break;
}
this->LocalVarsAllocInstr = SaveAddr;
this->LocalVarsDeallocInstr = 0;
// As soon as we have found the local vars allocation,
// we can try to fix incorrect sets of UseFP by IDA.
// NOTE: We might want to extend this in the future to
// handle functions that have no locals. **!!**
bool FixedUseFP = this->MDFixUseFP();
#if SMP_DEBUG_FRAMEFIXUP
if (FixedUseFP) {
SMP_msg("Fixed UseFP in %s\n", this->GetFuncName());
}
#endif
if (this->UsesFramePointer()) { // now that MDFixUseFP() has validated this flag ...
this->FindFramePointerDelta(); // find stack delta that is saved in frame pointer in function prologue
}
} // end if (LocalVarsSize > 0) ... else ...
clc5q
committed
return;
} // end of SMPFunction::FindAllAllocsAndDeallocs()
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
// Compute FramePointerStackDelta as soon as possible so that it is available for SyncAllRTs().
void SMPFunction::FindFramePointerDelta(void) {
bool FirstBlockProcessed = false;
bool FPSaved = false; // have seen push of frame pointer reg
bool SPintoFP = false; // have seen copy of stack pointer into frame pointer
sval_t IncomingDelta = 0;
sval_t CurrentDelta;
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
#endif
while (!FirstBlockProcessed && (InstIter != this->Instrs.end())) {
SMPInstr *CurrInst = (*InstIter);
// Accumulate stack delta values.
CurrentDelta = CurrInst->AnalyzeStackPointerDelta(IncomingDelta, this->GetFramePtrStackDelta());
if (SMP_STACK_POINTER_BITWISE_AND_CODE == CurrentDelta) {
// For now, we ignore instructions that AND a constant into the stack pointer.
CurrentDelta = 0;
}
else if (SMP_STACK_DELTA_ERROR_CODE == CurrentDelta) {
this->AnalyzedSP = false;
break; // error exit
}
// Look for initialization of frame pointer, record its stack delta
FirstBlockProcessed = CurrInst->IsLastInBlock();
if (!FPSaved) { // still looking for "push <framepointerreg>"
if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(MD_FRAME_POINTER_REG)) {
FPSaved = true;
}
}
else if (!SPintoFP) { // found "push <framepointerreg>", looking for "fp := sp"
insn_t CurrCmd = CurrInst->GetCmd();
if ((CurrCmd.itype == MD_MOVE_INSTRUCTION)
&& (CurrInst->GetFirstDef()->GetOp().is_reg(MD_FRAME_POINTER_REG))
&& (CurrInst->GetFirstUse()->GetOp().is_reg(MD_STACK_POINTER_REG))) {
SPintoFP = true;
this->FramePointerStackDelta = IncomingDelta;
FirstBlockProcessed = true; // stop looking
assert(this->UsesFramePointer());
}
}
IncomingDelta += CurrentDelta;
++InstIter;
}
return;
} // end of SMPFunction::FindFramePointerDelta()
clc5q
committed
// Figure out the different regions of the stack frame, and find the
// instructions that allocate and deallocate the local variables space
// on the stack frame.
// The stack frame info will be used to emit stack
// annotations when Analyze() reaches the stack allocation
// instruction that sets aside space for local vars.
// Set the address of the instruction at which these
// annotations should be emitted. This should normally
// be an instruction such as: sub esp,48
// However, for a function with no local variables at all,
// we will need to determine which instruction should be
// considered to be the final instruction of the function
// prologue and return its address.
// Likewise, we find the stack deallocating instruction in
// the function epilogue.
void SMPFunction::SetStackFrameInfo(void) {
for (list<SMPInstr *>::iterator InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++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.
(*InstIter)->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.
(*InstIter)->MDFixupDefUseLists();
#if SMP_COMPUTE_STACK_GRANULARITY
// Now, find the boundaries between local variables.
this->BuildLocalVarTable();
#endif
// Get callee-saved regs info for remediation use.
clc5q
committed
if (BADADDR != this->GetFirstFrameAllocInstAddr()) {
this->MDFindSavedRegs();
}
return;
} // end of SMPFunction::SetStackFrameInfo()
// IDA Pro defines the sizes of regions in the stack frame in a way
// that suits its purposes but not ours. The frsize field of the func_info_t
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
// structure measures the distance between the stack pointer and the
// frame pointer (ESP and EBP in the x86). This region includes some
// of the callee-saved registers. So, the frregs field only includes
// the callee-saved registers that are above the frame pointer.
// x86 standard prologue on gcc/linux:
// push ebp ; save old frame pointer
// mov ebp,esp ; new frame pointer = current stack pointer
// push esi ; callee save reg
// push edi ; callee save reg
// sub esp,34h ; allocate 52 bytes for local variables
//
// Notice that EBP acquires its final frame pointer value AFTER the
// old EBP has been pushed. This means that, of the three callee saved
// registers, one is above where EBP points and two are below.
// IDA Pro is concerned with generating readable addressing expressions
// for items on the stack. None of the callee-saved regs will ever
// be addressed in the function; they will be dormant until they are popped
// off the stack in the function epilogue. In order to create readable
// disassembled code, IDA defines named constant offsets for locals. These
// offsets are negative values (x86 stack grows downward from EBP toward
// ESP). When ESP_relative addressing occurs, IDA converts a statement:
// mov eax,[esp+12]
// into the statement:
// mov eax,[esp+3Ch+var_30]
// Here, 3Ch == 60 decimal is the distance between ESP and EBP, and
// var_30 is defined to have the value -30h == -48 decimal. So, the
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
// "frame size" in IDA Pro is 60 bytes, and a certain local can be
// addressed in ESP-relative manner as shown, or as [ebp+var_30] for
// EBP-relative addressing. The interactive IDA user can then edit
// the name var_30 to something mnemonic, such as "virus_size", and IDA
// will replace all occurrences with the new name, so that code references
// automatically become [ebp+virus_size]. As the user proceeds
// interactively, he eventually produces very understandable code.
// This all makes sense for producing readable assembly text. However,
// our analyses have a compiler perspective as well as a memory access
// defense perspective. SMP distinguishes between callee saved regs,
// which should not be overwritten in the function body, and local
// variables, which can be written. We view the stack frame in logical
// pieces: here are the saved regs, here are the locals, here is the
// return address, etc. We don't care which direction from EBP the
// callee-saved registers lie; we don't want to lump them in with the
// local variables. We also don't like the fact that IDA Pro will take
// the function prologue code shown above and declare frregs=4 and
// frsize=60, because frsize no longer matches the stack allocation
// statement sub esp,34h == sub esp,52. We prefer frsize=52 and frregs=12.
// So, the task of this function is to fix these stack sizes in our
// private data members for the function, while leaving the IDA database
// alone because IDA needs to maintain its own definitions of these
// variables.
// Fixing means we will update the data members LocalVarsSize and
// CalleeSavedRegsSize.
// NOTE: This function is both machine dependent and platform dependent.
// The prologue and epilogue code generated by gcc-linux is as discussed
// above, while on Visual Studio and other Windows x86 compilers, the
// saving of registers other than EBP happens AFTER local stack allocation.
// A Windows version of the function would expect to see the pushing
// of ESI and EDI AFTER the sub esp,34h statement.
bool SMPFunction::MDFixFrameInfo(void) {
int SavedRegsSize = 0;
int OtherPushesSize = 0; // besides callee-saved regs
int NewLocalsSize = 0;
int OldFrameTotal = this->CalleeSavedRegsSize + this->LocalVarsSize;
bool Changed = false;
bool DebugFlag = (0 == strcmp("__libc_csu_init", this->GetFuncName()));
// Iterate through the first basic block in the function. If we find
// a frame allocating Instr in it, then we have local vars. If not,
// we don't, and LocalVarsSize should have been zero. Count the callee
// register saves leading up to the local allocation. Set data members
// according to what we found if the values of the data members would
// change.
SMPBasicBlock *CurrBlock = this->Blocks.front();
list<SMPInstr *>::iterator CurrIter = CurrBlock->GetFirstInstr();
#if SMP_USE_SSA_FNOP_MARKER
++CurrIter; // skip marker instruction
for ( ; CurrIter != CurrBlock->GetLastInstr(); ++CurrIter) {
SMPInstr *CurrInstr = (*CurrIter);
if (CurrInstr->MDIsPushInstr()) {
// We will make the gcc-linux assumption that a PUSH in
// the first basic block, prior to the stack allocating
// instruction, is a callee register save. To make this
// more robust, we ensure that the register is from
// the callee saved group of registers, and that it has
// not been defined thus far in the function (else it might
// be a push of an outgoing argument to a call that happens
// in the first block when there are no locals). **!!!!**
if (CurrInstr->MDUsesCalleeSavedReg()
&& !CurrInstr->HasSourceMemoryOperand()) {
SavedRegsSize += 4; // **!!** should check the size
clc5q
committed
if (DebugFlag) SMP_msg("libc_csu_init SavedRegsSize: %d %s\n", SavedRegsSize,
}
else {
// Pushes of outgoing args can be scheduled so that
// they are mixed with the pushes of callee saved regs.
OtherPushesSize += 4;
clc5q
committed
if (DebugFlag) SMP_msg("libc_csu_init OtherPushesSize: %d %s\n", OtherPushesSize,
}
}
else if (CurrInstr->MDIsFrameAllocInstr()) {
clc5q
committed
if (DebugFlag) SMP_msg("libc_csu_init allocinstr: %s\n", CurrInstr->GetDisasm());
SavedRegsSize += OtherPushesSize;
// Get the size being allocated.
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = CurrInstr->GetFirstUse(); CurrUse != CurrInstr->GetLastUse(); ++CurrUse) {
// Find the immediate operand.
if (o_imm == CurrUse->GetOp().type) {
// Get its value into LocalVarsSize.
long AllocValue = (signed long) CurrUse->GetOp().value;
// One compiler might have sub esp,24 and another
// might have add esp,-24. Take the absolute value.
if (0 > AllocValue)
AllocValue = -AllocValue;
if (AllocValue != (long) this->LocalVarsSize) {
Changed = true;
#if SMP_DEBUG_FRAMEFIXUP
if (AllocValue + SavedRegsSize != OldFrameTotal)
SMP_msg("Total frame size changed: %s OldTotal: %d NewTotal: %ld\n",
this->GetFuncName(), OldFrameTotal, (AllocValue + SavedRegsSize));
#endif
this->LocalVarsSize = (asize_t) AllocValue;
this->CalleeSavedRegsSize = (ushort) SavedRegsSize;
NewLocalsSize = this->LocalVarsSize;
}
else { // Old value was correct; no change.
NewLocalsSize = this->LocalVarsSize;
if (SavedRegsSize != this->CalleeSavedRegsSize) {
this->CalleeSavedRegsSize = (ushort) SavedRegsSize;
Changed = true;
#if SMP_DEBUG_FRAMEFIXUP
clc5q
committed
SMP_msg("Only callee regs size changed: %s\n", this->GetFuncName());
#endif
}
}
} // end if (o_imm == ...)
} // end for all uses
break; // After frame allocation instr, we are done
} // end if (push) .. elsif frame allocating instr
} // end for all instructions in the first basic block
// If we did not find an allocating instruction, see if it would keep
// the total size the same to set LocalVarsSize to 0 and to set
// CalleeSavedRegsSize to SavedRegsSize. If so, do it. If not, we
// might be better off to leave the numbers alone.
if (!Changed && (NewLocalsSize == 0)) {
clc5q
committed
if (DebugFlag) SMP_msg("libc_csu_init OldFrameTotal: %d \n", OldFrameTotal);
if (OldFrameTotal == SavedRegsSize) {
this->LocalVarsSize = 0;
Changed = true;
}
#if SMP_DEBUG_FRAMEFIXUP
else {
clc5q
committed
SMP_msg("Could not update frame sizes: %s\n", this->GetFuncName());
}
#endif
}
#if SMP_DEBUG_FRAMEFIXUP
if ((0 < OtherPushesSize) && (0 < NewLocalsSize))
clc5q
committed
SMP_msg("Extra pushes found of size %d in %s\n", OtherPushesSize,
clc5q
committed
#if SMP_DEBUG_FRAMEFIXUP
if (Changed) {
SMP_msg("Fixed stack frame size info: %s\n", this->GetFuncName());
#if SMP_DEBUG_FRAMEFIXUP_VERBOSE
clc5q
committed
SMPBasicBlock *CurrBlock = this->Blocks.front();
SMP_msg("First basic block:\n");
list<SMPInstr *>::iterator CurrInstr = CurrBlock->GetFirstInstr();
#if SMP_USE_SSA_FNOP_MARKER
++CurrInstr;
#endif
while (CurrInstr != CurrBlock->GetLastInstr()) {
clc5q
committed
SMP_msg("%s\n", (*CurrInstr)->GetDisasm());
clc5q
committed
}
clc5q
committed
}
#endif
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) {
sval_t TargetSize = - ((sval_t) OriginalLocSize); // negate; stack grows down
#if SMP_DEBUG_FRAMEFIXUP
clc5q
committed
bool DebugFlag = (0 == strcmp("_dl_runtime_resolve", this->GetFuncName()));
clc5q
committed
SMP_msg("%s OriginalLocSize: %d\n", this->GetFuncName(), OriginalLocSize);
// Limit our analysis to the first basic block in the function.
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
for ( ; InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t addr = CurrInst->GetAddr();
// get_spd() returns a cumulative delta of ESP
clc5q
committed
sval_t sp_delta = get_spd(this->GetFuncInfo(), addr);
#if SMP_DEBUG_FRAMEFIXUP
if (DebugFlag)
clc5q
committed
SMP_msg("%s delta: %d at %x\n", this->GetFuncName(), sp_delta, addr);
if (sp_delta == TargetSize) { // <= instead of == here? **!!**
// Previous instruction hit the frame size.
if (InstIter == this->Instrs.begin()) {
return BADADDR; // cannot back up from first instruction
}
else {
ea_t PrevAddr = (*(--InstIter))->GetAddr();
#if SMP_USE_SSA_FNOP_MARKER
if ((*(this->Instrs.begin()))->GetAddr() == PrevAddr)
return BADADDR; // don't return marker instruction
else
return PrevAddr;
#else
return PrevAddr;
#endif
if (CurrInst->IsLastInBlock()) {
// It could be that the current instruction will cause the stack pointer
// delta to reach the TargetSize. sp_delta is not updated until after the
// current instruction, so we need to look ahead one instruction if the
// current block falls through. On the other hand, if the current block
// ends with a jump or return, we cannot hit TargetSize.
if (CurrInst->IsBasicBlockTerminator())
return BADADDR;
list<SMPInstr *>::iterator NextInstIter = InstIter;
++NextInstIter;
if (NextInstIter == this->Instrs.end())
return BADADDR;
sp_delta = get_spd(this->GetFuncInfo(), (*NextInstIter)->GetAddr());
if (sp_delta == TargetSize) {
// CurrInst will cause stack pointer delta to hit TargetSize.
return addr;
}
else {
return BADADDR;
}
} // end if LastInBlock
} // end for all instructions
} // end if (this->AnalyzedSP)
#if SMP_DEBUG_FRAMEFIXUP
else {
clc5q
committed
SMP_msg("AnalyzedSP 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
// the old value of EBP and give it a new value as a frame pointer. The
// allocation of local variable space would have to come AFTER the move
// instruction. A function that begins: push ebp; push esi; sub esp,24
// is obviously not using EBP as a frame pointer. IDA is apparently
// confused by the push ebp instruction being the first instruction
// in the function. We will reset UseFP to false in this case.
// The inverse problem happens with a function that begins with instructions
// other than push ebp; mov ebp,esp; ... etc. but eventually has those
// instructions in the first basic block. For example, a C compiler generates
// for the first block of main():
// lea ecx,[esp+arg0]
// and esp, 0xfffffff0
// push dword ptr [ecx-4]
// push ebp
// mov ebp,esp
// push ecx
// sub esp,<framesize>
//
// This function is obviously using EBP as a frame pointer, but IDA Pro marks
// the function as not using a frame pointer. We will reset UseFP to true in
// this case.
// NOTE: This logic should work for both Linux and Windows x86 prologues.
bool SMPFunction::MDFixUseFP(void) {
bool OldUseFP = this->UsesFramePointer();
bool HasLocals = (0 < this->LocalVarsSize);
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
SMPInstr *CurrInst;
if (!(this->UseFP)) {
// See if we can detect the instruction "push ebp" followed by the instruction
// "mov ebp,esp" in the first basic block. The instructions do not have to be
// consecutive. If we find them, we will reset UseFP to true.
bool FirstBlockProcessed = false;
bool EBPSaved = false;
bool ESPintoEBP = false;
do {
CurrInst = (*InstIter);
addr = CurrInst->GetAddr();
FirstBlockProcessed = CurrInst->IsLastInBlock();
if (!EBPSaved) { // still looking for "push ebp"
if (CurrInst->MDIsPushInstr() && CurrInst->GetCmd().Operands[0].is_reg(R_bp)) {
EBPSaved = true;
}
}
else if (!ESPintoEBP) { // 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))) {
ESPintoEBP = true;
FirstBlockProcessed = true; // exit loop
}
}
// We must get EBP set to its frame pointer value before we reach the
// local frame allocation instruction (i.e. the subtraction of locals space
// from the stack pointer).
if (HasLocals) {
FirstBlockProcessed |= (addr >= this->LocalVarsAllocInstr);
}
++InstIter;
} while (!FirstBlockProcessed);
// If we found ESPintoEBP, we also found EBPSaved first, and we need to change
// this->UseFP to true and return true. Otherwise, return false.
this->UseFP = ESPintoEBP;
bool changed = (ESPintoEBP != OldUseFP);
if (changed)
SMP_msg("INFO: MDFixUseFP toggled UseFP for %s\n", this->GetFuncName());
return (changed);
#if 0
} // end if (!(this->UseFP))
// At this point, this->UseFP must have been true on entry to this method and we will
// check whether it should be reset to false.
while (addr <= this->LocalVarsAllocInstr) {
set<DefOrUse, LessDefUse>::iterator CurrDef = CurrInst->GetFirstDef();
while (CurrDef != CurrInst->GetLastDef()) {
if (CurrDef->GetOp().is_reg(R_bp))
return false; // EBP got set before locals were allocated
++InstIter;
CurrInst = (*InstIter);
addr = CurrInst->GetAddr();
}
// If we found no defs of the frame pointer before the local vars
// allocation, then the frame pointer register is not being used
// as a frame pointer, just as a general callee-saved register.
this->UseFP = false;
clc5q
committed
SMP_msg("INFO: MDFixUseFP reset UseFP to false for %s\n", this->GetFuncName());
} // end of SMPFunction::MDFixUseFP()
// Find the callee-saved reg offsets (negative offset from return address)
// for all registers pushed onto the stack before the stack frame allocation
// instruction.
void SMPFunction::MDFindSavedRegs(void) {
list<SMPInstr *>::iterator InstIter;
int RegIndex;
clc5q
committed
func_t *CurrFunc = SMP_get_func(this->GetStartAddr());
assert(NULL != CurrFunc);
for (InstIter = this->Instrs.begin(); InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
if (CurrInst->GetAddr() > this->LocalVarsAllocInstr)
break;
if (!(CurrInst->MDIsPushInstr()))
continue;
sval_t CurrOffset = get_spd(CurrFunc, CurrInst->GetAddr());
if (CurrInst->GetCmd().itype == NN_push) {
op_t PushedReg = CurrInst->GetPushedOpnd();
if (o_reg == PushedReg.type) {