#include "flow.hh"
FlowInfo::FlowInfo(Funcdata &d,PcodeOpBank &o,BlockGraph &b,vector<FuncCallSpecs *> &q) :
data(d), obank(o), bblocks(b), qlst(q),
baddr(d.getAddress().getSpace(),0),
eaddr(d.getAddress().getSpace(),~((uintb)0)),
minaddr(d.getAddress()),
maxaddr(d.getAddress())
{
glb = data.getArch();
flags = 0;
emitter.setFuncdata(&d);
inline_head = (Funcdata *)0;
inline_recursion = (set<Address> *)0;
insn_count = 0;
insn_max = ~((uint4)0);
flowoverride_present = data.getOverride().hasFlowOverride();
}
FlowInfo::FlowInfo(Funcdata &d,PcodeOpBank &o,BlockGraph &b,vector<FuncCallSpecs *> &q,const FlowInfo *op2) :
data(d), obank(o), bblocks(b), qlst(q),
baddr(op2->baddr),
eaddr(op2->eaddr),
minaddr(d.getAddress()),
maxaddr(d.getAddress())
{
glb = data.getArch();
flags = op2->flags;
emitter.setFuncdata(&d);
unprocessed = op2->unprocessed; addrlist = op2->addrlist;
visited = op2->visited;
inline_head = op2->inline_head;
if (inline_head != (Funcdata *)0) {
inline_base = op2->inline_base;
inline_recursion = &inline_base;
}
else
inline_recursion = (set<Address> *)0;
insn_count = op2->insn_count;
insn_max = op2->insn_max;
flowoverride_present = data.getOverride().hasFlowOverride();
}
void FlowInfo::clearProperties(void)
{
flags &= ~((uint4)(unimplemented_present|baddata_present|outofbounds_present));
insn_count = 0;
}
PcodeOp *FlowInfo::fallthruOp(PcodeOp *op) const
{
PcodeOp *retop;
list<PcodeOp *>::const_iterator iter = op->getInsertIter();
++iter;
if (iter != obank.endDead()) {
retop = *iter;
if (!retop->isInstructionStart()) return retop; }
map<Address,VisitStat>::const_iterator miter;
miter = visited.upper_bound(op->getAddr());
if (miter == visited.begin()) return (PcodeOp *)0;
--miter;
if ((*miter).first + (*miter).second.size <= op->getAddr())
return (PcodeOp *)0;
return target( (*miter).first + (*miter).second.size);
}
PcodeOp *FlowInfo::target(const Address &addr) const
{
map<Address,VisitStat>::const_iterator iter;
iter = visited.find(addr);
while(iter != visited.end()) {
const SeqNum &seq( (*iter).second.seqnum );
if (!seq.getAddr().isInvalid()) {
PcodeOp *retop = obank.findOp(seq);
if (retop != (PcodeOp *)0)
return retop;
break;
}
iter = visited.find( (*iter).first + (*iter).second.size );
}
ostringstream errmsg;
errmsg << "Could not find op at target address: (";
errmsg << addr.getSpace()->getName() << ',';
addr.printRaw(errmsg);
errmsg << ')';
throw LowlevelError(errmsg.str());
}
PcodeOp *FlowInfo::findRelTarget(PcodeOp *op,Address &res) const
{
const Address &addr(op->getIn(0)->getAddr());
uintm id = op->getTime() + addr.getOffset();
SeqNum seqnum(op->getAddr(),id);
PcodeOp *retop = obank.findOp(seqnum);
if (retop != (PcodeOp *)0) return retop;
SeqNum seqnum1(op->getAddr(),id-1);
retop = obank.findOp(seqnum1); if (retop != (PcodeOp *)0) {
map<Address,VisitStat>::const_iterator miter;
miter = visited.upper_bound(retop->getAddr());
if (miter != visited.begin()) {
--miter;
res = (*miter).first + (*miter).second.size;
if (op->getAddr() < res)
return (PcodeOp *)0; }
}
ostringstream errmsg;
errmsg << "Bad relative branch at instruction : (";
errmsg << op->getAddr().getSpace()->getName() << ',';
op->getAddr().printRaw(errmsg);
errmsg << ')';
throw LowlevelError(errmsg.str());
}
PcodeOp *FlowInfo::branchTarget(PcodeOp *op) const
{
const Address &addr(op->getIn(0)->getAddr());
if (addr.isConstant()) { Address res;
PcodeOp *retop = findRelTarget(op,res);
if (retop != (PcodeOp *)0)
return retop;
return target(res);
}
return target(addr); }
void FlowInfo::newAddress(PcodeOp *from,const Address &to)
{
if ((to < baddr)||(eaddr < to)) {
handleOutOfBounds(from->getAddr(),to);
unprocessed.push_back(to);
return;
}
if (seenInstruction(to)) { PcodeOp *op = target(to);
data.opMarkStartBasic(op);
return;
}
addrlist.push_back(to);
}
void FlowInfo::deleteRemainingOps(list<PcodeOp *>::const_iterator oiter)
{
while(oiter != obank.endDead()) {
PcodeOp *op = *oiter;
++oiter;
data.opDestroyRaw(op);
}
}
PcodeOp *FlowInfo::xrefControlFlow(list<PcodeOp *>::const_iterator oiter,bool &startbasic,bool &isfallthru,FuncCallSpecs *fc)
{
PcodeOp *op = (PcodeOp *)0;
isfallthru = false;
uintm maxtime=0; while(oiter != obank.endDead()) {
op = *oiter++;
if (startbasic) {
data.opMarkStartBasic(op);
startbasic = false;
}
switch(op->code()) {
case CPUI_CBRANCH:
{
const Address &destaddr( op->getIn(0)->getAddr() );
if (destaddr.isConstant()) {
Address fallThruAddr;
PcodeOp *destop = findRelTarget(op,fallThruAddr);
if (destop != (PcodeOp *)0) {
data.opMarkStartBasic(destop); uintm newtime = destop->getTime();
if (newtime > maxtime)
maxtime = newtime;
}
else
isfallthru = true; }
else
newAddress(op,destaddr); startbasic = true;
}
break;
case CPUI_BRANCH:
{
const Address &destaddr( op->getIn(0)->getAddr() );
if (destaddr.isConstant()) {
Address fallThruAddr;
PcodeOp *destop = findRelTarget(op,fallThruAddr);
if (destop != (PcodeOp *)0) {
data.opMarkStartBasic(destop); uintm newtime = destop->getTime();
if (newtime > maxtime)
maxtime = newtime;
}
else
isfallthru = true; }
else
newAddress(op,destaddr); if (op->getTime() >= maxtime) {
deleteRemainingOps(oiter);
oiter = obank.endDead();
}
startbasic = true;
}
break;
case CPUI_BRANCHIND:
tablelist.push_back(op); if (op->getTime() >= maxtime) {
deleteRemainingOps(oiter);
oiter = obank.endDead();
}
startbasic = true;
break;
case CPUI_RETURN:
if (op->getTime() >= maxtime) {
deleteRemainingOps(oiter);
oiter = obank.endDead();
}
startbasic = true;
break;
case CPUI_CALL:
if (setupCallSpecs(op,fc))
--oiter; break;
case CPUI_CALLIND:
if (setupCallindSpecs(op,true,fc))
--oiter; break;
case CPUI_CALLOTHER:
{
InjectedUserOp *userop = dynamic_cast<InjectedUserOp *>(glb->userops.getOp(op->getIn(0)->getOffset()));
if (userop != (InjectedUserOp *)0)
injectlist.push_back(op);
break;
}
default:
break;
}
}
if (isfallthru) startbasic = true; else { if (op == (PcodeOp *)0)
isfallthru = true; else {
switch(op->code()) {
case CPUI_BRANCH:
case CPUI_BRANCHIND:
case CPUI_RETURN:
break; default:
isfallthru = true; break;
}
}
}
return op;
}
bool FlowInfo::processInstruction(const Address &curaddr,bool &startbasic)
{
bool emptyflag;
bool isfallthru = true;
list<PcodeOp *>::const_iterator oiter;
int4 step;
uint4 flowoverride;
if (insn_count >= insn_max) {
if ((flags & error_toomanyinstructions)!=0)
throw LowlevelError("Flow exceeded maximum allowable instructions");
else {
step = 1;
artificialHalt(curaddr,PcodeOp::badinstruction);
data.warning("Too many instructions -- Truncating flow here",curaddr);
if (!hasTooManyInstructions()) {
flags |= toomanyinstructions_present;
data.warningHeader("Exceeded maximum allowable instructions: Some flow is truncated");
}
}
}
insn_count += 1;
if (obank.empty())
emptyflag = true;
else {
emptyflag = false;
oiter = obank.endDead();
--oiter;
}
if (flowoverride_present)
flowoverride = data.getOverride().getFlowOverride(curaddr);
else
flowoverride = Override::NONE;
try {
step = glb->translate->oneInstruction(emitter,curaddr); }
catch(UnimplError &err) { if ((flags & ignore_unimplemented)!=0) {
step = err.instruction_length;
if (!hasUnimplemented()) {
flags |= unimplemented_present;
data.warningHeader("Control flow ignored unimplemented instructions");
}
}
else if ((flags & error_unimplemented)!=0)
throw err; else {
step = 1; artificialHalt(curaddr,PcodeOp::unimplemented);
data.warning("Unimplemented instruction - Truncating control flow here",curaddr);
if (!hasUnimplemented()) {
flags |= unimplemented_present;
data.warningHeader("Control flow encountered unimplemented instructions");
}
}
}
catch(BadDataError &err) {
if ((flags & error_unimplemented)!=0)
throw err; else {
step = 1; artificialHalt(curaddr,PcodeOp::badinstruction);
data.warning("Bad instruction - Truncating control flow here",curaddr);
if (!hasBadData()) {
flags |= baddata_present;
data.warningHeader("Control flow encountered bad instruction data");
}
}
}
VisitStat &stat(visited[curaddr]); stat.size = step;
if (curaddr < minaddr) minaddr = curaddr;
if (maxaddr < curaddr+step) maxaddr = curaddr+step;
if (emptyflag) oiter = obank.beginDead();
else
++oiter;
if (oiter != obank.endDead()) {
stat.seqnum = (*oiter)->getSeqNum();
data.opMarkStartInstruction(*oiter); if (flowoverride != Override::NONE)
data.overrideFlow(curaddr,flowoverride);
xrefControlFlow(oiter,startbasic,isfallthru,(FuncCallSpecs *)0);
}
if (isfallthru)
addrlist.push_back(curaddr+step);
return isfallthru;
}
bool FlowInfo::setFallthruBound(Address &bound)
{
map<Address,VisitStat>::const_iterator iter;
const Address &addr( addrlist.back() );
iter = visited.upper_bound(addr); if (iter!=visited.begin()) {
--iter; if (addr == (*iter).first) { PcodeOp *op = target(addr); data.opMarkStartBasic(op); addrlist.pop_back(); return false;
}
if (addr < (*iter).first + (*iter).second.size)
reinterpreted(addr);
++iter;
}
if (iter!=visited.end()) bound = (*iter).first;
else
bound = eaddr;
return true;
}
void FlowInfo::handleOutOfBounds(const Address &fromaddr,const Address &toaddr)
{
if ((flags&ignore_outofbounds)==0) { ostringstream errmsg;
errmsg << "Function flow out of bounds: ";
errmsg << fromaddr.getShortcut();
fromaddr.printRaw(errmsg);
errmsg << " flows to ";
errmsg << toaddr.getShortcut();
toaddr.printRaw(errmsg);
if ((flags&error_outofbounds)==0) {
data.warning(errmsg.str(),toaddr);
if (!hasOutOfBounds()) {
flags |= outofbounds_present;
data.warningHeader("Function flows out of bounds");
}
}
else
throw LowlevelError(errmsg.str());
}
}
void FlowInfo::fallthru(void)
{
Address bound;
if (!setFallthruBound(bound)) return;
Address curaddr;
bool startbasic = true;
bool fallthruflag;
for(;;) {
curaddr = addrlist.back();
addrlist.pop_back();
fallthruflag = processInstruction(curaddr,startbasic);
if (!fallthruflag) break;
if (addrlist.empty()) break;
if (bound <= addrlist.back()) {
if (bound == eaddr) {
handleOutOfBounds(eaddr,addrlist.back());
unprocessed.push_back(addrlist.back());
addrlist.pop_back();
return;
}
if (bound == addrlist.back()) { if (startbasic) {
PcodeOp *op = target(addrlist.back());
data.opMarkStartBasic(op);
}
addrlist.pop_back();
break;
}
if (!setFallthruBound(bound)) return; }
}
}
PcodeOp *FlowInfo::artificialHalt(const Address &addr,uint4 flag)
{
PcodeOp *haltop = data.newOp(1,addr);
data.opSetOpcode(haltop,CPUI_RETURN);
data.opSetInput(haltop,data.newConstant(4,1),0);
if (flag != 0)
data.opMarkHalt(haltop,flag); return haltop;
}
void FlowInfo::reinterpreted(const Address &addr)
{
map<Address,VisitStat>::const_iterator iter;
iter = visited.upper_bound(addr);
if (iter==visited.begin()) return; --iter;
const Address &addr2( (*iter).first );
ostringstream s;
s << "Instruction at (" << addr.getSpace()->getName() << ',';
addr.printRaw(s);
s << ") overlaps instruction at (" << addr2.getSpace()->getName() << ',';
addr2.printRaw(s);
s << ')' << endl;
if ((flags & error_reinterpreted)!=0)
throw LowlevelError(s.str());
if ((flags & reinterpreted_present)==0) {
flags |= reinterpreted_present;
data.warningHeader(s.str());
}
}
bool FlowInfo::checkForFlowModification(FuncCallSpecs &fspecs)
{
if (fspecs.isInline())
injectlist.push_back(fspecs.getOp());
if (fspecs.isNoReturn()) {
PcodeOp *op = fspecs.getOp();
PcodeOp *haltop = artificialHalt(op->getAddr(),PcodeOp::noreturn);
data.opDeadInsertAfter(haltop,op);
if (!fspecs.isInline())
data.warning("Subroutine does not return",op->getAddr());
return true;
}
return false;
}
void FlowInfo::queryCall(FuncCallSpecs &fspecs)
{
if (!fspecs.getEntryAddress().isInvalid()) { Funcdata *otherfunc = data.getScopeLocal()->getParent()->queryFunction( fspecs.getEntryAddress() );
if (otherfunc != (Funcdata *)0) {
fspecs.setFuncdata(otherfunc); if (!fspecs.hasModel()) { fspecs.copyFlowEffects(otherfunc->getFuncProto()); }
}
}
}
bool FlowInfo::setupCallSpecs(PcodeOp *op,FuncCallSpecs *fc)
{
FuncCallSpecs *res;
res = new FuncCallSpecs(op);
data.opSetInput(op,data.newVarnodeCallSpecs(res),0);
qlst.push_back(res);
data.getOverride().applyPrototype(data,*res);
queryCall(*res);
if (fc != (FuncCallSpecs *)0) { if (fc->getEntryAddress() == res->getEntryAddress())
res->cancelInjectId(); }
return checkForFlowModification(*res);
}
bool FlowInfo::setupCallindSpecs(PcodeOp *op,bool tryoverride,FuncCallSpecs *fc)
{
FuncCallSpecs *res;
res = new FuncCallSpecs(op);
qlst.push_back(res);
if (tryoverride) {
data.getOverride().applyIndirect(data,*res);
data.getOverride().applyPrototype(data,*res);
}
queryCall(*res);
if (fc != (FuncCallSpecs *)0) {
if (fc->getEntryAddress() == res->getEntryAddress()) {
res->cancelInjectId();
res->setAddress(Address()); }
}
if (!res->getEntryAddress().isInvalid()) { data.opSetOpcode(op,CPUI_CALL); data.opSetInput(op,data.newVarnodeCallSpecs(res),0);
}
return checkForFlowModification(*res);
}
void FlowInfo::truncateIndirectJump(PcodeOp *op,int4 failuremode)
{
data.opSetOpcode(op,CPUI_CALLIND); bool tryoverride = (failuremode == 2);
setupCallindSpecs(op,tryoverride,(FuncCallSpecs *)0);
data.getCallSpecs(op)->setBadJumpTable(true);
PcodeOp *truncop = artificialHalt(op->getAddr(),0);
data.opDeadInsertAfter(truncop,op);
data.warning("Treating indirect jump as call",op->getAddr());
}
bool FlowInfo::isInArray(vector<PcodeOp *> &array,PcodeOp *op)
{
for(int4 i=0;i<array.size();++i) {
if (array[i] == op) return true;
}
return false;
}
void FlowInfo::generateOps(void)
{
vector<PcodeOp *> notreached; int4 notreachcnt = 0;
clearProperties();
addrlist.push_back(data.getAddress());
while(!addrlist.empty()) fallthru();
if (hasInject())
injectPcode();
do {
bool collapsed_jumptable = false;
while(!tablelist.empty()) { PcodeOp *op = tablelist.back();
tablelist.pop_back();
int4 failuremode;
JumpTable *jt = data.recoverJumpTable(op,this,failuremode); if (jt == (JumpTable *)0) { if ((failuremode == 3) && (!tablelist.empty()) && (!isInArray(notreached,op))) {
notreached.push_back(op); }
else if (!isFlowForInline()) truncateIndirectJump(op,failuremode); }
else {
int4 num = jt->numEntries();
for(int4 i=0;i<num;++i)
newAddress(op,jt->getAddressByIndex(i));
if (jt->isPossibleMultistage())
collapsed_jumptable = true;
while(!addrlist.empty()) fallthru();
}
}
checkContainedCall(); if (collapsed_jumptable)
checkMultistageJumptables();
while(notreachcnt < notreached.size()) {
tablelist.push_back(notreached[notreachcnt]);
notreachcnt += 1;
}
if (hasInject())
injectPcode();
} while(!tablelist.empty()); }
void FlowInfo::generateBlocks(void)
{
fillinBranchStubs();
collectEdges();
splitBasic(); connectBasic(); if (bblocks.getSize()!=0) {
FlowBlock *startblock = bblocks.getBlock(0);
if (startblock->sizeIn() != 0) {
BlockBasic *newfront = bblocks.newBlockBasic(&data);
bblocks.addEdge(newfront,startblock);
bblocks.setStartBlock(newfront);
data.setBasicBlockRange(newfront, data.getAddress(), data.getAddress());
}
}
if (hasPossibleUnreachable())
data.removeUnreachableBlocks(false,true);
}
void FlowInfo::findUnprocessed(void)
{
vector<Address>::iterator iter;
for(iter=addrlist.begin();iter!=addrlist.end();++iter) {
if (seenInstruction(*iter)) {
PcodeOp *op = target(*iter);
data.opMarkStartBasic(op);
}
else
unprocessed.push_back(*iter);
}
}
void FlowInfo::dedupUnprocessed(void)
{
if (unprocessed.empty()) return;
sort(unprocessed.begin(),unprocessed.end());
vector<Address>::iterator iter1,iter2;
iter1 = unprocessed.begin();
Address lastaddr = *iter1++;
iter2 = iter1;
while(iter1 != unprocessed.end()) {
if (*iter1==lastaddr)
iter1++;
else {
lastaddr = *iter1++;
*iter2++ = lastaddr;
}
}
unprocessed.erase(iter2,unprocessed.end());
}
void FlowInfo::fillinBranchStubs(void)
{
vector<Address>::iterator iter;
findUnprocessed();
dedupUnprocessed();
for(iter=unprocessed.begin();iter!=unprocessed.end();++iter) {
PcodeOp *op = artificialHalt(*iter,PcodeOp::missing);
data.opMarkStartBasic(op);
data.opMarkStartInstruction(op);
}
}
void FlowInfo::collectEdges(void)
{
list<PcodeOp *>::const_iterator iter,iterend,iter1,iter2;
PcodeOp *op,*targ_op;
JumpTable *jt;
bool nextstart;
int4 i,num;
if (bblocks.getSize() != 0)
throw RecovError("Basic blocks already calculated\n");
iter = obank.beginDead();
iterend = obank.endDead();
while(iter!=iterend) {
op = *iter++;
if (iter==iterend)
nextstart = true;
else
nextstart = (*iter)->isBlockStart();
switch(op->code()) {
case CPUI_BRANCH:
targ_op = branchTarget(op);
block_edge1.push_back(op);
block_edge2.push_back(targ_op);
break;
case CPUI_BRANCHIND:
jt = data.findJumpTable(op);
if (jt == (JumpTable *)0) break;
num = jt->numEntries();
for(i=0;i<num;++i) {
targ_op = target(jt->getAddressByIndex(i));
if (targ_op->isMark()) continue; targ_op->setMark();
block_edge1.push_back(op);
block_edge2.push_back(targ_op);
}
iter1 = block_edge1.end(); iter2 = block_edge2.end();
while(iter1 != block_edge1.begin()) {
--iter1;
--iter2;
if ((*iter1)==op)
(*iter2)->clearMark();
else
break;
}
break;
case CPUI_RETURN:
break;
case CPUI_CBRANCH:
targ_op = fallthruOp(op); block_edge1.push_back(op);
block_edge2.push_back(targ_op);
targ_op = branchTarget(op);
block_edge1.push_back(op);
block_edge2.push_back(targ_op);
break;
default:
if (nextstart) { targ_op = fallthruOp(op);
block_edge1.push_back(op);
block_edge2.push_back(targ_op);
}
break;
}
}
}
void FlowInfo::splitBasic(void)
{
PcodeOp *op;
BlockBasic *cur;
list<PcodeOp *>::const_iterator iter,iterend;
iter = obank.beginDead();
iterend = obank.endDead();
if (iter == iterend) return;
op = *iter++;
if (!op->isBlockStart())
throw LowlevelError("First op not marked as entry point");
cur = bblocks.newBlockBasic(&data);
data.opInsert(op,cur,cur->endOp());
bblocks.setStartBlock(cur);
Address start = op->getAddr();
Address stop = start;
while(iter != iterend) {
op = *iter++;
if (op->isBlockStart()) {
data.setBasicBlockRange(cur, start, stop);
cur = bblocks.newBlockBasic(&data); start = op->getSeqNum().getAddr();
stop = start;
}
else {
const Address &nextAddr( op->getAddr() );
if (stop < nextAddr)
stop = nextAddr;
}
data.opInsert(op,cur,cur->endOp());
}
data.setBasicBlockRange(cur, start, stop);
}
void FlowInfo::connectBasic(void)
{
PcodeOp *op,*targ_op;
BlockBasic *bs,*targ_bs;
list<PcodeOp *>::const_iterator iter,iter2;
iter = block_edge1.begin();
iter2 = block_edge2.begin();
while(iter != block_edge1.end()) {
op = *iter++;
targ_op = *iter2++;
bs = op->getParent();
targ_bs = targ_op->getParent();
bblocks.addEdge(bs,targ_bs);
}
}
void FlowInfo::forwardRecursion(const FlowInfo &op2)
{
inline_recursion = op2.inline_recursion;
inline_head = op2.inline_head;
}
void FlowInfo::xrefInlinedBranch(PcodeOp *op)
{
if (op->code() == CPUI_CALL)
setupCallSpecs(op,(FuncCallSpecs *)0);
else if (op->code() == CPUI_CALLIND)
setupCallindSpecs(op,true,(FuncCallSpecs *)0);
else if (op->code() == CPUI_BRANCHIND) {
JumpTable *jt = data.linkJumpTable(op);
if (jt == (JumpTable *)0)
tablelist.push_back(op); }
}
void FlowInfo::inlineClone(const FlowInfo &inlineflow,const Address &retaddr)
{
list<PcodeOp *>::const_iterator iter;
for(iter=inlineflow.data.beginOpDead();iter!=inlineflow.data.endOpDead();++iter) {
PcodeOp *op = *iter;
PcodeOp *cloneop;
if ((op->code() == CPUI_RETURN)&&(!retaddr.isInvalid())) {
cloneop = data.newOp(1,op->getSeqNum());
data.opSetOpcode(cloneop,CPUI_BRANCH);
Varnode *vn = data.newCodeRef(retaddr);
data.opSetInput(cloneop,vn,0);
}
else
cloneop = data.cloneOp(op,op->getSeqNum());
if (cloneop->isCallOrBranch())
xrefInlinedBranch(cloneop);
}
unprocessed.insert(unprocessed.end(),inlineflow.unprocessed.begin(),
inlineflow.unprocessed.end());
addrlist.insert(addrlist.end(),inlineflow.addrlist.begin(),
inlineflow.addrlist.end());
visited.insert(inlineflow.visited.begin(),inlineflow.visited.end());
}
void FlowInfo::inlineEZClone(const FlowInfo &inlineflow,const Address &calladdr)
{
list<PcodeOp *>::const_iterator iter;
for(iter=inlineflow.data.beginOpDead();iter!=inlineflow.data.endOpDead();++iter) {
PcodeOp *op = *iter;
if (op->code() == CPUI_RETURN) break;
SeqNum myseq(calladdr,op->getSeqNum().getTime());
data.cloneOp(op,myseq);
}
}
bool FlowInfo::testHardInlineRestrictions(Funcdata *inlinefd,PcodeOp *op,Address &retaddr)
{
if (inline_recursion->find( inlinefd->getAddress() ) != inline_recursion->end()) {
inline_head->warning("Could not inline here",op->getAddr());
return false;
}
if (!inlinefd->getFuncProto().isNoReturn()) {
list<PcodeOp *>::iterator iter = op->getInsertIter();
++iter;
if (iter == obank.endDead()) {
inline_head->warning("No fallthrough prevents inlining here",op->getAddr());
return false;
}
PcodeOp *nextop = *iter;
retaddr = nextop->getAddr();
if (op->getAddr() == retaddr) {
inline_head->warning("Return address prevents inlining here",op->getAddr());
return false;
}
data.opMarkStartBasic(nextop);
}
inline_recursion->insert(inlinefd->getAddress());
return true;
}
bool FlowInfo::checkEZModel(void) const
{
list<PcodeOp *>::const_iterator iter = obank.beginDead();
while(iter != obank.endDead()) {
PcodeOp *op = *iter;
if (op->isCallOrBranch()) return false;
++iter;
}
return true;
}
void FlowInfo::doInjection(InjectPayload *payload,InjectContext &icontext,PcodeOp *op,FuncCallSpecs *fc)
{
list<PcodeOp *>::const_iterator iter = obank.endDead();
--iter;
payload->inject(icontext,emitter);
bool startbasic = op->isBlockStart();
++iter; PcodeOp *firstop = *iter;
bool isfallthru = true;
PcodeOp *lastop = xrefControlFlow(iter,startbasic,isfallthru,fc);
if (startbasic) { iter = op->getInsertIter();
++iter; if (iter != obank.endDead())
data.opMarkStartBasic(*iter); }
if (payload->isIncidentalCopy())
obank.markIncidentalCopy(firstop, lastop);
obank.moveSequenceDead(firstop,lastop,op);
map<Address,VisitStat>::iterator viter = visited.find(op->getAddr());
if (viter != visited.end()) { if ((*viter).second.seqnum == op->getSeqNum()) (*viter).second.seqnum = firstop->getSeqNum(); }
data.opDestroyRaw(op);
}
void FlowInfo::injectUserOp(PcodeOp *op)
{
InjectedUserOp *userop = (InjectedUserOp *)glb->userops.getOp((int4)op->getIn(0)->getOffset());
InjectPayload *payload = glb->pcodeinjectlib->getPayload(userop->getInjectId());
InjectContext &icontext(glb->pcodeinjectlib->getCachedContext());
icontext.clear();
icontext.baseaddr = op->getAddr();
icontext.nextaddr = icontext.baseaddr;
for(int4 i=1;i<op->numInput();++i) { Varnode *vn = op->getIn(i);
icontext.inputlist.emplace_back();
icontext.inputlist.back().space = vn->getSpace();
icontext.inputlist.back().offset = vn->getOffset();
icontext.inputlist.back().size = vn->getSize();
}
Varnode *outvn = op->getOut();
if (outvn != (Varnode *)0) {
icontext.output.emplace_back();
icontext.output.back().space = outvn->getSpace();
icontext.output.back().offset = outvn->getOffset();
icontext.output.back().size = outvn->getSize();
}
doInjection(payload,icontext,op,(FuncCallSpecs *)0);
}
bool FlowInfo::inlineSubFunction(FuncCallSpecs *fc)
{
Funcdata *fd = fc->getFuncdata();
if (fd == (Funcdata *)0) return false;
PcodeOp *op = fc->getOp();
Address retaddr;
if (!data.inlineFlow( fd, *this, op))
return false;
setPossibleUnreachable();
return true;
}
bool FlowInfo::injectSubFunction(FuncCallSpecs *fc)
{
PcodeOp *op = fc->getOp();
InjectContext &icontext(glb->pcodeinjectlib->getCachedContext());
icontext.clear();
icontext.baseaddr = op->getAddr();
icontext.nextaddr = icontext.baseaddr;
icontext.calladdr = fc->getEntryAddress();
InjectPayload *payload = glb->pcodeinjectlib->getPayload(fc->getInjectId());
doInjection(payload,icontext,op,fc);
if (payload->getParamShift() != 0)
qlst.back()->setParamshift(payload->getParamShift());
return true; }
void FlowInfo::deleteCallSpec(FuncCallSpecs *fc)
{
int4 i;
for(i=0;i<qlst.size();++i)
if (qlst[i] == fc) break;
if (i == qlst.size())
throw LowlevelError("Misplaced callspec");
delete fc;
qlst.erase(qlst.begin() + i);
}
void FlowInfo::injectPcode(void)
{
if (inline_head == (Funcdata *)0) {
inline_head = &data; inline_recursion = &inline_base;
inline_recursion->insert(data.getAddress()); }
else {
inline_recursion->insert(data.getAddress()); }
for(int4 i=0;i<injectlist.size();++i) {
PcodeOp *op = injectlist[i];
if (op == (PcodeOp *)0) continue;
injectlist[i] = (PcodeOp *)0; if (op->code() == CPUI_CALLOTHER) {
injectUserOp(op);
}
else { FuncCallSpecs *fc = FuncCallSpecs::getFspecFromConst(op->getIn(0)->getAddr());
if (fc->isInline()) {
if (fc->getInjectId() >= 0) {
if (injectSubFunction(fc)) {
data.warningHeader("Function: "+fc->getName()+" replaced with injection: "+
glb->pcodeinjectlib->getCallFixupName(fc->getInjectId()));
deleteCallSpec(fc);
}
}
else if (inlineSubFunction(fc)) {
data.warningHeader("Inlined function: "+fc->getName());
deleteCallSpec(fc);
}
}
}
}
injectlist.clear();
}
void FlowInfo::checkContainedCall(void)
{
vector<FuncCallSpecs *>::iterator iter;
for(iter=qlst.begin();iter!=qlst.end();++iter) {
FuncCallSpecs *fc = *iter;
Funcdata *fd = fc->getFuncdata();
if (fd != (Funcdata *)0) continue;
PcodeOp *op = fc->getOp();
if (op->code() != CPUI_CALL) continue;
const Address &addr( fc->getEntryAddress() );
map<Address,VisitStat>::const_iterator miter;
miter = visited.upper_bound(addr);
if (miter == visited.begin()) continue;
--miter;
if ((*miter).first + (*miter).second.size <= addr)
continue;
if ((*miter).first == addr) {
ostringstream s;
s << "Possible PIC construction at ";
op->getAddr().printRaw(s);
s << ": Changing call to branch";
data.warningHeader(s.str());
data.opSetOpcode(op,CPUI_BRANCH);
PcodeOp *targ = target(addr);
data.opMarkStartBasic(targ);
list<PcodeOp *>::const_iterator oiter = op->getInsertIter();
++oiter;
if (oiter != obank.endDead())
data.opMarkStartBasic(*oiter);
data.opSetInput(op,data.newCodeRef(addr),0);
iter = qlst.erase(iter); delete fc;
if (iter == qlst.end()) break;
}
else {
data.warning("Call to offcut address within same function",op->getAddr());
}
}
}
void FlowInfo::checkMultistageJumptables(void)
{
int4 num = data.numJumpTables();
for(int4 i=0;i<num;++i) {
JumpTable *jt = data.getJumpTable(i);
if (jt->checkForMultistage(&data))
tablelist.push_back(jt->getIndirectOp());
}
}