#include "funcdata.hh"
#include "flow.hh"
void Funcdata::printBlockTree(ostream &s) const
{
if (sblocks.getSize() != 0)
sblocks.printTree(s,0);
}
void Funcdata::clearBlocks(void)
{
bblocks.clear();
sblocks.clear();
}
void Funcdata::clearJumpTables(void)
{
vector<JumpTable *> remain;
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
JumpTable *jt = *iter;
if (jt->isOverride()) {
jt->clear(); remain.push_back(jt); }
else
delete jt;
}
jumpvec = remain;
}
void Funcdata::removeJumpTable(JumpTable *jt)
{
vector<JumpTable *> remain;
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter)
if ((*iter) != jt)
remain.push_back(*iter);
PcodeOp *op = jt->getIndirectOp();
delete jt;
if (op != (PcodeOp *)0)
op->getParent()->clearFlag(FlowBlock::f_switch_out);
jumpvec = remain;
}
void Funcdata::pushMultiequals(BlockBasic *bb)
{
BlockBasic *outblock;
PcodeOp *origop,*replaceop;
Varnode *origvn,*replacevn;
list<PcodeOp *>::iterator iter;
list<PcodeOp *>::const_iterator citer;
if (bb->sizeOut()==0) return;
if (bb->sizeOut()>1)
warningHeader("push_multiequal on block with multiple outputs");
outblock = (BlockBasic *) bb->getOut(0); int4 outblock_ind = bb->getOutRevIndex(0);
for(iter=bb->beginOp();iter!=bb->endOp();++iter) {
origop = *iter;
if (origop->code() != CPUI_MULTIEQUAL) continue;
origvn = origop->getOut();
if (origvn->hasNoDescend()) continue;
bool needreplace = false;
bool neednewunique = false;
for(citer=origvn->beginDescend();citer!=origvn->endDescend();++citer) {
PcodeOp *op = *citer;
if ((op->code()==CPUI_MULTIEQUAL)&&(op->getParent()==outblock)) {
bool deadEdge = true; for(int4 i=0;i<op->numInput();++i) {
if (i == outblock_ind) continue; if (op->getIn(i) == origvn) { deadEdge = false;
break;
}
}
if (deadEdge) {
if ((origvn->getAddr() == op->getOut()->getAddr())&&origvn->isAddrTied())
neednewunique = true;
continue;
}
}
needreplace = true;
break;
}
if (!needreplace) continue;
vector<Varnode *> branches;
if (neednewunique)
replacevn = newUnique(origvn->getSize());
else
replacevn = newVarnode(origvn->getSize(),origvn->getAddr());
for(int4 i=0;i<outblock->sizeIn();++i) {
if (outblock->getIn(i) == bb)
branches.push_back(origvn);
else
branches.push_back( replacevn );
}
replaceop = newOp(branches.size(),outblock->getStart());
opSetOpcode(replaceop,CPUI_MULTIEQUAL);
opSetOutput(replaceop,replacevn);
opSetAllInput(replaceop,branches);
opInsertBegin(replaceop,outblock);
int4 i;
list<PcodeOp *>::iterator titer = origvn->descend.begin();
while(titer != origvn->descend.end()) {
PcodeOp *op = *titer++;
i = op->getSlot(origvn);
if ((op->code()==CPUI_MULTIEQUAL)&&(op->getParent()==outblock)&&(i==outblock_ind))
continue;
opSetInput(op,replacevn,i);
}
}
}
void Funcdata::opZeroMulti(PcodeOp *op)
{
if (op->numInput()==0) { opInsertInput(op,newVarnode(op->getOut()->getSize(),op->getOut()->getAddr()),0);
setInputVarnode(op->getIn(0)); opSetOpcode(op,CPUI_COPY);
}
else if (op->numInput()==1)
opSetOpcode(op,CPUI_COPY);
}
void Funcdata::branchRemoveInternal(BlockBasic *bb,int4 num)
{
BlockBasic *bbout;
list<PcodeOp *>::iterator iter;
PcodeOp *op;
int4 blocknum;
if (bb->sizeOut() == 2) opDestroy(bb->lastOp());
bbout = (BlockBasic *) bb->getOut(num);
blocknum = bbout->getInIndex(bb);
bblocks.removeEdge(bb,bbout); for(iter=bbout->beginOp();iter!=bbout->endOp();++iter) {
op = *iter;
if (op->code() != CPUI_MULTIEQUAL) continue;
opRemoveInput(op,blocknum);
opZeroMulti(op);
}
}
void Funcdata::removeBranch(BlockBasic *bb,int4 num)
{
branchRemoveInternal(bb,num);
structureReset();
}
bool Funcdata::descendantsOutside(Varnode *vn)
{
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter)
if (!(*iter)->getParent()->isDead()) return true;
return false;
}
void Funcdata::blockRemoveInternal(BlockBasic *bb,bool unreachable)
{
BlockBasic *bbout;
Varnode *deadvn;
PcodeOp *op,*deadop;
list<PcodeOp *>::iterator iter;
int4 i,j,blocknum;
bool desc_warning;
op = bb->lastOp();
if ((op != (PcodeOp *)0)&&(op->code() == CPUI_BRANCHIND)) {
JumpTable *jt = findJumpTable(op);
if (jt != (JumpTable *)0)
removeJumpTable(jt);
}
if (!unreachable) {
pushMultiequals(bb);
for(i=0;i<bb->sizeOut();++i) {
bbout = (BlockBasic *) bb->getOut(i);
if (bbout->isDead()) continue;
blocknum = bbout->getInIndex(bb); for(iter=bbout->beginOp();iter!=bbout->endOp();++iter) {
op = *iter;
if (op->code() != CPUI_MULTIEQUAL) continue;
deadvn = op->getIn(blocknum);
opRemoveInput(op,blocknum); deadop = deadvn->getDef();
if ((deadvn->isWritten())&&(deadop->code()==CPUI_MULTIEQUAL)&&(deadop->getParent()==bb)) {
for(j=0;j<bb->sizeIn();++j)
opInsertInput(op,deadop->getIn(j),op->numInput());
}
else {
for(j=0;j<bb->sizeIn();++j)
opInsertInput(op,deadvn,op->numInput()); }
opZeroMulti(op);
}
}
}
bblocks.removeFromFlow(bb);
desc_warning = false;
iter = bb->beginOp();
while(iter!=bb->endOp()) { op = *iter;
if (op->isAssignment()) { deadvn = op->getOut();
if (unreachable) {
bool undef = descend2Undef(deadvn);
if (undef&&(!desc_warning)) { warningHeader("Creating undefined varnodes in (possibly) reachable block");
desc_warning = true; }
}
if (descendantsOutside(deadvn)) throw LowlevelError("Deleting op with descendants\n");
}
if (op->isCall())
deleteCallSpecs(op);
iter++; opDestroy(op); }
bblocks.removeBlock(bb); }
void Funcdata::removeDoNothingBlock(BlockBasic *bb)
{
if (bb->sizeOut()>1)
throw LowlevelError("Cannot delete a reachable block unless it has 1 out or less");
bb->setDead();
blockRemoveInternal(bb,false);
structureReset(); }
bool Funcdata::removeUnreachableBlocks(bool issuewarning,bool checkexistence)
{
vector<FlowBlock *> list;
uint4 i;
if (checkexistence) { for(i=0;i<bblocks.getSize();++i) {
FlowBlock *blk = bblocks.getBlock(i);
if (blk->isEntryPoint()) continue; if (blk->getImmedDom() == (FlowBlock *)0) break;
}
if (i==bblocks.getSize()) return false;
}
else if (!hasUnreachableBlocks()) return false;
for(i=0;i<bblocks.getSize();++i) if (bblocks.getBlock(i)->isEntryPoint()) break;
bblocks.collectReachable(list,bblocks.getBlock(i),true);
for(i=0;i<list.size();++i) {
list[i]->setDead();
if (issuewarning) {
ostringstream s;
BlockBasic *bb = (BlockBasic *)list[i];
s << "Removing unreachable block (";
s << bb->getStart().getSpace()->getName();
s << ',';
bb->getStart().printRaw(s);
s << ')';
warningHeader(s.str());
}
}
for(i=0;i<list.size();++i) {
BlockBasic *bb = (BlockBasic *)list[i];
while(bb->sizeOut() > 0)
branchRemoveInternal(bb,0);
}
for(i=0;i<list.size();++i) {
BlockBasic *bb = (BlockBasic *)list[i];
blockRemoveInternal(bb,true);
}
structureReset();
return true;
}
void Funcdata::pushBranch(BlockBasic *bb,int4 slot,BlockBasic *bbnew)
{
PcodeOp *cbranch = bb->lastOp();
if ((cbranch->code() != CPUI_CBRANCH)||(bb->sizeOut() != 2))
throw LowlevelError("Cannot push non-conditional edge");
PcodeOp *indop = bbnew->lastOp();
if (indop->code() != CPUI_BRANCHIND)
throw LowlevelError("Can only push branch into indirect jump");
opRemoveInput(cbranch,1); opSetOpcode(cbranch,CPUI_BRANCH);
bblocks.moveOutEdge(bb,slot,bbnew);
structureReset();
}
JumpTable *Funcdata::linkJumpTable(PcodeOp *op)
{
vector<JumpTable *>::iterator iter;
JumpTable *jt;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
jt = *iter;
if (jt->getOpAddress() == op->getAddr()) {
jt->setIndirectOp(op);
return jt;
}
}
return (JumpTable *)0;
}
JumpTable *Funcdata::findJumpTable(const PcodeOp *op) const
{
vector<JumpTable *>::const_iterator iter;
JumpTable *jt;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
jt = *iter;
if (jt->getOpAddress() == op->getAddr()) return jt;
}
return (JumpTable *)0;
}
JumpTable *Funcdata::installJumpTable(const Address &addr)
{
if (isProcStarted())
throw LowlevelError("Cannot install jumptable if flow is already traced");
for(int4 i=0;i<jumpvec.size();++i) {
JumpTable *jt = jumpvec[i];
if (jt->getOpAddress() == addr)
throw LowlevelError("Trying to install over existing jumptable");
}
JumpTable *newjt = new JumpTable(glb,addr);
jumpvec.push_back(newjt);
return newjt;
}
int4 Funcdata::stageJumpTable(JumpTable *jt,PcodeOp *op,FlowInfo *flow)
{
PcodeOp *partop = (PcodeOp *)0;
string oldactname;
ostringstream s1;
s1 << name << "@@jump@";
op->getAddr().printRaw(s1);
Funcdata partial(s1.str(),localmap->getParent(),baseaddr,(FunctionSymbol *)0);
partial.flags |= jumptablerecovery_on; partial.truncatedFlow(this,flow);
partop = partial.findOp(op->getSeqNum());
if ((partop==(PcodeOp *)0) ||
(partop->code() != CPUI_BRANCHIND)||
(partop->getAddr() != op->getAddr()))
throw LowlevelError("Error recovering jumptable: Bad partial clone");
oldactname = glb->allacts.getCurrentName(); glb->allacts.setCurrent("jumptable");
try {
#ifdef OPACTION_DEBUG
if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0)
(*jtcallback)(*this,partial); else {
#endif
glb->allacts.getCurrent()->reset( partial );
glb->allacts.getCurrent()->perform( partial ); #ifdef OPACTION_DEBUG
}
#endif
glb->allacts.setCurrent(oldactname); if (partop->isDead()) return 0; jt->setLoadCollect(flow->doesJumpRecord());
jt->setIndirectOp(partop);
if (jt->getStage()>0)
jt->recoverMultistage(&partial);
else
jt->recoverAddresses(&partial); }
catch(JumptableNotReachableError &err) {
glb->allacts.setCurrent(oldactname);
return 3;
}
catch(JumptableThunkError &err) {
glb->allacts.setCurrent(oldactname);
return 2;
}
catch(LowlevelError &err) {
glb->allacts.setCurrent(oldactname);
warning(err.explain,op->getAddr());
return 1;
}
return 0;
}
JumpTable *Funcdata::recoverJumpTable(PcodeOp *op,FlowInfo *flow,int4 &failuremode)
{
JumpTable *jt;
failuremode = 0;
jt = linkJumpTable(op); if (jt != (JumpTable *)0) {
if (!jt->isOverride()) {
if (jt->getStage() != 1)
return jt; }
failuremode = stageJumpTable(jt,op,flow); if (failuremode != 0)
return (JumpTable *)0;
jt->setIndirectOp(op); return jt;
}
if ((flags & jumptablerecovery_dont)!=0)
return (JumpTable *)0; JumpTable trialjt(glb);
failuremode = stageJumpTable(&trialjt,op,flow);
if (failuremode != 0)
return (JumpTable *)0;
jt = new JumpTable(&trialjt); jumpvec.push_back(jt);
jt->setIndirectOp(op); return jt;
}
void Funcdata::switchOverJumpTables(const FlowInfo &flow)
{
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter)
(*iter)->switchOver(flow);
}
void Funcdata::installSwitchDefaults(void)
{
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
JumpTable *jt = *iter;
PcodeOp *indop = jt->getIndirectOp();
BlockBasic *ind = indop->getParent();
if (jt->getDefaultBlock() != -1) ind->setDefaultSwitch(jt->getDefaultBlock());
}
}
void Funcdata::structureReset(void)
{
vector<JumpTable *>::iterator iter;
vector<FlowBlock *> rootlist;
flags &= ~blocks_unreachable; bblocks.structureLoops(rootlist);
bblocks.calcForwardDominator(rootlist);
if (rootlist.size() > 1)
flags |= blocks_unreachable;
vector<JumpTable *> alivejumps;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
JumpTable *jt = *iter;
PcodeOp *indop = jt->getIndirectOp();
if (indop->isDead()) {
warningHeader("Recovered jumptable eliminated as dead code");
delete jt;
continue;
}
alivejumps.push_back(jt);
}
jumpvec = alivejumps;
sblocks.clear(); heritage.forceRestructure();
}
bool Funcdata::forceGoto(const Address &pcop,const Address &pcdest)
{
FlowBlock *bl,*bl2;
PcodeOp *op,*op2;
int4 i,j;
for(i=0;i<bblocks.getSize();++i) {
bl = bblocks.getBlock(i);
op = bl->lastOp();
if (op == (PcodeOp *)0) continue;
if (op->getAddr() != pcop) continue; for(j=0;j<bl->sizeOut();++j) {
bl2 = bl->getOut(j);
op2 = bl2->lastOp();
if (op2 == (PcodeOp *)0) continue;
if (op2->getAddr() != pcdest) continue; bl->setGotoBranch(j);
return true;
}
}
return false;
}
BlockBasic *Funcdata::nodeJoinCreateBlock(BlockBasic *block1,BlockBasic *block2,
BlockBasic *exita,BlockBasic *exitb,
bool fora_block1ishigh,bool forb_block1ishigh,const Address &addr)
{
BlockBasic *newblock = bblocks.newBlockBasic(this);
newblock->setFlag(FlowBlock::f_joined_block);
newblock->setInitialRange(addr, addr);
FlowBlock *swapa,*swapb;
if (fora_block1ishigh) { bblocks.removeEdge(block1,exita);
swapa = block2;
}
else {
bblocks.removeEdge(block2,exita);
swapa = block1;
}
if (forb_block1ishigh) {
bblocks.removeEdge(block1,exitb);
swapb = block2;
}
else {
bblocks.removeEdge(block2,exitb);
swapb = block1;
}
bblocks.moveOutEdge(swapa,swapa->getOutIndex(exita),newblock);
bblocks.moveOutEdge(swapb,swapb->getOutIndex(exitb),newblock);
bblocks.addEdge(block1,newblock);
bblocks.addEdge(block2,newblock);
structureReset();
return newblock;
}
BlockBasic *Funcdata::nodeSplitBlockEdge(BlockBasic *b,int4 inedge)
{
FlowBlock *a = b->getIn(inedge);
BlockBasic *bprime;
bprime = bblocks.newBlockBasic(this);
bprime->setFlag(FlowBlock::f_duplicate_block);
bprime->copyRange(b);
bblocks.switchEdge(a,b,bprime);
for(int4 i=0;i<b->sizeOut();++i)
bblocks.addEdge(bprime,b->getOut(i));
return bprime;
}
PcodeOp *Funcdata::nodeSplitCloneOp(PcodeOp *op)
{
PcodeOp *dup;
if (op->isBranch()) {
if (op->code() != CPUI_BRANCH)
throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit");
return (PcodeOp *)0;
}
dup = newOp(op->numInput(),op->getAddr());
opSetOpcode(dup,op->code());
uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse |
PcodeOp::startmark);
dup->setFlag(fl);
return dup;
}
void Funcdata::nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop)
{
Varnode *opvn = op->getOut();
Varnode *newvn;
if (opvn == (Varnode *)0) return;
newvn = newVarnodeOut(opvn->getSize(),opvn->getAddr(),newop);
uint4 vflags = opvn->getFlags();
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy |
Varnode::readonly | Varnode::persist |
Varnode::addrtied | Varnode::addrforce);
newvn->setFlags(vflags);
}
void Funcdata::nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime)
{
PcodeOp *b_op,*prime_op;
list<PcodeOp *>::iterator iter;
for(iter=b->beginOp();iter!=b->endOp();++iter) {
b_op = *iter;
prime_op = nodeSplitCloneOp(b_op);
if (prime_op == (PcodeOp *)0) continue;
nodeSplitCloneVarnode(b_op,prime_op);
opInsertEnd(prime_op,bprime);
}
}
void Funcdata::nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge)
{
list<PcodeOp *>::iterator biter,piter;
PcodeOp *bop,*pop;
Varnode *bvn,*pvn;
map<PcodeOp *,PcodeOp *> btop; vector<PcodeOp *> pind; vector<PcodeOp *> bind; vector<int4> pslot;
biter = b->beginOp();
piter = bprime->beginOp();
while(piter != bprime->endOp()) {
bop = *biter;
pop = *piter;
btop[bop] = pop; if (bop->code() == CPUI_MULTIEQUAL) {
pop->setNumInputs(1); opSetOpcode(pop,CPUI_COPY);
opSetInput(pop,bop->getIn(inedge),0);
opRemoveInput(bop,inedge); if (bop->numInput() == 1)
opSetOpcode(bop,CPUI_COPY);
}
else if (bop->code() == CPUI_INDIRECT) {
throw LowlevelError("Can't handle INDIRECTs in nodesplit");
}
else if (bop->isCall()) {
throw LowlevelError("Can't handle CALLs in nodesplit");
}
else {
for(int4 i=0;i<pop->numInput();++i) {
bvn = bop->getIn(i);
if (bvn->isConstant())
pvn = newConstant(bvn->getSize(),bvn->getOffset());
else if (bvn->isAnnotation())
pvn = newCodeRef(bvn->getAddr());
else if (bvn->isFree())
throw LowlevelError("Can't handle free varnode in nodesplit");
else {
if (bvn->isWritten()) {
if (bvn->getDef()->getParent() == b) {
pind.push_back(pop); bind.push_back(bvn->getDef());
pslot.push_back(i);
continue;
}
else
pvn = bvn;
}
else
pvn = bvn;
}
opSetInput(pop,pvn,i);
}
}
++piter;
++biter;
}
for(int4 i=0;i<pind.size();++i) {
pop = pind[i];
PcodeOp *cross = btop[bind[i]];
opSetInput(pop,cross->getOut(),pslot[i]);
}
}
void Funcdata::nodeSplit(BlockBasic *b,int4 inedge)
{ if (b->sizeOut() != 0)
throw LowlevelError("Cannot (currently) nodesplit block with out flow");
if (b->sizeIn()<=1)
throw LowlevelError("Cannot nodesplit block with only 1 in edge");
for(int4 i=0;i<b->sizeIn();++i) {
if (b->getIn(i)->isMark())
throw LowlevelError("Cannot nodesplit block with redundant in edges");
b->setMark();
}
for(int4 i=0;i<b->sizeIn();++i)
b->clearMark();
BlockBasic *bprime = nodeSplitBlockEdge(b,inedge);
nodeSplitRawDuplicate(b,bprime);
nodeSplitInputPatch(b,bprime,inedge);
structureReset();
}
void Funcdata::removeFromFlowSplit(BlockBasic *bl,bool swap)
{
if (!bl->emptyOp())
throw LowlevelError("Can only split the flow for an empty block");
bblocks.removeFromFlowSplit(bl,swap);
bblocks.removeBlock(bl);
structureReset();
}
void Funcdata::switchEdge(FlowBlock *inblock,BlockBasic *outbefore,FlowBlock *outafter)
{
bblocks.switchEdge(inblock,outbefore,outafter);
structureReset();
}
void Funcdata::spliceBlockBasic(BlockBasic *bl)
{
BlockBasic *outbl = (BlockBasic *)0;
if (bl->sizeOut() == 1) {
outbl = (BlockBasic *)bl->getOut(0);
if (outbl->sizeIn() != 1)
outbl = (BlockBasic *)0;
}
if (outbl == (BlockBasic *)0)
throw LowlevelError("Cannot splice basic blocks");
if (!bl->op.empty()) {
PcodeOp *jumpop = bl->op.back();
if (jumpop->isBranch())
opDestroy(jumpop);
}
if (!outbl->op.empty()) {
PcodeOp *firstop = outbl->op.front();
if (firstop->code() == CPUI_MULTIEQUAL)
throw LowlevelError("Splicing block with MULTIEQUAL");
firstop->clearFlag(PcodeOp::startbasic);
list<PcodeOp *>::iterator iter;
for(iter=outbl->beginOp();iter!=outbl->endOp();++iter) {
PcodeOp *op = *iter;
op->setParent(bl); }
bl->op.splice(bl->op.end(),outbl->op,outbl->op.begin(),outbl->op.end());
bl->setOrder(); }
bl->mergeRange(outbl); bblocks.spliceBlock(bl);
structureReset();
}