Newer
Older
// 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) {
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;
clc5q
committed
if (ESPintoEBP)
SMP_msg("MDFixUseFP reset UseFP to true for %s\n", this->GetFuncName());
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);
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
}
} // 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()
clc5q
committed
// Limit damage from garbage stack offset values produced by IDA Pro.
#define IDAPRO_KLUDGE_STACK_FRAME_SIZE_LIMIT 5000000
// 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 %zu 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);
clc5q
committed
if (offset > IDAPRO_KLUDGE_STACK_FRAME_SIZE_LIMIT) {
SMP_msg("ERROR: Rejected enormous stack offset %ld for var %s in func %s\n", offset, MemberName, this->GetFuncName());
continue;
}
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 %zu 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;
// 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(),
CurrInst->GetDisasm(), sp_delta);
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
}
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 = %zu limit = %zu StackFrameMap size = %zu 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 = %zu FrameMapSize = %zu\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 = %zu FrameMapSize = %zu\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 %zu: 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
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
|| !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