upto f8cca3e -- preparing for MULTIEQUAL/INDIRECT traversals

This commit is contained in:
caheckman 2019-06-06 15:17:57 -04:00
parent 25894ff9ae
commit 5d2a7a3134
3 changed files with 296 additions and 29 deletions

View file

@ -454,6 +454,220 @@ void Heritage::splitPieces(const vector<Varnode *> &vnlist,PcodeOp *insertop,
} }
} }
/// \brief Find the last PcodeOps that write to specific addresses that flow to specific sites
///
/// Given a set of sites for which data-flow needs to be preserved at a specific address, find
/// the \e last ops that write to the address such that data flows to the site
/// only through \e artificial COPYs and MULTIEQUALs. A COPY/MULTIEQUAL is artificial if all
/// of its input and output Varnodes have the same storage address. The specific sites are
/// presented as artificial COPY ops. The final set of ops that are not artificial will all
/// have an output Varnode that matches the specific address of a COPY sink and will need to
/// be marked address forcing. The original set of COPY sinks will be extended to all artificial
/// COPY/MULTIEQUALs encountered. Every PcodeOp encountered will have its mark set.
/// \param copySinks is the list of sinks that we are trying to find flow to
/// \param forces is the final list of address forcing PcodeOps
void Heritage::findAddressForces(vector<PcodeOp *> &copySinks,vector<PcodeOp *> &forces)
{
// Mark the sinks
for(int4 i=0;i<copySinks.size();++i) {
PcodeOp *op = copySinks[i];
op->setMark();
}
// Mark everything back-reachable from a sink, trimming at non-artificial ops
int4 pos = 0;
while(pos < copySinks.size()) {
PcodeOp *op = copySinks[pos];
Address addr = op->getOut()->getAddr(); // Address being flowed to
pos += 1;
int4 maxIn = op->numInput();
for(int4 i=0;i<maxIn;++i) {
Varnode *vn = op->getIn(i);
if (!vn->isWritten()) continue;
PcodeOp *newOp = vn->getDef();
if (newOp->isMark()) continue; // Already visited this op
newOp->setMark();
OpCode opc = newOp->code();
bool isArtificial = (opc == CPUI_COPY || opc == CPUI_MULTIEQUAL);
if (isArtificial) {
int4 maxInNew = newOp->numInput();
for(int4 j=0;j<maxInNew;++j) {
Varnode *inVn = newOp->getIn(j);
if (addr != inVn->getAddr()) {
isArtificial = false;
break;
}
}
}
if (isArtificial)
copySinks.push_back(newOp);
else
forces.push_back(newOp);
}
}
}
/// \brief Eliminate a COPY sink preserving its data-flow
///
/// Given a COPY from a storage location to itself, propagate the input Varnode
/// version of the storage location to all the ops reading the output Varnode, so
/// the output no longer has any descendants. Then eliminate the COPY.
/// \param op is the given COPY sink
void Heritage::propagateCopyAway(PcodeOp *op)
{
Varnode *inVn = op->getIn(0);
while(inVn->isWritten()) { // Follow any COPY chain to earliest input
PcodeOp *nextOp = inVn->getDef();
if (nextOp->code() != CPUI_COPY) break;
Varnode *nextIn = nextOp->getIn(0);
if (nextIn->getAddr() != inVn->getAddr()) break;
inVn = nextIn;
}
fd->totalReplace(op->getOut(),inVn);
fd->opDestroy(op);
}
/// \brief Mark the boundary of artificial ops introduced by load guards
///
/// Having just completed renaming, run through all new COPY sinks from load guards
/// and mark boundary Varnodes (Varnodes whose data-flow along all paths traverses only
/// COPY/INDIRECT/MULTIEQUAL ops and hits a load guard). This lets dead code removal
/// run forward from the boundary while still preserving the address force on the load guard.
void Heritage::handleNewLoadCopies(void)
{
vector<PcodeOp *> forces;
int4 copySinkSize = loadCopyOps.size();
findAddressForces(loadCopyOps, forces);
// Mark everything on the boundary as address forced to prevent dead-code removal
for(int4 i=0;i<forces.size();++i) {
PcodeOp *op = forces[i];
op->getOut()->setAddrForce();
op->clearMark();
}
// Eliminate or propagate away original COPY sinks
for(int4 i=0;i<copySinkSize;++i) {
PcodeOp *op = loadCopyOps[i];
propagateCopyAway(op); // Make sure load guard COPYs no longer exist
}
// Clear marks on remaining artificial COPYs
for(int4 i=copySinkSize;i<loadCopyOps.size();++i) {
PcodeOp *op = loadCopyOps[i];
op->clearMark();
}
loadCopyOps.clear(); // We have handled all the load guard COPY ops
}
/// \brief Generate a guard record given an indexed LOAD into a stack space
///
/// Record the LOAD op and the (likely) range of addresses in the stack space that
/// might be loaded from.
/// \param node is the path element containing the constructed Address
/// \param op is the LOAD PcodeOp
/// \param spc is the stack space
void Heritage::generateLoadGuard(StackNode &node,PcodeOp *op,AddrSpace *spc)
{
uintb spaceMax = spc->getHighest();
if (node.offset > spaceMax) return; // Something unusual is going on
loadGuard.push_back(LoadGuard());
LoadGuard &guard( loadGuard.back() );
guard.op = op;
guard.spc = spc;
guard.minimumOffset = node.offset;
uintb sz = spaceMax - node.offset;
if (sz > 0x1000)
guard.maximumOffset = node.offset + 0x1000;
else
guard.maximumOffset = spaceMax;
}
/// \brief Trace input stackpointer to any indexed loads
///
/// Look for expressions of the form val = *(SP(i) + vn + #c), where the base stack
/// pointer has an (optional) constant added to it and a non-constant index, then a
/// value is loaded from the resulting address. The LOAD operations are added to the list
/// of ops that potentially need to be guarded during a heritage pass.
/// \param spc is the particular address space with a stackpointer (into it)
void Heritage::discoverIndexedStackLoads(AddrSpace *spc)
{
for(int4 i=0;i<spc->numSpacebase();++i) {
const VarnodeData &stackPointer(spc->getSpacebase(i));
Varnode *spInput = fd->findVarnodeInput(stackPointer.size, stackPointer.getAddr());
if (spInput == (Varnode *)0) continue;
vector<StackNode> path;
spInput->setMark();
path.push_back(StackNode(spInput,0,0));
while(!path.empty()) {
StackNode &curNode(path.back());
if (curNode.iter == curNode.vn->endDescend()) {
path.back().vn->clearMark();
path.pop_back();
continue;
}
PcodeOp *op = *curNode.iter;
++curNode.iter;
Varnode *outVn = op->getOut();
if (outVn == (Varnode *)0 || outVn->isMark()) continue; // Trim loops
switch(op->code()) {
case CPUI_INT_ADD:
{
Varnode *otherVn = op->getIn(1-op->getSlot(curNode.vn));
if (otherVn->isConstant()) {
uintb newOffset = curNode.offset + otherVn->getOffset();
StackNode nextNode(outVn,newOffset,curNode.traversals);
if (nextNode.iter != nextNode.vn->endDescend()) {
outVn->setMark();
path.push_back(nextNode);
}
}
else {
StackNode nextNode(outVn,curNode.offset,curNode.traversals | StackNode::nonconstant_index);
if (nextNode.iter != nextNode.vn->endDescend()) {
outVn->setMark();
path.push_back(nextNode);
}
}
break;
}
// case CPUI_INDIRECT:
case CPUI_COPY:
{
StackNode nextNode(outVn,curNode.offset,curNode.traversals);
if (nextNode.iter != nextNode.vn->endDescend()) {
outVn->setMark();
path.push_back(nextNode);
}
break;
}
// case CPUI_MULTIEQUAL:
// {
// StackNode nextNode(outVn,curNode.offset,curNode.traversals | StackNode::multiequal);
// if (nextNode.iter != nextNode.vn->endDescend()) {
// outVn->setMark();
// path.push_back(nextNode);
// }
// break;
// }
case CPUI_LOAD:
{
if (curNode.traversals != 0) {
generateLoadGuard(curNode,op,spc);
}
break;
}
default:
break;
}
}
}
}
/// \brief Normalize p-code ops so that phi-node placement and renaming works /// \brief Normalize p-code ops so that phi-node placement and renaming works
/// ///
/// The traditional phi-node placement and renaming algorithms don't expect /// The traditional phi-node placement and renaming algorithms don't expect
@ -516,7 +730,7 @@ void Heritage::guard(const Address &addr,int4 size,vector<Varnode *> &read,vecto
guardReturns(flags,addr,size,write); guardReturns(flags,addr,size,write);
if (fd->getArch()->highPtrPossible(addr,size)) { if (fd->getArch()->highPtrPossible(addr,size)) {
guardStores(addr,size,write); guardStores(addr,size,write);
// guardLoads(flags,addr,size,write); guardLoads(flags,addr,size,write);
} }
} }
} }
@ -632,6 +846,41 @@ void Heritage::guardStores(const Address &addr,int4 size,vector<Varnode *> &writ
} }
} }
/// \brief Guard LOAD ops in preparation for the renaming algorithm
///
/// The op must be in the loadGuard list, which means it may pull values from an indexed
/// range on the stack. A COPY guard is placed for the given range on any LOAD op whose
/// indexed range it intersects.
/// \param flags is boolean properties associated with the address
/// \param addr is the first address of the given range
/// \param size is the number of bytes in the given range
/// \param write is the list of written Varnodes in the range (may be updated)
void Heritage::guardLoads(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write)
{
PcodeOp *copyop;
list<LoadGuard>::iterator iter;
if ((flags & Varnode::addrtied)==0) return; // If not address tied, don't consider for index alias
for(iter=loadGuard.begin();iter!=loadGuard.end();++iter) {
LoadGuard &guardRec(*iter);
if (guardRec.spc != addr.getSpace()) continue;
if (addr.getOffset() < guardRec.minimumOffset) continue;
if (addr.getOffset() > guardRec.maximumOffset) continue;
if (guardRec.op->isDead()) continue;
copyop = fd->newOp(1,guardRec.op->getAddr());
Varnode *vn = fd->newVarnodeOut(size,addr,copyop);
vn->setActiveHeritage();
vn->setAddrForce();
fd->opSetOpcode(copyop,CPUI_COPY);
Varnode *invn = fd->newVarnode(size,addr);
invn->setActiveHeritage();
fd->opSetInput(copyop,invn,0);
fd->opInsertBefore(copyop,guardRec.op);
loadCopyOps.push_back(copyop);
}
}
/// \brief Guard global data-flow at RETURN ops in preparation for renaming /// \brief Guard global data-flow at RETURN ops in preparation for renaming
/// ///
/// For the given global (persistent) address range, data-flow must persist up to /// For the given global (persistent) address range, data-flow must persist up to
@ -682,31 +931,6 @@ void Heritage::guardReturns(uint4 flags,const Address &addr,int4 size,vector<Var
} }
} }
// void Heritage::guardLoads(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write)
// {
// list<PcodeOp *>::const_iterator iter,iterend;
// PcodeOp *op,*copyop;
// iterend = fd->endOp(CPUI_LOAD);
// for(iter=fd->beginOp(CPUI_LOAD);iter!=iterend;++iter) {
// op = *iter;
// if (op->isDead()) continue;
// // Check if load could possible read from this addr
// if (!Address::getSpaceFromConst(op->getIn(0)->getAddr())->contain(addr.getSpace()))
// continue;
// copyop = fd->newOp(1,op->getAddr());
// Varnode *vn = fd->newVarnodeOut(size,addr,copyop);
// vn->setActiveHeritage();
// vn->setAddrForce();
// fd->opSetOpcode(copyop,CPUI_COPY);
// Varnode *invn = fd->newVarnode(size,addr);
// vn->setActiveHeritage();
// fd->opSetInput(copyop,invn,0);
// fd->opInsertBefore(copyop,op);
// }
// }
/// \brief Build a refinement array given an address range and a list of Varnodes /// \brief Build a refinement array given an address range and a list of Varnodes
/// ///
/// The array is a preallocated array of ints, one for each byte in the address /// The array is a preallocated array of ints, one for each byte in the address
@ -1674,6 +1898,10 @@ void Heritage::heritage(void)
if (!space->isHeritaged()) continue; if (!space->isHeritaged()) continue;
info = getInfo(space); info = getInfo(space);
if (pass < info->delay) continue; // It is too soon to heritage this space if (pass < info->delay) continue; // It is too soon to heritage this space
if (!info->loadGuardSearch) {
info->loadGuardSearch = true;
discoverIndexedStackLoads(info->space);
}
needwarning = false; needwarning = false;
iter = fd->beginLoc(space); iter = fd->beginLoc(space);
enditer = fd->endLoc(space); enditer = fd->endLoc(space);
@ -1727,6 +1955,8 @@ void Heritage::heritage(void)
} }
placeMultiequals(); placeMultiequals();
rename(); rename();
if (!loadCopyOps.empty())
handleNewLoadCopies();
if (pass == 0) if (pass == 0)
splitmanage.splitAdditional(); splitmanage.splitAdditional();
pass += 1; pass += 1;
@ -1835,6 +2065,7 @@ void Heritage::clear(void)
depth.clear(); depth.clear();
merge.clear(); merge.clear();
clearInfoList(); clearInfoList();
loadGuard.clear();
maxdepth = -1; maxdepth = -1;
pass = 0; pass = 0;
} }

View file

@ -87,9 +87,10 @@ class HeritageInfo {
int4 delay; ///< How many passes to delay heritage of this space int4 delay; ///< How many passes to delay heritage of this space
int4 deadcodedelay; ///< How many passes to delay deadcode removal of this space int4 deadcodedelay; ///< How many passes to delay deadcode removal of this space
int4 deadremoved; ///< >0 if Varnodes in this space have been eliminated int4 deadremoved; ///< >0 if Varnodes in this space have been eliminated
bool loadGuardSearch; ///< \b true if the search for LOAD ops to guard has been performed
bool warningissued; ///< \b true if warning issued previously bool warningissued; ///< \b true if warning issued previously
HeritageInfo(AddrSpace *spc,int4 dl,int4 dcdl) { HeritageInfo(AddrSpace *spc,int4 dl,int4 dcdl) {
space=spc; delay=dl; deadcodedelay=dcdl; deadremoved=0; warningissued=false; } ///< Constructor space=spc; delay=dl; deadcodedelay=dcdl; deadremoved=0; loadGuardSearch=false; warningissued=false; } ///< Constructor
}; };
/// \brief Manage the construction of Static Single Assignment (SSA) form /// \brief Manage the construction of Static Single Assignment (SSA) form
@ -134,6 +135,33 @@ class Heritage {
mark_node = 2, ///< Node has already been in queue mark_node = 2, ///< Node has already been in queue
merged_node = 4 ///< Node has already been merged merged_node = 4 ///< Node has already been merged
}; };
/// \brief Node for depth-first traversal of stack references
struct StackNode {
enum {
nonconstant_index = 1,
multiequal = 2
};
Varnode *vn; ///< Varnode being traversed
uintb offset; ///< Offset relative to base
uint4 traversals; ///< What kind of operations has this pointer accumulated
list<PcodeOp *>::const_iterator iter; ///< Next PcodeOp to follow
StackNode(Varnode *v,uintb o,uint4 trav) {
vn = v;
offset = o;
iter = v->beginDescend();
traversals = trav;
}
};
/// \brief Description of a LOAD operation that needs to be guarded
struct LoadGuard {
PcodeOp *op; ///< The LOAD op
AddrSpace *spc; ///< The stack space being loaded from
uintb minimumOffset; ///< Minimum offset of the LOAD
uintb maximumOffset; ///< Maximum offset of the LOAD
};
Funcdata *fd; ///< The function \b this is controlling SSA construction Funcdata *fd; ///< The function \b this is controlling SSA construction
LocationMap globaldisjoint; ///< Disjoint cover of every heritaged memory location LocationMap globaldisjoint; ///< Disjoint cover of every heritaged memory location
LocationMap disjoint; ///< Disjoint cover of memory locations currently being heritaged LocationMap disjoint; ///< Disjoint cover of memory locations currently being heritaged
@ -147,6 +175,8 @@ class Heritage {
PriorityQueue pq; ///< Priority queue for phi-node placement PriorityQueue pq; ///< Priority queue for phi-node placement
vector<FlowBlock *> merge; ///< Calculate merge points (blocks containing phi-nodes) vector<FlowBlock *> merge; ///< Calculate merge points (blocks containing phi-nodes)
vector<HeritageInfo> infolist; ///< Heritage status for individual address spaces vector<HeritageInfo> infolist; ///< Heritage status for individual address spaces
list<LoadGuard> loadGuard; ///< List of LOAD operations that need to be guarded
vector<PcodeOp *> loadCopyOps; ///< List of COPY ops generated by load guards
void clearInfoList(void); ///< Reset heritage status for all address spaces void clearInfoList(void); ///< Reset heritage status for all address spaces
/// \brief Get the heritage status for the given address space /// \brief Get the heritage status for the given address space
@ -168,12 +198,17 @@ class Heritage {
Varnode *normalizeWriteSize(Varnode *vn,const Address &addr,int4 size); Varnode *normalizeWriteSize(Varnode *vn,const Address &addr,int4 size);
Varnode *concatPieces(const vector<Varnode *> &vnlist,PcodeOp *insertop,Varnode *finalvn); Varnode *concatPieces(const vector<Varnode *> &vnlist,PcodeOp *insertop,Varnode *finalvn);
void splitPieces(const vector<Varnode *> &vnlist,PcodeOp *insertop,const Address &addr,int4 size,Varnode *startvn); void splitPieces(const vector<Varnode *> &vnlist,PcodeOp *insertop,const Address &addr,int4 size,Varnode *startvn);
void findAddressForces(vector<PcodeOp *> &copySinks,vector<PcodeOp *> &forces);
void propagateCopyAway(PcodeOp *op);
void handleNewLoadCopies(void);
void generateLoadGuard(StackNode &node,PcodeOp *op,AddrSpace *spc);
void discoverIndexedStackLoads(AddrSpace *spc);
void guard(const Address &addr,int4 size,vector<Varnode *> &read,vector<Varnode *> &write,vector<Varnode *> &inputvars); void guard(const Address &addr,int4 size,vector<Varnode *> &read,vector<Varnode *> &write,vector<Varnode *> &inputvars);
void guardInput(const Address &addr,int4 size,vector<Varnode *> &input); void guardInput(const Address &addr,int4 size,vector<Varnode *> &input);
void guardCalls(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write); void guardCalls(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write);
void guardStores(const Address &addr,int4 size,vector<Varnode *> &write); void guardStores(const Address &addr,int4 size,vector<Varnode *> &write);
void guardLoads(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write);
void guardReturns(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write); void guardReturns(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write);
// void guardLoads(uint4 flags,const Address &addr,int4 size,vector<Varnode *> &write);
static void buildRefinement(vector<int4> &refine,const Address &addr,int4 size,const vector<Varnode *> &vnlist); static void buildRefinement(vector<int4> &refine,const Address &addr,int4 size,const vector<Varnode *> &vnlist);
void splitByRefinement(Varnode *vn,const Address &addr,const vector<int4> &refine,vector<Varnode *> &split); void splitByRefinement(Varnode *vn,const Address &addr,const vector<int4> &refine,vector<Varnode *> &split);
void refineRead(Varnode *vn,const Address &addr,const vector<int4> &refine,vector<Varnode *> &newvn); void refineRead(Varnode *vn,const Address &addr,const vector<int4> &refine,vector<Varnode *> &newvn);

View file

@ -3347,7 +3347,8 @@ int4 RulePropagateCopy::applyOp(PcodeOp *op,Funcdata &data)
if (invn == vn) if (invn == vn)
throw LowlevelError("Self-defined varnode"); throw LowlevelError("Self-defined varnode");
if (op->isMarker()) { if (op->isMarker()) {
if (invn->isConstant()) continue; // Don't propagate constants into markers if (invn->isConstant()) continue; // Don't propagate constants into markers
if (vn->isAddrForce()) continue; // Don't propagate if we are keeping the COPY anyway
if (invn->isAddrTied() && op->getOut()->isAddrTied() && if (invn->isAddrTied() && op->getOut()->isAddrTied() &&
(op->getOut()->getAddr() != invn->getAddr())) (op->getOut()->getAddr() != invn->getAddr()))
continue; // We must not allow merging of different addrtieds continue; // We must not allow merging of different addrtieds