#include "jumptable.hh"
#include "emulate.hh"
#include "flow.hh"
void LoadTable::saveXml(ostream &s) const
{
s << "<loadtable";
a_v_i(s,"size",size);
a_v_i(s,"num",num);
s << ">\n ";
addr.saveXml(s);
s << "</loadtable>\n";
}
void LoadTable::restoreXml(const Element *el,Architecture *glb)
{
istringstream s1(el->getAttributeValue("size"));
s1.unsetf(ios::dec | ios::hex | ios::oct);
s1 >> size;
istringstream s2(el->getAttributeValue("num"));
s2.unsetf(ios::dec | ios::hex | ios::oct);
s2 >> num;
const List &list( el->getChildren() );
List::const_iterator iter = list.begin();
addr = Address::restoreXml( *iter, glb);
}
void LoadTable::collapseTable(vector<LoadTable> &table)
{
if (table.empty()) return;
vector<LoadTable>::iterator iter,lastiter;
int4 count = 1;
iter = table.begin();
lastiter = iter;
Address nextaddr = (*iter).addr + (*iter).size * (*iter).num;
++iter;
for(;iter!=table.end();++iter) {
if (( (*iter).addr == nextaddr ) && ((*iter).size == (*lastiter).size)) {
(*lastiter).num += (*iter).num;
nextaddr = (*iter).addr + (*iter).size * (*iter).num;
}
else if (( nextaddr < (*iter).addr )|| ((*iter).size != (*lastiter).size)) {
lastiter++;
*lastiter = *iter;
nextaddr = (*iter).addr + (*iter).size * (*iter).num;
count += 1;
}
}
table.resize(count,LoadTable(nextaddr,0));
}
void EmulateFunction::executeLoad(void)
{
if (collectloads) {
uintb off = getVarnodeValue(currentOp->getIn(1));
AddrSpace *spc = Address::getSpaceFromConst(currentOp->getIn(0)->getAddr());
off = AddrSpace::addressToByte(off,spc->getWordSize());
int4 sz = currentOp->getOut()->getSize();
loadpoints.push_back(LoadTable(Address(spc,off),sz));
}
EmulatePcodeOp::executeLoad();
}
void EmulateFunction::executeBranch(void)
{
throw LowlevelError("Branch encountered emulating jumptable calculation");
}
void EmulateFunction::executeBranchind(void)
{
throw LowlevelError("Indirect branch encountered emulating jumptable calculation");
}
void EmulateFunction::executeCall(void)
{
fallthruOp();
}
void EmulateFunction::executeCallind(void)
{
fallthruOp();
}
void EmulateFunction::executeCallother(void)
{
fallthruOp();
}
EmulateFunction::EmulateFunction(Funcdata *f)
: EmulatePcodeOp(f->getArch())
{
fd = f;
collectloads = false;
}
void EmulateFunction::setExecuteAddress(const Address &addr)
{
if (!addr.getSpace()->hasPhysical())
throw LowlevelError("Bad execute address");
currentOp = fd->target(addr);
if (currentOp == (PcodeOp *)0)
throw LowlevelError("Could not set execute address");
currentBehave = currentOp->getOpcode()->getBehavior();
}
uintb EmulateFunction::getVarnodeValue(Varnode *vn) const
{ if (vn->isConstant())
return vn->getOffset();
map<Varnode *,uintb>::const_iterator iter;
iter = varnodeMap.find(vn);
if (iter != varnodeMap.end())
return (*iter).second;
return getLoadImageValue(vn->getSpace(),vn->getOffset(),vn->getSize());
}
void EmulateFunction::setVarnodeValue(Varnode *vn,uintb val)
{
varnodeMap[vn] = val;
}
void EmulateFunction::fallthruOp(void)
{
lastOp = currentOp; }
uintb EmulateFunction::emulatePath(uintb val,const PathMeld &pathMeld,
PcodeOp *startop,Varnode *startvn)
{
uint4 i;
for(i=0;i<pathMeld.numOps();++i)
if (pathMeld.getOp(i) == startop) break;
if (startop->code() == CPUI_MULTIEQUAL) { int4 j;
for(j=0;j<startop->numInput();++j) { if (startop->getIn(j) == startvn)
break;
}
if ((j == startop->numInput())||(i==0)) throw LowlevelError("Cannot start jumptable emulation with unresolved MULTIEQUAL");
startvn = startop->getOut(); i -= 1; startop = pathMeld.getOp(i);
}
if (i==pathMeld.numOps())
throw LowlevelError("Bad jumptable emulation");
if (!startvn->isConstant())
setVarnodeValue(startvn,val);
while(i>0) {
PcodeOp *curop = pathMeld.getOp(i);
--i;
setCurrentOp( curop );
try {
executeCurrentOp();
}
catch(DataUnavailError &err) {
ostringstream msg;
msg << "Could not emulate address calculation at " << curop->getAddr();
throw LowlevelError(msg.str());
}
}
Varnode *invn = pathMeld.getOp(0)->getIn(0);
return getVarnodeValue(invn);
}
void EmulateFunction::collectLoadPoints(vector<LoadTable> &res) const
{
if (loadpoints.empty()) return;
bool issorted = true;
vector<LoadTable>::const_iterator iter;
vector<LoadTable>::iterator lastiter;
iter = loadpoints.begin();
res.push_back( *iter ); ++iter;
lastiter = res.begin();
Address nextaddr = (*lastiter).addr + (*lastiter).size;
for(;iter!=loadpoints.end();++iter) {
if (issorted && (( (*iter).addr == nextaddr ) && ((*iter).size == (*lastiter).size))) {
(*lastiter).num += (*iter).num;
nextaddr = (*iter).addr + (*iter).size;
}
else {
issorted = false;
res.push_back( *iter );
}
}
if (!issorted) {
sort(res.begin(),res.end());
LoadTable::collapseTable(res);
}
}
void JumpValuesRange::truncate(int4 nm)
{
int4 rangeSize = 8*sizeof(uintb) - count_leading_zeros(range.getMask());
rangeSize >>= 3;
uintb left = range.getMin();
int4 step = range.getStep();
uintb right = (left + step * nm) & range.getMask();
range.setRange(left, right, rangeSize, step);
}
uintb JumpValuesRange::getSize(void) const
{
return range.getSize();
}
bool JumpValuesRange::contains(uintb val) const
{
return range.contains(val);
}
bool JumpValuesRange::initializeForReading(void) const
{
if (range.getSize()==0) return false;
curval = range.getMin();
return true;
}
bool JumpValuesRange::next(void) const
{
return range.getNext(curval);
}
uintb JumpValuesRange::getValue(void) const
{
return curval;
}
Varnode *JumpValuesRange::getStartVarnode(void) const
{
return normqvn;
}
PcodeOp *JumpValuesRange::getStartOp(void) const
{
return startop;
}
JumpValues *JumpValuesRange::clone(void) const
{
JumpValuesRange *res = new JumpValuesRange();
res->range = range;
res->normqvn = normqvn;
res->startop = startop;
return res;
}
uintb JumpValuesRangeDefault::getSize(void) const
{
return range.getSize() + 1;
}
bool JumpValuesRangeDefault::contains(uintb val) const
{
if (extravalue == val)
return true;
return range.contains(val);
}
bool JumpValuesRangeDefault::initializeForReading(void) const
{
if (range.getSize()==0) return false;
curval = range.getMin();
lastvalue = false;
return true;
}
bool JumpValuesRangeDefault::next(void) const
{
if (lastvalue) return false;
if (range.getNext(curval))
return true;
lastvalue = true;
curval = extravalue;
return true;
}
Varnode *JumpValuesRangeDefault::getStartVarnode(void) const
{
return lastvalue ? extravn : normqvn;
}
PcodeOp *JumpValuesRangeDefault::getStartOp(void) const
{
return lastvalue ? extraop : startop;
}
JumpValues *JumpValuesRangeDefault::clone(void) const
{
JumpValuesRangeDefault *res = new JumpValuesRangeDefault();
res->range = range;
res->normqvn = normqvn;
res->startop = startop;
res->extravalue = extravalue;
res->extravn = extravn;
res->extraop = extraop;
return res;
}
bool JumpModelTrivial::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)
{
size = indop->getParent()->sizeOut();
return ((size != 0)&&(size<=matchsize));
}
void JumpModelTrivial::buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const
{
addresstable.clear();
BlockBasic *bl = indop->getParent();
for(int4 i=0;i<bl->sizeOut();++i) {
const BlockBasic *outbl = (const BlockBasic *)bl->getOut(i);
addresstable.push_back( outbl->getStart() );
}
}
void JumpModelTrivial::buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const
{
for(uint4 i=0;i<addresstable.size();++i)
label.push_back(addresstable[i].getOffset()); }
JumpModel *JumpModelTrivial::clone(JumpTable *jt) const
{
JumpModelTrivial *res = new JumpModelTrivial(jt);
res->size = size;
return res;
}
bool JumpBasic::isprune(Varnode *vn)
{
if (!vn->isWritten()) return true;
PcodeOp *op = vn->getDef();
if (op->isCall()||op->isMarker()) return true;
if (op->numInput()==0) return true;
return false;
}
bool JumpBasic::ispoint(Varnode *vn)
{
if (vn->isConstant()) return false;
if (vn->isAnnotation()) return false;
if (vn->isReadOnly()) return false;
return true;
}
int4 JumpBasic::getStride(Varnode *vn)
{
uintb mask = vn->getNZMask();
if ((mask & 0x3f)==0) return 32;
int4 stride = 1;
while((mask&1)==0) {
mask >>= 1;
stride <<= 1;
}
return stride;
}
uintb JumpBasic::backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode *invn)
{
Varnode *curvn = outvn;
PcodeOp *op;
TypeOp *top;
int4 slot;
while(curvn != invn) {
op = curvn->getDef();
top = op->getOpcode();
for(slot=0;slot<op->numInput();++slot) if (!op->getIn(slot)->isConstant()) break;
if (op->getEvalType() == PcodeOp::binary) {
const Address &addr(op->getIn(1-slot)->getAddr());
uintb otherval;
if (!addr.isConstant()) {
MemoryImage mem(addr.getSpace(),4,1024,fd->getArch()->loader);
otherval = mem.getValue(addr.getOffset(),op->getIn(1-slot)->getSize());
}
else
otherval = addr.getOffset();
output = top->recoverInputBinary(slot,op->getOut()->getSize(),output,
op->getIn(slot)->getSize(),otherval);
curvn = op->getIn(slot);
}
else if (op->getEvalType() == PcodeOp::unary) {
output = top->recoverInputUnary(op->getOut()->getSize(),output,op->getIn(slot)->getSize());
curvn = op->getIn(slot);
}
else
throw LowlevelError("Bad switch normalization op");
}
return output;
}
void JumpBasic::findDeterminingVarnodes(PcodeOp *op,int4 slot)
{
vector<PcodeOpNode> path;
bool firstpoint = false;
path.push_back(PcodeOpNode(op,slot));
do { PcodeOpNode &node(path.back());
Varnode *curvn = node.op->getIn(node.slot);
if (isprune(curvn)) { if (ispoint(curvn)) { if (!firstpoint) { pathMeld.set(path); firstpoint = true;
}
else pathMeld.meld(path);
}
path.back().slot += 1;
while(path.back().slot >= path.back().op->numInput()) {
path.pop_back();
if (path.empty()) break;
path.back().slot += 1;
}
}
else { path.push_back(PcodeOpNode(curvn->getDef(),0));
}
} while(path.size() > 1);
if (pathMeld.empty()) { pathMeld.set(op,op->getIn(slot));
}
}
static bool matching_constants(Varnode *vn1,Varnode *vn2)
{
if (!vn1->isConstant()) return false;
if (!vn2->isConstant()) return false;
if (vn1->getOffset() != vn2->getOffset()) return false;
return true;
}
GuardRecord::GuardRecord(PcodeOp *bOp,PcodeOp *rOp,int4 path,const CircleRange &rng,Varnode *v)
{
cbranch = bOp;
readOp = rOp;
indpath = path;
range = rng;
vn = v;
baseVn = quasiCopy(v,bitsPreserved); }
int4 GuardRecord::valueMatch(Varnode *vn2,Varnode *baseVn2,int4 bitsPreserved2) const
{
if (vn == vn2) return 1; PcodeOp *loadOp,*loadOp2;
if (bitsPreserved == bitsPreserved2) { if (baseVn == baseVn2) return 1; loadOp = baseVn->getDef(); loadOp2 = baseVn2->getDef();
}
else {
loadOp = vn->getDef(); loadOp2 = vn2->getDef();
}
if (loadOp == (PcodeOp *)0) return 0;
if (loadOp2 == (PcodeOp *)0) return 0;
if (oneOffMatch(loadOp,loadOp2) == 1) return 1;
if (loadOp->code() != CPUI_LOAD) return 0;
if (loadOp2->code() != CPUI_LOAD) return 0;
if (loadOp->getIn(0)->getOffset() != loadOp2->getIn(0)->getOffset()) return 0;
Varnode *ptr = loadOp->getIn(1);
Varnode *ptr2 = loadOp2->getIn(1);
if (ptr == ptr2) return 2;
if (!ptr->isWritten()) return 0;
if (!ptr2->isWritten()) return 0;
PcodeOp *addop = ptr->getDef();
if (addop->code() != CPUI_INT_ADD) return 0;
Varnode *constvn = addop->getIn(1);
if (!constvn->isConstant()) return 0;
PcodeOp *addop2 = ptr2->getDef();
if (addop2->code() != CPUI_INT_ADD) return 0;
Varnode *constvn2 = addop2->getIn(1);
if (!constvn2->isConstant()) return 0;
if (addop->getIn(0) != addop2->getIn(0)) return 0;
if (constvn->getOffset() != constvn2->getOffset()) return 0;
return 2;
}
int4 GuardRecord::oneOffMatch(PcodeOp *op1,PcodeOp *op2)
{
if (op1->code() != op2->code())
return 0;
switch(op1->code()) {
case CPUI_INT_AND:
case CPUI_INT_ADD:
case CPUI_INT_XOR:
case CPUI_INT_OR:
case CPUI_INT_LEFT:
case CPUI_INT_RIGHT:
case CPUI_INT_SRIGHT:
case CPUI_INT_MULT:
case CPUI_SUBPIECE:
if (op2->getIn(0) != op1->getIn(0)) return 0;
if (matching_constants(op2->getIn(1),op1->getIn(1)))
return 1;
break;
default:
break;
}
return 0;
}
Varnode *GuardRecord::quasiCopy(Varnode *vn,int4 &bitsPreserved)
{
bitsPreserved = mostsigbit_set(vn->getNZMask()) + 1;
if (bitsPreserved == 0) return vn;
uintb mask = 1;
mask <<= bitsPreserved;
mask -= 1;
PcodeOp *op = vn->getDef();
Varnode *constVn;
while(op != (PcodeOp *)0) {
switch(op->code()) {
case CPUI_COPY:
vn = op->getIn(0);
op = vn->getDef();
break;
case CPUI_INT_AND:
constVn = op->getIn(1);
if (constVn->isConstant() && constVn->getOffset() == mask) {
vn = op->getIn(0);
op = vn->getDef();
}
else
op = (PcodeOp *)0;
break;
case CPUI_INT_OR:
constVn = op->getIn(1);
if (constVn->isConstant() && ((constVn->getOffset() | mask) == (constVn->getOffset() ^ mask))) {
vn = op->getIn(0);
op = vn->getDef();
}
else
op = (PcodeOp *)0;
break;
case CPUI_INT_SEXT:
case CPUI_INT_ZEXT:
if (op->getIn(0)->getSize() * 8 >= bitsPreserved) {
vn = op->getIn(0);
op = vn->getDef();
}
else
op = (PcodeOp *)0;
break;
case CPUI_PIECE:
if (op->getIn(1)->getSize() * 8 >= bitsPreserved) {
vn = op->getIn(1);
op = vn->getDef();
}
else
op = (PcodeOp *)0;
break;
case CPUI_SUBPIECE:
constVn = op->getIn(1);
if (constVn->isConstant() && constVn->getOffset() == 0) {
vn = op->getIn(0);
op = vn->getDef();
}
else
op = (PcodeOp *)0;
break;
default:
op = (PcodeOp *)0;
break;
}
}
return vn;
}
void PathMeld::internalIntersect(vector<int4> &parentMap)
{
vector<Varnode *> newVn;
int4 lastIntersect = -1;
for(int4 i=0;i<commonVn.size();++i) {
Varnode *vn = commonVn[i];
if (vn->isMark()) { lastIntersect = newVn.size();
parentMap.push_back(lastIntersect);
newVn.push_back(vn);
vn->clearMark();
}
else
parentMap.push_back(-1);
}
commonVn = newVn;
lastIntersect = -1;
for(int4 i=parentMap.size()-1;i>=0;--i) {
int4 val = parentMap[i];
if (val == -1) parentMap[i] = lastIntersect; else
lastIntersect = val;
}
}
int4 PathMeld::meldOps(const vector<PcodeOpNode> &path,int4 cutOff,const vector<int4> &parentMap)
{
for(int4 i=0;i<opMeld.size();++i) {
int4 pos = parentMap[opMeld[i].rootVn];
if (pos == -1) {
opMeld[i].op = (PcodeOp *)0; }
else
opMeld[i].rootVn = pos; }
vector<RootedOp> newMeld;
int4 curRoot = -1;
int4 meldPos = 0; const BlockBasic *lastBlock = (const BlockBasic *)0;
for(int4 i=0;i<cutOff;++i) {
PcodeOp *op = path[i].op; PcodeOp *curOp = (PcodeOp *)0;
while(meldPos < opMeld.size()) {
PcodeOp *trialOp = opMeld[meldPos].op; if (trialOp == (PcodeOp *)0) {
meldPos += 1;
continue;
}
if (trialOp->getParent() != op->getParent()) {
if (op->getParent() == lastBlock) {
curOp = (PcodeOp *)0; break;
}
else if (trialOp->getParent() != lastBlock) {
int4 res = opMeld[meldPos].rootVn;
opMeld = newMeld; return res; }
}
else if (trialOp->getSeqNum().getOrder() <= op->getSeqNum().getOrder()) {
curOp = trialOp; break;
}
lastBlock = trialOp->getParent();
newMeld.push_back(opMeld[meldPos]); curRoot = opMeld[meldPos].rootVn;
meldPos += 1;
}
if (curOp == op) {
newMeld.push_back(opMeld[meldPos]);
curRoot = opMeld[meldPos].rootVn;
meldPos += 1;
}
else {
newMeld.push_back(RootedOp(op,curRoot));
}
lastBlock = op->getParent();
}
opMeld = newMeld;
return -1;
}
void PathMeld::truncatePaths(int4 cutPoint)
{
while(opMeld.size() > 1) {
if (opMeld.back().rootVn < cutPoint) break; opMeld.pop_back(); }
commonVn.resize(cutPoint); }
void PathMeld::set(const PathMeld &op2)
{
commonVn = op2.commonVn;
opMeld = op2.opMeld;
}
void PathMeld::set(const vector<PcodeOpNode> &path)
{
for(int4 i=0;i<path.size();++i) {
const PcodeOpNode &node(path[i]);
Varnode *vn = node.op->getIn(node.slot);
opMeld.push_back(RootedOp(node.op,i));
commonVn.push_back(vn);
}
}
void PathMeld::set(PcodeOp *op,Varnode *vn)
{
commonVn.push_back(vn);
opMeld.push_back(RootedOp(op,0));
}
void PathMeld::append(const PathMeld &op2)
{
commonVn.insert(commonVn.begin(),op2.commonVn.begin(),op2.commonVn.end());
opMeld.insert(opMeld.begin(),op2.opMeld.begin(),op2.opMeld.end());
for(int4 i=op2.opMeld.size();i<opMeld.size();++i)
opMeld[i].rootVn += op2.commonVn.size();
}
void PathMeld::clear(void)
{
commonVn.clear();
opMeld.clear();
}
void PathMeld::meld(vector<PcodeOpNode> &path)
{
vector<int4> parentMap;
for(int4 i=0;i<path.size();++i) {
PcodeOpNode &node(path[i]);
node.op->getIn(node.slot)->setMark(); }
internalIntersect(parentMap); int4 cutOff = -1;
for(int4 i=0;i<path.size();++i) {
PcodeOpNode &node(path[i]);
Varnode *vn = node.op->getIn(node.slot);
if (!vn->isMark()) { cutOff = i + 1; }
else
vn->clearMark();
}
int4 newCutoff = meldOps(path,cutOff,parentMap); if (newCutoff >= 0) truncatePaths(newCutoff); path.resize(cutOff);
}
void PathMeld::markPaths(bool val,int4 startVarnode)
{
int4 startOp;
for(startOp=opMeld.size()-1;startOp>=0;--startOp) {
if (opMeld[startOp].rootVn == startVarnode)
break;
}
if (startOp < 0) return;
if (val) {
for(int4 i=0;i<=startOp;++i)
opMeld[i].op->setMark();
}
else {
for(int4 i=0;i<=startOp;++i)
opMeld[i].op->clearMark();
}
}
PcodeOp *PathMeld::getEarliestOp(int4 pos) const
{
for(int4 i=opMeld.size()-1;i>=0;--i) {
if (opMeld[i].rootVn == pos)
return opMeld[i].op;
}
return (PcodeOp *)0;
}
void JumpBasic::analyzeGuards(BlockBasic *bl,int4 pathout)
{
int4 i,j,indpath;
int4 maxbranch = 2; int4 maxpullback = 2;
bool usenzmask = (jumptable->getStage() == 0);
selectguards.clear();
BlockBasic *prevbl;
Varnode *vn;
for(i=0;i<maxbranch;++i) {
if ((pathout>=0)&&(bl->sizeOut()==2)) {
prevbl = bl;
bl = (BlockBasic *)prevbl->getOut(pathout);
indpath = pathout;
pathout = -1;
}
else {
pathout = -1; for(;;) {
if (bl->sizeIn() != 1) return; prevbl = (BlockBasic *)bl->getIn(0);
if (prevbl->sizeOut() != 1) break; bl = prevbl; }
indpath = bl->getInRevIndex(0);
}
PcodeOp *cbranch = prevbl->lastOp();
if ((cbranch==(PcodeOp *)0)||(cbranch->code() != CPUI_CBRANCH))
break;
if (i != 0) {
BlockBasic *otherbl = (BlockBasic *)prevbl->getOut(1-indpath);
PcodeOp *otherop = otherbl->lastOp();
if (otherop != (PcodeOp *)0 && otherop->code() == CPUI_BRANCHIND) {
if (otherop != jumptable->getIndirectOp())
break;
}
}
bool toswitchval = (indpath == 1);
if (cbranch->isBooleanFlip())
toswitchval = !toswitchval;
bl = prevbl;
vn = cbranch->getIn(1);
CircleRange rng(toswitchval);
int4 indpathstore = prevbl->getFlipPath() ? 1-indpath : indpath;
selectguards.push_back(GuardRecord(cbranch,cbranch,indpathstore,rng,vn));
for(j=0;j<maxpullback;++j) {
Varnode *markup; if (!vn->isWritten()) break;
PcodeOp *readOp = vn->getDef();
vn = rng.pullBack(readOp,&markup,usenzmask);
if (vn == (Varnode *)0) break;
if (rng.isEmpty()) break;
selectguards.push_back(GuardRecord(cbranch,readOp,indpathstore,rng,vn));
}
}
}
void JumpBasic::calcRange(Varnode *vn,CircleRange &rng) const
{
int4 stride = 1;
if (vn->isConstant())
rng = CircleRange(vn->getOffset(),vn->getSize());
else if (vn->isWritten() && vn->getDef()->isBoolOutput())
rng = CircleRange(0,2,1,1); else { uintb maxValue = 0; if (vn->isWritten()) {
PcodeOp *andop = vn->getDef();
if (andop->code() == CPUI_INT_AND) {
Varnode *constvn = andop->getIn(1);
if (constvn->isConstant()) {
maxValue = coveringmask( constvn->getOffset() );
maxValue = (maxValue + 1) & calc_mask(vn->getSize());
}
}
}
stride = getStride(vn);
rng = CircleRange(0,maxValue,vn->getSize(),stride);
}
int4 bitsPreserved;
Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved);
vector<GuardRecord>::const_iterator iter;
for(iter=selectguards.begin();iter!=selectguards.end();++iter) {
const GuardRecord &guard( *iter );
int4 matchval = guard.valueMatch(vn,baseVn,bitsPreserved);
if (matchval==0) continue;
if (rng.intersect(guard.getRange())!=0) continue;
}
if (rng.getSize() > 0x10000) {
CircleRange positive(0,(rng.getMask()>>1)+1,vn->getSize(),stride);
positive.intersect(rng);
if (!positive.isEmpty())
rng = positive;
}
}
void JumpBasic::findSmallestNormal(uint4 matchsize)
{
CircleRange rng;
uintb sz,maxsize;
varnodeIndex = 0;
calcRange(pathMeld.getVarnode(0),rng);
jrange->setRange(rng);
jrange->setStartVn(pathMeld.getVarnode(0));
jrange->setStartOp(pathMeld.getOp(0));
maxsize = rng.getSize();
for(uint4 i=1;i<pathMeld.numCommonVarnode();++i) {
if (maxsize == matchsize) return;
calcRange(pathMeld.getVarnode(i),rng);
sz = rng.getSize();
if (sz < maxsize) {
if ((sz != 256)||(pathMeld.getVarnode(i)->getSize()!=1)) {
varnodeIndex = i;
maxsize = sz;
jrange->setRange(rng);
jrange->setStartVn(pathMeld.getVarnode(i));
jrange->setStartOp(pathMeld.getEarliestOp(i));
}
}
}
}
void JumpBasic::findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize)
{
uintb sz;
analyzeGuards(rootbl,pathout);
findSmallestNormal(matchsize);
sz = jrange->getSize();
if ((sz > maxtablesize)&&(pathMeld.numCommonVarnode()==1)) {
Architecture *glb = fd->getArch();
Varnode *vn = pathMeld.getVarnode(0);
if (vn->isReadOnly()) {
MemoryImage mem(vn->getSpace(),4,16,glb->loader);
uintb val = mem.getValue(vn->getOffset(),vn->getSize());
varnodeIndex = 0;
jrange->setRange(CircleRange(val,vn->getSize()));
jrange->setStartVn(vn);
jrange->setStartOp(pathMeld.getOp(0));
}
}
}
void JumpBasic::markFoldableGuards(void)
{
Varnode *vn = pathMeld.getVarnode(varnodeIndex);
int4 bitsPreserved;
Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved);
for(int4 i=0;i<selectguards.size();++i) {
if (selectguards[i].valueMatch(vn,baseVn,bitsPreserved)==0) {
selectguards[i].clear(); }
}
}
void JumpBasic::markModel(bool val)
{
pathMeld.markPaths(val, varnodeIndex);
for(int4 i=0;i<selectguards.size();++i) {
PcodeOp *op = selectguards[i].getBranch();
if (op == (PcodeOp *)0) continue;
PcodeOp *readOp = selectguards[i].getReadOp();
if (val)
readOp->setMark();
else
readOp->clearMark();
}
}
bool JumpBasic::flowsOnlyToModel(Varnode *vn,PcodeOp *trailOp)
{
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
if (op == trailOp) continue;
if (!op->isMark())
return false;
}
return true;
}
bool JumpBasic::foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump)
{
PcodeOp *cbranch = guard.getBranch();
int4 indpath = guard.getPath(); BlockBasic *cbranchblock = cbranch->getParent();
if (cbranchblock->getFlipPath()) indpath = 1 - indpath; BlockBasic *guardtarget = (BlockBasic *)cbranchblock->getOut(1-indpath);
bool change = false;
int4 pos;
if (cbranchblock->sizeOut() != 2) return false; BlockBasic *switchbl = jump->getIndirectOp()->getParent();
for(pos=0;pos<switchbl->sizeOut();++pos)
if (switchbl->getOut(pos) == guardtarget) break;
if (pos == switchbl->sizeOut()) {
if (BlockBasic::noInterveningStatement(cbranch,indpath,switchbl->lastOp())) {
jump->addBlockToSwitch(guardtarget,0xBAD1ABE1);
jump->setLastAsMostCommon();
fd->pushBranch(cbranchblock,1-indpath,switchbl);
guard.clear();
change = true;
}
}
else {
uintb val = ((indpath==0)!=(cbranch->isBooleanFlip())) ? 0 : 1;
fd->opSetInput(cbranch,fd->newConstant(cbranch->getIn(0)->getSize(),val),1);
jump->setDefaultBlock(pos); guard.clear();
change = true;
}
return change;
}
JumpBasic::~JumpBasic(void)
{
if (jrange != (JumpValuesRange *)0)
delete jrange;
}
bool JumpBasic::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)
{
jrange = new JumpValuesRange();
findDeterminingVarnodes(indop,0);
findNormalized(fd,indop->getParent(),-1,matchsize,maxtablesize);
if (jrange->getSize() > maxtablesize)
return false;
markFoldableGuards();
return true;
}
void JumpBasic::buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const
{
uintb val,addr;
addresstable.clear(); EmulateFunction emul(fd);
if (loadpoints != (vector<LoadTable> *)0)
emul.setLoadCollect(true);
uintb mask = ~((uintb)0);
int4 bit = fd->getArch()->funcptr_align;
if (bit != 0) {
mask = (mask >> bit) << bit;
}
AddrSpace *spc = indop->getAddr().getSpace();
bool notdone = jrange->initializeForReading();
while(notdone) {
val = jrange->getValue();
addr = emul.emulatePath(val,pathMeld,jrange->getStartOp(),jrange->getStartVarnode());
addr = AddrSpace::addressToByte(addr,spc->getWordSize());
addr &= mask;
addresstable.push_back(Address(spc,addr));
notdone = jrange->next();
}
if (loadpoints != (vector<LoadTable> *)0)
emul.collectLoadPoints(*loadpoints);
}
void JumpBasic::findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)
{
int4 i,j;
i = varnodeIndex;
normalvn = pathMeld.getVarnode(i++);
switchvn = normalvn;
markModel(true);
int4 countaddsub=0;
int4 countext=0;
PcodeOp *normop = (PcodeOp *)0;
while(i<pathMeld.numCommonVarnode()) {
if (!flowsOnlyToModel(switchvn, normop)) break; Varnode *testvn = pathMeld.getVarnode(i);
if (!switchvn->isWritten()) break;
normop = switchvn->getDef();
for(j=0;j<normop->numInput();++j)
if (normop->getIn(j) == testvn) break;
if (j==normop->numInput()) break;
switch(normop->code()) {
case CPUI_INT_ADD:
case CPUI_INT_SUB:
countaddsub += 1;
if (countaddsub > maxaddsub) break;
if (!normop->getIn(1-j)->isConstant()) break;
switchvn = testvn;
break;
case CPUI_INT_ZEXT:
case CPUI_INT_SEXT:
countext += 1;
if (countext > maxext) break;
switchvn = testvn;
break;
default:
break;
}
if (switchvn != testvn) break;
i += 1;
}
markModel(false);
}
void JumpBasic::buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const
{
uintb val,switchval;
const JumpValuesRange *origrange = (( const JumpBasic *)orig)->getValueRange();
bool notdone = origrange->initializeForReading();
while(notdone) {
val = origrange->getValue();
int4 needswarning = 0; if (origrange->isReversible()) { if (!jrange->contains(val))
needswarning = 1;
try {
switchval = backup2Switch(fd,val,normalvn,switchvn); } catch(EvaluationError &err) {
switchval = 0xBAD1ABE1;
needswarning = 2;
}
}
else
switchval = 0xBAD1ABE1; if (needswarning==1)
fd->warning("This code block may not be properly labeled as switch case",addresstable[label.size()]);
else if (needswarning==2)
fd->warning("Calculation of case label failed",addresstable[label.size()]);
label.push_back(switchval);
if (label.size() >= addresstable.size()) break;
notdone = origrange->next();
}
while(label.size() < addresstable.size()) {
fd->warning("Bad switch case",addresstable[label.size()]);
label.push_back(0xBAD1ABE1);
}
}
Varnode *JumpBasic::foldInNormalization(Funcdata *fd,PcodeOp *indop)
{
fd->opSetInput(indop,switchvn,0);
return switchvn;
}
bool JumpBasic::foldInGuards(Funcdata *fd,JumpTable *jump)
{
bool change = false;
for(int4 i=0;i<selectguards.size();++i) {
PcodeOp *cbranch = selectguards[i].getBranch();
if (cbranch == (PcodeOp *)0) continue; if (cbranch->isDead()) {
selectguards[i].clear();
continue;
}
if (foldInOneGuard(fd,selectguards[i],jump))
change = true;
}
return change;
}
bool JumpBasic::sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable)
{
int4 i;
uintb diff;
if (addresstable.empty()) return true;
Address addr = addresstable[0];
i = 0;
if (addr.getOffset() != 0) {
for(i=1;i<addresstable.size();++i) {
if (addresstable[i].getOffset() == 0) break;
diff = (addr.getOffset() < addresstable[i].getOffset()) ?
(addresstable[i].getOffset()-addr.getOffset()) :
(addr.getOffset()-addresstable[i].getOffset());
if (diff > 0xffff) {
uint1 buffer[8];
LoadImage *loadimage = fd->getArch()->loader;
bool dataavail = true;
try {
loadimage->loadFill(buffer,4,addresstable[i]);
} catch(DataUnavailError &err) {
dataavail = false;
}
if (!dataavail) break;
}
}
}
if (i==0)
return false;
if (i!=addresstable.size()) {
addresstable.resize(i);
jrange->truncate(i);
}
return true;
}
JumpModel *JumpBasic::clone(JumpTable *jt) const
{
JumpBasic *res = new JumpBasic(jt);
res->jrange = (JumpValuesRange *)jrange->clone(); return res;
}
void JumpBasic::clear(void)
{
if (jrange != (JumpValuesRange *)0) {
delete jrange;
jrange = (JumpValuesRange *)0;
}
pathMeld.clear();
selectguards.clear();
normalvn = (Varnode *)0;
switchvn = (Varnode *)0;
}
bool JumpBasic2::foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump)
{
jump->setLastAsMostCommon(); guard.clear(); return true;
}
void JumpBasic2::initializeStart(const PathMeld &pMeld)
{
if (pMeld.empty()) {
extravn = (Varnode *)0;
return;
}
extravn = pMeld.getVarnode(pMeld.numCommonVarnode()-1);
origPathMeld.set(pMeld);
}
bool JumpBasic2::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)
{
Varnode *othervn = (Varnode *)0;
PcodeOp *copyop = (PcodeOp *)0;
uintb extravalue = 0;
Varnode *joinvn = extravn; if (joinvn == (Varnode *)0) return false;
if (!joinvn->isWritten()) return false;
PcodeOp *multiop = joinvn->getDef();
if (multiop->code() != CPUI_MULTIEQUAL) return false;
if (multiop->numInput() != 2) return false; int4 path;
for(path=0;path<2;++path) {
Varnode *vn = multiop->getIn(path);
if (!vn->isWritten()) continue;
copyop = vn->getDef();
if (copyop->code() != CPUI_COPY) continue;
othervn = copyop->getIn(0);
if (othervn->isConstant()) {
extravalue = othervn->getOffset();
break;
}
}
if (path == 2) return false;
BlockBasic *rootbl = (BlockBasic *)multiop->getParent()->getIn(1-path);
int4 pathout = multiop->getParent()->getInRevIndex(1-path);
JumpValuesRangeDefault *jdef = new JumpValuesRangeDefault();
jrange = jdef;
jdef->setExtraValue(extravalue);
jdef->setDefaultVn(joinvn); jdef->setDefaultOp(origPathMeld.getOp(origPathMeld.numOps()-1));
findDeterminingVarnodes(multiop,1-path);
findNormalized(fd,rootbl,pathout,matchsize,maxtablesize);
if (jrange->getSize() > maxtablesize)
return false;
pathMeld.append(origPathMeld);
varnodeIndex += origPathMeld.numCommonVarnode(); return true;
}
bool JumpBasic2::checkNormalDominance(void) const
{
if (normalvn->isInput())
return true;
FlowBlock *defblock = normalvn->getDef()->getParent();
FlowBlock *switchblock = pathMeld.getOp(0)->getParent();
while(switchblock != (FlowBlock *)0) {
if (switchblock == defblock)
return true;
switchblock = switchblock->getImmedDom();
}
return false;
}
void JumpBasic2::findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)
{
normalvn = pathMeld.getVarnode(varnodeIndex); if (checkNormalDominance()) { JumpBasic::findUnnormalized(maxaddsub,maxleftright,maxext); return;
}
switchvn = extravn;
PcodeOp *multiop = extravn->getDef(); if ((multiop->getIn(0)==normalvn)||(multiop->getIn(1)==normalvn)) {
normalvn = switchvn; }
else
throw LowlevelError("Backward normalization not implemented");
}
JumpModel *JumpBasic2::clone(JumpTable *jt) const
{
JumpBasic2 *res = new JumpBasic2(jt);
res->jrange = (JumpValuesRange *)jrange->clone(); return res;
}
void JumpBasic2::clear(void)
{
extravn = (Varnode *)0;
origPathMeld.clear();
JumpBasic::clear();
}
JumpBasicOverride::JumpBasicOverride(JumpTable *jt)
: JumpBasic(jt)
{
startingvalue = 0;
hash = 0;
istrivial = false;
}
void JumpBasicOverride::setAddresses(const vector<Address> &adtable)
{
for(int4 i=0;i<adtable.size();++i)
adset.insert(adtable[i]);
}
int4 JumpBasicOverride::findStartOp(Varnode *vn)
{
list<PcodeOp *>::const_iterator iter,enditer;
iter = vn->beginDescend();
enditer = vn->endDescend();
for(;iter!=enditer;++iter)
(*iter)->setMark();
int4 res = -1;
for(int4 i=0;i<pathMeld.numOps();++i) {
if (pathMeld.getOp(i)->isMark()) {
res = i;
break;
}
}
for(iter=vn->beginDescend();iter!=enditer;++iter)
(*iter)->clearMark();
return res;
}
int4 JumpBasicOverride::trialNorm(Funcdata *fd,Varnode *trialvn,uint4 tolerance)
{
int4 opi = findStartOp(trialvn);
if (opi < 0) return -1;
PcodeOp *startop = pathMeld.getOp(opi);
if (!values.empty()) return opi;
EmulateFunction emul(fd);
AddrSpace *spc = startop->getAddr().getSpace();
uintb val = startingvalue;
uintb addr;
uint4 total = 0;
uint4 miss = 0;
set<Address> alreadyseen;
while(total < adset.size()) {
try {
addr = emul.emulatePath(val,pathMeld,startop,trialvn);
} catch(LowlevelError &err) { addr = 0;
miss = tolerance; }
addr = AddrSpace::addressToByte(addr,spc->getWordSize());
Address newaddr(spc,addr);
if (adset.find(newaddr) != adset.end()) {
if (alreadyseen.insert(newaddr).second) total += 1; values.push_back(val);
addrtable.push_back(newaddr);
if (values.size() > adset.size() + 100) break;
miss = 0;
}
else {
miss += 1;
if (miss >= tolerance) break;
}
val += 1;
}
if (total == adset.size())
return opi;
values.clear();
addrtable.clear();
return -1;
}
void JumpBasicOverride::setupTrivial(void)
{
set<Address>::const_iterator iter;
if (addrtable.empty()) {
for(iter=adset.begin();iter!=adset.end();++iter) {
const Address &addr( *iter );
addrtable.push_back(addr);
}
}
values.clear();
for(int4 i=0;i<addrtable.size();++i)
values.push_back( addrtable[i].getOffset() );
varnodeIndex = 0;
normalvn = pathMeld.getVarnode(0);
istrivial = true;
}
Varnode *JumpBasicOverride::findLikelyNorm(void)
{
Varnode *res = (Varnode *)0;
PcodeOp *op;
uint4 i;
for(i=0;i<pathMeld.numOps();++i) { op = pathMeld.getOp(i);
if (op->code() == CPUI_LOAD) {
res = pathMeld.getOpParent(i);
break;
}
}
if (res == (Varnode *)0) return res;
i += 1;
while(i<pathMeld.numOps()) { op = pathMeld.getOp(i);
if (op->code() == CPUI_INT_ADD) {
res = pathMeld.getOpParent(i);
break;
}
++i;
}
i += 1;
while(i<pathMeld.numOps()) { op = pathMeld.getOp(i);
if (op->code() == CPUI_INT_MULT) {
res = pathMeld.getOpParent(i);
break;
}
++i;
}
return res;
}
void JumpBasicOverride::clearCopySpecific(void)
{
selectguards.clear();
pathMeld.clear();
normalvn = (Varnode *)0;
switchvn = (Varnode *)0;
}
bool JumpBasicOverride::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)
{
clearCopySpecific();
findDeterminingVarnodes(indop,0);
if (!istrivial) { Varnode *trialvn = (Varnode *)0;
if (hash != 0) {
DynamicHash dyn;
trialvn = dyn.findVarnode(fd,normaddress,hash);
}
if ((trialvn == (Varnode *)0)&&(values.empty()||(hash==0)))
trialvn = findLikelyNorm();
if (trialvn != (Varnode *)0) {
int4 opi = trialNorm(fd,trialvn,10);
if (opi >= 0) {
varnodeIndex = opi;
normalvn = trialvn;
return true;
}
}
}
setupTrivial();
return true;
}
void JumpBasicOverride::buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const
{
addresstable = addrtable; }
void JumpBasicOverride::buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const
{
uintb addr;
for(uint4 i=0;i<values.size();++i) {
try {
addr = backup2Switch(fd,values[i],normalvn,switchvn);
} catch(EvaluationError &err) {
addr = 0xBAD1ABE1;
}
label.push_back(addr);
if (label.size() >= addresstable.size()) break; }
while(label.size() < addresstable.size()) {
fd->warning("Bad switch case",addresstable[label.size()]); label.push_back(0xBAD1ABE1);
}
}
JumpModel *JumpBasicOverride::clone(JumpTable *jt) const
{
JumpBasicOverride *res = new JumpBasicOverride(jt);
res->adset = adset;
res->values = values;
res->addrtable = addrtable;
res->startingvalue = startingvalue;
res->normaddress = normaddress;
res->hash = hash;
return res;
}
void JumpBasicOverride::clear(void)
{
values.clear();
addrtable.clear();
istrivial = false;
}
void JumpBasicOverride::saveXml(ostream &s) const
{
set<Address>::const_iterator iter;
s << "<basicoverride>\n";
for(iter=adset.begin();iter!=adset.end();++iter) {
s << " <dest";
AddrSpace *spc = (*iter).getSpace();
uintb off = (*iter).getOffset();
spc->saveXmlAttributes(s,off);
s << "/>\n";
}
if (hash != 0) {
s << " <normaddr";
normaddress.getSpace()->saveXmlAttributes(s,normaddress.getOffset());
s << "/>\n";
s << " <normhash>0x" << hex << hash << "</normhash>\n";
}
if (startingvalue != 0) {
s << " <startval>0x" << hex << startingvalue << "</startval>\n";
}
s << "</basicoverride>\n";
}
void JumpBasicOverride::restoreXml(const Element *el,Architecture *glb)
{
const List &list( el->getChildren() );
List::const_iterator iter = list.begin();
while(iter != list.end()) {
const Element *subel = *iter;
++iter;
if (subel->getName() == "dest") {
adset.insert( Address::restoreXml(subel,glb) );
}
else if (subel->getName() == "normaddr")
normaddress = Address::restoreXml(subel,glb);
else if (subel->getName() == "normhash") {
istringstream s1(subel->getContent());
s1.unsetf(ios::dec | ios::hex | ios::oct);
s1 >> hash;
}
else if (subel->getName() == "startval") {
istringstream s2(subel->getContent());
s2.unsetf(ios::dec | ios::hex | ios::oct);
s2 >> startingvalue;
}
}
if (adset.empty())
throw LowlevelError("Empty jumptable override");
}
bool JumpAssisted::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)
{
Varnode *addrVn = indop->getIn(0);
if (!addrVn->isWritten()) return false;
assistOp = addrVn->getDef();
if (assistOp == (PcodeOp *)0) return false;
if (assistOp->code() != CPUI_CALLOTHER) return false;
if (assistOp->numInput() < 3) return false;
int4 index = assistOp->getIn(0)->getOffset();
userop = dynamic_cast<JumpAssistOp *>(fd->getArch()->userops.getOp(index));
if (userop == (JumpAssistOp *)0) return false;
switchvn = assistOp->getIn(1); for(int4 i=2;i<assistOp->numInput();++i)
if (!assistOp->getIn(i)->isConstant())
return false; if (userop->getCalcSize() == -1) sizeIndices = assistOp->getIn(2)->getOffset();
else {
ExecutablePcode *pcodeScript = (ExecutablePcode *)fd->getArch()->pcodeinjectlib->getPayload(userop->getCalcSize());
vector<uintb> inputs;
int4 numInputs = assistOp->numInput() - 1; if (pcodeScript->sizeInput() != numInputs)
throw LowlevelError(userop->getName() + ": <size_pcode> has wrong number of parameters");
for(int4 i=0;i<numInputs;++i)
inputs.push_back(assistOp->getIn(i+1)->getOffset());
sizeIndices = pcodeScript->evaluate(inputs);
}
if (matchsize !=0 && matchsize-1 != sizeIndices) return false; if (sizeIndices > maxtablesize)
return false;
return true;
}
void JumpAssisted::buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const
{
if (userop->getIndex2Addr() == -1)
throw LowlevelError("Final index2addr calculation outside of jumpassist");
ExecutablePcode *pcodeScript = (ExecutablePcode *)fd->getArch()->pcodeinjectlib->getPayload(userop->getIndex2Addr());
addresstable.clear();
AddrSpace *spc = indop->getAddr().getSpace();
vector<uintb> inputs;
int4 numInputs = assistOp->numInput() - 1; if (pcodeScript->sizeInput() != numInputs)
throw LowlevelError(userop->getName() + ": <addr_pcode> has wrong number of parameters");
for(int4 i=0;i<numInputs;++i)
inputs.push_back(assistOp->getIn(i+1)->getOffset());
uintb mask = ~((uintb)0);
int4 bit = fd->getArch()->funcptr_align;
if (bit != 0) {
mask = (mask >> bit) << bit;
}
for(int4 index=0;index<sizeIndices;++index) {
inputs[0] = index;
uintb output = pcodeScript->evaluate(inputs);
output &= mask;
addresstable.push_back(Address(spc,output));
}
ExecutablePcode *defaultScript = (ExecutablePcode *)fd->getArch()->pcodeinjectlib->getPayload(userop->getDefaultAddr());
if (defaultScript->sizeInput() != numInputs)
throw LowlevelError(userop->getName() + ": <default_pcode> has wrong number of parameters");
inputs[0] = 0;
uintb defaultAddress = defaultScript->evaluate(inputs);
addresstable.push_back(Address(spc,defaultAddress)); }
void JumpAssisted::buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const
{
if ((( const JumpAssisted *)orig)->sizeIndices != sizeIndices)
throw LowlevelError("JumpAssisted table size changed during recovery");
if (userop->getIndex2Case() == -1) {
for(int4 i=0;i<sizeIndices;++i)
label.push_back(i); }
else {
ExecutablePcode *pcodeScript = (ExecutablePcode *)fd->getArch()->pcodeinjectlib->getPayload(userop->getIndex2Case());
vector<uintb> inputs;
int4 numInputs = assistOp->numInput() - 1; if (numInputs != pcodeScript->sizeInput())
throw LowlevelError(userop->getName() + ": <case_pcode> has wrong number of parameters");
for(int4 i=0;i<numInputs;++i)
inputs.push_back(assistOp->getIn(i+1)->getOffset());
for(int4 index=0;index<sizeIndices;++index) {
inputs[0] = index;
uintb output = pcodeScript->evaluate(inputs);
label.push_back(output);
}
}
label.push_back(0xBAD1ABE1); }
Varnode *JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop)
{
Varnode *outvn = assistOp->getOut();
list<PcodeOp *>::const_iterator iter = outvn->beginDescend();
while(iter != outvn->endDescend()) {
PcodeOp *op = *iter;
++iter;
fd->opSetInput(op,switchvn,0);
}
fd->opDestroy(assistOp); return switchvn;
}
bool JumpAssisted::foldInGuards(Funcdata *fd,JumpTable *jump)
{
int4 origVal = jump->getDefaultBlock();
jump->setLastAsMostCommon(); return (origVal != jump->getDefaultBlock());
}
JumpModel *JumpAssisted::clone(JumpTable *jt) const
{
JumpAssisted *clone = new JumpAssisted(jt);
clone->userop = userop;
clone->sizeIndices = sizeIndices;
return clone;
}
void JumpTable::recoverModel(Funcdata *fd)
{
if (jmodel != (JumpModel *)0) {
if (jmodel->isOverride()) { jmodel->recoverModel(fd,indirect,0,maxtablesize);
return;
}
delete jmodel; }
Varnode *vn = indirect->getIn(0);
if (vn->isWritten()) {
PcodeOp *op = vn->getDef();
if (op->code() == CPUI_CALLOTHER) {
JumpAssisted *jassisted = new JumpAssisted(this);
jmodel = jassisted;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize))
return;
}
}
JumpBasic *jbasic = new JumpBasic(this);
jmodel = jbasic;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize))
return;
jmodel = new JumpBasic2(this);
((JumpBasic2 *)jmodel)->initializeStart(jbasic->getPathMeld());
delete jbasic;
if (jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize))
return;
delete jmodel;
jmodel = (JumpModel *)0;
}
void JumpTable::sanityCheck(Funcdata *fd)
{
uint4 sz = addresstable.size();
if (!isReachable(indirect))
throw JumptableNotReachableError("No legal flow");
if (addresstable.size() == 1) { bool isthunk = false;
uintb diff;
Address addr = addresstable[0];
if (addr.getOffset()==0)
isthunk = true;
else {
Address addr2 = indirect->getAddr();
diff = (addr.getOffset() < addr2.getOffset()) ?
(addr2.getOffset() - addr.getOffset()) :
(addr.getOffset() - addr2.getOffset());
if (diff > 0xffff)
isthunk = true;
}
if (isthunk) {
throw JumptableThunkError("Likely thunk");
}
}
if (!jmodel->sanityCheck(fd,indirect,addresstable)) {
ostringstream err;
err << "Jumptable at " << opaddress << " did not pass sanity check.";
throw LowlevelError(err.str());
}
if (sz!=addresstable.size()) fd->warning("Sanity check requires truncation of jumptable",opaddress);
}
int4 JumpTable::block2Position(const FlowBlock *bl) const
{
FlowBlock *parent;
int4 position;
parent = indirect->getParent();
for(position=0;position<bl->sizeIn();++position)
if (bl->getIn(position) == parent) break;
if (position==bl->sizeIn())
throw LowlevelError("Requested block, not in jumptable");
return bl->getInRevIndex(position);
}
bool JumpTable::isReachable(PcodeOp *op)
{
BlockBasic *parent = op->getParent();
for(int4 i=0;i<2;++i) { if (parent->sizeIn() != 1) return true;
BlockBasic *bl = (BlockBasic *)parent->getIn(0);
if (bl->sizeOut() != 2) continue; PcodeOp *cbranch = bl->lastOp();
if ((cbranch==(PcodeOp *)0)||(cbranch->code() != CPUI_CBRANCH))
continue;
Varnode *vn = cbranch->getIn(1); if (!vn->isConstant()) continue; int4 trueslot = cbranch->isBooleanFlip() ? 0: 1;
if (vn->getOffset() == 0)
trueslot = 1 - trueslot;
if (bl->getOut(trueslot) != parent) return false; parent = bl;
}
return true;
}
JumpTable::JumpTable(Architecture *g,Address ad)
: opaddress(ad)
{
glb = g;
jmodel = (JumpModel *)0;
origmodel = (JumpModel *)0;
indirect = (PcodeOp *)0;
switchVarConsume = ~((uintb)0);
defaultBlock = -1;
lastBlock = -1;
maxtablesize = 1024;
maxaddsub = 1;
maxleftright = 1;
maxext = 1;
recoverystage = 0;
collectloads = false;
}
JumpTable::JumpTable(const JumpTable *op2)
{
glb = op2->glb;
jmodel = (JumpModel *)0;
origmodel = (JumpModel *)0;
indirect = (PcodeOp *)0;
switchVarConsume = ~((uintb)0);
defaultBlock = -1;
lastBlock = op2->lastBlock;
maxtablesize = op2->maxtablesize;
maxaddsub = op2->maxaddsub;
maxleftright = op2->maxleftright;
maxext = op2->maxext;
recoverystage = op2->recoverystage;
collectloads = op2->collectloads;
addresstable = op2->addresstable;
loadpoints = op2->loadpoints;
opaddress = op2->opaddress;
if (op2->jmodel != (JumpModel *)0)
jmodel = op2->jmodel->clone(this);
}
JumpTable::~JumpTable(void)
{
if (jmodel != (JumpModel *)0)
delete jmodel;
if (origmodel != (JumpModel *)0)
delete origmodel;
}
int4 JumpTable::numIndicesByBlock(const FlowBlock *bl) const
{
IndexPair val(block2Position(bl),0);
pair<vector<IndexPair>::const_iterator,vector<IndexPair>::const_iterator> range;
range = equal_range(block2addr.begin(),block2addr.end(),val,IndexPair::compareByPosition);
return range.second - range.first;
}
bool JumpTable::isOverride(void) const
{
if (jmodel == (JumpModel *)0)
return false;
return jmodel->isOverride();
}
void JumpTable::setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv)
{
if (jmodel != (JumpModel *)0)
delete jmodel;
JumpBasicOverride *override;
jmodel = override = new JumpBasicOverride(this);
override->setAddresses(addrtable);
override->setNorm(naddr,h);
override->setStartingValue(sv);
}
int4 JumpTable::getIndexByBlock(const FlowBlock *bl,int4 i) const
{
IndexPair val(block2Position(bl),0);
int4 count = 0;
vector<IndexPair>::const_iterator iter = lower_bound(block2addr.begin(),block2addr.end(),val,IndexPair::compareByPosition);
while(iter != block2addr.end()) {
if ((*iter).blockPosition == val.blockPosition) {
if (count == i)
return (*iter).addressIndex;
count += 1;
}
++iter;
}
throw LowlevelError("Could not get jumptable index for block");
}
void JumpTable::setLastAsMostCommon(void)
{
defaultBlock = lastBlock;
}
void JumpTable::addBlockToSwitch(BlockBasic *bl,uintb lab)
{
addresstable.push_back(bl->getStart());
lastBlock = indirect->getParent()->sizeOut(); block2addr.push_back(IndexPair(lastBlock,addresstable.size()-1));
label.push_back(lab);
}
void JumpTable::switchOver(const FlowInfo &flow)
{
FlowBlock *parent,*tmpbl;
int4 pos;
PcodeOp *op;
block2addr.clear();
block2addr.reserve(addresstable.size());
parent = indirect->getParent();
for(int4 i=0;i<addresstable.size();++i) {
Address addr = addresstable[i];
op = flow.target(addr);
tmpbl = op->getParent();
for(pos=0;pos<parent->sizeOut();++pos)
if (parent->getOut(pos) == tmpbl) break;
if (pos==parent->sizeOut())
throw LowlevelError("Jumptable destination not linked");
block2addr.push_back(IndexPair(pos,i));
}
lastBlock = block2addr.back().blockPosition; sort(block2addr.begin(),block2addr.end());
defaultBlock = -1; int4 maxcount = 1; vector<IndexPair>::const_iterator iter = block2addr.begin();
while(iter != block2addr.end()) {
int4 curPos = (*iter).blockPosition;
vector<IndexPair>::const_iterator nextiter = iter;
int4 count = 0;
while(nextiter != block2addr.end() && (*nextiter).blockPosition == curPos) {
count += 1;
++nextiter;
}
iter = nextiter;
if (count > maxcount) {
maxcount = count;
defaultBlock = curPos;
}
}
}
void JumpTable::foldInNormalization(Funcdata *fd)
{
Varnode *switchvn = jmodel->foldInNormalization(fd,indirect);
if (switchvn != (Varnode *)0) {
switchVarConsume = minimalmask(switchvn->getNZMask());
if (switchVarConsume >= calc_mask(switchvn->getSize())) { if (switchvn->isWritten()) {
PcodeOp *op = switchvn->getDef();
if (op->code() == CPUI_INT_SEXT) { switchVarConsume = calc_mask(op->getIn(0)->getSize()); }
}
}
}
}
void JumpTable::trivialSwitchOver(void)
{
FlowBlock *parent;
block2addr.clear();
block2addr.reserve(addresstable.size());
parent = indirect->getParent();
if (parent->sizeOut() != addresstable.size())
throw LowlevelError("Trivial addresstable and switch block size do not match");
for(uint4 i=0;i<parent->sizeOut();++i)
block2addr.push_back(IndexPair(i,i)); lastBlock = parent->sizeOut()-1;
defaultBlock = -1; }
void JumpTable::recoverAddresses(Funcdata *fd)
{
recoverModel(fd);
if (jmodel == (JumpModel *)0) {
ostringstream err;
err << "Could not recover jumptable at " << opaddress << ". Too many branches";
throw LowlevelError(err.str());
}
if (jmodel->getTableSize() == 0) {
ostringstream err;
err << "Impossible to reach jumptable at " << opaddress;
throw JumptableNotReachableError(err.str());
}
if (collectloads)
jmodel->buildAddresses(fd,indirect,addresstable,&loadpoints);
else
jmodel->buildAddresses(fd,indirect,addresstable,(vector<LoadTable> *)0);
sanityCheck(fd);
}
void JumpTable::recoverMultistage(Funcdata *fd)
{
if (origmodel != (JumpModel *)0)
delete origmodel;
origmodel = jmodel;
jmodel = (JumpModel *)0;
vector<Address> oldaddresstable = addresstable;
addresstable.clear();
loadpoints.clear();
try {
recoverAddresses(fd);
}
catch(JumptableThunkError &err) {
if (jmodel != (JumpModel *)0)
delete jmodel;
jmodel = origmodel;
origmodel = (JumpModel *)0;
addresstable = oldaddresstable;
fd->warning("Second-stage recovery error",indirect->getAddr());
}
catch(LowlevelError &err) {
if (jmodel != (JumpModel *)0)
delete jmodel;
jmodel = origmodel;
origmodel = (JumpModel *)0;
addresstable = oldaddresstable;
fd->warning("Second-stage recovery error",indirect->getAddr());
}
recoverystage = 2;
if (origmodel != (JumpModel *)0) { delete origmodel;
origmodel = (JumpModel *)0;
}
}
bool JumpTable::recoverLabels(Funcdata *fd)
{
if (!isRecovered())
throw LowlevelError("Trying to recover jumptable labels without addresses");
if (jmodel != (JumpModel *)0) {
if (origmodel != (JumpModel *)0)
delete origmodel;
if (!jmodel->isOverride()) {
origmodel = jmodel;
jmodel = (JumpModel *)0;
}
else
fd->warning("Switch is manually overridden",opaddress);
}
bool multistagerestart = false;
recoverModel(fd); if (jmodel != (JumpModel *)0) {
if (jmodel->getTableSize() != addresstable.size()) {
fd->warning("Could not find normalized switch variable to match jumptable",opaddress);
if ((addresstable.size()==1)&&(jmodel->getTableSize() > 1))
multistagerestart = true;
}
if ((origmodel == (JumpModel *)0)||(origmodel->getTableSize()==0)) {
jmodel->findUnnormalized(maxaddsub,maxleftright,maxext);
jmodel->buildLabels(fd,addresstable,label,jmodel);
}
else {
jmodel->findUnnormalized(maxaddsub,maxleftright,maxext);
jmodel->buildLabels(fd,addresstable,label,origmodel);
}
}
else {
jmodel = new JumpModelTrivial(this);
jmodel->recoverModel(fd,indirect,addresstable.size(),maxtablesize);
jmodel->buildAddresses(fd,indirect,addresstable,(vector<LoadTable> *)0);
trivialSwitchOver();
jmodel->buildLabels(fd,addresstable,label,origmodel);
}
if (origmodel != (JumpModel *)0) {
delete origmodel;
origmodel = (JumpModel *)0;
}
return multistagerestart;
}
void JumpTable::clear(void)
{
if (origmodel != (JumpModel *)0) {
delete origmodel;
origmodel = (JumpModel *)0;
}
if (jmodel->isOverride())
jmodel->clear();
else {
delete jmodel;
jmodel = (JumpModel *)0;
}
block2addr.clear();
lastBlock = -1;
label.clear();
loadpoints.clear();
indirect = (PcodeOp *)0;
switchVarConsume = ~((uintb)0);
recoverystage = 0;
}
void JumpTable::saveXml(ostream &s) const
{
if (!isRecovered())
throw LowlevelError("Trying to save unrecovered jumptable");
s << "<jumptable>\n";
opaddress.saveXml(s);
s << '\n';
for(int4 i=0;i<addresstable.size();++i) {
s << "<dest";
AddrSpace *spc = addresstable[i].getSpace();
uintb off = addresstable[i].getOffset();
if (spc != (AddrSpace *)0)
spc->saveXmlAttributes(s,off);
if (i<label.size()) {
if (label[i] != 0xBAD1ABE1)
a_v_u(s,"label",label[i]);
}
s << "/>\n";
}
if (!loadpoints.empty()) {
for(int4 i=0;i<loadpoints.size();++i)
loadpoints[i].saveXml(s);
}
if ((jmodel != (JumpModel *)0)&&(jmodel->isOverride()))
jmodel->saveXml(s);
s << "</jumptable>\n";
}
void JumpTable::restoreXml(const Element *el)
{
const List &list( el->getChildren() );
List::const_iterator iter = list.begin();
opaddress = Address::restoreXml( *iter, glb);
bool missedlabel = false;
++iter;
while(iter != list.end()) {
const Element *subel = *iter;
if (subel->getName() == "dest") {
addresstable.push_back( Address::restoreXml( subel, glb) );
int4 maxnum = subel->getNumAttributes();
int4 i;
for(i=0;i<maxnum;++i) {
if (subel->getAttributeName(i) == "label") break;
}
if (i<maxnum) { if (missedlabel)
throw LowlevelError("Jumptable entries are missing labels");
istringstream s1(subel->getAttributeValue(i));
s1.unsetf(ios::dec | ios::hex | ios::oct);
uintb lab;
s1 >> lab;
label.push_back(lab);
}
else missedlabel = true; }
else if (subel->getName() == "loadtable") {
loadpoints.emplace_back();
loadpoints.back().restoreXml(subel,glb);
}
else if (subel->getName() == "basicoverride") {
if (jmodel != (JumpModel *)0)
throw LowlevelError("Duplicate jumptable override specs");
jmodel = new JumpBasicOverride(this);
jmodel->restoreXml(subel,glb);
}
++iter;
}
if (label.size()!=0) {
while(label.size() < addresstable.size())
label.push_back(0xBAD1ABE1);
}
}
bool JumpTable::checkForMultistage(Funcdata *fd)
{
if (addresstable.size()!=1) return false;
if (recoverystage != 0) return false;
if (indirect == (PcodeOp *)0) return false;
if (fd->getOverride().queryMultistageJumptable(indirect->getAddr())) {
recoverystage = 1; return true;
}
return false;
}