Newer
Older
bool SMPFunction::MDFixUseFP(void) {
list<SMPInstr *>::iterator InstIter = this->Instrs.begin();
ea_t addr = (*InstIter)->GetAddr();
#if SMP_USE_SSA_FNOP_MARKER
++InstIter; // skip marker instruction
SMPInstr *CurrInst = (*InstIter);
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 {
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
}
}
++InstIter;
CurrInst = (*InstIter);
addr = CurrInst->GetAddr();
// 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).
FirstBlockProcessed |= (addr >= this->LocalVarsAllocInstr);
} 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;
return ESPintoEBP;
} // 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("MDFixUseFP reset UseFP to false for %s\n", this->GetFuncName());
return true;
} // 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) {
RegIndex = (int) PushedReg.reg;
if (RegIndex > R_di) {
clc5q
committed
SMP_msg("WARNING: Skipping save of register %d\n", RegIndex);
continue;
}
if (this->SavedRegLoc.at((size_t) RegIndex) == 0) {
this->SavedRegLoc[(size_t) RegIndex] = CurrOffset - 4;
}
else {
clc5q
committed
SMP_msg("WARNING: Multiple saves of register %d\n", RegIndex);
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
}
} // end if register push operand
} // end if PUSH instruction
else if (NN_pusha == CurrInst->GetCmd().itype) {
// **!!** Handle pushes of all regs.
this->SavedRegLoc[(size_t) R_ax] = CurrOffset - 4;
this->SavedRegLoc[(size_t) R_cx] = CurrOffset - 8;
this->SavedRegLoc[(size_t) R_dx] = CurrOffset - 12;
this->SavedRegLoc[(size_t) R_bx] = CurrOffset - 16;
this->SavedRegLoc[(size_t) R_sp] = CurrOffset - 20;
this->SavedRegLoc[(size_t) R_bp] = CurrOffset - 24;
this->SavedRegLoc[(size_t) R_si] = CurrOffset - 28;
this->SavedRegLoc[(size_t) R_di] = CurrOffset - 32;
break; // all regs accounted for
}
else if (CurrInst->MDIsEnterInstr()) {
this->SavedRegLoc[(size_t) R_bp] = CurrOffset - 4;
}
} // end for all instructions
return;
} // end of SMPFunction::MDFindSavedRegs()
// Compute the ReturnRegTypes[] as the meet over all register types
// at all return instructions.
void SMPFunction::MDFindReturnTypes(void) {
list<SMPBasicBlock *>::iterator BlockIter;
SMPBasicBlock *CurrBlock;
list<SMPInstr *>::iterator InstIter;
vector<SMPOperandType> RegTypes;
SMPInstr *CurrInst;
for (BlockIter = this->Blocks.begin(); BlockIter != this->Blocks.end(); ++BlockIter) {
CurrBlock = (*BlockIter);
if (CurrBlock->HasReturn()) {
// Get the types of all registers at the RETURN point.
// Calculate the meet function over them.
InstIter = CurrBlock->GetLastInstr();
--InstIter;
assert(RETURN == CurrInst->GetDataFlowType());
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = CurrInst->GetFirstUse();
CurrUse != CurrInst->GetLastUse();
++CurrUse) {
op_t UseOp = CurrUse->GetOp();
if ((o_reg != UseOp.type) || (R_di < UseOp.reg))
continue;
this->ReturnRegTypes[UseOp.reg]
= SMPTypeMeet(this->ReturnRegTypes.at(UseOp.reg),
CurrUse->GetType());
} // for all USEs in the RETURN instruction
} // end if current block has a RETURN
} // end for all blocks
return;
} // end of SMPFunction::MDFindReturnTypes()
// Determine local variable boundaries in the stack frame.
void SMPFunction::BuildLocalVarTable(void) {
// Currently we just use the info that IDA Pro has inferred from the direct
// addressing of stack locations.
this->SemiNaiveLocalVarID();
return;
} // end of SMPFunction::BuildLocalVarTable()
// Use the local variable offset list from IDA's stack frame structure to compute
// the table of local variable boundaries.
void SMPFunction::SemiNaiveLocalVarID(void) {
// NOTE: We use IDA Pro's offsets from this->FuncInfo (e.g. frsize) and NOT
// our own corrected values in our private data members. The offsets we
// read from the stack frame structure returned by get_frame() are consistent
// with other IDA Pro values, not with our corrected values.
list<SMPInstr *>::iterator InstIter;
bool DebugFlag = false;
this->LocalVarOffsetLimit = -20000;
#if SMP_DEBUG_STACK_GRANULARITY
DebugFlag |= (0 == strcmp("qSort3", this->GetFuncName()));
#endif
clc5q
committed
func_t *FuncPtr = SMP_get_func(this->FuncInfo.startEA);
if (NULL == FuncPtr) {
clc5q
committed
SMP_msg("ERROR in SMPFunction::SemiNaiveLocalVarID; no func ptr\n");
}
assert(NULL != FuncPtr);
struc_t *StackFrame = get_frame(FuncPtr);
if (NULL == StackFrame) {
clc5q
committed
SMP_msg("WARNING: No stack frame info from get_frame for %s\n", this->GetFuncName());
return;
}
member_t *Member = StackFrame->members;
for (size_t i = 0; i < StackFrame->memqty; ++i, ++Member) {
long offset;
if (NULL == Member) {
clc5q
committed
SMP_msg("NULL stack frame member pointer in %s\n", this->GetFuncName());
break;
}
get_member_name(Member->id, MemberName, MAXSMPVARSTR - 1);
if (MemberName == NULL) {
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("NULL stack frame member in %s\n", this->GetFuncName());
continue;
}
if (Member->unimem()) {
// Not a separate variable; name for member of a union.
// The union itself should have a separate entry, so we skip this.
clc5q
committed
SMP_msg("STACK INFO: Skipping union member %s frame member %u in stack frame for %s\n",
continue;
}
offset = (long) Member->get_soff(); // Would be 0 for union member, so we skipped them above.
if (DebugFlag) {
clc5q
committed
SMP_msg("%s local var %s at offset %ld\n", this->GetFuncName(), MemberName, offset);
struct LocalVar TempLocal;
TempLocal.offset = offset;
TempLocal.size = Member->eoff - Member->soff; // audit later
clc5q
committed
SMP_strncpy(TempLocal.VarName, MemberName, sizeof(TempLocal.VarName) - 1);
this->LocalVarTable.push_back(TempLocal);
if ((offset + (long) TempLocal.size) >= this->LocalVarOffsetLimit) {
this->LocalVarOffsetLimit = (long) (TempLocal.offset + TempLocal.size);
}
} // end for all stack frame members
// If AnalyzedSP is false, that is all we can do.
if (!this->AnalyzedSP) {
// No allocations; sometimes happens in library functions.
this->OutgoingArgsSize = 0;
this->MinStackDelta = 0;
this->AllocPointDelta = 0;
return;
}
// Calculate min and max stack point deltas.
this->MinStackDelta = 20000; // Final value should be negative or zero
this->MaxStackDelta = -1000; // Final value should be zero.
InstIter = this->Instrs.begin();
if ((*InstIter)->IsFloatNop())
++InstIter; // skip marker instruction
for ( ; InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t addr = CurrInst->GetAddr();
sval_t sp_delta = get_spd(this->GetFuncInfo(), addr);
if (sp_delta < this->MinStackDelta)
this->MinStackDelta = sp_delta;
if (sp_delta > this->MaxStackDelta)
this->MaxStackDelta = sp_delta;
if (addr == this->LocalVarsAllocInstr) {
// Total stack pointer delta is sp_delta for the next instruction,
// because IDA updates the sp delta AFTER each instruction.
list<SMPInstr *>::iterator NextInstIter = InstIter;
++NextInstIter;
sp_delta = get_spd(this->GetFuncInfo(), (*NextInstIter)->GetAddr());
this->AllocPointDelta = sp_delta;
}
}
// IDA Pro sometimes fails to add stack frame members for all incoming args, etc.
// Find and correct these omissions by examining stack accesses in instructions
// and extend the LocalVarTable to cover whatever is out of range.
if (!this->AuditLocalVarTable()) {
// Catastrophic error must have occurred, probably due to errors in IDA's
// stack pointer analysis, despite AnalyzedSP being true.
if (!(this->LocalVarTable.empty())) {
this->GoodLocalVarTable = true;
// Sort the LocalVarTable so that we do not depend on IDA Pro
// presenting the stack frame members in order.
std::sort(this->LocalVarTable.begin(), this->LocalVarTable.end(), LocalVarCompare);
}
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("Computing %d local var sizes\n", this->LocalVarTable.size());
// Now we want to audit the size field for each local
if (this->GoodLocalVarTable) {
size_t VarLimit = this->LocalVarTable.size() - 1;
assert(this->LocalVarTable.size() > 0);
for (size_t VarIndex = 0; VarIndex < VarLimit; ++VarIndex) {
struct LocalVar TempLocEntry = this->LocalVarTable[VarIndex];
bool AboveLocalsRegion = (TempLocEntry.offset >= this->LocalVarsSize);
size_t TempSize = this->LocalVarTable[VarIndex + 1].offset
- TempLocEntry.offset;
int DiffSize = ((int) TempSize) - ((int) TempLocEntry.size);
// We don't have IDA Pro stack frame members for callee saved registers. This
// omission can make it seem that there is a gap between the uppermost local
// variable and the return address or saved frame pointer. Avoid expanding the
// last local variable into the callee saved registers region.
if (DiffSize > 0) { // We are expanding the size.
if (!AboveLocalsRegion && ((TempLocEntry.offset + TempLocEntry.size + DiffSize) > this->LocalVarsSize)) {
// Current local does not start above the locals region, but its new size will
// carry it into the locals region.
if ((TempLocEntry.offset + TempLocEntry.size) > this->LocalVarsSize) {
// Weird. It already overlapped the callee saved regs region.
clc5q
committed
SMP_msg("WARNING: Local var at offset %ld size %u in %s extends above local vars region.\n",
TempLocEntry.offset, TempLocEntry.size, this->GetFuncName());
}
// Limit DiffSize to avoid overlapping callee saved regs.
DiffSize = this->LocalVarsSize - (TempLocEntry.offset + TempLocEntry.size);
if (DiffSize < 0)
DiffSize = 0; // started out positive, cap it at zero.
}
}
if (DiffSize < 0)
DiffSize = 0; // should not happen with sorted LocalVarTable unless duplicate entries.
if (DiffSize != 0) {
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("STACK INFO: Adjusted size for stack frame member at %ld in %s\n",
#endif
this->LocalVarTable[VarIndex].size += DiffSize;
}
#if 0 // Using Member->eoff seems to be working for all members, including the last one.
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("Computing last local var size for frsize %d\n", this->FuncInfo.frsize);
// Size of last local is total frsize minus savedregs in frame minus offset of last local
size_t SavedRegsSpace = 0; // portion of frsize that is saved regs, not locals.
if (this->CalleeSavedRegsSize > this->FuncInfo.frregs) {
// IDA Pro counts the save of EBP in frregs, but then EBP gets its new
// value and callee saved regs other than the old EBP push get counted
// in frsize rather than frregs. CalleeSavedRegsSize includes all saved
// regs on the stack, both above and below the current EBP offset.
// NOTE: For windows, this has to be done differently, as callee saved regs
// happen at the bottom of the local frame, not the top.
#if 0
SavedRegsSpace = this->CalleeSavedRegsSize - this->FuncInfo.frregs;
#else
SavedRegsSpace = this->FuncInfo.frsize - this->LocalVarsSize;
#endif
this->LocalVarTable.back().size = this->FuncInfo.frsize
- SavedRegsSpace - this->LocalVarTable.back().offset;
this->LocalVarOffsetLimit = this->LocalVarTable.back().offset
+ (adiff_t) this->LocalVarTable.back().size;
#if 0 // AboveLocalsSize is not a reliable number.
// IDA Pro can have difficulty with some irregular functions such as are found
// in the C startup code. The frsize value might be bogus. Just punt on the
// local variable ID if that is the case.
if ((this->LocalVarOffsetLimit - AboveLocalsSize) > (adiff_t) this->FuncInfo.frsize) {
this->LocalVarTable.clear();
this->GoodLocalVarTable = false;
clc5q
committed
SMP_msg("WARNING: Bad frsize %d for %s OffsetLimit: %d AboveLocalsSize: %d LocalVarsSize: %d ; abandoning SemiNaiveLocalVarID.\n",
this->FuncInfo.frsize, this->GetFuncName(), this->LocalVarOffsetLimit, AboveLocalsSize, this->LocalVarsSize);
return;
}
assert((this->LocalVarOffsetLimit - AboveLocalsSize) <= (adiff_t) this->FuncInfo.frsize);
// Find out how many of the locals are really outgoing args.
if (this->AnalyzedSP && !this->CallsAlloca && (BADADDR != this->LocalVarsAllocInstr)) {
this->FindOutgoingArgsSize();
}
else {
clc5q
committed
SMP_msg("FindOutgoingArgsSize not called for %s ", this->GetFuncName());
SMP_msg("AnalyzedSP: %d CallsAlloca: %d LocalVarsAllocInstr: %x \n",
this->AnalyzedSP, this->CallsAlloca, this->LocalVarsAllocInstr);
}
return;
} // end of SMPFunction::SemiNaiveLocalVarID()
// Check and correct the LocalVarTable derived from IDA Pro stack frame members.
// Examine each instruction and see if any stack accesses are beyond the LocalVarTable
// and create new entries in the LocalVarTable if so.
bool SMPFunction::AuditLocalVarTable(void) {
list<SMPInstr *>::iterator InstIter;
int SignedOffset;
// We cannot depend on IDA Pro making Member
// entries for everything that is accessed on the stack.
// When an incoming arg is accessed but no Member is
// created, then LocalVarOffsetLimit will be too small
// and we will get ERROR messages. Just loop through the
// instructions, find offsets higher than the LocalVarTable
// currently holds, and add new entries to LocalVarTable to
// handle them.
// Iterate through all instructions and record stack frame accesses in the StackFrameMap.
InstIter = this->Instrs.begin();
if ((*InstIter)->IsFloatNop())
++InstIter; // skip marker instruction
for ( ; InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t InstAddr = CurrInst->GetAddr();
sval_t sp_delta = get_spd(this->GetFuncInfo(), InstAddr);
if (0 < sp_delta) {
// Stack underflow; about to assert
clc5q
committed
SMP_msg("Stack underflow at %x %s sp_delta: %d\n", CurrInst->GetAddr(),
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
CurrInst->GetDisasm(), sp_delta);
}
assert(0 >= sp_delta);
ea_t offset;
size_t DataSize;
bool UsedFramePointer;
bool IndexedAccess;
bool SignedMove;
bool UnsignedMove;
if (CurrInst->HasDestMemoryOperand()) {
// NOTE: We need to catch stack pushes here also (callee-saved regs). !!!!!*******!!!!!!!!
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) {
op_t TempOp = CurrDef->GetOp();
if (TempOp.type != o_phrase && TempOp.type != o_displ)
continue;
if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, sp_delta, offset, DataSize, UsedFramePointer,
IndexedAccess, SignedMove, UnsignedMove)) {
SignedOffset = (int) offset;
if (IndexedAccess && ((0 > SignedOffset) || ((offset + DataSize) > this->StackFrameMap.size()))) {
continue; // Indexed expressions can be within frame but offset is outside frame
}
#if 0 // ls_O3.exe has IDA trouble on chunked function get_funky_string().
assert(0 <= SignedOffset);
#else
if (0 > SignedOffset) { // negative offset but not Indexed; very bad
clc5q
committed
SMP_msg("ERROR: Negative stack offset at %x in %s. Abandoning LocalVar ID.\n", CurrInst->GetAddr(), this->GetFuncName());
return false;
}
#endif
if ((SignedOffset + (long) DataSize) > this->LocalVarOffsetLimit) {
// Going out of range. Extend LocalVarTable.
struct LocalVar TempLocal;
char TempStr[20];
TempLocal.offset = (long) SignedOffset;
TempLocal.size = DataSize;
clc5q
committed
SMP_strncpy(TempLocal.VarName, "SMP_InArg", sizeof(TempLocal.VarName) - 1);
(void) SMP_snprintf(TempStr, 18, "%d", offset);
SMP_strncat(TempLocal.VarName, TempStr, sizeof(TempLocal.VarName) - 1);
this->LocalVarTable.push_back(TempLocal);
this->LocalVarOffsetLimit = (long) (SignedOffset + (long) DataSize);
}
}
}
}
if (CurrInst->HasSourceMemoryOperand()) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) {
op_t TempOp = CurrUse->GetOp();
clc5q
committed
if ((TempOp.type != o_phrase) && (TempOp.type != o_displ))
continue;
if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, sp_delta, offset, DataSize, UsedFramePointer,
IndexedAccess, SignedMove, UnsignedMove)) {
SignedOffset = (int) offset;
if (IndexedAccess && ((0 > SignedOffset) || ((offset + DataSize) > this->StackFrameMap.size()))) {
continue; // Indexed expressions can be within frame but offset is outside frame
}
assert(0 <= SignedOffset);
if ((SignedOffset + (long) DataSize) > this->LocalVarOffsetLimit) {
// Going out of range. Extend LocalVarTable.
struct LocalVar TempLocal;
char TempStr[20];
TempLocal.offset = (long) SignedOffset;
TempLocal.size = DataSize;
clc5q
committed
SMP_strncpy(TempLocal.VarName, "SMP_InArg", sizeof(TempLocal.VarName) - 1);
(void) SMP_snprintf(TempStr, 18, "%d", offset);
SMP_strncat(TempLocal.VarName, TempStr, sizeof(TempLocal.VarName) - 1);
this->LocalVarTable.push_back(TempLocal);
this->LocalVarOffsetLimit = (long) (SignedOffset + (long) DataSize);
}
}
}
}
} // end for all instructions
// Fill in the gaps with new variables as well. SHOULD WE? WHY?
return true;
} // end of SMPFunction::AuditLocalVarTable()
// Determine how many bytes at the bottom of the stack frame (i.e. at bottom of
// this->LocalVarsSize) are used for outgoing args. This is the case when the cdecl
// calling convention is used, e.g. gcc/linux allocates local var space + out args space
// in a single allocation and then writes outarg values directly to ESP+0, ESP+4, etc.
void SMPFunction::FindOutgoingArgsSize(void) {
// Compute the lowest value reached by the stack pointer.
list<SMPInstr *>::iterator InstIter;
unsigned short BitWidthMask;
#if SMP_DEBUG_STACK_GRANULARITY
DebugFlag = (0 == strcmp("BZ2_blockSort", this->GetFuncName()));
this->OutgoingArgsComputed = true;
clc5q
committed
SMP_msg("DEBUG: Entered FindOutgoingArgsSize for %s\n", this->GetFuncName());
#if SMP_IDAPRO52_WORKAROUND
this->OutgoingArgsSize = 16;
return;
#if SMP_DEBUG_STACK_GRANULARITY
clc5q
committed
SMP_msg("AllocPointDelta: %d MinStackDelta: %d\n", this->AllocPointDelta, this->MinStackDelta);
if ((0 <= this->MinStackDelta) || (0 <= this->AllocPointDelta)) {
// No allocations; sometimes happens in library functions.
this->OutgoingArgsSize = 0;
this->MinStackDelta = 0;
this->AllocPointDelta = 0;
return;
}
assert(0 > this->MinStackDelta);
// Allocate a vector of stack frame entries, one for each byte of the stack frame.
// This will be our memory map for analyzing stack usage.
int limit = 0;
#if 1
if (this->LocalVarOffsetLimit > 0) {
if (limit < (this->LocalVarOffsetLimit + this->MinStackDelta)) {
// Make room for incoming args, other stuff above local vars.
limit = this->LocalVarOffsetLimit + this->MinStackDelta;
if (this->MinStackDelta < this->AllocPointDelta) {
// Also have stuff below alloc point to make room for.
limit += (this->AllocPointDelta - this->MinStackDelta);
}
#endif
for (int i = this->MinStackDelta; i < limit; ++i) {
struct StackFrameEntry TempEntry;
TempEntry.VarPtr = NULL;
TempEntry.offset = (long) i;
TempEntry.Read = false;
TempEntry.Written = false;
TempEntry.AddressTaken = false;
TempEntry.ESPRelativeAccess = false;
TempEntry.EBPRelativeAccess = false;
TempEntry.IndexedAccess = false;
this->StackFrameMap.push_back(TempEntry);
struct FineGrainedInfo TempFineGrained;
TempFineGrained.SignMiscInfo = 0;
TempFineGrained.SizeInfo = 0;
this->FineGrainedStackTable.push_back(TempFineGrained);
#if 0
for (int i = 0; i < this->LocalVarOffsetLimit; ++i) {
struct FineGrainedInfo TempFineGrained;
TempFineGrained.SignMiscInfo = 0;
TempFineGrained.SizeInfo = 0;
this->FineGrainedStackTable.push_back(TempFineGrained);
#endif
// Fill in the VarPtr fields for each StackFrameMap entry.
if (0 <= this->AllocPointDelta) {
clc5q
committed
SMP_msg("FATAL ERROR: AllocPointDelta = %d in %s\n", this->AllocPointDelta, this->GetFuncName());
assert(0 > this->AllocPointDelta);
for (size_t i = 0; i < this->LocalVarTable.size(); ++i) {
assert(this->LocalVarTable.at(i).offset >= 0);
// Picture that AllocPointDelta is -200, MinStackDelta is -210, and
// the LocalVarTable[i].offset is +8 (i.e. 8 bytes above alloc point).
// Then base = 8 + (-200 - -210) = 8 + 10 = 18, the proper offset into
// the StackFrameMap.
size_t base = (size_t) (this->LocalVarTable.at(i).offset
+ (this->AllocPointDelta - this->MinStackDelta));
size_t limit = base + this->LocalVarTable.at(i).size;
if (limit > this->StackFrameMap.size()) {
clc5q
committed
SMP_msg("ERROR: FindOutArgsSize: base = %u limit = %u StackFrameMap size = %u in %s\n",
base, limit, this->StackFrameMap.size(), this->GetFuncName());
this->OutgoingArgsComputed = false;
this->OutgoingArgsSize = 0;
return;
}
assert(limit <= this->StackFrameMap.size());
for (size_t MapIndex = base; MapIndex < limit; ++MapIndex) {
this->StackFrameMap[MapIndex].VarPtr = &(this->LocalVarTable.at(i));
}
}
// Iterate through all instructions and record stack frame accesses in the StackFrameMap.
InstIter = this->Instrs.begin();
#if SMP_USE_SSA_FNOP_MARKER
if ((*InstIter)->IsFloatNop())
++InstIter; // skip marker instruction
for ( ; InstIter != this->Instrs.end(); ++InstIter) {
SMPInstr *CurrInst = (*InstIter);
ea_t InstAddr = CurrInst->GetAddr();
sval_t sp_delta = get_spd(this->GetFuncInfo(), InstAddr);
if (0 < sp_delta) {
// Stack underflow; about to assert
clc5q
committed
SMP_msg("FATAL ERROR: Stack underflow at %x %s sp_delta: %d\n", CurrInst->GetAddr(),
CurrInst->GetDisasm(), sp_delta);
}
assert(0 >= sp_delta);
ea_t offset;
size_t DataSize;
bool UsedFramePointer;
bool IndexedAccess;
bool SignedMove;
bool UnsignedMove;
if (CurrInst->HasDestMemoryOperand()) {
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = CurrInst->GetFirstDef(); CurrDef != CurrInst->GetLastDef(); ++CurrDef) {
op_t TempOp = CurrDef->GetOp();
if (TempOp.type != o_phrase && TempOp.type != o_displ)
continue;
if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, sp_delta, offset, DataSize, UsedFramePointer,
IndexedAccess, SignedMove, UnsignedMove)) {
SignedOffset = (int) offset;
if (IndexedAccess && ((0 > SignedOffset) || ((offset + DataSize) > this->StackFrameMap.size()))) {
continue; // Indexed expressions can be within frame even when offset is outside frame
}
assert(0 <= SignedOffset);
#if 0
if (offset >= this->FuncInfo.frsize)
continue; // limit processing to outgoing args and locals
if ((offset + DataSize) > this->StackFrameMap.size()) {
clc5q
committed
SMP_msg("ERROR: offset = %u DataSize = %u FrameMapSize = %u\n",
offset, DataSize, this->StackFrameMap.size());
}
assert((offset + DataSize) <= this->StackFrameMap.size());
for (int j = 0; j < (int) DataSize; ++j) {
this->StackFrameMap[offset + j].Written = true;
this->StackFrameMap[offset + j].IndexedAccess = IndexedAccess;
if (!UsedFramePointer) {
this->StackFrameMap[offset + j].ESPRelativeAccess = true;
}
else {
this->StackFrameMap[offset + j].EBPRelativeAccess = true;
BitWidthMask = ComputeOperandBitWidthMask(TempOp, DataSize);
this->FineGrainedStackTable.at(offset).SizeInfo |= BitWidthMask;
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_WRITTEN;
if (IndexedAccess) {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_INDEXED_ACCESS;
}
if (!UsedFramePointer) {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_SP_RELATIVE;
}
else {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_FP_RELATIVE;
}
// We will process the signedness of stores later, so that loads can take precedence
// over stores in determining signedness.
} // end if MDGetStackOffsetAndSize()
} // end for all DEFs
} // end if DestMemoryOperand
if (CurrInst->HasSourceMemoryOperand()) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = CurrInst->GetFirstUse(); CurrUse != CurrInst->GetLastUse(); ++CurrUse) {
op_t TempOp = CurrUse->GetOp();
if (TempOp.type != o_phrase && TempOp.type != o_displ)
continue;
if (this->MDGetStackOffsetAndSize(CurrInst, TempOp, sp_delta, offset, DataSize, UsedFramePointer,
IndexedAccess, SignedMove, UnsignedMove)) {
SignedOffset = (int) offset;
if (IndexedAccess && ((0 > SignedOffset) || ((SignedOffset + DataSize) > this->StackFrameMap.size()))) {
continue; // Indexed expressions can be within frame but offset is outside frame
}
assert(0 <= SignedOffset);
#if 0
if (offset >= this->FuncInfo.frsize)
continue; // limit processing to outgoing args and locals
#endif
if ((SignedOffset + DataSize) > this->StackFrameMap.size()) {
clc5q
committed
SMP_msg("ERROR: offset = %u DataSize = %u FrameMapSize = %u\n",
offset, DataSize, this->StackFrameMap.size());
assert((SignedOffset + DataSize) <= this->StackFrameMap.size());
for (int j = 0; j < (int) DataSize; ++j) {
this->StackFrameMap[offset + j].Read = true;
this->StackFrameMap[offset + j].IndexedAccess |= IndexedAccess;
if (!UsedFramePointer)
this->StackFrameMap[offset + j].ESPRelativeAccess = true;
else
this->StackFrameMap[offset + j].EBPRelativeAccess = true;
}
BitWidthMask = ComputeOperandBitWidthMask(TempOp, DataSize);
this->FineGrainedStackTable.at(offset).SizeInfo |= BitWidthMask;
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_READ;
if (IndexedAccess) {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_INDEXED_ACCESS;
}
if (!UsedFramePointer) {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_SP_RELATIVE;
}
else {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_FP_RELATIVE;
}
if (SignedMove) {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_SIGNED;
}
else if (UnsignedMove) {
this->FineGrainedStackTable.at(offset).SignMiscInfo |= FG_MASK_UNSIGNED;
}
} // end if MDGetStackOffsetAndSize()
} // end if SourceMemoryOperand
// NOTE: Detect taking the address of stack locations. **!!**
} // end for all instructions
// If function is a leaf function, set OutgoingArgsSize to zero and return.
if (this->IsLeaf()) {
this->OutgoingArgsSize = 0;
return;
}
// For non-leaf functions, set the OutgoingArgsSize to the write-only, ESP-relative
// region of the bottom of the StackFrameMap.
bool OutgoingArgsRegionFinished = false;
bool IndexedOutgoingArgs = false; // Any indexed accesses to outgoing args?
size_t FramePadSize = 0;
for (size_t MapIndex = 0; MapIndex < this->StackFrameMap.size(); ++MapIndex) {
// Some of the bottom of the stack frame might be below the local frame allocation.
// These are pushes that happened after allocation, etc. We skip over these
// locations and define the outgoing args region to start strictly at the bottom
// of the local frame allocation.
struct StackFrameEntry TempEntry = this->StackFrameMap.at(MapIndex);
if (DebugFlag) {
clc5q
committed
SMP_msg("StackFrameMap entry %u: offset: %ld Read: %d Written: %d ESP: %d EBP: %d\n",
MapIndex, TempEntry.offset, TempEntry.Read, TempEntry.Written,
TempEntry.ESPRelativeAccess, TempEntry.EBPRelativeAccess);
}
if (TempEntry.offset < this->AllocPointDelta)
continue;
if (OutgoingArgsRegionFinished) {
// We are just processing the stack frame padding.
if (!TempEntry.Read && !TempEntry.Written) {
// Could be stack frame padding.
++FramePadSize;
}
else {
break; // No more padding region
}
}
clc5q
committed
else if (TempEntry.Read || TempEntry.EBPRelativeAccess || !TempEntry.Written
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
|| !TempEntry.ESPRelativeAccess) {
OutgoingArgsRegionFinished = true;
if (!TempEntry.Read && !TempEntry.Written) {
// Could be stack frame padding.
++FramePadSize;
}
else {
break; // No padding region
}
}
else {
this->OutgoingArgsSize++;
if (TempEntry.IndexedAccess) {
IndexedOutgoingArgs = true;
}
}
}
// If any outgoing arg was accessed using an index register, then we don't know how high
// the index register value went. It could potentially consume the so-called padding
// region, which might be just the region we did not detect direct accesses to because
// the accesses were indirect. To be safe, we expand the outgoing args region to fill
// the padding region above it in this indexed access case.
if (IndexedOutgoingArgs) {
this->OutgoingArgsSize += FramePadSize;
// Sometimes we encounter unused stack space above the outgoing args. Lump this space
// in with the outgoing args. We detect this by noting when the outgoing args space
// has only partially used the space assigned to a local var.
// NOTE: This is usually just stack padding to maintain stack alignment. It could
// also be the case that the lowest local variable is accessed indirectly and we missed
// seeing its address taken, in which case it would be unsound to lump it into the
// outgoing args region. We might want to create a local var called STACKPAD
// to occupy this space.
if ((0 < this->OutgoingArgsSize) && (this->OutgoingArgsSize < this->FuncInfo.frsize)) {
long MapIndex = (this->AllocPointDelta - this->MinStackDelta);
assert(0 <= MapIndex);
MapIndex += (((long) this->OutgoingArgsSize) - 1);
struct StackFrameEntry TempEntry = this->StackFrameMap.at((size_t) MapIndex);
clc5q
committed
if (NULL == TempEntry.VarPtr) { // Gap in stack frame; IDA 6.0
clc5q
committed
SMP_msg("Gap in stack frame: %s\n", this->GetFuncName());
clc5q
committed
}
else if (this->OutgoingArgsSize < (TempEntry.VarPtr->offset + TempEntry.VarPtr->size)) {
clc5q
committed
#if SMP_DEBUG_FRAMEFIXUP
clc5q
committed
SMP_msg("OutGoingArgsSize = %d", this->OutgoingArgsSize);
clc5q
committed
#endif
this->OutgoingArgsSize = TempEntry.VarPtr->offset + TempEntry.VarPtr->size;
clc5q
committed
#if SMP_DEBUG_FRAMEFIXUP
clc5q
committed
SMP_msg(" adjusted to %d\n", this->OutgoingArgsSize);
clc5q
committed
#endif
return;
} // end of SMPFunction::FindOutgoingArgsSize()
// If TempOp reads or writes to a stack location, return the offset (relative to the initial
// stack pointer value) and the size in bytes of the data access. Also return whether the
// access was frame-pointer-relative, and whether signedness can be inferred due to a load
// from the stack being zero-extended or sign-extended.
// NOTE: TempOp must be of type o_displ or o_phrase, as no other operand type could be a
// stack memory access.
// sp_delta is the stack pointer delta of the current instruction, relative to the initial
// stack pointer value for the function.
// Return true if a stack memory access was found in TempOp, false otherwise.
bool SMPFunction::MDGetStackOffsetAndSize(SMPInstr *Instr, op_t TempOp, sval_t sp_delta, ea_t &offset, size_t &DataSize, bool &FP,
bool &Indexed, bool &Signed, bool &Unsigned) {
clc5q
committed
int BaseReg;
int IndexReg;
ushort ScaleFactor;
int SignedOffset;
assert((o_displ == TempOp.type) || (o_phrase == TempOp.type));
clc5q
committed
MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset);
clc5q
committed
if (TempOp.type == o_phrase) {
assert(offset == 0); // implicit zero, as in [esp] ==> [esp+0]
SignedOffset = (int) offset; // avoid sign errors during adjustment arithmetic
if ((BaseReg == R_sp) || (IndexReg == R_sp)) {
// ESP-relative constant offset
SignedOffset += sp_delta; // base offsets from entry ESP value
SignedOffset -= this->MinStackDelta; // convert to StackFrameMap index
offset = (ea_t) SignedOffset;
// Get size of data written
DataSize = GetOpDataSize(TempOp);
FP = false;
Indexed = ((BaseReg != R_none) && (IndexReg != R_none)); // two regs used
unsigned short opcode = Instr->GetCmd().itype;
Unsigned = (opcode == NN_movzx);
Signed = (opcode == NN_movsx);
if ((0 > SignedOffset) && (!Indexed)) {
// Consider asserting here.
clc5q
committed
SMP_msg("ERROR: Negative offset in MDGetStackOffsetAndSize for inst dump: \n");
return true;
}
else if (this->UseFP && ((BaseReg == R_bp) || (IndexReg == R_bp))) {
SignedOffset -= this->FuncInfo.frregs; // base offsets from entry ESP value
SignedOffset -= this->MinStackDelta; // convert to StackFrameMap index
offset = (ea_t) SignedOffset;
DataSize = GetOpDataSize(TempOp);
FP = true;
Indexed = ((BaseReg != R_none) && (IndexReg != R_none)); // two regs used
unsigned short opcode = Instr->GetCmd().itype;
Unsigned = (opcode == NN_movzx);
Signed = (opcode == NN_movsx);
if ((0 > SignedOffset) && (!Indexed)) {
// Consider asserting here.
clc5q
committed
SMP_msg("ERROR: Negative offset in MDGetStackOffsetAndSize for inst dump: \n");
return true;
}
else {
return false;
}
} // end of SMPFunction::MDGetStackOffsetAndSize()
// Return fine grained stack entry for stack op TempOp from instruction at InstAddr
bool SMPFunction::MDGetFGStackLocInfo(ea_t InstAddr, op_t TempOp, struct FineGrainedInfo &FGEntry) {
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t offset;
int SignedOffset;
assert((o_displ == TempOp.type) || (o_phrase == TempOp.type));
MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset);
sval_t sp_delta = get_spd(this->GetFuncInfo(), InstAddr);
SignedOffset = (int) offset;
if (TempOp.type == o_phrase) {
assert(SignedOffset == 0); // implicit zero, as in [esp] ==> [esp+0]
}
if ((BaseReg == R_sp) || (IndexReg == R_sp)) {
// ESP-relative constant offset
SignedOffset += sp_delta; // base offsets from entry ESP value
SignedOffset -= this->MinStackDelta; // convert to StackFrameMap index
}
else if (this->UseFP && ((BaseReg == R_bp) || (IndexReg == R_bp))) {
SignedOffset -= this->FuncInfo.frregs; // base offsets from entry ESP value
SignedOffset -= this->MinStackDelta; // convert to StackFrameMap index
}
else {
return false;
}
// We did not return false, so we should have a good offset. Use it to
// pass back the fine grained stack table entry for that offset.
if ((0 > SignedOffset) || (SignedOffset >= (int) this->FineGrainedStackTable.size())) {
clc5q
committed
SMP_msg("ERROR: FG stack table index out of range in MDGetFGStackLocInfo at %x\n", InstAddr);
FGEntry.SignMiscInfo = 0; // We cannot figure out signedness info without an FG info stack table.
FGEntry.SizeInfo = ComputeOperandBitWidthMask(TempOp, 0); // IDA can figure out width, anyway.
}
else {
FGEntry = this->FineGrainedStackTable.at((size_t) SignedOffset);
}
return true;
} // end of SMPFunction::MDGetFGStackLocInfo()
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
// Return true if we update fine grained stack entry for stack op TempOp from instruction at InstAddr
bool SMPFunction::MDUpdateFGStackLocInfo(ea_t InstAddr, op_t TempOp, struct FineGrainedInfo NewFG) {
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t offset;
int SignedOffset;
struct FineGrainedInfo OldFG, UnionFG;
assert((o_displ == TempOp.type) || (o_phrase == TempOp.type));
MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset);
sval_t sp_delta = get_spd(this->GetFuncInfo(), InstAddr);
SignedOffset = (int) offset;
if (TempOp.type == o_phrase) {
assert(SignedOffset == 0); // implicit zero, as in [esp] ==> [esp+0]
}
if ((BaseReg == R_sp) || (IndexReg == R_sp)) {
// ESP-relative constant offset
SignedOffset += sp_delta; // base offsets from entry ESP value
SignedOffset -= this->MinStackDelta; // convert to StackFrameMap index
}
else if (this->UseFP && ((BaseReg == R_bp) || (IndexReg == R_bp))) {
SignedOffset -= this->FuncInfo.frregs; // base offsets from entry ESP value
SignedOffset -= this->MinStackDelta; // convert to StackFrameMap index
}
else {
return false;
}
// We did not return false, so we should have a good offset. Use it to
// retrieve the fine grained stack table entry for that offset.
if ((0 > SignedOffset) || (SignedOffset >= (int) this->FineGrainedStackTable.size())) {
if (this->OutgoingArgsComputed) {
clc5q
committed
SMP_msg("ERROR: FG stack table index out of range in MDGetFGStackLocInfo at %x\n", InstAddr);
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
}
return false;
}
else if (this->OutgoingArgsComputed && (((size_t)SignedOffset) < this->OutgoingArgsSize)) {
// We don't want to update the outgoing args region, as it will not be consistent
// over multiple function calls. NOTE: We could fine tune this by seeing if we
// call mutliple target functions or not; if only one, then outgoing args region
// would be consistent in the absence of varargs targets.
return false;
}
else {
OldFG = this->FineGrainedStackTable.at((size_t) SignedOffset);
UnionFG.SignMiscInfo = OldFG.SignMiscInfo | NewFG.SignMiscInfo;
UnionFG.SizeInfo = OldFG.SizeInfo | NewFG.SizeInfo;
if ((OldFG.SignMiscInfo != UnionFG.SignMiscInfo) || (OldFG.SizeInfo != UnionFG.SizeInfo)) {
// The signs they are a-changin'. Or maybe the sizes.
this->FineGrainedStackTable.at(SignedOffset).SignMiscInfo |= NewFG.SignMiscInfo;
this->FineGrainedStackTable.at(SignedOffset).SizeInfo |= NewFG.SizeInfo;
}
}
return true;
} // end of SMPFunction::MDUpdateFGStackLocInfo()
// retrieve DEF addr from GlobalDefAddrBySSA or return BADADDR
ea_t SMPFunction::GetGlobalDefAddr(op_t DefOp, int SSANum) {
map<int, ea_t>::iterator DefAddrMapIter;
map<int, ea_t>::iterator MapResult;
ea_t DefAddr = BADADDR; // BADADDR means we did not find it
int HashedName = HashGlobalNameAndSSA(DefOp, SSANum);
MapResult = this->GlobalDefAddrBySSA.find(HashedName);
if (MapResult != this->GlobalDefAddrBySSA.end()) { // Found it.
DefAddr = (ea_t) MapResult->second;
}
return DefAddr;
} // end of SMPFunction::GetGlobalDefAddr()
// Retrieve block iterator for InstAddr from InstBlockMap; assert if failure
SMPBasicBlock *SMPFunction::GetBlockFromInstAddr(ea_t InstAddr) {
map<ea_t, SMPBasicBlock *>::iterator MapEntry;
MapEntry = this->InstBlockMap.find(InstAddr);
assert(MapEntry != this->InstBlockMap.end());
return MapEntry->second;
}
clc5q
committed
// Retrieve inst iterator for InstAddr; assert if failure on block find.
SMPInstr *SMPFunction::GetInstFromAddr(ea_t InstAddr) {
SMPBasicBlock *CurrBlock = this->GetBlockFromInstAddr(InstAddr);
SMPInstr *CurrInst = CurrBlock->FindInstr(InstAddr);
return CurrInst;