#include "subflow.hh"
int4 SubvariableFlow::doesOrSet(PcodeOp *orop,uintb mask)
{
int4 index = (orop->getIn(1)->isConstant() ? 1 : 0);
if (!orop->getIn(index)->isConstant())
return -1;
uintb orval = orop->getIn(index)->getOffset();
if ((mask&(~orval))==(uintb)0) return index;
return -1;
}
int4 SubvariableFlow::doesAndClear(PcodeOp *andop,uintb mask)
{
int4 index = (andop->getIn(1)->isConstant() ? 1 : 0);
if (!andop->getIn(index)->isConstant())
return -1;
uintb andval = andop->getIn(index)->getOffset();
if ((mask&andval)==(uintb)0) return index;
return -1;
}
SubvariableFlow::ReplaceVarnode *SubvariableFlow::setReplacement(Varnode *vn,uintb mask,bool &inworklist)
{
ReplaceVarnode *res;
if (vn->isMark()) { map<Varnode *,ReplaceVarnode>::iterator iter;
iter = varmap.find(vn);
res = &(*iter).second;
inworklist = false;
if (res->mask != mask)
return (ReplaceVarnode *)0;
return res;
}
if (vn->isConstant()) {
inworklist = false;
if (sextrestrictions) { uintb cval = vn->getOffset();
uintb smallval = cval & mask; uintb sextval = sign_extend(smallval,flowsize,vn->getSize()); if (sextval != cval)
return (ReplaceVarnode *)0;
}
return addConstant((ReplaceOp *)0,mask,0,vn);
}
if (vn->isFree())
return (ReplaceVarnode *)0;
if (vn->isAddrForce() && (vn->getSize() != flowsize))
return (ReplaceVarnode *)0;
if (sextrestrictions) {
if (vn->getSize() != flowsize) {
if ((!aggressive)&& vn->isInput()) return (ReplaceVarnode *)0; if (vn->isPersist()) return (ReplaceVarnode *)0;
}
if (vn->isTypeLock()) {
if (vn->getType()->getSize() != flowsize)
return (ReplaceVarnode *)0;
}
}
else {
if (bitsize >= 8) { if ((!aggressive)&&((vn->getConsume()&~mask)!=0)) return (ReplaceVarnode *)0; if (vn->isTypeLock()) {
int4 sz = vn->getType()->getSize();
if (sz != flowsize)
return (ReplaceVarnode *)0;
}
}
if (vn->isInput()) { if (bitsize < 8) return (ReplaceVarnode *)0; if ((mask&1)==0) return (ReplaceVarnode *)0; }
}
res = & varmap[ vn ];
vn->setMark();
res->vn = vn;
res->replacement = (Varnode *)0;
res->mask = mask;
res->def = (ReplaceOp *)0;
inworklist = true;
if (vn->getSize() == flowsize) {
if (mask == calc_mask(flowsize)) {
inworklist = false;
res->replacement = vn;
}
else if (mask == 1) {
if ((vn->isWritten())&&(vn->getDef()->isBoolOutput())) {
inworklist = false;
res->replacement = vn;
}
}
}
return res;
}
SubvariableFlow::ReplaceOp *SubvariableFlow::createOp(OpCode opc,int4 numparam,ReplaceVarnode *outrvn)
{
if (outrvn->def != (ReplaceOp *)0)
return outrvn->def;
oplist.emplace_back();
ReplaceOp *rop = &oplist.back();
outrvn->def = rop;
rop->op = outrvn->vn->getDef();
rop->numparams = numparam;
rop->opc = opc;
rop->output = outrvn;
return rop;
}
SubvariableFlow::ReplaceOp *SubvariableFlow::createOpDown(OpCode opc,int4 numparam,PcodeOp *op,ReplaceVarnode *inrvn,int4 slot)
{
oplist.emplace_back();
ReplaceOp *rop = &oplist.back();
rop->op = op;
rop->opc = opc;
rop->numparams = numparam;
rop->output = (ReplaceVarnode *)0;
while(rop->input.size() <= slot)
rop->input.push_back((ReplaceVarnode *)0);
rop->input[slot] = inrvn;
return rop;
}
bool SubvariableFlow::tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
{
if (slot == 0) return false;
if (!aggressive) {
if ((rvn->vn->getConsume()&~rvn->mask)!=0) return false; }
FuncCallSpecs *fc = fd->getCallSpecs(op);
if (fc == (FuncCallSpecs *)0) return false;
if (fc->isInputActive()) return false; if (fc->isInputLocked() && (!fc->isDotdotdot())) return false;
patchlist.emplace_back();
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = rvn;
patchlist.back().slot = slot;
pullcount += 1; return true;
}
bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
{
if (slot == 0) return false; if (fd->getFuncProto().isOutputLocked()) return false;
if (!aggressive) {
if ((rvn->vn->getConsume()&~rvn->mask)!=0) return false; }
if (!returnsTraversed) {
list<PcodeOp *>::const_iterator iter,enditer;
iter = fd->beginOp(CPUI_RETURN);
enditer = fd->endOp(CPUI_RETURN);
while(iter != enditer) {
PcodeOp *retop = *iter;
++iter;
if (retop->getHaltType() != 0) continue; Varnode *retvn = retop->getIn(slot);
bool inworklist;
ReplaceVarnode *rep = setReplacement(retvn,rvn->mask,inworklist);
if (rep == (ReplaceVarnode *)0)
return false;
if (inworklist)
worklist.push_back(rep);
else if (retvn->isConstant() && retop != op) {
patchlist.emplace_back();
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = retop;
patchlist.back().in1 = rep;
patchlist.back().slot = slot;
pullcount += 1;
}
}
returnsTraversed = true;
}
patchlist.emplace_back();
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = rvn;
patchlist.back().slot = slot;
pullcount += 1; return true;
}
bool SubvariableFlow::tryCallReturnPush(PcodeOp *op,ReplaceVarnode *rvn)
{
if (!aggressive) {
if ((rvn->vn->getConsume()&~rvn->mask)!=0) return false; }
if ((rvn->mask & 1) == 0) return false; if (bitsize < 8) return false; FuncCallSpecs *fc = fd->getCallSpecs(op);
if (fc == (FuncCallSpecs *)0) return false;
if (fc->isOutputLocked()) return false;
if (fc->isOutputActive()) return false;
addPush(op,rvn);
return true;
}
bool SubvariableFlow::trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn)
{
if ((rvn->mask & 1) == 0) return false; if ((rvn->vn->getConsume()&~rvn->mask)!=0) return false; patchlist.emplace_back();
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = rvn;
patchlist.back().slot = 0;
pullcount += 1; return true;
}
bool SubvariableFlow::traceForward(ReplaceVarnode *rvn)
{
ReplaceOp *rop;
PcodeOp *op;
Varnode *outvn;
int4 slot;
int4 sa;
uintb newmask;
bool booldir;
int4 dcount = 0;
int4 hcount = 0;
int4 callcount = 0;
list<PcodeOp *>::const_iterator iter,enditer;
enditer = rvn->vn->endDescend();
for(iter = rvn->vn->beginDescend();iter != enditer;++iter) {
op = *iter;
outvn = op->getOut();
if ((outvn!=(Varnode *)0)&&outvn->isMark()&&!op->isCall())
continue;
dcount += 1; slot = op->getSlot(rvn->vn);
switch(op->code()) {
case CPUI_COPY:
case CPUI_MULTIEQUAL:
case CPUI_INT_NEGATE:
case CPUI_INT_XOR:
rop = createOpDown(op->code(),op->numInput(),op,rvn,slot);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_OR:
if (doesOrSet(op,rvn->mask)!=-1) break; rop = createOpDown(CPUI_INT_OR,2,op,rvn,slot);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_AND:
if ((op->getIn(1)->isConstant())&&(op->getIn(1)->getOffset() == rvn->mask)) {
if ((outvn->getSize() == flowsize)&&((rvn->mask & 1)!=0)) {
addTerminalPatch(op,rvn);
hcount += 1; break;
}
if ((!aggressive)&&((outvn->getConsume() & rvn->mask) != outvn->getConsume())) {
addSuggestedPatch(rvn,op,-1);
hcount += 1; break;
}
}
if (doesAndClear(op,rvn->mask)!=-1) break; rop = createOpDown(CPUI_INT_AND,2,op,rvn,slot);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_ZEXT:
case CPUI_INT_SEXT:
rop = createOpDown(CPUI_COPY,1,op,rvn,0);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_MULT:
if ((rvn->mask & 1)==0)
return false; sa = leastsigbit_set(op->getIn(1-slot)->getNZMask());
sa &= ~7; if (bitsize + sa > 8*rvn->vn->getSize()) return false;
rop = createOpDown(CPUI_INT_MULT,2,op,rvn,slot);
if (!createLink(rop,rvn->mask<<sa,-1,outvn)) return false;
hcount += 1;
break;
case CPUI_INT_ADD:
if ((rvn->mask & 1)==0)
return false; rop = createOpDown(CPUI_INT_ADD,2,op,rvn,slot);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_LEFT:
if (slot == 1) { if ((rvn->mask & 1)==0) return false; if (bitsize <8) return false;
addTerminalPatchSameOp(op,rvn,slot);
hcount += 1;
break;
}
if (!op->getIn(1)->isConstant()) return false; sa = (int4)op->getIn(1)->getOffset();
newmask = (rvn->mask << sa) & calc_mask( outvn->getSize() );
if (newmask == 0) break; if (rvn->mask != (newmask >> sa)) return false; if (((rvn->mask & 1)!=0)&&(sa + bitsize == 8*outvn->getSize())
&&(calc_mask(outvn->getSize()) == outvn->getConsume())) {
addSuggestedPatch(rvn,op,sa);
hcount += 1;
break;
}
rop = createOpDown(CPUI_COPY,1,op,rvn,0);
if (!createLink(rop,newmask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_RIGHT:
case CPUI_INT_SRIGHT:
if (slot == 1) { if ((rvn->mask & 1)==0) return false; if (bitsize <8) return false;
addTerminalPatchSameOp(op,rvn,slot);
hcount += 1;
break;
}
if (!op->getIn(1)->isConstant()) return false;
sa = (int4)op->getIn(1)->getOffset();
newmask = rvn->mask >> sa;
if (newmask == 0) {
if (op->code()==CPUI_INT_RIGHT) break; return false;
}
if (rvn->mask != (newmask << sa)) return false;
if ((outvn->getSize()==flowsize)&&((newmask&1)==1)&&
(op->getIn(0)->getNZMask()==rvn->mask)) {
addTerminalPatch(op,rvn);
hcount += 1; break;
}
if (((newmask&1)==1)&&(sa + bitsize == 8*outvn->getSize())
&&(calc_mask(outvn->getSize()) == outvn->getConsume())) {
addSuggestedPatch(rvn,op,0);
hcount += 1;
break;
}
rop = createOpDown(CPUI_COPY,1,op,rvn,0);
if (!createLink(rop,newmask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_SUBPIECE:
sa = (int4)op->getIn(1)->getOffset() * 8;
newmask = (rvn->mask >> sa) & calc_mask(outvn->getSize());
if (newmask == 0) break; if (rvn->mask != (newmask << sa)) { if (flowsize > ((sa/8) + outvn->getSize()) && (rvn->mask & 1) != 0) {
addTerminalPatchSameOp(op, rvn, 0);
hcount += 1;
break;
}
return false;
}
if (((newmask & 1)!=0)&&(outvn->getSize()==flowsize)) {
addTerminalPatch(op,rvn);
hcount += 1; break;
}
rop = createOpDown(CPUI_COPY,1,op,rvn,0);
if (!createLink(rop,newmask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_PIECE:
if (rvn->vn == op->getIn(0))
newmask = rvn->mask << (8*op->getIn(1)->getSize());
else
newmask = rvn->mask;
rop = createOpDown(CPUI_COPY,1,op,rvn,0);
if (!createLink(rop,newmask,-1,outvn)) return false;
hcount += 1; break;
case CPUI_INT_LESS:
case CPUI_INT_LESSEQUAL:
outvn = op->getIn(1-slot); if ((!aggressive)&&(((rvn->vn->getNZMask() | rvn->mask) != rvn->mask)))
return false; if (outvn->isConstant()) {
if ((rvn->mask | outvn->getOffset()) != rvn->mask)
return false; }
else {
if ((!aggressive)&&(((rvn->mask | outvn->getNZMask()) != rvn->mask))) return false;
}
if (!createCompareBridge(op,rvn,slot,outvn))
return false;
hcount += 1; break;
case CPUI_INT_NOTEQUAL:
case CPUI_INT_EQUAL:
outvn = op->getIn(1-slot); if (bitsize != 1) {
if ((!aggressive)&&(((rvn->vn->getNZMask() | rvn->mask) != rvn->mask)))
return false; if (outvn->isConstant()) {
if ((rvn->mask | outvn->getOffset()) != rvn->mask)
return false; }
else {
if ((!aggressive)&&(((rvn->mask | outvn->getNZMask()) != rvn->mask))) return false;
}
if (!createCompareBridge(op,rvn,slot,outvn))
return false;
}
else { if (!outvn->isConstant()) return false;
newmask = rvn->vn->getNZMask();
if (newmask != rvn->mask) return false;
if (op->getIn(1-slot)->getOffset() == (uintb)0)
booldir = true;
else if (op->getIn(1-slot)->getOffset() == newmask)
booldir = false;
else
return false;
if (op->code() == CPUI_INT_EQUAL)
booldir = !booldir;
if (booldir)
addTerminalPatch(op,rvn);
else {
rop = createOpDown(CPUI_BOOL_NEGATE,1,op,rvn,0);
createNewOut(rop,(uintb)1);
addTerminalPatch(op,rop->output);
}
}
hcount += 1; break;
case CPUI_CALL:
case CPUI_CALLIND:
callcount += 1;
if (callcount > 1)
slot = op->getRepeatSlot(rvn->vn, slot, iter);
if (!tryCallPull(op,rvn,slot)) return false;
hcount += 1; break;
case CPUI_RETURN:
if (!tryReturnPull(op,rvn,slot)) return false;
hcount += 1;
break;
case CPUI_BRANCHIND:
if (!trySwitchPull(op, rvn)) return false;
hcount += 1;
break;
case CPUI_BOOL_NEGATE:
case CPUI_BOOL_AND:
case CPUI_BOOL_OR:
case CPUI_BOOL_XOR:
if (bitsize != 1) return false;
if (rvn->mask != 1) return false;
addBooleanPatch(op,rvn,slot);
break;
case CPUI_CBRANCH:
if ((bitsize != 1)||(slot != 1)) return false;
if (rvn->mask != 1) return false;
addBooleanPatch(op,rvn,1);
hcount += 1;
break;
default:
return false;
}
}
if (dcount != hcount) {
if (rvn->vn->isInput()) return false;
}
return true;
}
bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
{
PcodeOp *op = rvn->vn->getDef();
if (op == (PcodeOp *)0) return true; int4 sa;
uintb newmask;
ReplaceOp *rop;
switch(op->code()) {
case CPUI_COPY:
case CPUI_MULTIEQUAL:
case CPUI_INT_NEGATE:
case CPUI_INT_XOR:
rop = createOp(op->code(),op->numInput(),rvn);
for(int4 i=0;i<op->numInput();++i)
if (!createLink(rop,rvn->mask,i,op->getIn(i))) return false;
return true;
case CPUI_INT_AND:
sa = doesAndClear(op,rvn->mask);
if (sa != -1) {
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,op->getIn(sa));
}
else {
rop = createOp(CPUI_INT_AND,2,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false;
if (!createLink(rop,rvn->mask,1,op->getIn(1))) return false;
}
return true;
case CPUI_INT_OR:
sa = doesOrSet(op,rvn->mask);
if (sa != -1) {
rop = createOp(CPUI_COPY,1,rvn);
addConstant(rop,rvn->mask,0,op->getIn(sa));
}
else {
rop = createOp(CPUI_INT_OR,2,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false;
if (!createLink(rop,rvn->mask,1,op->getIn(1))) return false;
}
return true;
case CPUI_INT_ZEXT:
case CPUI_INT_SEXT:
if ((rvn->mask & calc_mask(op->getIn(0)->getSize())) != rvn->mask) {
if ((rvn->mask & 1)!=0 && flowsize > op->getIn(0)->getSize()) {
addPush(op,rvn);
return true;
}
break; }
rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false;
return true;
case CPUI_INT_ADD:
if ((rvn->mask & 1)==0)
break; if (rvn->mask == (uintb)1)
rop = createOp(CPUI_INT_XOR,2,rvn); else
rop = createOp(CPUI_INT_ADD,2,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false;
if (!createLink(rop,rvn->mask,1,op->getIn(1))) return false;
return true;
case CPUI_INT_LEFT:
if (!op->getIn(1)->isConstant()) break; sa = (int4)op->getIn(1)->getOffset();
newmask = rvn->mask >> sa; if (newmask == 0) { rop = createOp(CPUI_COPY,1,rvn);
addNewConstant(rop,0,(uintb)0);
return true;
}
if ((newmask<<sa) != rvn->mask)
break; rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,newmask,0,op->getIn(0))) return false;
return true;
case CPUI_INT_RIGHT:
if (!op->getIn(1)->isConstant()) break; sa = (int4)op->getIn(1)->getOffset();
newmask = (rvn->mask << sa) & calc_mask(op->getIn(0)->getSize());
if (newmask == 0) { rop = createOp(CPUI_COPY,1,rvn);
addNewConstant(rop,0,(uintb)0);
return true;
}
if ((newmask>>sa) != rvn->mask)
break; rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,newmask,0,op->getIn(0))) return false;
return true;
case CPUI_INT_SRIGHT:
if (!op->getIn(1)->isConstant()) break; sa = (int4)op->getIn(1)->getOffset();
newmask = (rvn->mask << sa) & calc_mask(op->getIn(0)->getSize());
if ((newmask>>sa) != rvn->mask)
break; rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,newmask,0,op->getIn(0))) return false;
return true;
case CPUI_INT_MULT:
sa = leastsigbit_set(rvn->mask);
if (sa!=0) {
int4 sa2 = leastsigbit_set(op->getIn(1)->getNZMask());
if (sa2 < sa) return false; newmask = rvn->mask >> sa;
rop = createOp(CPUI_INT_MULT,2,rvn);
if (!createLink(rop,newmask,0,op->getIn(0))) return false;
if (!createLink(rop,rvn->mask,1,op->getIn(1))) return false;
}
else {
if (rvn->mask == (uintb)1)
rop = createOp(CPUI_INT_AND,2,rvn); else
rop = createOp(CPUI_INT_MULT,2,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false;
if (!createLink(rop,rvn->mask,1,op->getIn(1))) return false;
}
return true;
case CPUI_SUBPIECE:
sa = (int4)op->getIn(1)->getOffset() * 8;
newmask = rvn->mask << sa;
rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,newmask,0,op->getIn(0))) return false;
return true;
case CPUI_PIECE:
if ((rvn->mask & calc_mask(op->getIn(1)->getSize()))==rvn->mask) {
rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(1))) return false;
return true;
}
sa = op->getIn(1)->getSize() * 8;
newmask = rvn->mask>>sa;
if (newmask<<sa == rvn->mask) {
rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,newmask,0,op->getIn(0))) return false;
return true;
}
break;
case CPUI_CALL:
case CPUI_CALLIND:
if (tryCallReturnPush(op,rvn))
return true;
break;
case CPUI_INT_EQUAL:
case CPUI_INT_NOTEQUAL:
case CPUI_INT_SLESS:
case CPUI_INT_SLESSEQUAL:
case CPUI_INT_LESS:
case CPUI_INT_LESSEQUAL:
case CPUI_INT_CARRY:
case CPUI_INT_SCARRY:
case CPUI_INT_SBORROW:
case CPUI_BOOL_NEGATE:
case CPUI_BOOL_XOR:
case CPUI_BOOL_AND:
case CPUI_BOOL_OR:
case CPUI_FLOAT_EQUAL:
case CPUI_FLOAT_NOTEQUAL:
case CPUI_FLOAT_LESSEQUAL:
case CPUI_FLOAT_NAN:
if ((rvn->mask&1)==1) break; rop = createOp(CPUI_COPY,1,rvn);
addNewConstant(rop,0,(uintb)0);
return true;
default:
break; }
return false;
}
bool SubvariableFlow::traceForwardSext(ReplaceVarnode *rvn)
{
ReplaceOp *rop;
PcodeOp *op;
Varnode *outvn;
int4 slot;
int4 dcount = 0;
int4 hcount = 0;
int4 callcount = 0;
list<PcodeOp *>::const_iterator iter,enditer;
enditer = rvn->vn->endDescend();
for(iter=rvn->vn->beginDescend();iter != enditer;++iter) {
op = *iter;
outvn = op->getOut();
if ((outvn!=(Varnode *)0)&&outvn->isMark()&&!op->isCall())
continue;
dcount += 1; slot = op->getSlot(rvn->vn);
switch(op->code()) {
case CPUI_COPY:
case CPUI_MULTIEQUAL:
case CPUI_INT_NEGATE:
case CPUI_INT_XOR:
case CPUI_INT_OR:
case CPUI_INT_AND:
rop = createOpDown(op->code(),op->numInput(),op,rvn,slot);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1;
break;
case CPUI_INT_SEXT: rop = createOpDown(CPUI_COPY,1,op,rvn,0);
if (!createLink(rop,rvn->mask,-1,outvn)) return false;
hcount += 1;
break;
case CPUI_INT_SRIGHT:
if (!op->getIn(1)->isConstant()) return false; rop = createOpDown(CPUI_INT_SRIGHT,2,op,rvn,0);
if (!createLink(rop,rvn->mask,-1,outvn)) return false; addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)); hcount += 1;
break;
case CPUI_SUBPIECE:
if (op->getIn(1)->getOffset() != 0) return false; if (outvn->getSize() > flowsize) return false;
if (outvn->getSize() == flowsize)
addTerminalPatch(op,rvn); else
addTerminalPatchSameOp(op,rvn,0); hcount +=1;
break;
case CPUI_INT_LESS: case CPUI_INT_LESSEQUAL:
case CPUI_INT_SLESS:
case CPUI_INT_SLESSEQUAL:
case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL:
outvn = op->getIn(1-slot); if (!createCompareBridge(op,rvn,slot,outvn)) return false;
hcount += 1;
break;
case CPUI_CALL:
case CPUI_CALLIND:
callcount += 1;
if (callcount > 1)
slot = op->getRepeatSlot(rvn->vn, slot, iter);
if (!tryCallPull(op,rvn,slot)) return false;
hcount += 1; break;
case CPUI_RETURN:
if (!tryReturnPull(op,rvn,slot)) return false;
hcount += 1;
break;
case CPUI_BRANCHIND:
if (!trySwitchPull(op,rvn)) return false;
hcount += 1;
break;
default:
return false;
}
}
if (dcount != hcount) {
if (rvn->vn->isInput()) return false;
}
return true;
}
bool SubvariableFlow::traceBackwardSext(ReplaceVarnode *rvn)
{
PcodeOp *op = rvn->vn->getDef();
if (op == (PcodeOp *)0) return true; ReplaceOp *rop;
switch(op->code()) {
case CPUI_COPY:
case CPUI_MULTIEQUAL:
case CPUI_INT_NEGATE:
case CPUI_INT_XOR:
case CPUI_INT_AND:
case CPUI_INT_OR:
rop = createOp(op->code(),op->numInput(),rvn);
for(int4 i=0;i<op->numInput();++i)
if (!createLink(rop,rvn->mask,i,op->getIn(i))) return false;
return true;
case CPUI_INT_ZEXT:
if (op->getIn(0)->getSize() < flowsize) {
addPush(op,rvn);
return true;
}
break;
case CPUI_INT_SEXT:
if (flowsize != op->getIn(0)->getSize()) return false;
rop = createOp(CPUI_COPY,1,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false;
return true;
case CPUI_INT_SRIGHT:
if (!op->getIn(1)->isConstant()) return false;
rop = createOp(CPUI_INT_SRIGHT,2,rvn);
if (!createLink(rop,rvn->mask,0,op->getIn(0))) return false; if (rop->input.size()==1)
addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)); return true;
case CPUI_CALL:
case CPUI_CALLIND:
if (tryCallReturnPush(op,rvn))
return true;
break;
default:
break;
}
return false;
}
bool SubvariableFlow::createLink(ReplaceOp *rop,uintb mask,int4 slot,Varnode *vn)
{
bool inworklist;
ReplaceVarnode *rep = setReplacement(vn,mask,inworklist);
if (rep == (ReplaceVarnode *)0) return false;
if (rop != (ReplaceOp *)0) {
if (slot == -1) {
rop->output = rep;
rep->def = rop;
}
else {
while(rop->input.size() <= slot)
rop->input.push_back((ReplaceVarnode *)0);
rop->input[slot] = rep;
}
}
if (inworklist)
worklist.push_back(rep);
return true;
}
bool SubvariableFlow::createCompareBridge(PcodeOp *op,ReplaceVarnode *inrvn,int4 slot,Varnode *othervn)
{
bool inworklist;
ReplaceVarnode *rep = setReplacement(othervn,inrvn->mask,inworklist);
if (rep == (ReplaceVarnode *)0) return false;
if (slot==0)
addComparePatch(inrvn,rep,op);
else
addComparePatch(rep,inrvn,op);
if (inworklist)
worklist.push_back(rep);
return true;
}
SubvariableFlow::ReplaceVarnode *SubvariableFlow::addConstant(ReplaceOp *rop,uintb mask,
uint4 slot,Varnode *constvn)
{
newvarlist.emplace_back();
ReplaceVarnode *res = &newvarlist.back();
res->vn = constvn;
res->replacement = (Varnode *)0;
res->mask = mask;
int4 sa = leastsigbit_set(mask);
res->val = (mask & constvn->getOffset()) >> sa;
res->def = (ReplaceOp *)0;
if (rop != (ReplaceOp *)0) {
while(rop->input.size() <= slot)
rop->input.push_back((ReplaceVarnode *)0);
rop->input[slot] = res;
}
return res;
}
SubvariableFlow::ReplaceVarnode *SubvariableFlow::addNewConstant(ReplaceOp *rop,uint4 slot,uintb val)
{
newvarlist.emplace_back();
ReplaceVarnode *res = &newvarlist.back();
res->vn = (Varnode *)0;
res->replacement = (Varnode *)0;
res->mask = 0;
res->val = val;
res->def = (ReplaceOp *)0;
if (rop != (ReplaceOp *)0) {
while(rop->input.size() <= slot)
rop->input.push_back((ReplaceVarnode *)0);
rop->input[slot] = res;
}
return res;
}
void SubvariableFlow::createNewOut(ReplaceOp *rop,uintb mask)
{
newvarlist.emplace_back();
ReplaceVarnode *res = &newvarlist.back();
res->vn = (Varnode *)0;
res->replacement = (Varnode *)0;
res->mask = mask;
rop->output = res;
res->def = rop;
}
void SubvariableFlow::addPush(PcodeOp *pushOp,ReplaceVarnode *rvn)
{
patchlist.push_front(PatchRecord()); patchlist.front().type = PatchRecord::push_patch;
patchlist.front().patchOp = pushOp;
patchlist.front().in1 = rvn;
}
void SubvariableFlow::addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn)
{
patchlist.emplace_back();
patchlist.back().type = PatchRecord::copy_patch; patchlist.back().patchOp = pullop; patchlist.back().in1 = rvn; pullcount += 1; }
void SubvariableFlow::addTerminalPatchSameOp(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot)
{
patchlist.emplace_back();
patchlist.back().type = PatchRecord::parameter_patch; patchlist.back().patchOp = pullop; patchlist.back().in1 = rvn; patchlist.back().slot = slot;
pullcount += 1; }
void SubvariableFlow::addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 slot)
{
patchlist.emplace_back();
patchlist.back().type = PatchRecord::parameter_patch; patchlist.back().patchOp = pullop; patchlist.back().in1 = rvn; patchlist.back().slot = slot;
}
void SubvariableFlow::addSuggestedPatch(ReplaceVarnode *rvn,PcodeOp *pushop,int4 sa)
{
patchlist.emplace_back();
patchlist.back().type = PatchRecord::extension_patch;
patchlist.back().in1 = rvn;
patchlist.back().patchOp = pushop;
if (sa == -1)
sa = leastsigbit_set(rvn->mask);
patchlist.back().slot = sa;
}
void SubvariableFlow::addComparePatch(ReplaceVarnode *in1,ReplaceVarnode *in2,PcodeOp *op)
{
patchlist.emplace_back();
patchlist.back().type = PatchRecord::compare_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = in1;
patchlist.back().in2 = in2;
pullcount += 1;
}
void SubvariableFlow::replaceInput(ReplaceVarnode *rvn)
{
Varnode *newvn = fd->newUnique(rvn->vn->getSize());
newvn = fd->setInputVarnode(newvn);
fd->totalReplace(rvn->vn,newvn);
fd->deleteVarnode(rvn->vn);
rvn->vn = newvn;
}
bool SubvariableFlow::useSameAddress(ReplaceVarnode *rvn)
{
if (rvn->vn->isInput()) return true;
if (rvn->vn->isAddrTied()) return false;
if ((rvn->mask&1)==0) return false; if (bitsize >= 8) return true;
if (aggressive) return true;
uint4 bitmask = 1;
bitmask = (bitmask<<bitsize)-1;
uintb mask = rvn->vn->getConsume();
mask |= (uintb)bitmask;
if (mask == rvn->mask) return true;
return false; }
Address SubvariableFlow::getReplacementAddress(ReplaceVarnode *rvn) const
{
Address addr = rvn->vn->getAddr();
int4 sa = leastsigbit_set(rvn->mask) / 8; if (addr.isBigEndian())
addr = addr + (rvn->vn->getSize() - flowsize - sa);
else
addr = addr + sa;
addr.renormalize(flowsize);
return addr;
}
Varnode *SubvariableFlow::getReplaceVarnode(ReplaceVarnode *rvn)
{
if (rvn->replacement != (Varnode *)0)
return rvn->replacement;
if (rvn->vn == (Varnode *)0) {
if (rvn->def==(ReplaceOp *)0) return fd->newConstant(flowsize,rvn->val);
rvn->replacement = fd->newUnique(flowsize);
return rvn->replacement;
}
if (rvn->vn->isConstant()) {
Varnode *newVn = fd->newConstant(flowsize,rvn->val);
newVn->copySymbolIfValid(rvn->vn);
return newVn;
}
bool isinput = rvn->vn->isInput();
if (useSameAddress(rvn)) {
Address addr = getReplacementAddress(rvn);
if (isinput)
replaceInput(rvn); rvn->replacement = fd->newVarnode(flowsize,addr);
}
else
rvn->replacement = fd->newUnique(flowsize);
if (isinput) rvn->replacement = fd->setInputVarnode(rvn->replacement);
return rvn->replacement;
}
bool SubvariableFlow::processNextWork(void)
{
ReplaceVarnode *rvn = worklist.back();
worklist.pop_back();
if (sextrestrictions) {
if (!traceBackwardSext(rvn)) return false;
return traceForwardSext(rvn);
}
if (!traceBackward(rvn)) return false;
return traceForward(rvn);
}
SubvariableFlow::SubvariableFlow(Funcdata *f,Varnode *root,uintb mask,bool aggr,bool sext,bool big)
{
fd = f;
returnsTraversed = false;
if (mask == (uintb)0) {
fd = (Funcdata *)0;
return;
}
aggressive = aggr;
sextrestrictions = sext;
bitsize = (mostsigbit_set(mask)-leastsigbit_set(mask))+1;
if (bitsize <= 8)
flowsize = 1;
else if (bitsize <= 16)
flowsize = 2;
else if (bitsize <= 24)
flowsize = 3;
else if (bitsize <= 32)
flowsize = 4;
else if (bitsize <= 64) {
if (!big) {
fd = (Funcdata *)0;
return;
}
flowsize = 8;
}
else {
fd = (Funcdata *)0;
return;
}
createLink((ReplaceOp *)0,mask,0,root);
}
bool SubvariableFlow::doTrace(void)
{
pullcount = 0;
bool retval = false;
if (fd != (Funcdata *)0) {
retval = true;
while(!worklist.empty()) {
if (!processNextWork()) {
retval = false;
break;
}
}
}
map<Varnode *,ReplaceVarnode>::iterator iter;
for(iter=varmap.begin();iter!=varmap.end();++iter)
(*iter).first->clearMark();
if (!retval) return false;
if (pullcount == 0) return false;
return true;
}
void SubvariableFlow::doReplacement(void)
{
list<PatchRecord>::iterator piter;
list<ReplaceOp>::iterator iter;
for(piter=patchlist.begin();piter!=patchlist.end();++piter) {
if ((*piter).type != PatchRecord::push_patch) break;
PcodeOp *pushOp = (*piter).patchOp;
Varnode *newVn = getReplaceVarnode((*piter).in1);
Varnode *oldVn = pushOp->getOut();
fd->opSetOutput(pushOp, newVn);
PcodeOp *newZext = fd->newOp(1, pushOp->getAddr());
fd->opSetOpcode(newZext, CPUI_INT_ZEXT);
fd->opSetInput(newZext,newVn,0);
fd->opSetOutput(newZext,oldVn);
fd->opInsertAfter(newZext, pushOp);
}
for(iter=oplist.begin();iter!=oplist.end();++iter) {
PcodeOp *newop = fd->newOp((*iter).numparams,(*iter).op->getAddr());
(*iter).replacement = newop;
fd->opSetOpcode(newop,(*iter).opc);
ReplaceVarnode *rout = (*iter).output;
fd->opSetOutput(newop,getReplaceVarnode(rout));
fd->opInsertAfter(newop,(*iter).op);
}
for(iter=oplist.begin();iter!=oplist.end();++iter) {
PcodeOp *newop = (*iter).replacement;
for(uint4 i=0;i<(*iter).input.size();++i)
fd->opSetInput(newop,getReplaceVarnode((*iter).input[i]),i);
}
for(;piter!=patchlist.end();++piter) {
PcodeOp *pullop = (*piter).patchOp;
switch((*piter).type) {
case PatchRecord::copy_patch:
while(pullop->numInput() > 1)
fd->opRemoveInput(pullop,pullop->numInput()-1);
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0);
fd->opSetOpcode(pullop,CPUI_COPY);
break;
case PatchRecord::compare_patch:
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0);
fd->opSetInput(pullop,getReplaceVarnode((*piter).in2),1);
break;
case PatchRecord::parameter_patch:
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),(*piter).slot);
break;
case PatchRecord::extension_patch:
{
int4 sa = (*piter).slot;
vector<Varnode *> invec;
Varnode *inVn = getReplaceVarnode((*piter).in1);
int4 outSize = pullop->getOut()->getSize();
if (sa == 0) {
invec.push_back(inVn);
OpCode opc = (inVn->getSize() == outSize) ? CPUI_COPY : CPUI_INT_ZEXT;
fd->opSetOpcode(pullop, opc);
fd->opSetAllInput(pullop, invec);
}
else {
if (inVn->getSize() != outSize) {
PcodeOp *zextop = fd->newOp(1, pullop->getAddr());
fd->opSetOpcode(zextop, CPUI_INT_ZEXT);
Varnode *zextout = fd->newUniqueOut(outSize, zextop);
fd->opSetInput(zextop, inVn, 0);
fd->opInsertBefore(zextop, pullop);
invec.push_back(zextout);
}
else
invec.push_back(inVn);
invec.push_back(fd->newConstant(4, sa));
fd->opSetAllInput(pullop, invec);
fd->opSetOpcode(pullop, CPUI_INT_LEFT);
}
break;
}
case PatchRecord::push_patch:
break; }
}
}
TransformVar *SplitFlow::setReplacement(Varnode *vn)
{
TransformVar *res;
if (vn->isMark()) { res = getSplit(vn, laneDescription);
return res;
}
if (vn->isTypeLock())
return (TransformVar *)0;
if (vn->isInput())
return (TransformVar *)0; if (vn->isFree() && (!vn->isConstant()))
return (TransformVar *)0;
res = newSplit(vn, laneDescription); vn->setMark();
if (!vn->isConstant())
worklist.push_back(res);
return res;
}
bool SplitFlow::addOp(PcodeOp *op,TransformVar *rvn,int4 slot)
{
TransformVar *outvn;
if (slot == -1)
outvn = rvn;
else {
outvn = setReplacement(op->getOut());
if (outvn == (TransformVar *)0)
return false;
}
if (outvn->getDef() != (TransformOp *)0)
return true;
TransformOp *loOp = newOpReplace(op->numInput(), op->code(), op);
TransformOp *hiOp = newOpReplace(op->numInput(), op->code(), op);
int4 numParam = op->numInput();
if (op->code() == CPUI_INDIRECT) {
opSetInput(loOp,newIop(op->getIn(1)),1);
opSetInput(hiOp,newIop(op->getIn(1)),1);
numParam = 1;
}
for(int4 i=0;i<numParam;++i) {
TransformVar *invn;
if (i == slot)
invn = rvn;
else {
invn = setReplacement(op->getIn(i));
if (invn == (TransformVar *)0)
return false;
}
opSetInput(loOp,invn,i); opSetInput(hiOp,invn+1,i); }
opSetOutput(loOp,outvn);
opSetOutput(hiOp,outvn+1);
return true;
}
bool SplitFlow::traceForward(TransformVar *rvn)
{
Varnode *origvn = rvn->getOriginal();
list<PcodeOp *>::const_iterator iter,enditer;
iter = origvn->beginDescend();
enditer = origvn->endDescend();
while(iter != enditer) {
PcodeOp *op = *iter++;
Varnode *outvn = op->getOut();
if ((outvn!=(Varnode *)0)&&(outvn->isMark()))
continue;
switch(op->code()) {
case CPUI_COPY:
case CPUI_MULTIEQUAL:
case CPUI_INDIRECT:
case CPUI_INT_AND:
case CPUI_INT_OR:
case CPUI_INT_XOR:
if (!addOp(op,rvn,op->getSlot(origvn)))
return false;
break;
case CPUI_SUBPIECE:
{
if (outvn->isPrecisLo() || outvn->isPrecisHi())
return false; uintb val = op->getIn(1)->getOffset();
if ((val==0)&&(outvn->getSize() == laneDescription.getSize(0))) {
TransformOp *rop = newPreexistingOp(1,CPUI_COPY,op); opSetInput(rop, rvn, 0);
}
else if ((val == laneDescription.getSize(0))&&(outvn->getSize() == laneDescription.getSize(1))) {
TransformOp *rop = newPreexistingOp(1,CPUI_COPY,op); opSetInput(rop, rvn+1, 0);
}
else
return false;
break;
}
case CPUI_INT_LEFT:
{
Varnode *tmpvn = op->getIn(1);
if (!tmpvn->isConstant())
return false;
uintb val = tmpvn->getOffset();
if (val < laneDescription.getSize(1) * 8)
return false; TransformOp *rop = newPreexistingOp(2,CPUI_INT_LEFT,op); TransformOp *zextrop = newOp(1, CPUI_INT_ZEXT, rop);
opSetInput(zextrop, rvn, 0); opSetOutput(zextrop, newUnique(laneDescription.getWholeSize()));
opSetInput(rop, zextrop->getOut(), 0);
opSetInput(rop, newConstant(op->getIn(1)->getSize(), 0, op->getIn(1)->getOffset()), 1); break;
}
case CPUI_INT_SRIGHT:
case CPUI_INT_RIGHT:
{
Varnode *tmpvn = op->getIn(1);
if (!tmpvn->isConstant())
return false;
uintb val = tmpvn->getOffset();
if (val < laneDescription.getSize(0) * 8)
return false;
OpCode extOpCode = (op->code() == CPUI_INT_RIGHT) ? CPUI_INT_ZEXT : CPUI_INT_SEXT;
if (val == laneDescription.getSize(0) * 8) { TransformOp *rop = newPreexistingOp(1,extOpCode,op);
opSetInput(rop, rvn+1, 0); }
else {
uintb remainShift = val - laneDescription.getSize(0) * 8;
TransformOp *rop = newPreexistingOp(2,op->code(),op);
TransformOp *extrop = newOp(1, extOpCode, rop);
opSetInput(extrop, rvn+1, 0); opSetOutput(extrop, newUnique(laneDescription.getWholeSize()));
opSetInput(rop, extrop->getOut(), 0);
opSetInput(rop, newConstant(op->getIn(1)->getSize(), 0, remainShift), 1); }
break;
}
default:
return false;
}
}
return true;
}
bool SplitFlow::traceBackward(TransformVar *rvn)
{
PcodeOp *op = rvn->getOriginal()->getDef();
if (op == (PcodeOp *)0) return true;
switch(op->code()) {
case CPUI_COPY:
case CPUI_MULTIEQUAL:
case CPUI_INT_AND:
case CPUI_INT_OR:
case CPUI_INT_XOR:
case CPUI_INDIRECT:
if (!addOp(op,rvn,-1))
return false;
break;
case CPUI_PIECE:
{
if (op->getIn(0)->getSize() != laneDescription.getSize(1))
return false;
if (op->getIn(1)->getSize() != laneDescription.getSize(0))
return false;
TransformOp *loOp = newOpReplace(1, CPUI_COPY, op);
TransformOp *hiOp = newOpReplace(1, CPUI_COPY, op);
opSetInput(loOp,getPreexistingVarnode(op->getIn(1)),0);
opSetOutput(loOp,rvn); opSetInput(hiOp,getPreexistingVarnode(op->getIn(0)),0);
opSetOutput(hiOp,rvn+1); break;
}
case CPUI_INT_ZEXT:
{
if (op->getIn(0)->getSize() != laneDescription.getSize(0))
return false;
if (op->getOut()->getSize() != laneDescription.getWholeSize())
return false;
TransformOp *loOp = newOpReplace(1, CPUI_COPY, op);
TransformOp *hiOp = newOpReplace(1, CPUI_COPY, op);
opSetInput(loOp,getPreexistingVarnode(op->getIn(0)),0);
opSetOutput(loOp,rvn); opSetInput(hiOp,newConstant(laneDescription.getSize(1), 0, 0), 0);
opSetOutput(hiOp,rvn+1); break;
}
case CPUI_INT_LEFT:
{
Varnode *cvn = op->getIn(1);
if (!cvn->isConstant()) return false;
if (cvn->getOffset() != laneDescription.getSize(0) * 8) return false;
Varnode *invn = op->getIn(0);
if (!invn->isWritten()) return false;
PcodeOp *zextOp = invn->getDef();
if (zextOp->code() != CPUI_INT_ZEXT) return false;
invn = zextOp->getIn(0);
if (invn->getSize() != laneDescription.getSize(1)) return false;
if (invn->isFree()) return false;
TransformOp *loOp = newOpReplace(1, CPUI_COPY, op);
TransformOp *hiOp = newOpReplace(1, CPUI_COPY, op);
opSetInput(loOp,newConstant(laneDescription.getSize(0), 0, 0), 0);
opSetOutput(loOp, rvn); opSetInput(hiOp,getPreexistingVarnode(invn), 0);
opSetOutput(hiOp, rvn+1); break;
}
default:
return false;
}
return true;
}
bool SplitFlow::processNextWork(void)
{
TransformVar *rvn = worklist.back();
worklist.pop_back();
if (!traceBackward(rvn)) return false;
return traceForward(rvn);
}
SplitFlow::SplitFlow(Funcdata *f,Varnode *root,int4 lowSize)
: TransformManager(f), laneDescription(root->getSize(),lowSize,root->getSize()-lowSize)
{
setReplacement(root);
}
bool SplitFlow::doTrace(void)
{
if (worklist.empty())
return false; bool retval = true;
while(!worklist.empty()) { if (!processNextWork()) {
retval = false;
break;
}
}
clearVarnodeMarks();
if (!retval) return false;
return true;
}
TransformVar *SubfloatFlow::setReplacement(Varnode *vn)
{
if (vn->isMark()) return getPiece(vn, precision*8, 0);
if (vn->isConstant()) {
const FloatFormat *form2 = getFunction()->getArch()->translate->getFloatFormat(vn->getSize());
if (form2 == (const FloatFormat *)0)
return (TransformVar *)0; return newConstant(precision, 0, format->convertEncoding(vn->getOffset(),form2));
}
if (vn->isFree())
return (TransformVar *)0;
if (vn->isAddrForce() && (vn->getSize() != precision))
return (TransformVar *)0;
if (vn->isTypeLock()) {
int4 sz = vn->getType()->getSize();
if (sz != precision)
return (TransformVar *)0;
}
if (vn->isInput()) { if (vn->getSize() != precision) return (TransformVar *)0;
}
vn->setMark();
TransformVar *res;
if (vn->getSize() == precision)
res = newPreexistingVarnode(vn);
else {
res = newPiece(vn, precision*8, 0);
worklist.push_back(res);
}
return res;
}
bool SubfloatFlow::traceForward(TransformVar *rvn)
{
list<PcodeOp *>::const_iterator iter,enditer;
Varnode *vn = rvn->getOriginal();
iter = vn->beginDescend();
enditer = vn->endDescend();
while(iter != enditer) {
PcodeOp *op = *iter++;
Varnode *outvn = op->getOut();
if ((outvn!=(Varnode *)0)&&(outvn->isMark()))
continue;
switch(op->code()) {
case CPUI_COPY:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
case CPUI_MULTIEQUAL:
{
TransformOp *rop = newOpReplace(op->numInput(), op->code(), op);
TransformVar *outrvn = setReplacement(outvn);
if (outrvn == (TransformVar *)0) return false;
opSetInput(rop,rvn,op->getSlot(vn));
opSetOutput(rop,outrvn);
break;
}
case CPUI_FLOAT_FLOAT2FLOAT:
{
if (outvn->getSize() < precision)
return false;
TransformOp *rop = newPreexistingOp(1, (outvn->getSize() == precision) ? CPUI_COPY : CPUI_FLOAT_FLOAT2FLOAT, op);
opSetInput(rop,rvn,0);
terminatorCount += 1;
break;
}
case CPUI_FLOAT_EQUAL:
case CPUI_FLOAT_NOTEQUAL:
case CPUI_FLOAT_LESS:
case CPUI_FLOAT_LESSEQUAL:
{
int4 slot = op->getSlot(vn);
TransformVar *rvn2 = setReplacement(op->getIn(1-slot));
if (rvn2 == (TransformVar *)0) return false;
if (rvn == rvn2) {
list<PcodeOp *>::const_iterator ourIter = iter;
--ourIter; slot = op->getRepeatSlot(vn, slot, ourIter);
}
if (preexistingGuard(slot, rvn2)) {
TransformOp *rop = newPreexistingOp(2, op->code(), op);
opSetInput(rop, rvn, 0);
opSetInput(rop, rvn2, 1);
terminatorCount += 1;
}
break;
}
case CPUI_FLOAT_TRUNC:
case CPUI_FLOAT_NAN:
{
TransformOp *rop = newPreexistingOp(1,op->code(), op);
opSetInput(rop,rvn,0);
terminatorCount += 1;
break;
}
default:
return false;
}
}
return true;
}
bool SubfloatFlow::traceBackward(TransformVar *rvn)
{
PcodeOp *op = rvn->getOriginal()->getDef();
if (op == (PcodeOp *)0) return true;
switch(op->code()) {
case CPUI_COPY:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
case CPUI_MULTIEQUAL:
{
TransformOp *rop = rvn->getDef();
if (rop == (TransformOp *)0) {
rop = newOpReplace(op->numInput(), op->code(), op);
opSetOutput(rop, rvn);
}
for(int4 i=0;i<op->numInput();++i) {
TransformVar *newvar = rop->getIn(i);
if (newvar == (TransformVar *)0) {
newvar = setReplacement(op->getIn(i));
if (newvar == (TransformVar *)0)
return false;
opSetInput(rop,newvar,i);
}
}
return true;
}
case CPUI_FLOAT_INT2FLOAT:
{
Varnode *vn = op->getIn(0);
if (!vn->isConstant() && vn->isFree())
return false;
TransformOp *rop = newOpReplace(1, CPUI_FLOAT_INT2FLOAT, op);
opSetOutput(rop, rvn);
TransformVar *newvar = getPreexistingVarnode(vn);
opSetInput(rop,newvar,0);
return true;
}
case CPUI_FLOAT_FLOAT2FLOAT:
{
Varnode *vn = op->getIn(0);
TransformVar *newvar;
OpCode opc;
if (vn->isConstant()) {
opc = CPUI_COPY;
if (vn->getSize() == precision)
newvar = newConstant(precision, 0, vn->getOffset());
else {
newvar = setReplacement(vn); if (newvar == (TransformVar *)0)
return false; }
}
else {
if (vn->isFree()) return false;
opc = (vn->getSize() == precision) ? CPUI_COPY : CPUI_FLOAT_FLOAT2FLOAT;
newvar = getPreexistingVarnode(vn);
}
TransformOp *rop = newOpReplace(1, opc, op);
opSetOutput(rop, rvn);
opSetInput(rop,newvar,0);
return true;
}
default:
break; }
return false;
}
bool SubfloatFlow::processNextWork(void)
{
TransformVar *rvn = worklist.back();
worklist.pop_back();
if (!traceBackward(rvn)) return false;
return traceForward(rvn);
}
SubfloatFlow::SubfloatFlow(Funcdata *f,Varnode *root,int4 prec)
: TransformManager(f)
{
precision = prec;
format = f->getArch()->translate->getFloatFormat(precision);
if (format == (const FloatFormat *)0)
return;
setReplacement(root);
}
bool SubfloatFlow::preserveAddress(Varnode *vn,int4 bitSize,int4 lsbOffset) const
{
return vn->isInput(); }
bool SubfloatFlow::doTrace(void)
{
if (format == (const FloatFormat *)0)
return false;
terminatorCount = 0; bool retval = true;
while(!worklist.empty()) {
if (!processNextWork()) {
retval = false;
break;
}
}
clearVarnodeMarks();
if (!retval) return false;
if (terminatorCount == 0) return false; return true;
}
TransformVar *LaneDivide::setReplacement(Varnode *vn,int4 numLanes,int4 skipLanes)
{
if (vn->isMark()) return getSplit(vn, description, numLanes, skipLanes);
if (vn->isConstant()) {
return newSplit(vn,description, numLanes, skipLanes);
}
if (vn->isTypeLock())
return (TransformVar *)0;
vn->setMark();
TransformVar *res = newSplit(vn, description, numLanes, skipLanes);
if (!vn->isFree()) {
workList.emplace_back();
workList.back().lanes = res;
workList.back().numLanes = numLanes;
workList.back().skipLanes = skipLanes;
}
return res;
}
void LaneDivide::buildUnaryOp(OpCode opc,PcodeOp *op,TransformVar *inVars,TransformVar *outVars,int4 numLanes)
{
for(int4 i=0;i<numLanes;++i) {
TransformOp *rop = newOpReplace(1, opc, op);
opSetOutput(rop, outVars + i);
opSetInput(rop,inVars + i,0);
}
}
void LaneDivide::buildBinaryOp(OpCode opc,PcodeOp *op,TransformVar *in0Vars,TransformVar *in1Vars,
TransformVar *outVars,int4 numLanes)
{
for(int4 i=0;i<numLanes;++i) {
TransformOp *rop = newOpReplace(2, opc, op);
opSetOutput(rop, outVars + i);
opSetInput(rop,in0Vars + i, 0);
opSetInput(rop,in1Vars + i, 1);
}
}
bool LaneDivide::buildPiece(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
int4 highLanes,highSkip;
int4 lowLanes,lowSkip;
Varnode *highVn = op->getIn(0);
Varnode *lowVn = op->getIn(1);
if (!description.restriction(numLanes,skipLanes,lowVn->getSize(),highVn->getSize(),highLanes,highSkip))
return false;
if (!description.restriction(numLanes,skipLanes,0,lowVn->getSize(),lowLanes,lowSkip))
return false;
if (highLanes == 1) {
TransformVar *highRvn = getPreexistingVarnode(highVn);
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetInput(rop,highRvn,0);
opSetOutput(rop,outVars + (numLanes-1));
}
else { TransformVar *highRvn = setReplacement(highVn, highLanes, highSkip);
if (highRvn == (TransformVar *)0) return false;
int4 outHighStart = numLanes - highLanes;
for(int4 i=0;i<highLanes;++i) {
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetInput(rop,highRvn+i,0);
opSetOutput(rop,outVars + (outHighStart + i));
}
}
if (lowLanes == 1) {
TransformVar *lowRvn = getPreexistingVarnode(lowVn);
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetInput(rop,lowRvn,0);
opSetOutput(rop,outVars);
}
else { TransformVar *lowRvn = setReplacement(lowVn, lowLanes, lowSkip);
if (lowRvn == (TransformVar *)0) return false;
for(int4 i=0;i<lowLanes;++i) {
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetInput(rop,lowRvn+i,0);
opSetOutput(rop,outVars + i);
}
}
return true;
}
bool LaneDivide::buildMultiequal(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
vector<TransformVar *> inVarSets;
int4 numInput = op->numInput();
for(int4 i=0;i<numInput;++i) {
TransformVar *inVn = setReplacement(op->getIn(i), numLanes, skipLanes);
if (inVn == (TransformVar *)0) return false;
inVarSets.push_back(inVn);
}
for(int4 i=0;i<numLanes;++i) {
TransformOp *rop = newOpReplace(numInput, CPUI_MULTIEQUAL, op);
opSetOutput(rop, outVars + i);
for(int4 j=0;j<numInput;++j)
opSetInput(rop, inVarSets[j] + i, j);
}
return true;
}
bool LaneDivide::buildStore(PcodeOp *op,int4 numLanes,int4 skipLanes)
{
TransformVar *inVars = setReplacement(op->getIn(2), numLanes, skipLanes);
if (inVars == (TransformVar *)0) return false;
uintb spaceConst = op->getIn(0)->getOffset();
int4 spaceConstSize = op->getIn(0)->getSize();
AddrSpace *spc = Address::getSpaceFromConst(op->getIn(0)->getAddr()); Varnode *origPtr = op->getIn(1);
if (origPtr->isFree()) {
if (!origPtr->isConstant()) return false;
}
TransformVar *basePtr = getPreexistingVarnode(origPtr);
int4 ptrSize = origPtr->getSize();
Varnode *valueVn = op->getIn(2);
for(int4 i=0;i<numLanes;++i) {
TransformOp *ropStore = newOpReplace(3, CPUI_STORE, op);
int4 bytePos = description.getPosition(skipLanes + i);
int4 sz = description.getSize(skipLanes + i);
if (spc->isBigEndian())
bytePos = valueVn->getSize() - (bytePos + sz);
TransformVar *ptrVn;
if (bytePos == 0)
ptrVn = basePtr;
else {
ptrVn = newUnique(ptrSize);
TransformOp *addOp = newOp(2, CPUI_INT_ADD, ropStore);
opSetOutput(addOp,ptrVn);
opSetInput(addOp,basePtr,0);
opSetInput(addOp,newConstant(ptrSize, 0, bytePos), 1);
}
opSetInput(ropStore,newConstant(spaceConstSize,0,spaceConst),0);
opSetInput(ropStore,ptrVn,1);
opSetInput(ropStore,inVars+i,2);
}
return true;
}
bool LaneDivide::buildLoad(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
uintb spaceConst = op->getIn(0)->getOffset();
int4 spaceConstSize = op->getIn(0)->getSize();
AddrSpace *spc = Address::getSpaceFromConst(op->getIn(0)->getAddr()); Varnode *origPtr = op->getIn(1);
if (origPtr->isFree()) {
if (!origPtr->isConstant()) return false;
}
TransformVar *basePtr = getPreexistingVarnode(origPtr);
int4 ptrSize = origPtr->getSize();
int4 outSize = op->getOut()->getSize();
for(int4 i=0;i<numLanes;++i) {
TransformOp *ropLoad = newOpReplace(2, CPUI_LOAD, op);
int4 bytePos = description.getPosition(skipLanes + i);
int4 sz = description.getSize(skipLanes + i);
if (spc->isBigEndian())
bytePos = outSize - (bytePos + sz);
TransformVar *ptrVn;
if (bytePos == 0)
ptrVn = basePtr;
else {
ptrVn = newUnique(ptrSize);
TransformOp *addOp = newOp(2, CPUI_INT_ADD, ropLoad);
opSetOutput(addOp,ptrVn);
opSetInput(addOp,basePtr,0);
opSetInput(addOp,newConstant(ptrSize, 0, bytePos), 1);
}
opSetInput(ropLoad,newConstant(spaceConstSize,0,spaceConst),0);
opSetInput(ropLoad,ptrVn,1);
opSetOutput(ropLoad,outVars+i);
}
return true;
}
bool LaneDivide::buildRightShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
if (!op->getIn(1)->isConstant()) return false;
int4 shiftSize = (int4)op->getIn(1)->getOffset();
if ((shiftSize & 7) != 0) return false; shiftSize /= 8;
int4 startPos = shiftSize + description.getPosition(skipLanes);
int4 startLane = description.getBoundary(startPos);
if (startLane < 0) return false; int4 srcLane = startLane;
int4 destLane = skipLanes;
while(srcLane - skipLanes < numLanes) {
if (description.getSize(srcLane) != description.getSize(destLane)) return false;
srcLane += 1;
destLane += 1;
}
TransformVar *inVars = setReplacement(op->getIn(0), numLanes, skipLanes);
if (inVars == (TransformVar *)0) return false;
buildUnaryOp(CPUI_COPY, op, inVars + (startLane - skipLanes), outVars, numLanes - (startLane - skipLanes));
for(int4 zeroLane=numLanes - (startLane - skipLanes);zeroLane < numLanes;++zeroLane) {
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetOutput(rop,outVars + zeroLane);
opSetInput(rop,newConstant(description.getSize(zeroLane), 0, 0),0);
}
return true;
}
bool LaneDivide::traceForward(TransformVar *rvn,int4 numLanes,int4 skipLanes)
{
Varnode *origvn = rvn->getOriginal();
list<PcodeOp *>::const_iterator iter,enditer;
iter = origvn->beginDescend();
enditer = origvn->endDescend();
while(iter != enditer) {
PcodeOp *op = *iter++;
Varnode *outvn = op->getOut();
if ((outvn!=(Varnode *)0)&&(outvn->isMark()))
continue;
switch(op->code()) {
case CPUI_SUBPIECE:
{
int4 bytePos = (int4)op->getIn(1)->getOffset();
int4 outLanes,outSkip;
if (!description.restriction(numLanes, skipLanes, bytePos, outvn->getSize(), outLanes, outSkip)) {
if (allowSubpieceTerminator) {
int4 laneIndex = description.getBoundary(bytePos);
if (laneIndex < 0 || laneIndex >= description.getNumLanes()) return false;
if (description.getSize(laneIndex) <= outvn->getSize()) return false;
TransformOp *rop = newPreexistingOp(2, CPUI_SUBPIECE, op);
opSetInput(rop, rvn + (laneIndex - skipLanes), 0);
opSetInput(rop, newConstant(4, 0, 0), 1);
break;
}
return false;
}
if (outLanes == 1) {
TransformOp *rop = newPreexistingOp(1, CPUI_COPY, op);
opSetInput(rop,rvn + (outSkip-skipLanes), 0);
}
else {
TransformVar *outRvn = setReplacement(outvn,outLanes,outSkip);
if (outRvn == (TransformVar *)0) return false;
}
break;
}
case CPUI_PIECE:
{
int4 outLanes,outSkip;
int4 bytePos = (op->getIn(0) == origvn) ? op->getIn(1)->getSize() : 0;
if (!description.extension(numLanes, skipLanes, bytePos, outvn->getSize(), outLanes, outSkip))
return false;
TransformVar *outRvn = setReplacement(outvn,outLanes,outSkip);
if (outRvn == (TransformVar *)0) return false;
break;
}
case CPUI_COPY:
case CPUI_INT_NEGATE:
case CPUI_INT_AND:
case CPUI_INT_OR:
case CPUI_INT_XOR:
case CPUI_MULTIEQUAL:
{
TransformVar *outRvn = setReplacement(outvn,numLanes,skipLanes);
if (outRvn == (TransformVar *)0) return false;
break;
}
case CPUI_INT_RIGHT:
{
if (!op->getIn(1)->isConstant()) return false; TransformVar *outRvn = setReplacement(outvn, numLanes, skipLanes);
if (outRvn == (TransformVar *)0) return false;
break;
}
case CPUI_STORE:
if (op->getIn(2) != origvn) return false; if (!buildStore(op,numLanes,skipLanes))
return false;
break;
default:
return false;
}
}
return true;
}
bool LaneDivide::traceBackward(TransformVar *rvn,int4 numLanes,int4 skipLanes)
{
PcodeOp *op = rvn->getOriginal()->getDef();
if (op == (PcodeOp *)0) return true;
switch(op->code()) {
case CPUI_INT_NEGATE:
case CPUI_COPY:
{
TransformVar *inVars = setReplacement(op->getIn(0),numLanes,skipLanes);
if (inVars == (TransformVar *)0) return false;
buildUnaryOp(op->code(), op, inVars, rvn, numLanes);
break;
}
case CPUI_INT_AND:
case CPUI_INT_OR:
case CPUI_INT_XOR:
{
TransformVar *in0Vars = setReplacement(op->getIn(0),numLanes,skipLanes);
if (in0Vars == (TransformVar *)0) return false;
TransformVar *in1Vars = setReplacement(op->getIn(1),numLanes,skipLanes);
if (in1Vars == (TransformVar *)0) return false;
buildBinaryOp(op->code(),op,in0Vars,in1Vars,rvn,numLanes);
break;
}
case CPUI_MULTIEQUAL:
if (!buildMultiequal(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_SUBPIECE:
{
Varnode *inVn = op->getIn(0);
int4 bytePos = (int4)op->getIn(1)->getOffset();
int4 inLanes,inSkip;
if (!description.extension(numLanes, skipLanes, bytePos, inVn->getSize(), inLanes, inSkip))
return false;
TransformVar *inVars = setReplacement(inVn,inLanes,inSkip);
if (inVars == (TransformVar *)0) return false;
buildUnaryOp(CPUI_COPY,op,inVars + (skipLanes - inSkip), rvn, numLanes);
break;
}
case CPUI_PIECE:
if (!buildPiece(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_LOAD:
if (!buildLoad(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_INT_RIGHT:
if (!buildRightShift(op, rvn, numLanes, skipLanes))
return false;
break;
default:
return false;
}
return true;
}
bool LaneDivide::processNextWork(void)
{
TransformVar *rvn = workList.back().lanes;
int4 numLanes = workList.back().numLanes;
int4 skipLanes = workList.back().skipLanes;
workList.pop_back();
if (!traceBackward(rvn,numLanes,skipLanes)) return false;
return traceForward(rvn,numLanes,skipLanes);
}
LaneDivide::LaneDivide(Funcdata *f,Varnode *root,const LaneDescription &desc,bool allowDowncast)
: TransformManager(f), description(desc)
{
allowSubpieceTerminator = allowDowncast;
setReplacement(root, desc.getNumLanes(), 0);
}
bool LaneDivide::doTrace(void)
{
if (workList.empty())
return false; bool retval = true;
while(!workList.empty()) { if (!processNextWork()) {
retval = false;
break;
}
}
clearVarnodeMarks();
if (!retval) return false;
return true;
}