/* ###
* 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 "heritage.hh"
#include "funcdata.hh"
#include "prefersplit.hh"
/// Update disjoint cover making sure (addr,size) is contained in a single element
/// and return iterator to this element. Pass back \b intersect value:
/// - 0 if the only intersection is with range from the same pass
/// - 1 if there is a partial intersection with something old
/// - 2 if the range is contained in an old range
/// \param addr is the starting address of the range to add
/// \param size is the number of bytes in the range
/// \param pass is the pass number when the range was heritaged
/// \param intersect is a reference for passing back the intersect code
/// \return the iterator to the map element containing the added range
LocationMap::iterator LocationMap::add(Address addr,int4 size,int4 pass,int4 &intersect)
{
iterator iter = themap.lower_bound(addr);
if (iter != themap.begin())
--iter;
if ((iter!=themap.end())&&(-1 == addr.overlap(0,(*iter).first,(*iter).second.size)))
++iter;
int4 where=0;
intersect = 0;
if ((iter!=themap.end())&&(-1!=(where=addr.overlap(0,(*iter).first,(*iter).second.size)))) {
if (where+size<=(*iter).second.size) {
intersect = ((*iter).second.pass < pass) ? 2 : 0; // Completely contained in previous element
return iter;
}
addr = (*iter).first;
size = where+size;
if ((*iter).second.pass < pass)
intersect = 1; // Partial overlap with old element
themap.erase(iter++);
}
while((iter!=themap.end())&&(-1!=(where=(*iter).first.overlap(0,addr,size)))) {
if (where+(*iter).second.size>size)
size = where+(*iter).second.size;
if ((*iter).second.pass < pass)
intersect = 1;
themap.erase(iter++);
}
iter = themap.insert(pair
( addr, SizePass() )).first;
(*iter).second.size = size;
(*iter).second.pass = pass;
return iter;
}
/// If the given address was heritaged, return (the iterator to) the SizeMap entry
/// describing the associated range and when it was heritaged.
/// \param addr is the given address
/// \return the iterator to the SizeMap entry or the end iterator is the address is unheritaged
LocationMap::iterator LocationMap::find(const Address &addr)
{
iterator iter = themap.upper_bound(addr); // First range after address
if (iter == themap.begin()) return themap.end();
--iter; // First range before or equal to address
if (-1!=addr.overlap(0,(*iter).first,(*iter).second.size))
return iter;
return themap.end();
}
/// Return the pass number when the given address was heritaged, or -1 if it was not heritaged
/// \param addr is the given address
/// \return the pass number of -1
int4 LocationMap::findPass(const Address &addr) const
{
map::const_iterator iter = themap.upper_bound(addr); // First range after address
if (iter == themap.begin()) return -1;
--iter; // First range before or equal to address
if (-1!=addr.overlap(0,(*iter).first,(*iter).second.size))
return (*iter).second.pass;
return -1;
}
/// Any basic blocks currently in \b this queue are removed. Space is
/// reserved for a new set of prioritized stacks.
/// \param maxdepth is the number of stacks to allocate
void PriorityQueue::reset(int4 maxdepth)
{
if ((curdepth==-1)&&(maxdepth==queue.size()-1)) return; // Already reset
queue.clear();
queue.resize(maxdepth+1);
curdepth = -1;
}
/// The block is pushed onto the stack of the given priority.
/// \param bl is the block being added to the queue
/// \param depth is the priority to associate with the block
void PriorityQueue::insert(FlowBlock *bl,int4 depth)
{
queue[depth].push_back(bl);
if (depth > curdepth)
curdepth = depth;
}
/// The block at the top of the highest priority non-empty stack is popped
/// and returned. This will always return a block. It shouldn't be called if the
/// queue is empty.
/// \return the highest priority block
FlowBlock *PriorityQueue::extract(void)
{
FlowBlock *res = queue[curdepth].back();
queue[curdepth].pop_back();
while(queue[curdepth].empty()) {
curdepth -= 1;
if (curdepth <0) break;
}
return res;
}
/// Initialize heritage state information for a particular address space
/// \param spc is the address space
HeritageInfo::HeritageInfo(AddrSpace *spc)
{
if (spc == (AddrSpace *)0) {
space = (AddrSpace *)0;
delay = 0;
deadcodedelay = 0;
hasCallPlaceholders = false;
}
else if (!spc->isHeritaged()) {
space = (AddrSpace *)0;
delay = spc->getDelay();
deadcodedelay = spc->getDeadcodeDelay();
hasCallPlaceholders = false;
}
else {
space = spc;
delay = spc->getDelay();
deadcodedelay = spc->getDeadcodeDelay();
hasCallPlaceholders = (spc->getType() == IPTR_SPACEBASE);
}
deadremoved = 0;
warningissued = false;
loadGuardSearch = false;
}
void HeritageInfo::reset(void)
{
// Leave any override intact: deadcodedelay = delay;
deadremoved = 0;
if (space != (AddrSpace *)0)
hasCallPlaceholders = (space->getType() == IPTR_SPACEBASE);
warningissued = false;
loadGuardSearch = false;
}
/// Instantiate the heritage manager for a particular function.
/// \param data is the function
Heritage::Heritage(Funcdata *data)
{
fd = data;
pass = 0;
maxdepth = -1;
}
void Heritage::clearInfoList(void)
{
vector::iterator iter;
for(iter=infolist.begin();iter!=infolist.end();++iter)
(*iter).reset();
}
/// \brief Remove deprecated CPUI_MULTIEQUAL or CPUI_INDIRECT ops, preparing to re-heritage
///
/// If a previous Varnode was heritaged through a MULTIEQUAL or INDIRECT op, but now
/// a larger range containing the Varnode is being heritaged, we throw away the op,
/// letting the data-flow for the new larger range determine the data-flow for the
/// old Varnode. The original Varnode is redefined as the output of a SUBPIECE
/// of a larger free Varnode.
/// \param remove is the list of Varnodes written by MULTIEQUAL or INDIRECT
/// \param addr is the start of the larger range
/// \param size is the size of the range
void Heritage::removeRevisitedMarkers(const vector &remove,const Address &addr,int4 size)
{
vector newInputs;
list::iterator pos;
for(int4 i=0;igetDef();
BlockBasic *bl = op->getParent();
if (op->code() == CPUI_INDIRECT) {
Varnode *iopVn = op->getIn(1);
PcodeOp *targetOp = PcodeOp::getOpFromConst(iopVn->getAddr());
if (targetOp->isDead())
pos = op->getBasicIter();
else
pos = targetOp->getBasicIter();
++pos; // Insert SUBPIECE after target of INDIRECT
}
else {
pos = op->getBasicIter(); // Insert SUBPIECE after all MULTIEQUALs in block
++pos;
while(pos != bl->endOp() && (*pos)->code() == CPUI_MULTIEQUAL)
++pos;
}
int4 offset = vn->overlap(addr,size);
fd->opUninsert(op);
newInputs.clear();
Varnode *big = fd->newVarnode(size,addr);
big->setActiveHeritage();
newInputs.push_back(big);
newInputs.push_back(fd->newConstant(4, offset));
fd->opSetOpcode(op, CPUI_SUBPIECE);
fd->opSetAllInput(op, newInputs);
fd->opInsert(op, bl, pos);
vn->setWriteMask();
}
}
/// \brief Collect free reads, writes, and inputs in the given address range
///
/// \param addr is the starting address of the range
/// \param size is the number of bytes in the range
/// \param read will hold any read Varnodes in the range
/// \param write will hold any written Varnodes
/// \param input will hold any input Varnodes
/// \param remove will hold any PcodeOps that need to be removed
/// \return the maximum size of a write
int4 Heritage::collect(Address addr,int4 size,
vector &read,vector &write,
vector &input,vector &remove) const
{
Varnode *vn;
VarnodeLocSet::const_iterator viter = fd->beginLoc(addr);
VarnodeLocSet::const_iterator enditer;
uintb start = addr.getOffset();
addr = addr + size;
if (addr.getOffset() < start) { // Wraparound
Address tmp(addr.getSpace(),addr.getSpace()->getHighest());
enditer = fd->endLoc(tmp);
}
else
enditer = fd->beginLoc(addr);
int4 maxsize = 0;
while( viter != enditer ) {
vn = *viter;
if (!vn->isWriteMask()) {
if (vn->isWritten()) {
if (vn->getSize() < size && vn->getDef()->isMarker())
remove.push_back(vn);
else {
if (vn->getSize() > maxsize) // Look for maximum write size
maxsize = vn->getSize();
write.push_back(vn);
}
}
else if ((!vn->isHeritageKnown())&&(!vn->hasNoDescend()))
read.push_back(vn);
else if (vn->isInput())
input.push_back(vn);
}
++viter;
}
return maxsize;
}
/// \brief Determine if the address range is affected by the given call p-code op
///
/// We assume the op is CALL, CALLIND, CALLOTHER, or NEW and that its
/// output overlaps the given address range. We look up any effect
/// the op might have on the address range.
/// \param addr is the starting address of the range
/// \param size is the number of bytes in the range
/// \param op is the given \e call p-code op
/// \return \b true, unless the range is unaffected by the op
bool Heritage::callOpIndirectEffect(const Address &addr,int4 size,PcodeOp *op) const
{
if ((op->code() == CPUI_CALL)||(op->code() == CPUI_CALLIND)) {
// We should be able to get the callspec
FuncCallSpecs *fc = fd->getCallSpecs(op);
if (fc == (FuncCallSpecs *)0) return true; // Assume indirect effect
return (fc->hasEffectTranslate(addr,size) != EffectRecord::unaffected);
}
// If we reach here, this is a CALLOTHER, NEW
// We assume these do not have effects on -fd- variables except for op->getOut().
return false;
}
/// \brief Normalize the size of a read Varnode, prior to heritage
///
/// Given a Varnode being read that does not match the (larger) size
/// of the address range currently being linked, create a Varnode of
/// the correct size and define the original Varnode as a SUBPIECE.
/// \param vn is the given too small Varnode
/// \param addr is the start of the (larger) range
/// \param size is the number of bytes in the range
/// \return the new larger Varnode
Varnode *Heritage::normalizeReadSize(Varnode *vn,const Address &addr,int4 size)
{
int4 overlap;
Varnode *vn1,*vn2;
PcodeOp *op,*newop;
list::const_iterator oiter = vn->beginDescend();
op = *oiter++;
if (oiter != vn->endDescend())
throw LowlevelError("Free varnode with multiple reads");
newop = fd->newOp(2,op->getAddr());
fd->opSetOpcode(newop,CPUI_SUBPIECE);
vn1 = fd->newVarnode(size,addr);
overlap = vn->overlap(addr,size);
vn2 = fd->newConstant(addr.getAddrSize(),(uintb)overlap);
fd->opSetInput(newop,vn1,0);
fd->opSetInput(newop,vn2,1);
fd->opSetOutput(newop,vn); // Old vn is no longer a free read
newop->getOut()->setWriteMask();
fd->opInsertBefore(newop,op);
return vn1; // But we have new free read of uniform size
}
/// \brief Normalize the size of a written Varnode, prior to heritage
///
/// Given a Varnode that is written that does not match the (larger) size
/// of the address range currently being linked, create the missing
/// pieces in the range and concatenate everything into a new Varnode
/// of the correct size.
///
/// One or more Varnode pieces are created depending
/// on how the original Varnode overlaps the given range. An expression
/// is created using PIECE ops resulting in a final Varnode.
/// \param vn is the given too small Varnode
/// \param addr is the start of the (larger) range
/// \param size is the number of bytes in the range
/// \return the newly created final Varnode
Varnode *Heritage::normalizeWriteSize(Varnode *vn,const Address &addr,int4 size)
{
int4 overlap;
int4 mostsigsize;
PcodeOp *op,*newop;
Varnode *mostvn,*leastvn,*big,*bigout,*midvn;
mostvn = (Varnode *)0;
op = vn->getDef();
overlap = vn->overlap(addr,size);
mostsigsize = size-(overlap+vn->getSize());
if (mostsigsize != 0) {
Address pieceaddr;
if (addr.isBigEndian())
pieceaddr = addr;
else
pieceaddr = addr + (overlap+vn->getSize());
if (op->isCall() && callOpIndirectEffect(pieceaddr,mostsigsize,op)) { // Does CALL have an effect on piece
newop = fd->newIndirectCreation(op,pieceaddr,mostsigsize,false); // Don't create a new big read if write is from a CALL
mostvn = newop->getOut();
}
else {
newop = fd->newOp(2,op->getAddr());
mostvn = fd->newVarnodeOut(mostsigsize,pieceaddr,newop);
big = fd->newVarnode(size,addr); // The new read
big->setActiveHeritage();
fd->opSetOpcode(newop,CPUI_SUBPIECE);
fd->opSetInput(newop,big,0);
fd->opSetInput(newop,fd->newConstant(addr.getAddrSize(),(uintb)overlap+vn->getSize()),1);
fd->opInsertBefore(newop,op);
}
}
if (overlap != 0) {
Address pieceaddr;
if (addr.isBigEndian())
pieceaddr = addr + (size-overlap);
else
pieceaddr = addr;
if (op->isCall() && callOpIndirectEffect(pieceaddr,overlap,op)) { // Unless CALL definitely has no effect on piece
newop = fd->newIndirectCreation(op,pieceaddr,overlap,false); // Don't create a new big read if write is from a CALL
leastvn = newop->getOut();
}
else {
newop = fd->newOp(2,op->getAddr());
leastvn = fd->newVarnodeOut(overlap,pieceaddr,newop);
big = fd->newVarnode(size,addr); // The new read
big->setActiveHeritage();
fd->opSetOpcode(newop,CPUI_SUBPIECE);
fd->opSetInput(newop,big,0);
fd->opSetInput(newop,fd->newConstant(addr.getAddrSize(),0),1);
fd->opInsertBefore(newop,op);
}
}
if (overlap !=0 ) {
newop = fd->newOp(2,op->getAddr());
if (addr.isBigEndian())
midvn = fd->newVarnodeOut(overlap+vn->getSize(),vn->getAddr(),newop);
else
midvn = fd->newVarnodeOut(overlap+vn->getSize(),addr,newop);
fd->opSetOpcode(newop,CPUI_PIECE);
fd->opSetInput(newop,vn,0); // Most significant part
fd->opSetInput(newop,leastvn,1); // Least sig
fd->opInsertAfter(newop,op);
}
else
midvn = vn;
if (mostsigsize != 0) {
newop = fd->newOp(2,op->getAddr());
bigout = fd->newVarnodeOut(size,addr,newop);
fd->opSetOpcode(newop,CPUI_PIECE);
fd->opSetInput(newop,mostvn,0);
fd->opSetInput(newop,midvn,1);
fd->opInsertAfter(newop,midvn->getDef());
}
else
bigout = midvn;
vn->setWriteMask();
return bigout; // Replace small write with big write
}
/// \brief Concatenate a list of Varnodes together at the given location
///
/// There must be at least 2 Varnodes in list, they must be in order
/// from most to least significant. The Varnodes in the list become
/// inputs to a single expression of PIECE ops resulting in a
/// final specified Varnode
/// \param vnlist is the list of Varnodes to concatenate
/// \param insertop is the point where the expression should be inserted (before)
/// \param finalvn is the final specified output Varnode of the expression
/// \return the final unified Varnode
Varnode *Heritage::concatPieces(const vector &vnlist,PcodeOp *insertop,Varnode *finalvn)
{
Varnode *preexist = vnlist[0];
bool isbigendian = preexist->getAddr().isBigEndian();
Address opaddress;
BlockBasic *bl;
list::iterator insertiter;
if (insertop == (PcodeOp *)0) { // Insert at the beginning
bl = (BlockBasic *)fd->getBasicBlocks().getStartBlock();
insertiter = bl->beginOp();
opaddress = fd->getAddress();
}
else {
bl = insertop->getParent();
insertiter = insertop->getBasicIter();
opaddress = insertop->getAddr();
}
for(uint4 i=1;inewOp(2,opaddress);
fd->opSetOpcode(newop,CPUI_PIECE);
Varnode *newvn;
if (i==vnlist.size()-1) {
newvn = finalvn;
fd->opSetOutput(newop,newvn);
}
else
newvn = fd->newUniqueOut(preexist->getSize()+vn->getSize(),newop);
if (isbigendian) {
fd->opSetInput(newop,preexist,0); // Most sig part
fd->opSetInput(newop,vn,1); // Least sig part
}
else {
fd->opSetInput(newop,vn,0);
fd->opSetInput(newop,preexist,1);
}
fd->opInsert(newop,bl,insertiter);
preexist = newvn;
}
return preexist;
}
/// \brief Build a set of Varnode piece expression at the given location
///
/// Given a list of small Varnodes and the address range they are a piece of,
/// construct a SUBPIECE op that defines each piece. The truncation parameters
/// are calculated based on the overlap of the piece with the whole range,
/// and a single input Varnode is used for all SUBPIECE ops.
/// \param vnlist is the list of piece Varnodes
/// \param insertop is the point where the op expressions are inserted (before)
/// \param addr is the first address of the whole range
/// \param size is the number of bytes in the whole range
/// \param startvn is designated input Varnode
void Heritage::splitPieces(const vector &vnlist,PcodeOp *insertop,
const Address &addr,int4 size,Varnode *startvn)
{
Address opaddress;
uintb baseoff;
bool isbigendian;
BlockBasic *bl;
list::iterator insertiter;
isbigendian = addr.isBigEndian();
if (isbigendian)
baseoff = addr.getOffset() + size;
else
baseoff = addr.getOffset();
if (insertop == (PcodeOp *)0) {
bl = (BlockBasic *)fd->getBasicBlocks().getStartBlock();
insertiter = bl->beginOp();
opaddress = fd->getAddress();
}
else {
bl = insertop->getParent();
insertiter = insertop->getBasicIter();
++insertiter; // Insert AFTER the write
opaddress = insertop->getAddr();
}
for(uint4 i=0;inewOp(2,opaddress);
fd->opSetOpcode(newop,CPUI_SUBPIECE);
uintb diff;
if (isbigendian)
diff = baseoff - (vn->getOffset() + vn->getSize());
else
diff = vn->getOffset() - baseoff;
fd->opSetInput(newop,startvn,0);
fd->opSetInput(newop,fd->newConstant(4,diff),1);
fd->opSetOutput(newop,vn);
fd->opInsert(newop,bl,insertiter);
}
}
/// \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 ©Sinks,vector &forces)
{
// Mark the sinks
for(int4 i=0;isetMark();
}
// 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;igetIn(i);
if (!vn->isWritten()) continue;
if (vn->isAddrForce()) continue; // Already marked address forced
PcodeOp *newOp = vn->getDef();
if (newOp->isMark()) continue; // Already visited this op
newOp->setMark();
OpCode opc = newOp->code();
bool isArtificial = false;
if (opc == CPUI_COPY || opc == CPUI_MULTIEQUAL) {
isArtificial = true;
int4 maxInNew = newOp->numInput();
for(int4 j=0;jgetIn(j);
if (addr != inVn->getAddr()) {
isArtificial = false;
break;
}
}
}
else if (opc == CPUI_INDIRECT && newOp->isIndirectStore()) {
// An INDIRECT can be considered artificial if it is caused by a STORE
Varnode *inVn = newOp->getIn(0);
if (addr == inVn->getAddr())
isArtificial = true;
}
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)
{
if (loadCopyOps.empty()) return;
vector forces;
int4 copySinkSize = loadCopyOps.size();
findAddressForces(loadCopyOps, forces);
if (!forces.empty()) {
RangeList loadRanges;
for(list::const_iterator iter=loadGuard.begin();iter!=loadGuard.end();++iter) {
const LoadGuard &guard( *iter );
loadRanges.insertRange(guard.spc, guard.minimumOffset, guard.maximumOffset);
}
// Mark everything on the boundary as address forced to prevent dead-code removal
for(int4 i=0;igetOut();
if (loadRanges.inRange(vn->getAddr(), 1)) // If we are within one of the guarded ranges
vn->setAddrForce(); // then consider the output address forced
op->clearMark();
}
}
// Eliminate or propagate away original COPY sinks
for(int4 i=0;iclearMark();
}
loadCopyOps.clear(); // We have handled all the load guard COPY ops
}
/// Make some determination of the range of possible values for a LOAD based
/// an partial value set analysis. This can sometimes get
/// - minimumOffset - otherwise the original constant pulled with the LOAD is used
/// - step - the partial analysis shows step and direction
/// - maximumOffset - in rare cases
///
/// isAnalyzed is set to \b true, if full range analysis is not needed
/// \param valueSet is the calculated value set as seen by the LOAD operation
void LoadGuard::establishRange(const ValueSetRead &valueSet)
{
const CircleRange &range( valueSet.getRange() );
uintb rangeSize = range.getSize();
uintb size;
if (range.isEmpty()) {
minimumOffset = pointerBase;
size = 0x1000;
}
else if (range.isFull() || rangeSize > 0xffffff) {
minimumOffset = pointerBase;
size = 0x1000;
analysisState = 1; // Don't bother doing more analysis
}
else {
step = (rangeSize == 3) ? range.getStep() : 0; // Check for consistent step
size = 0x1000;
if (valueSet.isLeftStable()) {
minimumOffset = range.getMin();
}
else if (valueSet.isRightStable()) {
if (pointerBase < range.getEnd()) {
minimumOffset = pointerBase;
size = (range.getEnd() - pointerBase);
}
else {
minimumOffset = range.getMin();
size = rangeSize * range.getStep();
}
}
else
minimumOffset = pointerBase;
}
uintb max = spc->getHighest();
if (minimumOffset > max) {
minimumOffset = max;
maximumOffset = minimumOffset; // Something is seriously wrong
}
else {
uintb maxSize = (max - minimumOffset) + 1;
if (size > maxSize)
size = maxSize;
maximumOffset = minimumOffset + size -1;
}
}
void LoadGuard::finalizeRange(const ValueSetRead &valueSet)
{
analysisState = 1; // In all cases the settings determined here are final
const CircleRange &range( valueSet.getRange() );
uintb rangeSize = range.getSize();
if (rangeSize == 0x100 || rangeSize == 0x10000) {
// These sizes likely result from the storage size of the index
if (step == 0) // If we didn't see signs of iteration
rangeSize = 0; // don't use this range
}
if (rangeSize > 1 && rangeSize < 0xffffff) { // Did we converge to something reasonable
analysisState = 2; // Mark that we got a definitive result
if (rangeSize > 2)
step = range.getStep();
minimumOffset = range.getMin();
maximumOffset = (range.getEnd() - 1) & range.getMask(); // NOTE: Don't subtract a whole step
if (maximumOffset < minimumOffset) { // Values extend into what is usually stack parameters
maximumOffset = spc->getHighest();
analysisState = 1; // Remove the lock as we have likely overflowed
}
}
if (minimumOffset > spc->getHighest())
minimumOffset = spc->getHighest();
if (maximumOffset > spc->getHighest())
maximumOffset = spc->getHighest();
}
/// Check if the address falls within the range defined by \b this
/// \param addr is the given address
/// \return \b true if the address is contained
bool LoadGuard::isGuarded(const Address &addr) const
{
if (addr.getSpace() != spc) return false;
if (addr.getOffset() < minimumOffset) return false;
if (addr.getOffset() > maximumOffset) return false;
return true;
}
/// \brief Make final determination of what range new LoadGuards are protecting
///
/// Actual LOAD operations are guarded with an initial version of the LoadGuard record.
/// Now that heritage has completed, a full analysis of each LOAD is conducted, using
/// value set analysis, to reach a conclusion about what range of stack values the
/// LOAD might actually alias. All new LoadGuard records are updated with the analysis,
/// which then informs handling of LOAD COPYs and possible later heritage passes.
void Heritage::analyzeNewLoadGuards(void)
{
bool nothingToDo = true;
if (!loadGuard.empty()) {
if (loadGuard.back().analysisState == 0) // Check if unanalyzed
nothingToDo = false;
}
if (!storeGuard.empty()) {
if (storeGuard.back().analysisState == 0)
nothingToDo = false;
}
if (nothingToDo) return;
vector sinks;
vector reads;
list::iterator loadIter = loadGuard.end();
while(loadIter != loadGuard.begin()) {
--loadIter;
LoadGuard &guard( *loadIter );
if (guard.analysisState != 0) break;
reads.push_back(guard.op);
sinks.push_back(guard.op->getIn(1)); // The CPUI_LOAD pointer
}
list::iterator storeIter = storeGuard.end();
while(storeIter != storeGuard.begin()) {
--storeIter;
LoadGuard &guard( *storeIter );
if (guard.analysisState != 0) break;
reads.push_back(guard.op);
sinks.push_back(guard.op->getIn(1)); // The CPUI_STORE pointer
}
AddrSpace *stackSpc = fd->getArch()->getStackSpace();
Varnode *stackReg = (Varnode *)0;
if (stackSpc != (AddrSpace *)0 && stackSpc->numSpacebase() > 0)
stackReg = fd->findSpacebaseInput(stackSpc);
ValueSetSolver vsSolver;
vsSolver.establishValueSets(sinks, reads, stackReg, false);
WidenerNone widener;
vsSolver.solve(10000,widener);
list::iterator iter;
bool runFullAnalysis = false;
for(iter=loadIter;iter!=loadGuard.end(); ++iter) {
LoadGuard &guard( *iter );
guard.establishRange(vsSolver.getValueSetRead(guard.op->getSeqNum()));
if (guard.analysisState == 0)
runFullAnalysis = true;
}
for(iter=storeIter;iter!=storeGuard.end(); ++iter) {
LoadGuard &guard( *iter );
guard.establishRange(vsSolver.getValueSetRead(guard.op->getSeqNum()));
if (guard.analysisState == 0)
runFullAnalysis = true;
}
if (runFullAnalysis) {
WidenerFull fullWidener;
vsSolver.solve(10000, fullWidener);
for (iter = loadIter; iter != loadGuard.end(); ++iter) {
LoadGuard &guard(*iter);
guard.finalizeRange(vsSolver.getValueSetRead(guard.op->getSeqNum()));
}
for (iter = storeIter; iter != storeGuard.end(); ++iter) {
LoadGuard &guard(*iter);
guard.finalizeRange(vsSolver.getValueSetRead(guard.op->getSeqNum()));
}
}
}
/// \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)
{
if (!op->usesSpacebasePtr()) {
loadGuard.emplace_back();
loadGuard.back().set(op,spc,node.offset);
fd->opMarkSpacebasePtr(op);
}
}
/// \brief Generate a guard record given an indexed STORE to a stack space
///
/// Record the STORE op and the (likely) range of addresses in the stack space that
/// might be stored to.
/// \param node is the path element containing the constructed Address
/// \param op is the STORE PcodeOp
/// \param spc is the stack space
void Heritage::generateStoreGuard(StackNode &node,PcodeOp *op,AddrSpace *spc)
{
if (!op->usesSpacebasePtr()) {
storeGuard.emplace_back();
storeGuard.back().set(op,spc,node.offset);
fd->opMarkSpacebasePtr(op);
}
}
/// \brief Identify any CPUI_STORE ops that use a free pointer from a given address space
///
/// When performing heritage for stack Varnodes, data-flow around a STORE with a
/// free pointer must be guarded (with an INDIRECT) to be safe. This routine collects
/// and marks the STORE ops that trigger this guard.
/// \param spc is the given address space
/// \param freeStores will hold the list of STOREs if any
/// \return \b true if there are any new STOREs needing a guard
bool Heritage::protectFreeStores(AddrSpace *spc,vector &freeStores)
{
list::const_iterator iter = fd->beginOp(CPUI_STORE);
list::const_iterator enditer = fd->endOp(CPUI_STORE);
bool hasNew = false;
while(iter != enditer) {
PcodeOp *op = *iter;
++iter;
if (op->isDead()) continue;
Varnode *vn = op->getIn(1);
while (vn->isWritten()) {
PcodeOp *defOp = vn->getDef();
OpCode opc = defOp->code();
if (opc == CPUI_COPY)
vn = defOp->getIn(0);
else if (opc == CPUI_INT_ADD && defOp->getIn(1)->isConstant())
vn = defOp->getIn(0);
else
break;
}
if (vn->isFree() && vn->getSpace() == spc) {
fd->opMarkSpacebasePtr(op); // Mark op as spacebase STORE, even though we're not sure
freeStores.push_back(op);
hasNew = true;
}
}
return hasNew;
}
/// \brief Trace input stack-pointer 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. The routine can
/// checks for STOREs where the data-flow path hasn't been completed yet and returns
/// \b true if they exist, passing back a list of those that might use a pointer to the stack.
/// \param spc is the particular address space with a stackpointer (into it)
/// \param freeStores will hold the list of any STOREs that need follow-up analysis
/// \param checkFreeStores is \b true if the routine should check for free STOREs
/// \return \b true if there are incomplete STOREs
bool Heritage::discoverIndexedStackPointers(AddrSpace *spc,vector &freeStores,bool checkFreeStores)
{
// We need to be careful of exponential ladders, so we mark Varnodes independently of
// the depth first path we are traversing.
vector markedVn;
vector path;
bool unknownStackStorage = false;
for(int4 i=0;inumSpacebase();++i) {
const VarnodeData &stackPointer(spc->getSpacebase(i));
Varnode *spInput = fd->findVarnodeInput(stackPointer.size, stackPointer.getAddr());
if (spInput == (Varnode *)0) continue;
path.push_back(StackNode(spInput,0,0));
while(!path.empty()) {
StackNode &curNode(path.back());
if (curNode.iter == curNode.vn->endDescend()) {
path.pop_back();
continue;
}
PcodeOp *op = *curNode.iter;
++curNode.iter;
Varnode *outVn = op->getOut();
if (outVn != (Varnode *)0 && outVn->isMark()) continue; // Don't revisit Varnodes
switch(op->code()) {
case CPUI_INT_ADD:
{
Varnode *otherVn = op->getIn(1-op->getSlot(curNode.vn));
if (otherVn->isConstant()) {
uintb newOffset = spc->wrapOffset(curNode.offset + otherVn->getOffset());
StackNode nextNode(outVn,newOffset,curNode.traversals);
if (nextNode.iter != nextNode.vn->endDescend()) {
outVn->setMark();
path.push_back(nextNode);
markedVn.push_back(outVn);
}
else if (outVn->getSpace()->getType() == IPTR_SPACEBASE)
unknownStackStorage = true;
}
else {
StackNode nextNode(outVn,curNode.offset,curNode.traversals | StackNode::nonconstant_index);
if (nextNode.iter != nextNode.vn->endDescend()) {
outVn->setMark();
path.push_back(nextNode);
markedVn.push_back(outVn);
}
else if (outVn->getSpace()->getType() == IPTR_SPACEBASE)
unknownStackStorage = true;
}
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);
markedVn.push_back(outVn);
}
else if (outVn->getSpace()->getType() == IPTR_SPACEBASE)
unknownStackStorage = true;
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);
markedVn.push_back(outVn);
}
else if (outVn->getSpace()->getType() == IPTR_SPACEBASE)
unknownStackStorage = true;
break;
}
case CPUI_LOAD:
{
// Note that if ANY path has one of the traversals (non-constant ADD or MULTIEQUAL), then
// THIS path must have one of the traversals, because the only other acceptable path elements
// (INDIRECT/COPY/constant ADD) have only one path through.
if (curNode.traversals != 0) {
generateLoadGuard(curNode,op,spc);
}
break;
}
case CPUI_STORE:
{
if (op->getIn(1) == curNode.vn) { // Make sure the STORE pointer comes from our path
if (curNode.traversals != 0) {
generateStoreGuard(curNode, op, spc);
}
else {
// If there were no traversals (of non-constant ADD or MULTIEQUAL) then the
// pointer is equal to the stackpointer plus a constant (through an indirect is possible)
// This will likely get resolved in the next heritage pass, but we leave the
// spacebaseptr mark on, so that that the indirects don't get removed
fd->opMarkSpacebasePtr(op);
}
}
break;
}
default:
break;
}
}
}
for(int4 i=0;iclearMark();
if (unknownStackStorage && checkFreeStores)
return protectFreeStores(spc, freeStores);
return false;
}
/// \brief Revisit STOREs with free pointers now that a heritage pass has completed
///
/// We regenerate STORE LoadGuard records then cross-reference with STOREs that were
/// originally free to see if they actually needed a LoadGaurd. If not, the STORE
/// is unmarked and INDIRECTs it has caused are removed.
/// \param spc is the address space being guarded
/// \param freeStores is the list of STOREs that were marked as free
void Heritage::reprocessFreeStores(AddrSpace *spc,vector &freeStores)
{
for(int4 i=0;iopClearSpacebasePtr(freeStores[i]);
discoverIndexedStackPointers(spc, freeStores, false);
for(int4 i=0;iusesSpacebasePtr()) continue;
// If not the STORE may have triggered INDIRECTs that are unnecessary
PcodeOp *indOp = op->previousOp();
while(indOp != (PcodeOp *)0) {
if (indOp->code() != CPUI_INDIRECT) break;
Varnode *iopVn = indOp->getIn(1);
if (iopVn->getSpace()->getType()!=IPTR_IOP) break;
if (op != PcodeOp::getOpFromConst(iopVn->getAddr())) break;
PcodeOp *nextOp = indOp->previousOp();
if (indOp->getOut()->getSpace() == spc) {
fd->totalReplace(indOp->getOut(),indOp->getIn(0));
fd->opDestroy(indOp); // Get rid of the INDIRECT
}
indOp = nextOp;
}
}
}
/// \brief Normalize p-code ops so that phi-node placement and renaming works
///
/// The traditional phi-node placement and renaming algorithms don't expect
/// variable pairs where there is partial overlap. For the given address range,
/// we make all the free Varnode sizes look uniform by adding PIECE and SUBPIECE
/// ops. We also add INDIRECT ops, so that we can ignore indirect effects
/// of LOAD/STORE/CALL ops.
/// \param addr is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \param read is the set of Varnode values reading from the range
/// \param write is the set of written Varnodes in the range
/// \param inputvars is the set of Varnodes in the range already marked as input
void Heritage::guard(const Address &addr,int4 size,vector &read,vector &write,
vector &inputvars)
{
uint4 fl;
Varnode *vn;
vector::iterator iter;
bool guardneeded = true;
for(iter=read.begin();iter!=read.end();++iter) {
vn = *iter;
if (vn->getSize() < size)
*iter = vn = normalizeReadSize(vn,addr,size);
vn->setActiveHeritage();
}
for(iter=write.begin();iter!=write.end();++iter) {
vn = *iter;
if (vn->getSize() < size)
*iter = vn = normalizeWriteSize(vn,addr,size);
vn->setActiveHeritage();
if (vn->isAddrForce())
guardneeded = false;
else {
if (vn->isWritten()) {
if (vn->getDef()->code() == CPUI_INDIRECT) // Evidence of a previous guard
guardneeded = false;
}
}
}
if (read.empty() && write.empty() && inputvars.empty()) return;
// This may need to be adjusted in the future
// Basically we need to take into account the possibility
// that the full syntax tree may form over several stages
// so there is the possibility that we will see a new
// free for an address that has already been guarded before
// Because INDIRECTs for a single call or store really
// issue simultaneously, having multiple INDIRECT guards
// for the same address confuses the renaming algorithm
// SO we don't guard if we think we've guarded before
if (guardneeded) {
fl = 0;
// Query for generic properties of address (use empty usepoint)
fd->getScopeLocal()->queryProperties(addr,size,Address(),fl);
guardCalls(fl,addr,size,write);
guardReturns(fl,addr,size,write);
if (fd->getArch()->highPtrPossible(addr,size)) {
guardStores(addr,size,write);
guardLoads(fl,addr,size,write);
}
}
}
/// \brief Guard an address range that is larger than any single parameter
///
/// In this situation, an address range is being heritaged, but only a piece of
/// it can be a parameter for a given call. We have to construct a SUBPIECE that
/// pulls out the potential parameter.
/// \param fc is the call site potentially taking a parameter
/// \param addr is the starting address of the range
/// \param transAddr is the start of the same range from the callee's stack perspective
/// \param size is the size of the range in bytes
void Heritage::guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size)
{
VarnodeData vData;
if (fc->getBiggestContainedInputParam(transAddr, size, vData)) {
ParamActive *active = fc->getActiveInput();
Address truncAddr(vData.space,vData.offset);
if (active->whichTrial(truncAddr, size) < 0) { // If not already a trial
int4 truncateAmount = transAddr.justifiedContain(size, truncAddr, vData.size, false);
int4 diff = (int4)(truncAddr.getOffset() - transAddr.getOffset());
truncAddr = addr + diff; // Convert truncated Address to caller's perspective
PcodeOp *op = fc->getOp();
PcodeOp *subpieceOp = fd->newOp(2,op->getAddr());
fd->opSetOpcode(subpieceOp, CPUI_SUBPIECE);
Varnode *wholeVn = fd->newVarnode(size,addr);
wholeVn->setActiveHeritage();
fd->opSetInput(subpieceOp,wholeVn,0);
fd->opSetInput(subpieceOp,fd->newConstant(4,truncateAmount),1);
Varnode *vn = fd->newVarnodeOut(vData.size, truncAddr, subpieceOp);
fd->opInsertBefore(subpieceOp,op);
active->registerTrial(truncAddr, vData.size);
fd->opInsertInput(op, vn, op->numInput());
}
}
}
/// \brief Guard CALL/CALLIND ops in preparation for renaming algorithm
///
/// For the given address range, we decide what the data-flow effect is
/// across each call site in the function. If an effect is unknown, an
/// INDIRECT op is added, prepopulating data-flow through the call.
/// Any new INDIRECT causes a new Varnode to be added to the \b write list.
/// \param fl are any boolean properties associated with the address range
/// \param addr is the first address of given range
/// \param size is the number of bytes in the range
/// \param write is the list of written Varnodes in the range (may be updated)
void Heritage::guardCalls(uint4 fl,const Address &addr,int4 size,vector &write)
{
FuncCallSpecs *fc;
PcodeOp *indop;
uint4 effecttype;
bool holdind = ((fl&Varnode::addrtied)!=0);
for(int4 i=0;inumCalls();++i) {
fc = fd->getCallSpecs(i);
if (fc->getOp()->isAssignment()) {
Varnode *vn = fc->getOp()->getOut();
if ((vn->getAddr()==addr)&&(vn->getSize()==size)) continue;
}
effecttype = fc->hasEffectTranslate(addr,size);
bool possibleoutput = false;
if (fc->isOutputActive()) {
ParamActive *active = fc->getActiveOutput();
if (fc->possibleOutputParam(addr,size)) {
if (active->whichTrial(addr,size)<0) { // If not already a trial
active->registerTrial(addr,size);
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
possibleoutput = true;
}
}
}
if (fc->isInputActive()) {
AddrSpace *spc = addr.getSpace();
uintb off = addr.getOffset();
bool tryregister = true;
if (spc->getType() == IPTR_SPACEBASE) {
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
else
tryregister = false; // Do not attempt to register this stack loc as a trial
}
Address transAddr(spc,off); // Address relative to callee's stack
if (tryregister) {
int4 inputCharacter = fc->characterizeAsInputParam(transAddr,size);
if (inputCharacter == 1) { // Call could be using this range as an input parameter
ParamActive *active = fc->getActiveInput();
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
PcodeOp *op = fc->getOp();
active->registerTrial(transAddr,size);
Varnode *vn = fd->newVarnode(size,addr);
vn->setActiveHeritage();
fd->opInsertInput(op,vn,op->numInput());
}
}
else if (inputCharacter == 2) // Call may be using part of this range as an input parameter
guardCallOverlappingInput(fc, addr, transAddr, size);
}
}
// We do not guard the call if the effect is "unaffected" or "reload"
if ((effecttype == EffectRecord::unknown_effect)||(effecttype == EffectRecord::return_address)) {
indop = fd->newIndirectOp(fc->getOp(),addr,size,0);
indop->getIn(0)->setActiveHeritage();
indop->getOut()->setActiveHeritage();
write.push_back(indop->getOut());
if (holdind)
indop->getOut()->setAddrForce();
if (effecttype == EffectRecord::return_address)
indop->getOut()->setReturnAddress();
}
else if (effecttype == EffectRecord::killedbycall) {
indop = fd->newIndirectCreation(fc->getOp(),addr,size,possibleoutput);
indop->getOut()->setActiveHeritage();
write.push_back(indop->getOut());
}
}
}
/// \brief Guard STORE ops in preparation for the renaming algorithm
///
/// Depending on the pointer, a STORE operation may affect data-flow across the
/// given address range. This method adds an INDIRECT op, prepopulating
/// data-flow across the STORE.
/// Any new INDIRECT causes a new Varnode to be added to the \b write list.
/// \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::guardStores(const Address &addr,int4 size,vector &write)
{
list::const_iterator iter,iterend;
PcodeOp *op,*indop;
AddrSpace *spc = addr.getSpace();
AddrSpace *container = spc->getContain();
iterend = fd->endOp(CPUI_STORE);
for(iter=fd->beginOp(CPUI_STORE);iter!=iterend;++iter) {
op = *iter;
if (op->isDead()) continue;
AddrSpace *storeSpace = Address::getSpaceFromConst(op->getIn(0)->getAddr());
if ((container == storeSpace && op->usesSpacebasePtr()) ||
(spc == storeSpace)) {
indop = fd->newIndirectOp(op,addr,size,PcodeOp::indirect_store);
indop->getIn(0)->setActiveHeritage();
indop->getOut()->setActiveHeritage();
write.push_back(indop->getOut());
}
}
}
/// \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 fl 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 fl,const Address &addr,int4 size,vector &write)
{
PcodeOp *copyop;
list::iterator iter;
if ((fl & Varnode::addrtied)==0) return; // If not address tied, don't consider for index alias
iter = loadGuard.begin();
while(iter!=loadGuard.end()) {
LoadGuard &guardRec(*iter);
if (!guardRec.isValid(CPUI_LOAD)) {
list::iterator copyIter = iter;
++iter;
loadGuard.erase(copyIter);
continue;
}
++iter;
if (guardRec.spc != addr.getSpace()) continue;
if (addr.getOffset() < guardRec.minimumOffset) continue;
if (addr.getOffset() > guardRec.maximumOffset) 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
///
/// For the given global (persistent) address range, data-flow must persist up to
/// (beyond) the end of the function. This method prepopulates data-flow for the
/// range at all the RETURN ops, in order to enforce this. Either a Varnode
/// is added as input to the RETURN (for possible return values), or a COPY
/// is inserted right before the RETURN with its output marked as
/// \b address \b forced.
/// \param fl are any boolean properties associated with the address range
/// \param addr is the first address of the given range
/// \param size is the number of bytes in the range
/// \param write is the list of written Varnodes in the range (unused)
void Heritage::guardReturns(uint4 fl,const Address &addr,int4 size,vector &write)
{
list::const_iterator iter,iterend;
PcodeOp *op,*copyop;
ParamActive *active = fd->getActiveOutput();
if (active != (ParamActive *)0) {
if (fd->getFuncProto().possibleOutputParam(addr,size)) {
active->registerTrial(addr,size);
iterend = fd->endOp(CPUI_RETURN);
for(iter=fd->beginOp(CPUI_RETURN);iter!=iterend;++iter) {
op = *iter;
if (op->isDead()) continue;
if (op->getHaltType() != 0) continue; // Special halt points cannot take return values
Varnode *invn = fd->newVarnode(size,addr);
invn->setActiveHeritage();
fd->opInsertInput(op,invn,op->numInput());
}
}
}
if ((fl&Varnode::persist)==0) return;
iterend = fd->endOp(CPUI_RETURN);
for(iter=fd->beginOp(CPUI_RETURN);iter!=iterend;++iter) {
op = *iter;
if (op->isDead()) continue;
copyop = fd->newOp(1,op->getAddr());
Varnode *vn = fd->newVarnodeOut(size,addr,copyop);
vn->setAddrForce();
vn->setActiveHeritage();
fd->opSetOpcode(copyop,CPUI_COPY);
Varnode *invn = fd->newVarnode(size,addr);
invn->setActiveHeritage();
fd->opSetInput(copyop,invn,0);
fd->opInsertBefore(copyop,op);
}
}
/// \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
/// range. Each Varnode in the given list has a 1 entered in the refinement
/// array, at the position corresponding to the starting address of the Varnode
/// and at the position corresponding to the address immediately following the
/// Varnode.
/// \param refine is the refinement array
/// \param addr is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param vnlist is the list of Varnodes to add to the array
void Heritage::buildRefinement(vector &refine,const Address &addr,int4 size,const vector &vnlist)
{
for(uint4 i=0;igetAddr();
int4 sz = vnlist[i]->getSize();
uint4 diff = (uint4)(curaddr.getOffset() - addr.getOffset());
refine[diff] = 1;
refine[diff+sz] = 1;
}
}
/// \brief Split up a Varnode by the given \e refinement
///
/// The \e refinement array is an array of integers, one for each byte in the
/// given range. Any non-zero entry is the size of a particular element of the
/// refinement starting at that corresponding byte in the range. I.e. the array
/// [4,0,0,0,4,0,0,0] indicates the address range is 8-bytes long covered by
/// two elements of length 4, starting at offsets 0 and 4 respectively.
/// The given Varnode must be contained in the address range that the
/// refinement array describes.
///
/// A new set of Varnode pieces are returned in the \b split container, where
/// the pieces form a disjoint cover of the original Varnode, and where the
/// piece boundaries match the refinement.
/// \param vn is the given Varnode to split
/// \param addr is the starting address of the range described by the refinement
/// \param refine is the refinement array
/// \param split will hold the new Varnode pieces
void Heritage::splitByRefinement(Varnode *vn,const Address &addr,const vector &refine,vector &split)
{
Address curaddr = vn->getAddr();
int4 sz = vn->getSize();
AddrSpace *spc = curaddr.getSpace();
uint4 diff = (uint4)spc->wrapOffset(curaddr.getOffset() - addr.getOffset());
int4 cutsz = refine[diff];
if (sz <= cutsz) return; // Already refined
while(sz > 0) {
Varnode *vn2 = fd->newVarnode(cutsz,curaddr);
split.push_back(vn2);
curaddr = curaddr + cutsz;
sz -= cutsz;
diff = (uint4)spc->wrapOffset(curaddr.getOffset() - addr.getOffset());
cutsz = refine[diff];
if (cutsz > sz)
cutsz = sz; // Final piece
}
}
/// \brief Split up a \b free Varnode based on the given refinement
///
/// The \e refinement array is an array of integers, one for each byte in the
/// given range. Any non-zero entry is the size of a particular element of the
/// refinement starting at that corresponding byte in the range. I.e. the array
/// [4,0,0,0,4,0,0,0] indicates the address range is 8-bytes long covered by
/// two elements of length 4, starting at offsets 0 and 4 respectively.
///
/// If the Varnode overlaps the refinement, it is replaced with 2 or more
/// covering Varnodes with boundaries that are on the refinement. A concatenation
/// expression is formed reconstructing the original value from the pieces. The
/// original Varnode is replaced, in its p-code op, with a temporary Varnode that
/// is the final output of the concatenation expression.
/// \param vn is the given Varnode to split
/// \param addr is the starting address of the address range being refined
/// \param refine is the refinement array
/// \param newvn is preallocated space for the holding the array of Varnode pieces
void Heritage::refineRead(Varnode *vn,const Address &addr,const vector &refine,vector &newvn)
{
newvn.clear();
splitByRefinement(vn,addr,refine,newvn);
if (newvn.empty()) return;
Varnode *replacevn = fd->newUnique(vn->getSize());
PcodeOp *op = vn->loneDescend(); // Read is free so has 1 and only 1 descend
int4 slot = op->getSlot(vn);
concatPieces(newvn,op,replacevn);
fd->opSetInput(op,replacevn,slot);
if (vn->hasNoDescend())
fd->deleteVarnode(vn);
else
throw LowlevelError("Refining non-free varnode");
}
/// \brief Split up an output Varnode based on the given refinement
///
/// The \e refinement array is an array of integers, one for each byte in the
/// given range. Any non-zero entry is the size of a particular element of the
/// refinement starting at that corresponding byte in the range. I.e. the array
/// [4,0,0,0,4,0,0,0] indicates the address range is 8-bytes long covered by
/// two elements of length 4, starting at offsets 0 and 4 respectively.
///
/// If the Varnode overlaps the refinement, it is replaced with 2 or more
/// covering Varnodes with boundaries that are on the refinement. These pieces
/// may be supplemented with additional pieces to obtain a disjoint cover of the
/// entire address range. A defining SUBPIECE op is generated for each piece.
/// The original Varnode is replaced with a temporary Varnode.
/// \param vn is the given Varnode to split
/// \param addr is the starting address of the address range being refined
/// \param refine is the refinement array
/// \param newvn is preallocated space for the holding the array of Varnode pieces
void Heritage::refineWrite(Varnode *vn,const Address &addr,const vector &refine,vector &newvn)
{
newvn.clear();
splitByRefinement(vn,addr,refine,newvn);
if (newvn.empty()) return;
Varnode *replacevn = fd->newUnique(vn->getSize());
PcodeOp *def = vn->getDef();
fd->opSetOutput(def,replacevn);
splitPieces(newvn,def,vn->getAddr(),vn->getSize(),replacevn);
fd->totalReplace(vn,replacevn);
fd->deleteVarnode(vn);
}
/// \brief Split up a known input Varnode based on the given refinement
///
/// The \e refinement array is an array of integers, one for each byte in the
/// given range. Any non-zero entry is the size of a particular element of the
/// refinement starting at that corresponding byte in the range. I.e. the array
/// [4,0,0,0,4,0,0,0] indicates the address range is 8-bytes long covered by
/// two elements of length 4, starting at offsets 0 and 4 respectively.
///
/// If the Varnode overlaps the refinement, it is replaced with 2 or more
/// covering Varnodes with boundaries that are on the refinement. These pieces
/// may be supplemented with additional pieces to obtain a disjoint cover of the
/// entire address range. A defining SUBPIECE op is generated for each piece.
/// \param vn is the given Varnode to split
/// \param addr is the starting address of the address range being refined
/// \param refine is the refinement array
/// \param newvn is preallocated space for the holding the array of Varnode pieces
void Heritage::refineInput(Varnode *vn,const Address &addr,const vector &refine,vector &newvn)
{
newvn.clear();
splitByRefinement(vn,addr,refine,newvn);
if (newvn.empty()) return;
splitPieces(newvn,(PcodeOp *)0,vn->getAddr(),vn->getSize(),vn);
vn->setWriteMask();
}
/// \brief If we see 1-3 or 3-1 pieces in the partition, replace with a 4
///
/// A refinement of a 4-byte range into a 1-byte and 3-byte cover is highly likely
/// to be artificial, so we eliminate this configuration.
///
/// The \e refinement array is an array of integers, one for each byte in the
/// given range. Any non-zero entry is the size of a particular element of the
/// refinement starting at that corresponding byte in the range. I.e. the array
/// [4,0,0,0,4,0,0,0] indicates the address range is 8-bytes long covered by
/// two elements of length 4, starting at offsets 0 and 4 respectively.
/// \param refine is the refinement array
void Heritage::remove13Refinement(vector &refine)
{
if (refine.empty()) return;
int4 pos = 0;
int4 lastsize = refine[pos];
int4 cursize;
pos += lastsize;
while(pos < refine.size()) {
cursize = refine[pos];
if (cursize == 0) break;
if (((lastsize==1)&&(cursize==3))||((lastsize==3)&&(cursize==1))) {
refine[pos-lastsize] = 4;
lastsize = 4;
pos += cursize;
}
else {
lastsize = cursize;
pos += lastsize;
}
}
}
/// \brief Find the common refinement of all reads and writes in the address range
///
/// Split the reads and writes so they match the refinement.
/// \param addr is the first address in the range
/// \param size is the number of bytes in the range
/// \param readvars is all \e free Varnodes overlapping the address range
/// \param writevars is all written Varnodes overlapping the address range
/// \param inputvars is all known input Varnodes overlapping the address range
/// \return \b true if there is a non-trivial refinement
bool Heritage::refinement(const Address &addr,int4 size,const vector &readvars,const vector &writevars,const vector &inputvars)
{
if (size > 1024) return false;
vector refine(size+1,0);
buildRefinement(refine,addr,size,readvars);
buildRefinement(refine,addr,size,writevars);
buildRefinement(refine,addr,size,inputvars);
int4 lastpos = 0;
for(int4 curpos=1;curpos < size;++curpos) { // Convert boundary points to partition sizes
if (refine[curpos] != 0) {
refine[lastpos] = curpos - lastpos;
lastpos = curpos;
}
}
if (lastpos == 0) return false; // No non-trivial refinements
refine[lastpos] = size-lastpos;
remove13Refinement(refine);
vector newvn;
for(uint4 i=0;i &input)
{
if (input.empty()) return;
// If there is only one input and it fills everything
// it will get linked in automatically
if ((input.size()==1)&&(input[0]->getSize() == size)) return;
// Otherwise we need to make sure there are no holes
int4 i = 0;
uintb cur = addr.getOffset(); // Range that needs to be covered
uintb end = cur + size;
// bool seenunspliced = false;
Varnode *vn;
vector newinput;
// Make sure the input range is filled
while(cur < end) {
if (igetOffset()>cur) {
int4 sz = vn->getOffset() - cur;
vn = fd->newVarnode(sz,Address(addr.getSpace(),cur));
vn = fd->setInputVarnode(vn);
// seenunspliced = true;
}
else {
// if (vn->hasNoDescend())
// seenunspliced = true;
i += 1;
}
}
else {
int4 sz = end-cur;
vn = fd->newVarnode(sz,Address(addr.getSpace(),cur));
vn = fd->setInputVarnode(vn);
// seenunspliced = true;
}
newinput.push_back(vn);
cur += vn->getSize();
}
// Now we need to make sure that all the inputs get linked
// together into a single input
if (newinput.size()==1) return; // Will get linked in automatically
for(uint4 j=0;jsetWriteMask();
// if (!seenunspliced) {
// // Check to see if a concatenation of inputs already exists
// // If it existed already it would be defined at fd->getAddress()
// // and it would have full size
// VarnodeLocSet::const_iterator iter,enditer;
// iter = fd->beginLoc(size,addr,fd->getAddress());
// enditer = fd->endLoc(size,addr,fd->getAddress());
// if (iter != enditer) return; // It already exists
// }
Varnode *newout = fd->newVarnode(size,addr);
concatPieces(newinput,(PcodeOp *)0,newout)->setActiveHeritage();
}
#ifdef DFSVERIFY_DEBUG
static void verify_dfs(const vector &list,vector> &domchild)
{
int4 count = 0;
vector path;
path.push_back(0);
if (list[0]->getIndex() != 0)
throw LowlevelError("Initial block is not index 0");
count += 1;
while(!path.empty()) {
int4 cur = path.back();
int4 child;
FlowBlock *bl;
for(child=0;childgetIndex() == count)
break;
}
if (child == domchild[cur].size())
path.pop_back();
else {
path.push_back(bl->getIndex());
count += 1;
}
}
if (count != list.size())
throw LowlevelError("dfs does not verify");
}
#endif
/// Assuming we are just about to do heritage on an address space,
/// clear any placeholder LOADs associated with it on CALLs.
/// \param info is state for the specific address space
void Heritage::clearStackPlaceholders(HeritageInfo *info)
{
int4 numCalls = fd->numCalls();
for(int4 i=0;igetCallSpecs(i)->abortSpacebaseRelative(*fd);
}
info->hasCallPlaceholders = false; // Mark that clear has taken place
}
/// \brief Perform one level of Varnode splitting to match a JoinRecord
///
/// Split all the pieces in \b lastcombo, putting them into \b nextlev in order,
/// to get closer to the representation described by the given JoinRecord.
/// \b nextlev contains the two split pieces for each Varnode in \b lastcombo.
/// If a Varnode is not split this level, an extra \b null is put into
/// \b nextlev to maintain the 2-1 mapping.
/// \param lastcombo is the list of Varnodes to split
/// \param nextlev will hold the new split Varnodes in a 2-1 ratio
/// \param joinrec is the splitting specification we are trying to match
void Heritage::splitJoinLevel(vector &lastcombo,vector &nextlev,JoinRecord *joinrec)
{
int4 numpieces = joinrec->numPieces();
int4 recnum=0;
for(int4 i=0;igetSize() == joinrec->getPiece(recnum).size) {
nextlev.push_back(curvn);
nextlev.push_back((Varnode *)0);
recnum += 1;
}
else {
int4 sizeaccum = 0;
int4 j;
for(j=recnum;jgetPiece(recnum).size;
if (sizeaccum == curvn->getSize()) {
j += 1;
break;
}
}
int4 numinhalf = (j-recnum) / 2; // Will be at least 1
sizeaccum = 0;
for(int4 k=0;kgetPiece(recnum+k).size;
Varnode *mosthalf,*leasthalf;
if (numinhalf == 1)
mosthalf = fd->newVarnode(sizeaccum,joinrec->getPiece(recnum).space,joinrec->getPiece(recnum).offset);
else
mosthalf = fd->newUnique(sizeaccum);
if ((j-recnum)==2) {
const VarnodeData &vdata( joinrec->getPiece(recnum+1) );
leasthalf = fd->newVarnode(vdata.size,vdata.space,vdata.offset);
}
else
leasthalf = fd->newUnique(curvn->getSize() - sizeaccum);
nextlev.push_back(mosthalf);
nextlev.push_back(leasthalf);
recnum = j;
}
}
}
/// \brief Construct pieces for a \e join-space Varnode read by an operation.
///
/// Given a splitting specification (JoinRecord) and a Varnode, build a
/// concatenation expression (out of PIECE operations) that constructs the
/// the Varnode out of the specified Varnode pieces.
/// \param vn is the \e join-space Varnode to split
/// \param joinrec is the splitting specification
void Heritage::splitJoinRead(Varnode *vn,JoinRecord *joinrec)
{
PcodeOp *op = vn->loneDescend(); // vn isFree, so loneDescend must be non-null
vector lastcombo;
vector nextlev;
lastcombo.push_back(vn);
while(lastcombo.size() < joinrec->numPieces()) {
nextlev.clear();
splitJoinLevel(lastcombo,nextlev,joinrec);
for(int4 i=0;inewOp(2,op->getAddr());
fd->opSetOpcode(concat,CPUI_PIECE);
fd->opSetOutput(concat,curvn);
fd->opSetInput(concat,mosthalf,0);
fd->opSetInput(concat,leasthalf,1);
fd->opInsertBefore(concat,op);
mosthalf->setPrecisHi(); // Set precision flags to trigger "double precision" rules
leasthalf->setPrecisLo();
op = concat; // Keep -op- as the earliest op in the concatenation construction
}
lastcombo.clear();
for(int4 i=0;igetDef(); // vn cannot be free, either it has def, or it is input
BlockBasic *bb = (BlockBasic *)fd->getBasicBlocks().getBlock(0);
vector lastcombo;
vector nextlev;
lastcombo.push_back(vn);
while(lastcombo.size() < joinrec->numPieces()) {
nextlev.clear();
splitJoinLevel(lastcombo,nextlev,joinrec);
for(int4 i=0;iisInput())
split = fd->newOp(2,bb->getStart());
else
split = fd->newOp(2,op->getAddr());
fd->opSetOpcode(split,CPUI_SUBPIECE);
fd->opSetOutput(split,mosthalf);
fd->opSetInput(split,curvn,0);
fd->opSetInput(split,fd->newConstant(4,leasthalf->getSize()),1);
if (op == (PcodeOp *)0)
fd->opInsertBegin(split,bb);
else
fd->opInsertAfter(split,op);
op = split; // Keep -op- as the latest op in the split construction
split = fd->newOp(2,op->getAddr());
fd->opSetOpcode(split,CPUI_SUBPIECE);
fd->opSetOutput(split,leasthalf);
fd->opSetInput(split,curvn,0);
fd->opSetInput(split,fd->newConstant(4,0),1);
fd->opInsertAfter(split,op);
mosthalf->setPrecisHi(); // Make sure we set the precision flags to trigger "double precision" rules
leasthalf->setPrecisLo();
op = split; // Keep -op- as the latest op in the split construction
}
lastcombo.clear();
for(int4 i=0;iloneDescend(); // vn isFree, so loneDescend must be non-null
PcodeOp *trunc = fd->newOp(1,op->getAddr());
const VarnodeData &vdata( joinrec->getPiece(0) ); // Float extensions have exactly 1 piece
Varnode *bigvn = fd->newVarnode(vdata.size,vdata.space,vdata.offset);
fd->opSetOpcode(trunc,CPUI_FLOAT_FLOAT2FLOAT);
fd->opSetOutput(trunc,vn);
fd->opSetInput(trunc,bigvn,0);
fd->opInsertBefore(trunc,op);
}
/// \brief Create float extension from a lower precision \e join-space Varnode
///
/// Given a Varnode with logically lower precision, as given by a
/// float extension record (JoinRecord), create the full precision Varnode
/// specified by the record, making it defined by an extension (FLOAT2FLOAT).
/// \param vn is the lower precision \e join-space output Varnode
/// \param joinrec is the float extension record
void Heritage::floatExtensionWrite(Varnode *vn,JoinRecord *joinrec)
{
PcodeOp *op = vn->getDef();
BlockBasic *bb = (BlockBasic *)fd->getBasicBlocks().getBlock(0);
PcodeOp *ext;
if (vn->isInput())
ext = fd->newOp(1,bb->getStart());
else
ext = fd->newOp(1,op->getAddr());
const VarnodeData &vdata( joinrec->getPiece(0) ); // Float extensions have exactly 1 piece
fd->opSetOpcode(ext,CPUI_FLOAT_FLOAT2FLOAT);
fd->newVarnodeOut( vdata.size, vdata.getAddr(),ext);
fd->opSetInput( ext, vn, 0);
if (op == (PcodeOp *)0)
fd->opInsertBegin(ext,bb);
else
fd->opInsertAfter(ext,op);
}
/// \brief Split \e join-space Varnodes up into their real components
///
/// For any Varnode in the \e join-space, look up its JoinRecord and
/// split it up into the specified real components so that
/// join-space addresses play no role in the heritage process,
/// i.e. there should be no free Varnodes in the \e join-space.
void Heritage::processJoins(void)
{
AddrSpace *joinspace = fd->getArch()->getJoinSpace();
VarnodeLocSet::const_iterator iter,enditer;
iter = fd->beginLoc(joinspace);
enditer = fd->endLoc(joinspace);
while(iter != enditer) {
Varnode *vn = *iter++;
if (vn->getSpace() != joinspace) break; // New varnodes may get inserted before enditer
JoinRecord *joinrec = fd->getArch()->findJoin(vn->getOffset());
AddrSpace *piecespace = joinrec->getPiece(0).space;
if (joinrec->getUnified().size != vn->getSize())
throw LowlevelError("Joined varnode does not match size of record");
if (vn->isFree()) {
if (joinrec->isFloatExtension())
floatExtensionRead(vn,joinrec);
else
splitJoinRead(vn,joinrec);
}
HeritageInfo *info = getInfo(piecespace);
if (pass != info->delay) continue; // It is too soon to heritage this space
if (joinrec->isFloatExtension())
floatExtensionWrite(vn,joinrec);
else
splitJoinWrite(vn,joinrec); // Only do this once for a particular varnode
}
}
/// Assume the dominator tree is already built. Assume nodes are in dfs order.
void Heritage::buildADT(void)
{
const BlockGraph &bblocks(fd->getBasicBlocks());
int4 size = bblocks.getSize();
vector a(size);
vector b(size,0);
vector t(size,0);
vector z(size);
vector upstart,upend; // Up edges (node pair)
FlowBlock *x,*u,*v;
int4 i,j,k,l;
augment.clear();
augment.resize(size);
flags.clear();
flags.resize(size,0);
bblocks.buildDomTree(domchild);
#ifdef DFSVERIFY_DEBUG
verify_dfs(bblocks.getList(),domchild);
#endif
maxdepth = bblocks.buildDomDepth(depth);
for(i=0;isizeIn();++k) {
u = v->getIn(k);
if (u != v->getImmedDom()) { // If u->v is an up-edge
upstart.push_back(u); // Store edge (in dfs order)
upend.push_back(v);
b[u->getIndex()] += 1;
t[x->getIndex()] += 1;
}
}
}
}
for(i=size-1;i>=0;--i) {
k=0;
l=0;
for(j=0;jgetIndex() ];
l += z[ domchild[i][j]->getIndex() ];
}
a[i] = b[i] - t[i] + k;
z[i] = 1 + l;
if ((domchild[i].size()==0)||(z[i] > a[i] + 1)) {
flags[i] |= boundary_node; // Mark this node as a boundary node
z[i] = 1;
}
}
z[0] = -1;
for(i=1;igetImmedDom()->getIndex();
if ((flags[j]&boundary_node)!=0) // If j is a boundary node
z[i] = j;
else
z[i] = z[j];
}
for(i=0;igetImmedDom()->getIndex();
k = upstart[i]->getIndex();
while(j < k) { // while idom(v) properly dominates u
augment[ k ].push_back(v);
k = z[k];
}
}
}
/// \brief The heart of the phi-node placement algorithm
///
/// Recursively walk the dominance tree starting from a given block.
/// Calculate any children that are in the dominance frontier and add
/// them to the \b merge array.
/// \param qnode is the parent of the given block
/// \param vnode is the given block
void Heritage::visitIncr(FlowBlock *qnode,FlowBlock *vnode)
{
int4 i,j,k;
FlowBlock *v,*child;
vector::iterator iter,enditer;
i = vnode->getIndex();
j = qnode->getIndex();
iter = augment[i].begin();
enditer = augment[i].end();
for(;iter!=enditer;++iter) {
v = *iter;
if (v->getImmedDom()->getIndex() < j) { // If idom(v) is strict ancestor of qnode
k = v->getIndex();
if ((flags[k]&merged_node)==0) {
merge.push_back(v);
flags[k] |= merged_node;
}
if ((flags[k]&mark_node)==0) { // If v is not marked
flags[k] |= mark_node; // then mark it
pq.insert(v,depth[k]); // insert it into the queue
}
}
else
break;
}
if ((flags[i]&boundary_node)==0) { // If vnode is not a boundary node
for(j=0;jgetIndex()]&mark_node)==0) // If the child is not marked
visitIncr(qnode,child);
}
}
}
/// \brief Calculate blocks that should contain MULTIEQUALs for one address range
///
/// This is the main entry point for the phi-node placement algorithm. It is
/// provided the normalized list of written Varnodes in this range.
/// All refinement and guarding must already be performed for the Varnodes, and
/// the dominance tree and its augmentation must already be computed.
/// After this executes, the \b merge array holds blocks that should contain
/// a MULTIEQUAL.
/// \param write is the list of written Varnodes
void Heritage::calcMultiequals(const vector &write)
{
pq.reset(maxdepth);
merge.clear();
int4 i,j;
FlowBlock *bl;
// Place write blocks into the pq
for(i=0;igetDef()->getParent(); // Get block where this write occurs
j = bl->getIndex();
if ((flags[j]&mark_node)!=0) continue; // Already put in
pq.insert(bl,depth[j]); // Insert input node into priority queue
flags[j] |= mark_node; // mark input node
}
if ((flags[0]&mark_node)==0) { // Make sure start node is in input
pq.insert(fd->getBasicBlocks().getBlock(0),depth[0]);
flags[0] |= mark_node;
}
while(!pq.empty()) {
bl = pq.extract(); // Extract the next block
visitIncr(bl,bl);
}
for(i=0;i writelist; // List varnodes that are written in this block
BlockBasic *subbl;
list::iterator oiter,suboiter;
PcodeOp *op,*multiop;
Varnode *vnout,*vnin,*vnnew;
int4 i,slot;
for(oiter=bl->beginOp();oiter!=bl->endOp();++oiter) {
op = *oiter;
if (op->code() != CPUI_MULTIEQUAL) {
// First replace reads with top of stack
for(slot=0;slotnumInput();++slot) {
vnin = op->getIn(slot);
if (vnin->isHeritageKnown()) continue; // not free
if (!vnin->isActiveHeritage()) continue; // Not being heritaged this round
vnin->clearActiveHeritage();
vector &stack( varstack[ vnin->getAddr() ] );
if (stack.empty()) {
vnnew = fd->newVarnode(vnin->getSize(),vnin->getAddr());
vnnew = fd->setInputVarnode(vnnew);
stack.push_back(vnnew);
}
else
vnnew = stack.back();
// INDIRECTs and their op really happen AT SAME TIME
if (vnnew->isWritten() && (vnnew->getDef()->code()==CPUI_INDIRECT)) {
if (PcodeOp::getOpFromConst(vnnew->getDef()->getIn(1)->getAddr()) == op) {
if (stack.size()==1) {
vnnew = fd->newVarnode(vnin->getSize(),vnin->getAddr());
vnnew = fd->setInputVarnode(vnnew);
stack.insert(stack.begin(),vnnew);
}
else
vnnew = stack[stack.size()-2];
}
}
fd->opSetInput(op,vnnew,slot);
if (vnin->hasNoDescend())
fd->deleteVarnode(vnin);
}
}
// Then push writes onto stack
vnout = op->getOut();
if (vnout == (Varnode *)0) continue;
if (!vnout->isActiveHeritage()) continue; // Not a normalized write
vnout->clearActiveHeritage();
varstack[ vnout->getAddr() ].push_back(vnout); // Push write onto stack
writelist.push_back(vnout);
}
for(i=0;isizeOut();++i) {
subbl = (BlockBasic *)bl->getOut(i);
slot = bl->getOutRevIndex(i);
for(suboiter=subbl->beginOp();suboiter!=subbl->endOp();++suboiter) {
multiop = *suboiter;
if (multiop->code()!=CPUI_MULTIEQUAL) break; // For each MULTIEQUAL
vnin = multiop->getIn(slot);
if (!vnin->isHeritageKnown()) {
vector &stack( varstack[ vnin->getAddr() ] );
if (stack.empty()) {
vnnew = fd->newVarnode(vnin->getSize(),vnin->getAddr());
vnnew = fd->setInputVarnode(vnnew);
stack.push_back(vnnew);
}
else
vnnew = stack.back();
fd->opSetInput(multiop,vnnew,slot);
if (vnin->hasNoDescend())
fd->deleteVarnode(vnin);
}
}
}
// Now we recurse to subtrees
i = bl->getIndex();
for(slot=0;slotgetAddr()].pop_back();
}
}
/// \brief Increase the heritage delay for the given Varnode and request a restart
///
/// If applicable, look up the heritage stats for the address space for the given
/// Varnode and increment the delay. The address space must allow an additional
/// delay and can only be incremented once. If the increment succeeds, the
/// function is marked as having a \e restart pending.
/// \param vn is the given Varnode
void Heritage::bumpDeadcodeDelay(Varnode *vn)
{
AddrSpace *spc = vn->getSpace();
if ((spc->getType() != IPTR_PROCESSOR)&&(spc->getType() != IPTR_SPACEBASE))
return; // Not the right kind of space
if (spc->getDelay() != spc->getDeadcodeDelay())
return; // there is already a global delay
if (fd->getOverride().hasDeadcodeDelay(spc))
return; // A delay has already been installed
fd->getOverride().insertDeadcodeDelay(spc,spc->getDeadcodeDelay()+1);
fd->setRestartPending(true);
}
/// \brief Perform the renaming algorithm for the current set of address ranges
///
/// Phi-node placement must already have happened.
void Heritage::rename(void)
{
VariableStack varstack;
renameRecurse((BlockBasic *)fd->getBasicBlocks().getBlock(0),varstack);
disjoint.clear();
}
/// \brief Perform phi-node placement for the current set of address ranges
///
/// Main entry point for performing the phi-node placement algorithm.
/// Assume \b disjoint is filled with all the free Varnodes to be heritaged
void Heritage::placeMultiequals(void)
{
LocationMap::iterator iter;
vector readvars;
vector writevars;
vector inputvars;
vector removevars;
PcodeOp *multiop;
Varnode *vnin;
BlockBasic *bl;
int4 max;
for(iter=disjoint.begin();iter!=disjoint.end();++iter) {
Address addr = (*iter).first;
int4 size = (*iter).second.size;
readvars.clear();
writevars.clear();
inputvars.clear();
removevars.clear();
max = collect(addr,size,readvars,writevars,inputvars,removevars); // Collect reads/writes
if ((size > 4)&&(max < size)) {
if (refinement(addr,size,readvars,writevars,inputvars)) {
iter = disjoint.find(addr);
size =(*iter).second.size;
readvars.clear();
writevars.clear();
inputvars.clear();
removevars.clear();
collect(addr,size,readvars,writevars,inputvars,removevars);
}
}
if (readvars.empty() && (addr.getSpace()->getType() == IPTR_INTERNAL))
continue;
if (!removevars.empty())
removeRevisitedMarkers(removevars, addr, size);
guardInput(addr,size,inputvars);
guard(addr,size,readvars,writevars,inputvars);
if (readvars.empty()&&writevars.empty()) continue;
calcMultiequals(writevars); // Calculate where MULTIEQUALs go
for(int4 i=0;inewOp(bl->sizeIn(),bl->getStart());
Varnode *vnout = fd->newVarnodeOut(size,addr,multiop);
vnout->setActiveHeritage();
fd->opSetOpcode(multiop,CPUI_MULTIEQUAL); // Create each MULTIEQUAL
for(int4 j=0;jsizeIn();++j) {
vnin = fd->newVarnode(size,addr);
fd->opSetInput(multiop,vnin,j);
}
fd->opInsertBegin(multiop,bl); // Insert at beginning of block
}
}
merge.clear();
}
/// This is called once to initialize \b this class in preparation for doing the
/// heritage passes. An information structure is allocated and mapped to each
/// address space.
void Heritage::buildInfoList(void)
{
if (!infolist.empty()) return;
const AddrSpaceManager *manage = fd->getArch();
infolist.reserve(manage->numSpaces());
for(int4 i=0;inumSpaces();++i)
infolist.emplace_back(manage->getSpace(i));
}
/// From any address space that is active for this pass, free Varnodes are collected
/// and then fully integrated into SSA form. Reads are connected to writes, inputs
/// are identified, and phi-nodes are placed.
void Heritage::heritage(void)
{
VarnodeLocSet::const_iterator iter,enditer;
HeritageInfo *info;
Varnode *vn;
bool needwarning;
Varnode *warnvn = (Varnode *)0;
int4 reprocessStackCount = 0;
AddrSpace *stackSpace = (AddrSpace *)0;
vector freeStores;
PreferSplitManager splitmanage;
if (maxdepth == -1) // Has a restructure been forced
buildADT();
processJoins();
if (pass == 0) {
splitmanage.init(fd,&fd->getArch()->splitrecords);
splitmanage.split();
}
for(int4 i=0;iisHeritaged()) continue;
if (pass < info->delay) continue; // It is too soon to heritage this space
if (info->hasCallPlaceholders)
clearStackPlaceholders(info);
if (!info->loadGuardSearch) {
info->loadGuardSearch = true;
if (discoverIndexedStackPointers(info->space,freeStores,true)) {
reprocessStackCount += 1;
stackSpace = info->space;
}
}
needwarning = false;
iter = fd->beginLoc(info->space);
enditer = fd->endLoc(info->space);
while(iter != enditer) {
vn = *iter++;
if ((!vn->isWritten())&&vn->hasNoDescend()&&(!vn->isUnaffected())&&(!vn->isInput()))
continue;
if (vn->isWriteMask()) continue;
int4 prev = 0;
LocationMap::iterator liter = globaldisjoint.add(vn->getAddr(),vn->getSize(),pass,prev);
if (prev == 0) // All new location being heritaged, or intersecting with something new
disjoint.add((*liter).first,(*liter).second.size,pass,prev);
else if (prev==2) { // If completely contained in range from previous pass
if (vn->isHeritageKnown()) continue; // Don't heritage if we don't have to
if (vn->hasNoDescend()) continue;
if ((!needwarning)&&(info->deadremoved>0)) {
needwarning = true;
bumpDeadcodeDelay(vn);
warnvn = vn;
}
disjoint.add((*liter).first,(*liter).second.size,pass,prev);
}
else { // Partially contained in old range, but may contain new stuff
disjoint.add((*liter).first,(*liter).second.size,pass,prev);
if ((!needwarning)&&(info->deadremoved>0)) {
// TODO: We should check if this varnode is tiled by previously heritaged ranges
if (vn->isHeritageKnown()) continue; // Assume that it is tiled and produced by merging
// In most cases, a truly new overlapping read will hit the bumpDeadcodeDelay either here or in prev==2
needwarning = true;
bumpDeadcodeDelay(vn);
warnvn = vn;
}
}
}
if (needwarning) {
if (!info->warningissued) {
info->warningissued = true;
ostringstream errmsg;
errmsg << "Heritage AFTER dead removal. Example location: ";
warnvn->printRawNoMarkup(errmsg);
if (!warnvn->hasNoDescend()) {
PcodeOp *warnop = *warnvn->beginDescend();
errmsg << " : ";
warnop->getAddr().printRaw(errmsg);
}
fd->warningHeader(errmsg.str());
}
}
}
placeMultiequals();
rename();
if (reprocessStackCount > 0)
reprocessFreeStores(stackSpace, freeStores);
analyzeNewLoadGuards();
handleNewLoadCopies();
if (pass == 0)
splitmanage.splitAdditional();
pass += 1;
}
/// \param op is the given PcodeOp
/// \return the associated LoadGuard or NULL
const LoadGuard *Heritage::getStoreGuard(PcodeOp *op) const
{
list::const_iterator iter;
for(iter=storeGuard.begin();iter!=storeGuard.end();++iter) {
if ((*iter).op == op)
return &(*iter);
}
return (const LoadGuard *)0;
}
/// \brief Get the number times heritage was performed for the given address space
///
/// A negative number indicates the number of passes to wait before the first
/// heritage will occur.
/// \param spc is the given address space
/// \return the number of heritage passes performed
int4 Heritage::numHeritagePasses(AddrSpace *spc) const
{
const HeritageInfo *info = getInfo(spc);
if (!info->isHeritaged())
throw LowlevelError("Trying to calculate passes for non-heritaged space");
return (pass - info->delay);
}
/// Record that Varnodes have been removed from the given space so that we can
/// tell if there is any new heritage \e after the dead code removal.
/// \param spc is the given address space
void Heritage::seenDeadCode(AddrSpace *spc)
{
HeritageInfo *info = getInfo(spc);
info->deadremoved = 1;
}
/// Linking in Varnodes can be delayed for specific address spaces (to make sure all
/// Varnodes for the space have been generated. Return the number of \e passes to
/// delay for the given space. 0 means no delay.
/// \param spc is the given address space
/// \return the number of passes heritage is delayed
int4 Heritage::getDeadCodeDelay(AddrSpace *spc) const
{
const HeritageInfo *info = getInfo(spc);
return info->deadcodedelay;
}
/// Set the number of heritage passes that are skipped before allowing dead code
/// removal for Varnodes in the given address space (to make sure all Varnodes have
/// been linked in before deciding what is dead).
/// \param spc is the given address space
/// \param delay is the number of passes to delay
void Heritage::setDeadCodeDelay(AddrSpace *spc,int4 delay)
{
HeritageInfo *info = getInfo(spc);
if (delay < info->delay)
throw LowlevelError("Illegal deadcode delay setting");
info->deadcodedelay = delay;
}
/// Check if the required number of passes have transpired to allow removal of dead
/// Varnodes in the given address space. If allowed, presumably no new Varnodes will
/// be generated for the space.
/// \param spc is the given address space
/// \return \b true if dead code removal is allowed
bool Heritage::deadRemovalAllowed(AddrSpace *spc) const
{
const HeritageInfo *info = getInfo(spc);
return (pass > info->deadcodedelay);
}
/// \brief Check if dead code removal is safe and mark that removal has happened
///
/// A convenience function combining deadRemovalAllowed() and seenDeadCode().
/// Return \b true if it is \e safe to remove dead code, and, if so, also inform
/// the system that dead code has happened for the given space.
/// \param spc is the given address space
/// \return \b true if dead code removal is allowed
bool Heritage::deadRemovalAllowedSeen(AddrSpace *spc)
{
HeritageInfo *info = getInfo(spc);
bool res = (pass > info->deadcodedelay);
if (res)
info->deadremoved = 1;
return res;
}
/// Reset all analysis as if no heritage passes have yet taken place for the function.
/// This does not directly affect Varnodes and PcodeOps in the underlying Funcdata.
void Heritage::clear(void)
{
disjoint.clear();
globaldisjoint.clear();
domchild.clear();
augment.clear();
flags.clear();
depth.clear();
merge.clear();
clearInfoList();
loadGuard.clear();
storeGuard.clear();
maxdepth = -1;
pass = 0;
}