/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jumptable.hh"
#include "emulate.hh"
#include "flow.hh"
/// \param s is the XML stream to write to
void LoadTable::saveXml(ostream &s) const
{
s << "\n ";
addr.saveXml(s);
s << "\n";
}
/// \param el is the root \ tag
/// \param glb is the architecture for resolving address space tags
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);
}
/// We assume the list of LoadTable entries is sorted and perform an in-place
/// collapse of any sequences into a single LoadTable entry.
/// \param table is the list of entries to collapse
void LoadTable::collapseTable(vector &table)
{
if (table.empty()) return;
vector::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)) {
// Starting a new table
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)
{
// Ignore calls, as presumably they have nothing to do with final address
fallthruOp();
}
void EmulateFunction::executeCallind(void)
{
// Ignore calls, as presumably they have nothing to do with final address
fallthruOp();
}
void EmulateFunction::executeCallother(void)
{
// Ignore callothers
fallthruOp();
}
/// \param f is the function to emulate within
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
{ // Get the value of a Varnode which is in a syntax tree
// We can't just use the memory location as, within the tree,
// this is just part of the label
if (vn->isConstant())
return vn->getOffset();
map::const_iterator iter;
iter = varnodeMap.find(vn);
if (iter != varnodeMap.end())
return (*iter).second; // We have seen this varnode before
return getLoadImageValue(vn->getSpace(),vn->getOffset(),vn->getSize());
}
void EmulateFunction::setVarnodeValue(Varnode *vn,uintb val)
{
varnodeMap[vn] = val;
}
void EmulateFunction::fallthruOp(void)
{
lastOp = currentOp; // Keep track of lastOp for MULTIEQUAL
// Otherwise do nothing: outer loop is controlling execution flow
}
/// \brief Execute from a given starting point and value to the common end-point of the path set
///
/// Flow the given value through all paths in the path container to produce the
/// single output value.
/// \param val is the starting value
/// \param pathMeld is the set of paths to execute
/// \param startop is the starting PcodeOp within the path set
/// \param startvn is the Varnode holding the starting value
/// \return the calculated value at the common end-point
uintb EmulateFunction::emulatePath(uintb val,const PathMeld &pathMeld,
PcodeOp *startop,Varnode *startvn)
{
uint4 i;
for(i=0;icode() == CPUI_MULTIEQUAL) { // If we start on a MULTIEQUAL
int4 j;
for(j=0;jnumInput();++j) { // Is our startvn one of the branches
if (startop->getIn(j) == startvn)
break;
}
if ((j == startop->numInput())||(i==0)) // If not, we can't continue;
throw LowlevelError("Cannot start jumptable emulation with unresolved MULTIEQUAL");
// If the startvn was a branch of the MULTIEQUAL, emulate as if we just came from that branch
startvn = startop->getOut(); // So the output of the MULTIEQUAL is the new startvn (as if a COPY from old startvn)
i -= 1; // Move to the next instruction to be executed
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);
}
/// Pass back any LOAD records collected during emulation. The individual records
/// are sorted and collapsed into concise \e table descriptions.
/// \param res will hold any resulting table descriptions
void EmulateFunction::collectLoadPoints(vector &res) const
{
if (loadpoints.empty()) return;
bool issorted = true;
vector::const_iterator iter;
vector::iterator lastiter;
iter = loadpoints.begin();
res.push_back( *iter ); // Copy the first entry
++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);
}
}
/// The starting value for the range and the step is preserved. The
/// ending value is set so there are exactly the given number of elements
/// in the range.
/// \param nm is the given number
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 &addresstable,vector *loadpoints) const
{
addresstable.clear();
BlockBasic *bl = indop->getParent();
for(int4 i=0;isizeOut();++i) {
const BlockBasic *outbl = (const BlockBasic *)bl->getOut(i);
addresstable.push_back( outbl->getStart() );
}
}
void JumpModelTrivial::buildLabels(Funcdata *fd,vector &addresstable,vector &label,const JumpModel *orig) const
{
for(uint4 i=0;isize = size;
return res;
}
/// \param vn is the Varnode we are testing for pruning
/// \return \b true if the search should be pruned here
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;
}
/// \param vn is the given Varnode to test
/// \return \b false if it is impossible for the Varnode to be the switch variable
bool JumpBasic::ispoint(Varnode *vn)
{
if (vn->isConstant()) return false;
if (vn->isAnnotation()) return false;
if (vn->isReadOnly()) return false;
return true;
}
/// If the some of the least significant bits of the given Varnode are known to
/// be zero, translate this into a stride for the jumptable range.
/// \param vn is the given Varnode
/// \return the calculated stride = 1,2,4,...
int4 JumpBasic::getStride(Varnode *vn)
{
uintb mask = vn->getNZMask();
if ((mask & 0x3f)==0) // Limit the maximum stride we can return
return 32;
int4 stride = 1;
while((mask&1)==0) {
mask >>= 1;
stride <<= 1;
}
return stride;
}
/// \brief Back up the constant value in the output Varnode to the value in the input Varnode
///
/// This does the work of going from a normalized switch value to the unnormalized value.
/// PcodeOps between the output and input Varnodes must be reversible or an exception is thrown.
/// \param fd is the function containing the switch
/// \param output is the constant value to back up
/// \param outvn is the output Varnode of the data-flow
/// \param invn is the input Varnode to back up to
/// \return the recovered value associated with the input Varnode
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;slotnumInput();++slot) // Find first non-constant input
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;
}
/// \brief Calculate the initial set of Varnodes that might be switch variables
///
/// Paths that terminate at the given PcodeOp are calculated and organized
/// in a PathMeld object that determines Varnodes that are common to all the paths.
/// \param op is the given PcodeOp
/// \param slot is input slot to the PcodeOp all paths must terminate at
void JumpBasic::findDeterminingVarnodes(PcodeOp *op,int4 slot)
{
vector path;
bool firstpoint = false; // Have not seen likely switch variable yet
path.push_back(PcodeOpNode(op,slot));
do { // Traverse through tree of inputs to final address
PcodeOpNode &node(path.back());
Varnode *curvn = node.op->getIn(node.slot);
if (isprune(curvn)) { // Here is a node of the tree
if (ispoint(curvn)) { // Is it a possible switch variable
if (!firstpoint) { // If it is the first possible
pathMeld.set(path); // Take the current path as the result
firstpoint = true;
}
else // If we have already seen at least one possible
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 { // This varnode is not pruned
path.push_back(PcodeOpNode(curvn->getDef(),0));
}
} while(path.size() > 1);
if (pathMeld.empty()) { // Never found a likely point, which means that
// it looks like the address is uniquely determined
// but the constants/readonlys haven't been collapsed
pathMeld.set(op,op->getIn(slot));
}
}
/// \brief Check if the two given Varnodes are matching constants
///
/// \param vn1 is the first given Varnode
/// \param vn2 is the second given Varnode
/// \return \b true if the Varnodes are both constants with the same value
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;
}
/// \param bOp is the CBRANCH \e guarding the switch
/// \param rOp is the PcodeOp immediately reading the Varnode
/// \param path is the specific branch to take from the CBRANCH to reach the switch
/// \param rng is the range of values causing the switch path to be taken
/// \param v is the Varnode holding the value controlling the CBRANCH
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); // Look for varnode whose bits are copied
}
/// \brief Determine if \b this guard applies to the given Varnode
///
/// The guard applies if we know the given Varnode holds the same value as the Varnode
/// attached to the guard. So we return:
/// - 0, if the two Varnodes do not clearly hold the same value.
/// - 1, if the two Varnodes clearly hold the same value.
/// - 2, if the two Varnode clearly hold the same value, pending no writes between their defining op.
///
/// \param vn2 is the given Varnode being tested against \b this guard
/// \param baseVn2 is the earliest Varnode from which the given Varnode is quasi-copied.
/// \param bitsPreserved2 is the number of potentially non-zero bits in the given Varnode
/// \return the matching code 0, 1, or 2
int4 GuardRecord::valueMatch(Varnode *vn2,Varnode *baseVn2,int4 bitsPreserved2) const
{
if (vn == vn2) return 1; // Same varnode, same value
PcodeOp *loadOp,*loadOp2;
if (bitsPreserved == bitsPreserved2) { // Are the same number of bits being copied
if (baseVn == baseVn2) // Are bits being copied from same varnode
return 1; // If so, values are the same
loadOp = baseVn->getDef(); // Otherwise check if different base varnodes hold same value
loadOp2 = baseVn2->getDef();
}
else {
loadOp = vn->getDef(); // Check if different varnodes hold same value
loadOp2 = vn2->getDef();
}
if (loadOp == (PcodeOp *)0) return 0;
if (loadOp2 == (PcodeOp *)0) return 0;
if (oneOffMatch(loadOp,loadOp2) == 1) // Check for simple duplicate calculations
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;
}
/// \brief Return 1 if the two given PcodeOps produce exactly the same value, 0 if otherwise
///
/// We up through only one level of PcodeOp calculation and only for certain binary ops
/// where the second parameter is a constant.
/// \param op1 is the first given PcodeOp to test
/// \param op2 is the second given PcodeOp
/// \return 1 if the same value is produced, 0 otherwise
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;
}
/// \brief Compute the source of a quasi-COPY chain for the given Varnode
///
/// A value is a \b quasi-copy if a sequence of PcodeOps producing it always hold
/// the value as the least significant bits of their output Varnode, but the sequence
/// may put other non-zero values in the upper bits.
/// This method computes the earliest ancestor Varnode for which the given Varnode
/// can be viewed as a quasi-copy.
/// \param vn is the given Varnode
/// \param bitsPreserved will hold the number of least significant bits preserved by the sequence
/// \return the earliest source of the quasi-copy, which may just be the given Varnode
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;
}
/// \brief Calculate intersection of a new Varnode path with the old path
///
/// The new path of Varnodes must all be \e marked. The old path, commonVn,
/// is replaced with the intersection. A map is created from the index of each
/// Varnode in the old path with its index in the new path. If the Varnode is
/// not in the intersection, its index is mapped to -1.
/// \param parentMap will hold the new index map
void PathMeld::internalIntersect(vector &parentMap)
{
vector newVn;
int4 lastIntersect = -1;
for(int4 i=0;iisMark()) { // Look for previously marked varnode, so we know it is in both lists
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) // Fill in varnodes that are cut out of intersection
parentMap[i] = lastIntersect; // with next earliest varnode that is in intersection
else
lastIntersect = val;
}
}
/// \brief Meld in PcodeOps from a new path into \b this container
///
/// Execution order of the PcodeOps in the container is maintained. Each PcodeOp, old or new,
/// has its split point from the common path recalculated.
/// PcodeOps that split (use a vn not in intersection) and do not rejoin
/// (have a predecessor Varnode in the intersection) get removed.
/// If splitting PcodeOps can't be ordered with the existing meld, we get a new cut point.
/// \param path is the new path of PcodeOps in sequence
/// \param cutOff is the number of PcodeOps with an input in the common path
/// \param parentMap is the map from old common Varnodes to the new common Varnodes
/// \return the index of the last (earliest) Varnode in the common path or -1
int4 PathMeld::meldOps(const vector &path,int4 cutOff,const vector &parentMap)
{
// First update opMeld.rootVn with new intersection information
for(int4 i=0;i newMeld;
int4 curRoot = -1;
int4 meldPos = 0; // Ops moved from old opMeld into newMeld
const BlockBasic *lastBlock = (const BlockBasic *)0;
for(int4 i=0;igetParent() != op->getParent()) {
if (op->getParent() == lastBlock) {
curOp = (PcodeOp *)0; // op comes AFTER trialOp
break;
}
else if (trialOp->getParent() != lastBlock) {
// Both trialOp and op come from different blocks that are not the lastBlock
int4 res = opMeld[meldPos].rootVn; // Force truncatePath at (and above) this op
// Found a new cut point
opMeld = newMeld; // Take what we've melded so far
return res; // return the new cutpoint
}
}
else if (trialOp->getSeqNum().getOrder() <= op->getSeqNum().getOrder()) {
curOp = trialOp; // op is equal to or comes later than trialOp
break;
}
lastBlock = trialOp->getParent();
newMeld.push_back(opMeld[meldPos]); // Current old op moved into newMeld
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;
}
/// \brief Truncate all paths at the given new Varnode
///
/// The given Varnode is provided as an index into the current common Varnode list.
/// All Varnodes and PcodeOps involved in execution before this new cut point are removed.
/// \param cutPoint is the given new Varnode
void PathMeld::truncatePaths(int4 cutPoint)
{
while(opMeld.size() > 1) {
if (opMeld.back().rootVn < cutPoint) // If we see op using varnode earlier than cut point
break; // Keep that and all subsequent ops
opMeld.pop_back(); // Otherwise cut the op
}
commonVn.resize(cutPoint); // Since intersection is ordered, just resize to cutPoint
}
/// \param op2 is the path container to copy from
void PathMeld::set(const PathMeld &op2)
{
commonVn = op2.commonVn;
opMeld = op2.opMeld;
}
/// This container is initialized to hold a single data-flow path.
/// \param path is the list of PcodeOpNode edges in the path (in reverse execution order)
void PathMeld::set(const vector &path)
{
for(int4 i=0;igetIn(node.slot);
opMeld.push_back(RootedOp(node.op,i));
commonVn.push_back(vn);
}
}
/// \param op is the one PcodeOp in the path
/// \param vn is the one Varnode (input to the PcodeOp) in the path
void PathMeld::set(PcodeOp *op,Varnode *vn)
{
commonVn.push_back(vn);
opMeld.push_back(RootedOp(op,0));
}
/// The new paths must all start at the common end-point of the paths in
/// \b this container. The new set of melded paths start at the original common start
/// point for \b this container, flow through this old common end-point, and end at
/// the new common end-point.
/// \param op2 is the set of paths to be appended
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());
// Renumber all the rootVn refs to varnodes we have moved
for(int4 i=op2.opMeld.size();i &path)
{
vector parentMap;
for(int4 i=0;igetIn(node.slot)->setMark(); // Mark varnodes in the new path, so its easy to see intersection
}
internalIntersect(parentMap); // Calculate varnode intersection, and map from old intersection -> new
int4 cutOff = -1;
// Calculate where the cutoff point is in the new path
for(int4 i=0;igetIn(node.slot);
if (!vn->isMark()) { // If mark already cleared, we know it is in intersection
cutOff = i + 1; // Cut-off must at least be past this -vn-
}
else
vn->clearMark();
}
int4 newCutoff = meldOps(path,cutOff,parentMap); // Given cutoff point, meld in new ops
if (newCutoff >= 0) // If not all ops could be ordered
truncatePaths(newCutoff); // Cut off at the point where we couldn't order
path.resize(cutOff);
}
/// The starting Varnode, common to all paths, is provided as an index.
/// All PcodeOps up to the final BRANCHIND are (un)marked.
/// \param val is \b true for marking, \b false for unmarking
/// \param startVarnode is the index of the starting PcodeOp
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();
}
}
/// The Varnode is specified by an index into sequence of Varnodes common to all paths in \b this PathMeld.
/// We find the earliest (as in executed first) PcodeOp, within \b this PathMeld that uses the Varnode as input.
/// \param pos is the index of the Varnode
/// \return the earliest PcodeOp using the Varnode
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;
}
/// \brief Analyze CBRANCHs leading up to the given basic-block as a potential switch \e guard.
///
/// In general there is only one path to the switch, and the given basic-block will
/// hold the BRANCHIND. In some models, there is more than one path to the switch block,
/// and a path must be specified. In this case, the given basic-block will be a block that
/// flows into the switch block, and the \e pathout parameter describes which path leads
/// to the switch block.
///
/// For each CBRANCH, range restrictions on the various variables which allow
/// control flow to pass through the CBRANCH to the switch are analyzed.
/// A GuardRecord is created for each of these restrictions.
/// \param bl is the given basic-block
/// \param pathout is an optional path from the basic-block to the switch or -1
void JumpBasic::analyzeGuards(BlockBasic *bl,int4 pathout)
{
int4 i,j,indpath;
int4 maxbranch = 2; // Maximum number of CBRANCHs to consider
int4 maxpullback = 2;
bool usenzmask = (jumptable->getStage() == 0);
selectguards.clear();
BlockBasic *prevbl;
Varnode *vn;
for(i=0;i=0)&&(bl->sizeOut()==2)) {
prevbl = bl;
bl = (BlockBasic *)prevbl->getOut(pathout);
indpath = pathout;
pathout = -1;
}
else {
pathout = -1; // Make sure not to use pathout next time around
for(;;) {
if (bl->sizeIn() != 1) return; // Assume only 1 path to switch
prevbl = (BlockBasic *)bl->getIn(0);
if (prevbl->sizeOut() != 1) break; // Is it possible to deviate from switch path in this block
bl = prevbl; // If not, back up to next block
}
indpath = bl->getInRevIndex(0);
}
PcodeOp *cbranch = prevbl->lastOp();
if ((cbranch==(PcodeOp *)0)||(cbranch->code() != CPUI_CBRANCH))
break;
if (i != 0) {
// Check that this CBRANCH isn't protecting some other switch
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);
// The boolean variable could conceivably be the switch variable
int4 indpathstore = prevbl->getFlipPath() ? 1-indpath : indpath;
selectguards.push_back(GuardRecord(cbranch,cbranch,indpathstore,rng,vn));
for(j=0;jisWritten()) 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));
}
}
}
/// \brief Calculate the range of values in the given Varnode that direct control-flow to the switch
///
/// The Varnode is evaluated against each GuardRecord to determine if its range of values
/// can be restricted. Multiple guards may provide different restrictions.
/// \param vn is the given Varnode
/// \param rng will hold resulting range of values the Varnode can hold at the switch
void JumpBasic::calcRange(Varnode *vn,CircleRange &rng) const
{
// Get an initial range, based on the size/type of -vn-
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); // Only 0 or 1 possible
else { // Should we go ahead and use nzmask in all cases?
uintb maxValue = 0; // Every possible value
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);
}
// Intersect any guard ranges which apply to -vn-
int4 bitsPreserved;
Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved);
vector::const_iterator iter;
for(iter=selectguards.begin();iter!=selectguards.end();++iter) {
const GuardRecord &guard( *iter );
int4 matchval = guard.valueMatch(vn,baseVn,bitsPreserved);
// if (matchval == 2) TODO: we need to check for aliases
if (matchval==0) continue;
if (rng.intersect(guard.getRange())!=0) continue;
}
// It may be an assumption that the switch value is positive
// in which case the guard might not check for it. If the
// size is too big, we try only positive values
if (rng.getSize() > 0x10000) {
CircleRange positive(0,(rng.getMask()>>1)+1,vn->getSize(),stride);
positive.intersect(rng);
if (!positive.isEmpty())
rng = positive;
}
}
/// \brief Find the putative switch variable with the smallest range of values reaching the switch
///
/// The Varnode with the smallest range and closest to the BRANCHIND is assumed to be the normalized
/// switch variable. If an expected range size is provided, it is used to \e prefer a particular
/// Varnode as the switch variable. Whatever Varnode is selected,
/// the JumpValue object is set up to iterator over its range.
/// \param matchsize optionally gives an expected size of the range, or it can be 0
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;igetSize()!=1)) {
varnodeIndex = i;
maxsize = sz;
jrange->setRange(rng);
jrange->setStartVn(pathMeld.getVarnode(i));
jrange->setStartOp(pathMeld.getEarliestOp(i));
}
}
}
}
/// \brief Do all the work necessary to recover the normalized switch variable
///
/// The switch can be specified as the basic-block containing the BRANCHIND, or
/// as a block that flows to the BRANCHIND block by following the specified path out.
/// \param fd is the function containing the switch
/// \param rootbl is the basic-block
/// \param pathout is the (optional) path to the BRANCHIND or -1
/// \param matchsize is an (optional) size to expect for the normalized switch variable range
/// \param maxtablesize is the maximum size expected for the normalized switch variable range
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)) {
// Check for jump through readonly variable
// Note the normal jumptable algorithms are cavalier about
// the jumptable being in readonly memory or not because
// a jumptable construction almost always implies that the
// entries are readonly even if they aren't labelled properly
// The exception is if the jumptable has only one branch
// as it very common to have semi-dynamic vectors that are
// set up by the system. But the original LoadImage values
// are likely incorrect. So for 1 branch, we insist on readonly
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));
}
}
}
/// \brief Mark the guard CBRANCHs that are truly part of the model.
///
/// These CBRANCHs will be removed from the active control-flow graph, their
/// function \e folded into the action of the model, as represented by BRANCHIND.
void JumpBasic::markFoldableGuards(void)
{
Varnode *vn = pathMeld.getVarnode(varnodeIndex);
int4 bitsPreserved;
Varnode *baseVn = GuardRecord::quasiCopy(vn, bitsPreserved);
for(int4 i=0;isetMark();
else
readOp->clearMark();
}
}
/// The PcodeOps in \b this model must have been previously marked with markModel().
/// Run through the descendants of the given Varnode and look for this mark.
/// \param vn is the given Varnode
/// \param trailOp is an optional known PcodeOp that leads to the model
/// \return \b true if the only flow is into \b this model
bool JumpBasic::flowsOnlyToModel(Varnode *vn,PcodeOp *trailOp)
{
list::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(); // Get stored path to indirect block
BlockBasic *cbranchblock = cbranch->getParent();
if (cbranchblock->getFlipPath()) // Based on whether out branches have been flipped
indpath = 1 - indpath; // get actual path to indirect block
BlockBasic *guardtarget = (BlockBasic *)cbranchblock->getOut(1-indpath);
bool change = false;
int4 pos;
// Its possible the guard branch has been converted between the switch recovery and now
if (cbranchblock->sizeOut() != 2) return false; // In which case, we can't fold it in
BlockBasic *switchbl = jump->getIndirectOp()->getParent();
for(pos=0;possizeOut();++pos)
if (switchbl->getOut(pos) == guardtarget) break;
if (pos == switchbl->sizeOut()) {
if (BlockBasic::noInterveningStatement(cbranch,indpath,switchbl->lastOp())) {
// Adjust tables and control flow graph
// for new jumptable destination
jump->addBlockToSwitch(guardtarget,0xBAD1ABE1);
jump->setLastAsMostCommon();
fd->pushBranch(cbranchblock,1-indpath,switchbl);
guard.clear();
change = true;
}
}
else {
// We should probably check that there are no intervening
// statements between the guard and the switch. But the
// fact that the guard target is also a switch target
// is a good indicator that there are none
uintb val = ((indpath==0)!=(cbranch->isBooleanFlip())) ? 0 : 1;
fd->opSetInput(cbranch,fd->newConstant(cbranch->getIn(0)->getSize(),val),1);
jump->setDefaultBlock(pos); // A guard branch generally targets the default case
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)
{
// Basically there needs to be a straight line calculation from a switch variable to the final
// address used for the BRANCHIND. The switch variable is restricted to a small range by one
// or more "guard" instructions that, if the switch variable is not in range, branch to a default
// location.
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 &addresstable,vector *loadpoints) const
{
uintb val,addr;
addresstable.clear(); // Clear out any partial recoveries
// Build the emulation engine
EmulateFunction emul(fd);
if (loadpoints != (vector *)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 *)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(iisWritten()) break;
normop = switchvn->getDef();
for(j=0;jnumInput();++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 &addresstable,vector &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; // 0=nowarning, 1=this code block may not be properly labeled, 2=calculation failed
if (origrange->isReversible()) { // If the current value is reversible
if (!jrange->contains(val))
needswarning = 1;
try {
switchval = backup2Switch(fd,val,normalvn,switchvn); // Do reverse emulation to get original switch value
} catch(EvaluationError &err) {
switchval = 0xBAD1ABE1;
needswarning = 2;
}
}
else
switchval = 0xBAD1ABE1; // If can't reverse, hopefully this is the default or exit, otherwise give "badlabel"
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);
// Take into account the fact that the address table may have
// been truncated (via the sanity check)
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)
{
// Set the BRANCHIND input to be the unnormalized switch variable, so
// all the intervening code to calculate the final address is eliminated as dead.
fd->opSetInput(indop,switchvn,0);
return switchvn;
}
bool JumpBasic::foldInGuards(Funcdata *fd,JumpTable *jump)
{
bool change = false;
for(int4 i=0;iisDead()) {
selectguards[i].clear();
continue;
}
if (foldInOneGuard(fd,selectguards[i],jump))
change = true;
}
return change;
}
bool JumpBasic::sanityCheck(Funcdata *fd,PcodeOp *indop,vector &addresstable)
{
// Test all the addresses in \b this address table checking
// that they are reasonable. We cut off at the first unreasonable address.
int4 i;
uintb diff;
if (addresstable.empty()) return true;
Address addr = addresstable[0];
i = 0;
if (addr.getOffset() != 0) {
for(i=1;i 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(); // We only need to clone the JumpValues
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)
{ // The are two main cases here:
// If we recovered a switch in a loop,
// the guard is also the loop condition, so we don't want to remove it.
//
// If the guard is just deciding whether or not to use a default switch value,
// the guard will disappear anyway because the normalization foldin will make all its blocks donothings
//
// So we don't make any special mods, in case there are extra statements in these blocks
// The final block in the table is the single value produced by the model2 guard
jump->setLastAsMostCommon(); // It should be the default block
guard.clear(); // Mark that we are folded
return true;
}
void JumpBasic2::initializeStart(const PathMeld &pMeld)
{
if (pMeld.empty()) {
extravn = (Varnode *)0;
return;
}
// Initialize at point where the JumpBasic model failed
extravn = pMeld.getVarnode(pMeld.numCommonVarnode()-1);
origPathMeld.set(pMeld);
}
bool JumpBasic2::recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)
{ // Try to recover a jumptable using the second model
// Basically there is a guard on the main switch variable,
// Along one path, an intermediate value is set to a default constant.
// Along the other path, the intermediate value results in a straight line calculation from the switch var
// The two-pathed intermediate value comes together in a MULTIEQUAL, and there is a straightline
// calculation to the BRANCHIND
// We piggy back on the partial calculation from the basic model to see if we have the MULTIEQUAL
Varnode *othervn = (Varnode *)0;
PcodeOp *copyop = (PcodeOp *)0;
uintb extravalue = 0;
Varnode *joinvn = extravn; // extravn should be set to as far back as model 1 could trace
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; // Must be exactly 2 paths
// Search for a constant along one of the paths
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); // Emulate the default calculation from the join point
jdef->setDefaultOp(origPathMeld.getOp(origPathMeld.numOps()-1));
findDeterminingVarnodes(multiop,1-path);
findNormalized(fd,rootbl,pathout,matchsize,maxtablesize);
if (jrange->getSize() > maxtablesize)
return false; // We didn't find a good range
// Insert the final sequence of operations, after the MULTIEQUAL, for constructing the address
pathMeld.append(origPathMeld);
varnodeIndex += origPathMeld.numCommonVarnode(); // index is pushed up by the append
return true;
}
/// \brief Check if the block that defines the normalized switch variable dominates the block containing the switch
///
/// \return \b true if the switch block is dominated
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); // Normalized switch variable
if (checkNormalDominance()) { // If the normal switch variable dominates the switch itself
JumpBasic::findUnnormalized(maxaddsub,maxleftright,maxext); // We can use the basic form of calculating the unnormalized
return;
}
// We have the unusual situation that we must go BACKWARD from the unnormalized variable
// to get to the normalized variable
switchvn = extravn;
PcodeOp *multiop = extravn->getDef(); // Already tested that this is a MULTIEQUAL with 2 inputs
if ((multiop->getIn(0)==normalvn)||(multiop->getIn(1)==normalvn)) {
normalvn = switchvn; // No value difference between normalized and unnormalized
}
else
throw LowlevelError("Backward normalization not implemented");
}
JumpModel *JumpBasic2::clone(JumpTable *jt) const
{
JumpBasic2 *res = new JumpBasic2(jt);
res->jrange = (JumpValuesRange *)jrange->clone(); // We only need to clone the JumpValues
return res;
}
void JumpBasic2::clear(void)
{
extravn = (Varnode *)0;
origPathMeld.clear();
JumpBasic::clear();
}
/// \param jt is the parent JumpTable
JumpBasicOverride::JumpBasicOverride(JumpTable *jt)
: JumpBasic(jt)
{
startingvalue = 0;
hash = 0;
istrivial = false;
}
/// \param adtable is the list of externally provided addresses, which will be deduped
void JumpBasicOverride::setAddresses(const vector &adtable)
{
for(int4 i=0;i::const_iterator iter,enditer;
iter = vn->beginDescend();
enditer = vn->endDescend();
for(;iter!=enditer;++iter)
(*iter)->setMark();
int4 res = -1;
for(int4 i=0;iisMark()) {
res = i;
break;
}
}
for(iter=vn->beginDescend();iter!=enditer;++iter)
(*iter)->clearMark();
return res;
}
/// \brief Test a given Varnode as a potential normalized switch variable
///
/// This method tries to figure out the set of values for the Varnode that
/// produce the manually provided set of addresses. Starting with \e startingvalue
/// and simply incrementing by one to obtain new values, the path from the potential variable
/// to the BRANCHIND is emulated to produce addresses in the manual set. Duplicates and
/// misses are allowed. Once we see all addresses in the manual set,
/// the method returns the index of the starting op, otherwise -1 is returned.
/// \param fd is the function containing the switch
/// \param trialvn is the given trial normalized switch variable
/// \param tolerance is the number of misses that will be tolerated
/// \return the index of the starting PcodeOp within the PathMeld or -1
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()) // Have we already worked out the values and addresses
return opi;
EmulateFunction emul(fd);
// if (loadpoints != (vector *)0)
// emul.setLoadCollect(true);
AddrSpace *spc = startop->getAddr().getSpace();
uintb val = startingvalue;
uintb addr;
uint4 total = 0;
uint4 miss = 0;
set alreadyseen;
while(total < adset.size()) {
try {
addr = emul.emulatePath(val,pathMeld,startop,trialvn);
} catch(LowlevelError &err) { // Something went wrong with emulation
addr = 0;
miss = tolerance; // Terminate early
}
addr = AddrSpace::addressToByte(addr,spc->getWordSize());
Address newaddr(spc,addr);
if (adset.find(newaddr) != adset.end()) {
if (alreadyseen.insert(newaddr).second) // If this is the first time we've seen this address
total += 1; // Count it
values.push_back(val);
addrtable.push_back(newaddr);
// We may be seeing the same (valid) address over and over, without seeing others in -adset-
// Terminate if things get too large
if (values.size() > adset.size() + 100) break;
miss = 0;
}
else {
miss += 1;
if (miss >= tolerance) break;
}
val += 1;
}
// if ((loadpoint != (vector *)0)&&(total == adset.size()))
// emul.collectLoadPoints(*loadpoints);
if (total == adset.size())
return opi;
values.clear();
addrtable.clear();
return -1;
}
/// \brief Convert \b this to a trivial model
///
/// Since we have an absolute set of addresses, if all else fails we can use the indirect variable
/// as the normalized switch and the addresses as the values, similar to JumpModelTrivial
void JumpBasicOverride::setupTrivial(void)
{
set::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;icode() == CPUI_LOAD) {
res = pathMeld.getOpParent(i);
break;
}
}
if (res == (Varnode *)0) return res;
i += 1;
while(icode() == CPUI_INT_ADD) {
res = pathMeld.getOpParent(i);
break;
}
++i;
}
i += 1;
while(icode() == CPUI_INT_MULT) {
res = pathMeld.getOpParent(i);
break;
}
++i;
}
return res;
}
/// \brief Clear varnodes and ops that are specific to one instance of a function
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) { // If we haven't previously decided to use trivial model
Varnode *trialvn = (Varnode *)0;
if (hash != 0) {
DynamicHash dyn;
trialvn = dyn.findVarnode(fd,normaddress,hash);
}
// If there was never a specified norm, or the specified norm was never recovered
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 &addresstable,vector *loadpoints) const
{
addresstable = addrtable; // Addresses are already calculated, just copy them out
}
void JumpBasicOverride::buildLabels(Funcdata *fd,vector &addresstable,vector &label,const JumpModel *orig) const
{
uintb addr;
for(uint4 i=0;i= addresstable.size()) break; // This should never happen
}
while(label.size() < addresstable.size()) {
fd->warning("Bad switch case",addresstable[label.size()]); // This should never happen
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)
{
// -adset- is a permanent feature, do no clear
// -startingvalue- is permanent
// -normaddress- is permanent
// -hash- is permanent
values.clear();
addrtable.clear();
istrivial = false;
}
void JumpBasicOverride::saveXml(ostream &s) const
{
set::const_iterator iter;
s << "\n";
for(iter=adset.begin();iter!=adset.end();++iter) {
s << " saveXmlAttributes(s,off);
s << "/>\n";
}
if (hash != 0) {
s << " saveXmlAttributes(s,normaddress.getOffset());
s << "/>\n";
s << " 0x" << hex << hash << "\n";
}
if (startingvalue != 0) {
s << " 0x" << hex << startingvalue << "\n";
}
s << "\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)
{
// Look for the special "jumpassist" pseudo-op
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(fd->getArch()->userops.getOp(index));
if (userop == (JumpAssistOp *)0) return false;
switchvn = assistOp->getIn(1); // The switch variable
for(int4 i=2;inumInput();++i)
if (!assistOp->getIn(i)->isConstant())
return false; // All remaining params must be constant
if (userop->getCalcSize() == -1) // If no size script, first param after switch var is size
sizeIndices = assistOp->getIn(2)->getOffset();
else {
ExecutablePcode *pcodeScript = (ExecutablePcode *)fd->getArch()->pcodeinjectlib->getPayload(userop->getCalcSize());
vector inputs;
int4 numInputs = assistOp->numInput() - 1; // How many remaining varnodes after useropid
if (pcodeScript->sizeInput() != numInputs)
throw LowlevelError(userop->getName() + ": has wrong number of parameters");
for(int4 i=0;igetIn(i+1)->getOffset());
sizeIndices = pcodeScript->evaluate(inputs);
}
if (matchsize !=0 && matchsize-1 != sizeIndices) // matchsize has 1 added to it for the default case
return false; // Not matching the size we saw previously
if (sizeIndices > maxtablesize)
return false;
return true;
}
void JumpAssisted::buildAddresses(Funcdata *fd,PcodeOp *indop,vector &addresstable,vector *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 inputs;
int4 numInputs = assistOp->numInput() - 1; // How many remaining varnodes after useropid
if (pcodeScript->sizeInput() != numInputs)
throw LowlevelError(userop->getName() + ": has wrong number of parameters");
for(int4 i=0;igetIn(i+1)->getOffset());
uintb mask = ~((uintb)0);
int4 bit = fd->getArch()->funcptr_align;
if (bit != 0) {
mask = (mask >> bit) << bit;
}
for(int4 index=0;indexevaluate(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() + ": has wrong number of parameters");
inputs[0] = 0;
uintb defaultAddress = defaultScript->evaluate(inputs);
addresstable.push_back(Address(spc,defaultAddress)); // Add default location to end of addresstable
}
void JumpAssisted::buildLabels(Funcdata *fd,vector &addresstable,vector &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;igetArch()->pcodeinjectlib->getPayload(userop->getIndex2Case());
vector inputs;
int4 numInputs = assistOp->numInput() - 1; // How many remaining varnodes after useropid
if (numInputs != pcodeScript->sizeInput())
throw LowlevelError(userop->getName() + ": has wrong number of parameters");
for(int4 i=0;igetIn(i+1)->getOffset());
for(int4 index=0;indexevaluate(inputs);
label.push_back(output);
}
}
label.push_back(0xBAD1ABE1); // Add fake label to match the defaultAddress
}
Varnode *JumpAssisted::foldInNormalization(Funcdata *fd,PcodeOp *indop)
{
// Replace all outputs of jumpassist op with switchvn (including BRANCHIND)
Varnode *outvn = assistOp->getOut();
list::const_iterator iter = outvn->beginDescend();
while(iter != outvn->endDescend()) {
PcodeOp *op = *iter;
++iter;
fd->opSetInput(op,switchvn,0);
}
fd->opDestroy(assistOp); // Get rid of the assist op (it has served its purpose)
return switchvn;
}
bool JumpAssisted::foldInGuards(Funcdata *fd,JumpTable *jump)
{
int4 origVal = jump->getDefaultBlock();
jump->setLastAsMostCommon(); // Default case is always the last block
return (origVal != jump->getDefaultBlock());
}
JumpModel *JumpAssisted::clone(JumpTable *jt) const
{
JumpAssisted *clone = new JumpAssisted(jt);
clone->userop = userop;
clone->sizeIndices = sizeIndices;
return clone;
}
/// Try to recover each model in turn, until we find one that matches the specific BRANCHIND.
/// \param fd is the function containing the switch
void JumpTable::recoverModel(Funcdata *fd)
{
if (jmodel != (JumpModel *)0) {
if (jmodel->isOverride()) { // If preexisting model is override
jmodel->recoverModel(fd,indirect,0,maxtablesize);
return;
}
delete jmodel; // Otherwise this is an old attempt we should remove
}
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;
}
/// Check that the BRANCHIND is still reachable, if not throw JumptableNotReachableError.
/// Check pathological cases when there is only one address in the table, if we find
/// this, throw the JumptableThunkError. Let the model run its sanity check.
/// Print a warning if the sanity check truncates the original address table.
/// \param fd is the function containing the switch
void JumpTable::sanityCheck(Funcdata *fd)
{
uint4 sz = addresstable.size();
if (!isReachable(indirect))
throw JumptableNotReachableError("No legal flow");
if (addresstable.size() == 1) { // One entry is likely some kind of thunk
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()) // If address table was resized
fd->warning("Sanity check requires truncation of jumptable",opaddress);
}
/// Given a specific basic-block, figure out which edge out of the switch block
/// hits it. This \e position is different from the index into the address table,
/// the out edges are deduped and may include additional guard destinations.
/// If no edge hits it, throw an exception.
/// \param bl is the specific basic-block
/// \return the position of the basic-block
int4 JumpTable::block2Position(const FlowBlock *bl) const
{
FlowBlock *parent;
int4 position;
parent = indirect->getParent();
for(position=0;positionsizeIn();++position)
if (bl->getIn(position) == parent) break;
if (position==bl->sizeIn())
throw LowlevelError("Requested block, not in jumptable");
return bl->getInRevIndex(position);
}
/// We are not doing a complete check, we are looking for a guard that has collapsed to "if (false)"
/// \param op is the given PcodeOp to check
/// \return \b true is the PcodeOp is reachable
bool JumpTable::isReachable(PcodeOp *op)
{
BlockBasic *parent = op->getParent();
for(int4 i=0;i<2;++i) { // Only check two levels
if (parent->sizeIn() != 1) return true;
BlockBasic *bl = (BlockBasic *)parent->getIn(0);
if (bl->sizeOut() != 2) continue; // Check if -bl- looks like it contains a guard
PcodeOp *cbranch = bl->lastOp();
if ((cbranch==(PcodeOp *)0)||(cbranch->code() != CPUI_CBRANCH))
continue;
Varnode *vn = cbranch->getIn(1); // Get the boolean variable
if (!vn->isConstant()) continue; // Has the guard collapsed
int4 trueslot = cbranch->isBooleanFlip() ? 0: 1;
if (vn->getOffset() == 0)
trueslot = 1 - trueslot;
if (bl->getOut(trueslot) != parent) // If the remaining path does not lead to -op-
return false; // return that op is not reachable
parent = bl;
}
return true;
}
/// \param g is the Architecture the table exists within
/// \param ad is the Address of the BRANCHIND \b this models
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;
}
/// This is a partial clone of another jump-table. Objects that are specific
/// to the particular Funcdata instance must be recalculated.
/// \param op2 is the jump-table to clone
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;
// We just clone the addresses themselves
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;
}
/// \brief Return the number of address table entries that target the given basic-block
///
/// \param bl is the given basic-block
/// \return the count of entries
int4 JumpTable::numIndicesByBlock(const FlowBlock *bl) const
{
IndexPair val(block2Position(bl),0);
pair::const_iterator,vector::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();
}
/// \brief Force manual override information on \b this jump-table.
///
/// The model is switched over to JumpBasicOverride, which is initialized with an externally
/// provided list of addresses. The addresses are forced as the output addresses the BRANCHIND
/// for \b this jump-table. If a non-zero hash and an address is provided, this identifies a
/// specific Varnode to use as the normalized switch variable. A potential starting value for
/// normalized switch variable range is provided.
/// \param addrtable is the manually provided list of addresses to put in the address table
/// \param naddr is the address where the normalized switch variable is defined
/// \param h is a hash identifying the normalized switch variable (or 0)
/// \param sv is the starting value for the range of possible normalized switch variable values (usually 0)
void JumpTable::setOverride(const vector &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);
}
/// \brief Get the index of the i-th address table entry that corresponds to the given basic-block
///
/// An exception is thrown if no address table entry targets the block.
/// \param bl is the given basic-block
/// \param i requests a specific position within the duplicate entries
/// \return the address table index
int4 JumpTable::getIndexByBlock(const FlowBlock *bl,int4 i) const
{
IndexPair val(block2Position(bl),0);
int4 count = 0;
vector::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;
}
/// This is used to add address targets from guard branches if they are
/// not already in the address table. A specific case label for the block
/// can also be provided. The new target is appended directly to the end of the table.
/// \param bl is the given basic-block
/// \param lab is the case label for the block
void JumpTable::addBlockToSwitch(BlockBasic *bl,uintb lab)
{
addresstable.push_back(bl->getStart());
lastBlock = indirect->getParent()->sizeOut(); // The block WILL be added to the end of the out-edges
block2addr.push_back(IndexPair(lastBlock,addresstable.size()-1));
label.push_back(lab);
}
/// Convert addresses in \b this table to actual targeted basic-blocks.
///
/// This constructs a map from each out-edge from the basic-block containing the BRANCHIND
/// to addresses in the table targetting that out-block. The most common
/// address table entry is also calculated here.
/// \param flow is used to resolve address targets
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;igetParent();
for(pos=0;possizeOut();++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; // Out-edge of last address in table
sort(block2addr.begin(),block2addr.end());
defaultBlock = -1; // There is no default case initially
int4 maxcount = 1; // If the maxcount is less than 2
vector::const_iterator iter = block2addr.begin();
while(iter != block2addr.end()) {
int4 curPos = (*iter).blockPosition;
vector::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;
}
}
}
/// Eliminate any code involved in actually computing the destination address so
/// it looks like the CPUI_BRANCHIND operation does it all internally.
/// \param fd is the function containing \b this switch
void JumpTable::foldInNormalization(Funcdata *fd)
{
Varnode *switchvn = jmodel->foldInNormalization(fd,indirect);
if (switchvn != (Varnode *)0) {
// If possible, mark up the switch variable as not fully consumed so that
// subvariable flow can truncate it.
switchVarConsume = minimalmask(switchvn->getNZMask());
if (switchVarConsume >= calc_mask(switchvn->getSize())) { // If mask covers everything
if (switchvn->isWritten()) {
PcodeOp *op = switchvn->getDef();
if (op->code() == CPUI_INT_SEXT) { // Check for a signed extension
switchVarConsume = calc_mask(op->getIn(0)->getSize()); // Assume the extension is not consumed
}
}
}
}
}
/// Make exactly one case for each output edge of the switch block.
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;isizeOut();++i)
block2addr.push_back(IndexPair(i,i)); // Addresses corresponds exactly to out-edges of switch block
lastBlock = parent->sizeOut()-1;
defaultBlock = -1; // Trivial case does not have default case
}
/// The addresses that the raw BRANCHIND op might branch to itself are recovered,
/// not including other targets of the final model, like guard addresses. The normalized switch
/// variable and the guards are identified in the process however.
///
/// Generally this method is run during flow analysis when we only have partial information about
/// the function (and possibly the switch itself). The Funcdata instance is a partial clone of the
/// function and is different from the final instance that will hold the fully recovered jump-table.
/// The final instance inherits the addresses recovered here, but recoverModel() will need to be
/// run on it separately.
///
/// A sanity check is also run, which might truncate the original set of addresses.
/// \param fd is the function containing the switch
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 (sz < 2)
// fd->warning("Jumptable has only one branch",opaddress);
if (collectloads)
jmodel->buildAddresses(fd,indirect,addresstable,&loadpoints);
else
jmodel->buildAddresses(fd,indirect,addresstable,(vector *)0);
sanityCheck(fd);
}
/// Do a normal recoverAddresses, but save off the old JumpModel, and if we fail recovery, put back the old model.
/// \param fd is the function containing the switch
void JumpTable::recoverMultistage(Funcdata *fd)
{
if (origmodel != (JumpModel *)0)
delete origmodel;
origmodel = jmodel;
jmodel = (JumpModel *)0;
vector 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) { // Keep the new model if it was created successfully
delete origmodel;
origmodel = (JumpModel *)0;
}
}
/// This is run assuming the address table has already been recovered, via recoverAddresses() in another
/// Funcdata instance. So recoverModel() needs to be rerun on the instance passed in here.
///
/// The unnormalized switch variable is recovered, and for each possible address table entry, the variable
/// value that produces it is calculated and stored as the formal \e case label for the associated code block.
/// \param fd is the (final instance of the) function containing the switch
/// \return \b true if it looks like a multi-stage restart is needed.
bool JumpTable::recoverLabels(Funcdata *fd)
{
if (!isRecovered())
throw LowlevelError("Trying to recover jumptable labels without addresses");
// Unless the model is an override, move model (created on a flow copy) so we can create a current instance
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); // Create a current instance of the model
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 *)0);
trivialSwitchOver();
jmodel->buildLabels(fd,addresstable,label,origmodel);
}
if (origmodel != (JumpModel *)0) {
delete origmodel;
origmodel = (JumpModel *)0;
}
return multistagerestart;
}
/// Clear out any data that is specific to a Funcdata instance. The address table is not cleared
/// if it was recovered, and override information is left intact.
/// Right now this is only getting called, when the jumptable is an override in order to clear out derived data.
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;
// -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent
}
/// The recovered addresses and case labels are saved to the XML stream.
/// If override information is present, this is also incorporated into the tag.
/// \param s is the stream to write to
void JumpTable::saveXml(ostream &s) const
{
if (!isRecovered())
throw LowlevelError("Trying to save unrecovered jumptable");
s << "\n";
opaddress.saveXml(s);
s << '\n';
for(int4 i=0;isaveXmlAttributes(s,off);
if (i\n";
}
if (!loadpoints.empty()) {
for(int4 i=0;iisOverride()))
jmodel->saveXml(s);
s << "\n";
}
/// Restore the addresses, \e case labels, and any override information from the tag.
/// Other parts of the model and jump-table will still need to be recovered.
/// \param el is the root \ tag to restore from
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;igetAttributeName(i) == "label") break;
}
if (igetAttributeValue(i));
s1.unsetf(ios::dec | ios::hex | ios::oct);
uintb lab;
s1 >> lab;
label.push_back(lab);
}
else // No label attribute
missedlabel = true; // No following entries are allowed to have a label attribute
}
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);
}
}
/// Look for the override directive that indicates we need an additional recovery stage for
/// \b this jump-table.
/// \param fd is the function containing the switch
/// \return \b true if an additional recovery stage is required.
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; // Mark that we need additional recovery
return true;
}
return false;
}