Merge remote-tracking branch 'origin/GP-2845_PartialMerging'

This commit is contained in:
Ryan Kurtz 2022-12-07 02:04:02 -05:00
commit a04f7fbb03
9 changed files with 818 additions and 77 deletions

View file

@ -39,6 +39,7 @@ src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END|
src/decompile/datatests/offsetarray.xml||GHIDRA||||END|
src/decompile/datatests/packstructaccess.xml||GHIDRA||||END|
src/decompile/datatests/partialmerge.xml||GHIDRA||||END|
src/decompile/datatests/partialunion.xml||GHIDRA||||END|
src/decompile/datatests/pointercmp.xml||GHIDRA||||END|
src/decompile/datatests/pointerrel.xml||GHIDRA||||END|

View file

@ -356,6 +356,10 @@ public:
VarnodeLocSet::const_iterator endLoc(int4 s,const Address &addr,const Address &pc,uintm uniq=~((uintm)0)) const {
return vbank.endLoc(s,addr,pc,uniq); }
/// \brief Given start, return maximal range of overlapping Varnodes
uint4 overlapLoc(VarnodeLocSet::const_iterator iter,vector<VarnodeLocSet::const_iterator> &bounds) const {
return vbank.overlapLoc(iter,bounds); }
/// \brief Start of all Varnodes sorted by definition address
VarnodeDefSet::const_iterator beginDef(void) const { return vbank.beginDef(); }

View file

@ -112,6 +112,10 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in)
return false; // Map to different parts of same symbol
}
// Currently don't allow merging of variables that are in separate overlapping collections
if (high_out->piece != (VariablePiece *)0 && high_in->piece != (VariablePiece *)0)
return false;
return true;
}
@ -433,6 +437,7 @@ void Merge::eliminateIntersect(Varnode *vn,const vector<BlockVarnode> &blocksort
map<int4,CoverBlock>::const_iterator iter,enditer;
Varnode *vn2;
int4 boundtype;
int4 overlaptype;
bool insertop;
for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter) {
@ -456,6 +461,13 @@ void Merge::eliminateIntersect(Varnode *vn,const vector<BlockVarnode> &blocksort
if (vn2 == vn) continue;
boundtype = single.containVarnodeDef(vn2);
if (boundtype == 0) continue;
overlaptype = vn->characterizeOverlap(*vn2);
if (overlaptype == 0) continue; // No overlap in storage
if (overlaptype == 1) { // Partial overlap
int4 off = (int4)(vn->getOffset() - vn2->getOffset());
if (vn->partialCopyShadow(vn2,off))
continue; // SUBPIECE shadow, not a new value
}
if (boundtype == 2) { // We have to resolve things defined at same place
if (vn2->getDef() == (PcodeOp *)0) {
if (vn->getDef() == (PcodeOp *)0) {
@ -483,7 +495,13 @@ void Merge::eliminateIntersect(Varnode *vn,const vector<BlockVarnode> &blocksort
if (indop->code() != CPUI_INDIRECT) continue;
// The vn2 INDIRECT must be linked to the read op
if (op != PcodeOp::getOpFromConst(indop->getIn(1)->getAddr())) continue;
if (vn->copyShadow(indop->getIn(0))) continue; // If INDIRECT input shadows vn, don't consider as intersection
if (overlaptype != 1) {
if (vn->copyShadow(indop->getIn(0))) continue; // If INDIRECT input shadows vn, don't consider as intersection
}
else {
int4 off = (int4)(vn->getOffset() - vn2->getOffset());
if (vn->partialCopyShadow(indop->getIn(0),off)) continue;
}
}
insertop = true;
break; // No need to continue iterating through varnodes in block
@ -500,7 +518,7 @@ void Merge::eliminateIntersect(Varnode *vn,const vector<BlockVarnode> &blocksort
///
/// The list of Varnodes to be merged is provided as a range in the main location sorted
/// container. Any discovered intersection is \b snipped by splitting data-flow for one of
/// the Varnodes into two or more flows, which involves insert new COPY ops and temporaries.
/// the Varnodes into two or more flows, which involves inserting new COPY ops and temporaries.
/// \param startiter is the beginning of the range of Varnodes with the same storage address
/// \param enditer is the end of the range
void Merge::unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::const_iterator enditer)
@ -513,14 +531,14 @@ void Merge::unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::
for(iter=startiter;iter!=enditer;++iter) {
vn = *iter;
if (vn->isFree()) continue;
isectlist.push_back(vn);
}
blocksort.resize(isectlist.size());
for(int4 i=0;i<isectlist.size();++i)
blocksort[i].set(isectlist[i]);
stable_sort(blocksort.begin(),blocksort.end());
// BEWARE, its possible that eliminate_intersect
// will insert new varnodes in the original range
for(int4 i=0;i<isectlist.size();++i)
eliminateIntersect(isectlist[i],blocksort);
}
@ -534,23 +552,41 @@ void Merge::unifyAddress(VarnodeLocSet::const_iterator startiter,VarnodeLocSet::
void Merge::mergeAddrTied(void)
{
bool addrtied;
VarnodeLocSet::const_iterator startiter,enditer,iter;
VarnodeLocSet::const_iterator startiter;
vector<VarnodeLocSet::const_iterator> bounds;
for(startiter=data.beginLoc();startiter!=data.endLoc();) {
addrtied = false;
enditer = data.endLoc((*startiter)->getSize(),(*startiter)->getAddr(),Varnode::written);
for(iter=startiter;iter!=enditer;++iter) {
if ((*iter)->isAddrTied()) {
addrtied = true;
break;
AddrSpace *spc = (*startiter)->getSpace();
spacetype type = spc->getType();
if (type != IPTR_PROCESSOR && type != IPTR_SPACEBASE) {
startiter = data.endLoc(spc); // Skip over the whole space
continue;
}
VarnodeLocSet::const_iterator finaliter = data.endLoc(spc);
while(startiter != finaliter) {
Varnode *vn = *startiter;
if (vn->isFree()) {
startiter = data.endLoc(vn->getSize(),vn->getAddr(),0); // Skip over any free Varnodes
continue;
}
bounds.clear();
uint4 flags = data.overlapLoc(startiter,bounds); // Collect maximally overlapping range of Varnodes
int4 max = bounds.size() - 1; // Index of last iterator
if ((flags & Varnode::addrtied) != 0) {
unifyAddress(startiter,bounds[max]);
for(int4 i=0;i<max;i+=2) { // Skip last iterator
mergeRangeMust(bounds[i],bounds[i+1]);
}
if (max > 2) {
Varnode *vn1 = *bounds[0];
for(int4 i=2;i<max;i+=2) {
Varnode *vn2 = *bounds[i];
int4 off = (int4)(vn2->getOffset() - vn1->getOffset());
vn2->getHigh()->groupWith(off, vn1->getHigh());
}
}
}
startiter = bounds[max];
}
if (addrtied) {
unifyAddress(startiter,enditer); // unify_address may stick varnodes in our range
enditer = data.endLoc((*startiter)->getSize(),(*startiter)->getAddr(),Varnode::written);
mergeRangeMust(startiter,enditer);
}
startiter = data.endLoc((*startiter)->getSize(),(*startiter)->getAddr(),0);
}
}
@ -1396,23 +1432,15 @@ void Merge::markInternalCopies(void)
#endif
}
/// \brief Perform low-level details of merging two HighVariables if possible
/// \brief Translate any intersection tests for \e high2 into tests for \e high1
///
/// This routine only fails (returning \b false) if there is a Cover intersection between
/// the two variables. Otherwise, all the Varnode instances from the second HighVariable
/// are merged into the first and its Cover is updated. The cached intersection tests are
/// also updated to reflect the merge.
/// \param high1 is the first HighVariable being merged
/// \param high2 is the second
/// \param isspeculative is \b true if the desired merge is speculative
/// \return \b true if the merge was successful
bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative)
/// The two variables will be merged and \e high2, as an object, will be freed.
/// We update the cached intersection tests for \e high2 so that they will now apply to new merged \e high1
/// \param high1 is the variable object being kept
/// \param high2 is the variable object being eliminated
void Merge::moveIntersectTests(HighVariable *high1,HighVariable *high2)
{
if (high1 == high2) return true; // Already merged
if (intersection(high1,high2)) return false;
// Translate any tests for high2 into tests for high1
vector<HighVariable *> yesinter; // Highs that high2 intersects
vector<HighVariable *> nointer; // Highs that high2 does not intersect
map<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) );
@ -1454,13 +1482,33 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative)
vector<HighVariable *>::iterator titer;
for(titer=nointer.begin();titer!=nointer.end();++titer)
(*titer)->clearMark();
// Reinsert high2's intersection==true tests for high1 now
// Reinsert high2's intersection==true tests for high1 now
for(titer=yesinter.begin();titer!=yesinter.end();++titer) {
highedgemap[ HighEdge(high1,*titer) ] = true;
highedgemap[ HighEdge(*titer,high1) ] = true;
}
}
/// \brief Perform low-level details of merging two HighVariables if possible
///
/// This routine only fails (returning \b false) if there is a Cover intersection between
/// the two variables. Otherwise, all the Varnode instances from the second HighVariable
/// are merged into the first and its Cover is updated. The second variable is deleted.
/// The cached intersection tests are also updated to reflect the merge.
/// \param high1 is the first HighVariable being merged
/// \param high2 is the second
/// \param isspeculative is \b true if the desired merge is speculative
/// \return \b true if the merge was successful
bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative)
{
if (high1 == high2) return true; // Already merged
if (intersection(high1,high2)) return false;
moveIntersectTests(high1, high2);
high1->merge(high2,isspeculative); // Do the actual merge
high1->updateCover();
high1->updateCover(); // Update cover now so that updateHigh won't purge cached tests
return true;
}
@ -1473,10 +1521,8 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative)
bool Merge::updateHigh(HighVariable *a)
{
if ((a->highflags&HighVariable::coverdirty)==0) return true;
if (!a->isCoverDirty()) return true;
for(int4 i=0;i<a->numInstances();++i)
a->getInstance(i)->updateCover();
a->updateCover();
purgeHigh(a);
return false;
@ -1525,7 +1571,7 @@ bool Merge::intersection(HighVariable *a,HighVariable *b)
bool res = false;
int4 blk;
vector<int4> blockisect;
a->wholecover.intersectList(blockisect,b->wholecover,2);
a->getCover().intersectList(blockisect,b->getCover(),2);
for(blk=0;blk<blockisect.size();++blk) {
if (blockIntersection(a,b,blockisect[blk])) {
res = true;
@ -1537,6 +1583,56 @@ bool Merge::intersection(HighVariable *a,HighVariable *b)
return res;
}
/// \brief Gather Varnode instances of the given HighVariable that intersect a cover on a specific block
///
/// \param a is the given HighVariable
/// \param blk is the specific block number
/// \param cover is the Cover to test for intersection
/// \param res will hold the resulting intersecting Varnodes
void Merge::gatherBlockVarnodes(HighVariable *a,int4 blk,const Cover &cover,vector<Varnode *> &res)
{
for(int4 i=0;i<a->numInstances();++i) {
Varnode *vn = a->getInstance(i);
if (1<vn->getCover()->intersectByBlock(blk,cover))
res.push_back(vn);
}
}
/// \brief Test instances of a the given HighVariable for intersection on a specific block with a cover
///
/// A list of Varnodes has already been determined to intersect on the block. For an instance that does as
/// well, a final test of copy shadowing is performed with the Varnode list. If there is no shadowing,
/// a merging intersection has been found and \b true is returned.
/// \param a is the given HighVariable
/// \param blk is the specific block number
/// \param cover is the Cover to test for intersection
/// \param relOff is the relative byte offset of the HighVariable to the Varnodes
/// \param blist is the list of Varnodes for copy shadow testing
/// \return \b true if there is an intersection preventing merging
bool Merge::testBlockIntersection(HighVariable *a,int4 blk,const Cover &cover,int4 relOff,const vector<Varnode *> &blist)
{
for(int4 i=0;i<a->numInstances();++i) {
Varnode *vn = a->getInstance(i);
if (2>vn->getCover()->intersectByBlock(blk,cover)) continue;
for(int4 j=0;j<blist.size();++j) {
Varnode *vn2 = blist[j];
if (1<vn2->getCover()->intersectByBlock(blk,*vn->getCover())) {
if (vn->getSize() == vn2->getSize()) {
if (!vn->copyShadow(vn2))
return true;
}
else {
if (!vn->partialCopyShadow(vn2,relOff))
return true;
}
}
}
}
return false;
}
/// \brief Test if two HighVariables intersect on a given BlockBasic
///
/// Intersections are checked only on the specified block.
@ -1549,19 +1645,40 @@ bool Merge::blockIntersection(HighVariable *a,HighVariable *b,int4 blk)
{
vector<Varnode *> blist;
for(int4 i=0;i<b->numInstances();++i) {
Varnode *vn = b->getInstance(i);
if (1<vn->getCover()->intersectByBlock(blk,a->wholecover))
blist.push_back(vn);
const Cover &aCover(a->getCover());
const Cover &bCover(b->getCover());
gatherBlockVarnodes(b,blk,aCover,blist);
if (testBlockIntersection(a, blk, bCover, 0, blist))
return true;
if (a->piece != (VariablePiece *)0) {
int4 baseOff = a->piece->getOffset();
for(int4 i=0;i<a->piece->numIntersection();++i) {
const VariablePiece *interPiece = a->piece->getIntersection(i);
int4 off = interPiece->getOffset() - baseOff;
if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist))
return true;
}
}
for(int4 i=0;i<a->numInstances();++i) {
Varnode *vn = a->getInstance(i);
if (2>vn->getCover()->intersectByBlock(blk,b->wholecover)) continue;
for(int4 j=0;j<blist.size();++j) {
Varnode *vn2 = blist[j];
if (1<vn2->getCover()->intersectByBlock(blk,*vn->getCover()))
if (!vn->copyShadow(vn2))
return true;
if (b->piece != (VariablePiece *)0) {
int4 bBaseOff = b->piece->getOffset();
for(int4 i=0;i<b->piece->numIntersection();++i) {
blist.clear();
const VariablePiece *bPiece = b->piece->getIntersection(i);
int4 bOff = bPiece->getOffset() - bBaseOff;
gatherBlockVarnodes(bPiece->getHigh(),blk,aCover,blist);
if (testBlockIntersection(a, blk, bCover, -bOff, blist))
return true;
if (a->piece != (VariablePiece *)0) {
int4 baseOff = a->piece->getOffset();
for(int4 j=0;j<a->piece->numIntersection();++j) {
const VariablePiece *interPiece = a->piece->getIntersection(j);
int4 off = (interPiece->getOffset() - baseOff) - bOff;
if (off > 0 && off >= bPiece->getSize()) continue; // Do a piece and b piece intersect at all
if (off < 0 && -off >= interPiece->getSize()) continue;
if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist))
return true;
}
}
}
}
return false;
@ -1595,24 +1712,38 @@ void Merge::inflate(Varnode *a,HighVariable *high)
/// Varnode is not part of the HighVariable.
/// \param a is the given Varnode to inflate
/// \param high is the HighVariable being propagated
/// \return \b true if the Varnode can be inflated without intersection
/// \return \b true if inflating the Varnode causes an intersection
bool Merge::inflateTest(Varnode *a,HighVariable *high)
{
HighVariable *ahigh = a->getHigh();
bool res = false;
updateHigh(high);
const Cover &highCover( high->internalCover ); // Only check for intersections with cover contributing to inflate
for(int4 i=0;i<ahigh->numInstances();++i) {
Varnode *b = ahigh->getInstance(i);
if (b->copyShadow(a)) continue;
if (2==b->getCover()->intersect( high->wholecover )) {
res = true;
break;
if (b->copyShadow(a)) continue; // Intersection with a or shadows of a is allowed
if (2==b->getCover()->intersect( highCover )) {
return true;
}
}
return res;
VariablePiece *piece = ahigh->piece;
if (piece != (VariablePiece *)0) {
piece->updateIntersections();
for(int4 i=0;i<piece->numIntersection();++i) {
const VariablePiece *otherPiece = piece->getIntersection(i);
HighVariable *otherHigh = otherPiece->getHigh();
int4 off = otherPiece->getOffset() - piece->getOffset();
for(int4 i=0;i<otherHigh->numInstances();++i) {
Varnode *b = otherHigh->getInstance(i);
if (b->partialCopyShadow(a, off)) continue; // Intersection with partial shadow of a is allowed
if (2==b->getCover()->intersect( highCover ))
return true;
}
}
}
return false;
}
/// \brief Test for intersections between a given HighVariable and a list of other HighVariables

View file

@ -83,6 +83,8 @@ class Merge {
vector<PcodeOp *> copyTrims; ///< COPY ops inserted to facilitate merges
bool updateHigh(HighVariable *a); ///< Make sure given HighVariable's Cover is up-to-date
void purgeHigh(HighVariable *high); ///< Remove cached intersection tests for a given HighVariable
static void gatherBlockVarnodes(HighVariable *a,int4 blk,const Cover &cover,vector<Varnode *> &res);
static bool testBlockIntersection(HighVariable *a,int4 blk,const Cover &cover,int4 relOff,const vector<Varnode *> &blist);
bool blockIntersection(HighVariable *a,HighVariable *b,int4 blk);
static bool mergeTestRequired(HighVariable *high_out,HighVariable *high_in);
static bool mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in);
@ -96,6 +98,7 @@ class Merge {
void collectCovering(vector<Varnode *> &vlist,HighVariable *high,PcodeOp *op);
bool collectCorrectable(const vector<Varnode *> &vlist,list<PcodeOp *> &oplist,vector<int4> &slotlist,
PcodeOp *op);
void moveIntersectTests(HighVariable *high1,HighVariable *high2);
PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp);
void snipReads(Varnode *vn,list<PcodeOp *> &markedop);
void snipIndirect(PcodeOp *indop);
@ -148,7 +151,7 @@ public:
inline bool Merge::compareHighByBlock(const HighVariable *a,const HighVariable *b)
{
int4 result = a->wholecover.compareTo(b->wholecover);
int4 result = a->getCover().compareTo(b->getCover());
if ( result == 0 ) {
Varnode *v1 = a->getInstance( 0 );
Varnode *v2 = b->getInstance( 0 );

View file

@ -23,6 +23,170 @@ AttributeId ATTRIB_SYMREF = AttributeId("symref",68);
ElementId ELEM_HIGH = ElementId("high",82);
/// Compare by offset within the group, then by size.
/// \param op2 is the other piece to compare with \b this
/// \return \b true if \b this should be ordered before the other piece
bool VariableGroup::PieceCompareByOffset::operator()(const VariablePiece *a,const VariablePiece *b) const
{
if (a->getOffset() != b->getOffset())
return (a->getOffset() < b->getOffset());
return (a->getSize() < b->getSize());
}
/// The VariablePiece takes partial ownership of \b this, via refCount.
/// \param piece is the new piece to add
void VariableGroup::addPiece(VariablePiece *piece)
{
piece->group = this;
if (!pieceSet.insert(piece).second)
throw LowlevelError("Duplicate VariablePiece");
}
void VariableGroup::removePiece(VariablePiece *piece)
{
pieceSet.erase(piece);
}
/// Construct piece given a HighVariable and its position within the whole.
/// If \b this is the first piece in the group, allocate a new VariableGroup object.
/// \param h is the given HighVariable to treat as a piece
/// \param offset is the byte offset of the piece within the whole
/// \param grp is another HighVariable in the whole, or null if \b this is the first piece
VariablePiece::VariablePiece(HighVariable *h,int4 offset,HighVariable *grp)
{
high = h;
groupOffset = offset;
size = h->getInstance(0)->getSize();
if (grp != (HighVariable *)0)
group = grp->piece->getGroup();
else
group = new VariableGroup();
group->addPiece(this);
}
VariablePiece::~VariablePiece(void)
{
group->removePiece(this);
if (group->empty())
delete group;
else
markIntersectionDirty();
}
void VariablePiece::markIntersectionDirty(void) const
{
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::const_iterator iter;
for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter)
(*iter)->high->highflags |= (HighVariable::intersectdirty | HighVariable::extendcoverdirty);
}
void VariablePiece::markExtendCoverDirty(void) const
{
if ((high->highflags & HighVariable::intersectdirty)!=0)
return; // intersection list itself is dirty, extended covers will be recomputed anyway
for(int4 i=0;i<intersection.size();++i) {
intersection[i]->high->highflags |= HighVariable::extendcoverdirty;
}
high->highflags |= HighVariable::extendcoverdirty;
}
/// Compute list of exactly the HighVariable pieces that intersect with \b this.
void VariablePiece::updateIntersections(void) const
{
if ((high->highflags & HighVariable::intersectdirty)==0) return;
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::const_iterator iter;
int4 endOffset = groupOffset + size;
intersection.clear();
for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) {
VariablePiece *otherPiece = *iter;
if (otherPiece == this) continue;
if (endOffset <= otherPiece->groupOffset) continue;
int4 otherEndOffset = otherPiece->groupOffset + otherPiece->size;
if (groupOffset >= otherEndOffset) continue;
intersection.push_back(otherPiece);
}
high->highflags &= ~(uint4)HighVariable::intersectdirty;
}
/// Union internal covers of all pieces intersecting with \b this.
void VariablePiece::updateCover(void) const
{
if ((high->highflags & (HighVariable::coverdirty | HighVariable::extendcoverdirty))==0) return;
high->updateInternalCover();
cover = high->internalCover;
for(int4 i=0;i<intersection.size();++i) {
const HighVariable *high = intersection[i]->high;
high->updateInternalCover();
cover.merge(high->internalCover);
}
high->highflags &= ~(uint4)HighVariable::extendcoverdirty;
}
/// \param amt is the given amout to add to offset
void VariablePiece::adjustOffset(int4 amt)
{
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter;
for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) {
(*iter)->groupOffset += amt;
}
}
/// If there are no remaining references to the old VariableGroup it is deleted.
/// \param newGropu is the new VariableGroup to transfer \b this to
void VariablePiece::transferGroup(VariableGroup *newGroup)
{
group->removePiece(this);
if (group->empty())
delete group;
newGroup->addPiece(this);
}
/// Combine the VariableGroup associated with the given other VariablePiece and the VariableGroup of \b this
/// into one group. Combining in this way requires pieces of the same size and offset to be merged. This
/// method does not do the merging but passes back a list of HighVariable pairs that need to be merged.
/// The first element in the pair will have its VariablePiece in the new group, and the second element
/// will have its VariablePiece freed in preparation for the merge.
/// Offsets are adjusted so that \b this and the given other piece have the same offset;
/// \param op2 is the given other VariablePiece
/// \param mergePairs passes back the collection of HighVariable pairs that must be merged
void VariablePiece::combineOtherGroup(VariablePiece *op2,vector<HighVariable *> &mergePairs)
{
int4 diff = groupOffset - op2->groupOffset; // Add to op2, or subtract from this
if (diff > 0)
op2->adjustOffset(diff);
else if (diff < 0)
adjustOffset(-diff);
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->group->pieceSet.begin();
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator enditer = op2->group->pieceSet.end();
while(iter != enditer) {
VariablePiece *piece = *iter;
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator matchiter = group->pieceSet.find(piece);
if (matchiter != group->pieceSet.end()) {
mergePairs.push_back((*matchiter)->high);
mergePairs.push_back(piece->high);
piece->high->piece = (VariablePiece *)0; // Detach HighVariable from its original VariablePiece
delete piece;
}
else
piece->transferGroup(group);
}
}
/// The new instance starts off with no associate Symbol and all properties marked as \e dirty.
/// \param vn is the single Varnode member
HighVariable::HighVariable(Varnode *vn)
@ -32,6 +196,7 @@ HighVariable::HighVariable(Varnode *vn)
highflags = flagsdirty | namerepdirty | typedirty | coverdirty;
flags = 0;
type = (Datatype *)0;
piece = (VariablePiece *)0;
symbol = (Symbol *)0;
nameRepresentative = (Varnode *)0;
symboloffset = -1;
@ -41,6 +206,13 @@ HighVariable::HighVariable(Varnode *vn)
setSymbol(vn);
}
HighVariable::~HighVariable(void)
{
if (piece != (VariablePiece *)0)
delete piece;
}
/// The given Varnode \b must be a member and \b must have a non-null SymbolEntry
void HighVariable::setSymbol(Varnode *vn) const
@ -82,19 +254,41 @@ void HighVariable::setSymbolReference(Symbol *sym,int4 off)
highflags &= ~((uint4)symboldirty);
}
void HighVariable::transferPiece(HighVariable *tv2)
{
piece = tv2->piece;
tv2->piece = (VariablePiece *)0;
piece->setHigh(this);
highflags |= (tv2->highflags & (intersectdirty | extendcoverdirty));
tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty);
}
/// Only update if the cover is marked as \e dirty.
/// Merge the covers of all Varnode instances.
void HighVariable::updateInternalCover(void) const
{
if ((highflags & coverdirty) != 0) {
internalCover.clear();
if (inst[0]->hasCover()) {
for(int4 i = 0;i < inst.size();++i)
internalCover.merge(*inst[i]->getCover());
}
highflags &= ~coverdirty;
}
}
/// This is \b only called by the Merge class which knows when to call it properly.
void HighVariable::updateCover(void) const
{
if ((highflags & coverdirty)==0) return; // Cover info is upto date
highflags &= ~coverdirty;
wholecover.clear();
if (!inst[0]->hasCover()) return;
for(int4 i=0;i<inst.size();++i)
wholecover.merge(*inst[i]->getCover());
if (piece == (VariablePiece *)0)
updateInternalCover();
else {
piece->updateIntersections();
piece->updateCover();
}
}
/// Only update if flags are marked as \e dirty.
@ -275,6 +469,8 @@ void HighVariable::remove(Varnode *vn)
highflags |= (flagsdirty|namerepdirty|coverdirty|typedirty);
if (vn->getSymbolEntry() != (SymbolEntry *)0)
highflags |= symboldirty;
if (piece != (VariablePiece *)0)
piece->markExtendCoverDirty();
return;
}
}
@ -304,16 +500,49 @@ void HighVariable::finalizeDatatype(Datatype *tp)
highflags |= type_finalized;
}
/// If one of the HighVariables is already in a group, the other HighVariable is added to this group.
/// \param off is the relative byte offset of \b this with the other HighVariable
/// \param hi2 is the other HighVariable
void HighVariable::groupWith(int4 off,HighVariable *hi2)
{
if (piece == (VariablePiece *)0 && hi2->piece == (VariablePiece *)0) {
hi2->piece = new VariablePiece(hi2,0);
piece = new VariablePiece(this,off,hi2);
hi2->piece->markIntersectionDirty();
return;
}
if (piece == (VariablePiece *)0) {
if ((hi2->highflags & intersectdirty) == 0)
hi2->piece->markIntersectionDirty();
highflags |= intersectdirty | extendcoverdirty;
off += hi2->piece->getOffset();
piece = new VariablePiece(this,off,hi2);
}
else if (hi2->piece == (VariablePiece *)0) {
int4 hi2Off = piece->getOffset() - off;
if (hi2Off < 0) {
piece->adjustOffset(-hi2Off);
hi2Off = 0;
}
if ((highflags & intersectdirty) == 0)
piece->markIntersectionDirty();
hi2->highflags |= intersectdirty | extendcoverdirty;
hi2->piece = new VariablePiece(hi2,hi2Off,this);
}
else {
throw LowlevelError("Cannot group HighVariables that are already grouped");
}
}
/// The lists of members are merged and the other HighVariable is deleted.
/// \param tv2 is the other HighVariable to merge into \b this
/// \param isspeculative is \b true to keep the new members in separate \e merge classes
void HighVariable::merge(HighVariable *tv2,bool isspeculative)
void HighVariable::mergeInternal(HighVariable *tv2,bool isspeculative)
{
int4 i;
if (tv2 == this) return;
highflags |= (flagsdirty|namerepdirty|typedirty);
if (tv2->symbol != (Symbol *)0) { // Check if we inherit a Symbol
if ((tv2->highflags & symboldirty)==0) {
@ -344,13 +573,54 @@ void HighVariable::merge(HighVariable *tv2,bool isspeculative)
tv2->inst.clear();
if (((highflags&coverdirty)==0)&&((tv2->highflags&coverdirty)==0))
wholecover.merge(tv2->wholecover);
internalCover.merge(tv2->internalCover);
else
highflags |= coverdirty;
delete tv2;
}
/// The HighVariables are merged internally as with mergeInternal. If \b this is part of a VariableGroup,
/// extended covers of the group may be affected. If both HighVariables are part of separate groups,
/// the groups are combined into one, which may induce additional HighVariable pairs within the group to be merged.
/// In all cases, the other HighVariable is deleted.
/// \param tv2 is the other HighVariable to merge into \b this
/// \param isspeculative is \b true to keep the new members in separate \e merge classes
void HighVariable::merge(HighVariable *tv2,bool isspeculative)
{
if (tv2 == this) return;
if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) {
mergeInternal(tv2,isspeculative);
return;
}
if (tv2->piece == (VariablePiece *)0) {
// Keep group that this is already in
piece->markExtendCoverDirty();
mergeInternal(tv2,isspeculative);
return;
}
if (piece == (VariablePiece *)0) {
// Move ownership of the VariablePiece object from the HighVariable that will be freed
transferPiece(tv2);
piece->markExtendCoverDirty();
mergeInternal(tv2,isspeculative);
return;
}
// Reaching here both HighVariables are part of a group
throw LowlevelError("Merging variables in separate groups not supported");
// vector<HighVariable *> mergePairs;
// piece->combineOtherGroup(tv2->piece, mergePairs);
// for(int4 i=0;i<mergePairs.size();i+=2) {
// HighVariable *high1 = mergePairs[i];
// HighVariable *high2 = mergePairs[i+1];
// // Need to deal with cached intersect tests herev
// high1->mergeInternal(high2, isspeculative);
// }
// piece->markIntersectionDirty();
}
/// All Varnode objects are assigned a HighVariable, including those that don't get names like
/// indirect variables, constants, and annotations. Determine if \b this, as inherited from its
/// member Varnodes, can have a name.

View file

@ -29,6 +29,63 @@ extern AttributeId ATTRIB_SYMREF; ///< Marshaling attribute "symref"
extern ElementId ELEM_HIGH; ///< Marshaling element \<high>
class HighVariable; ///< Forward declaration
class VariablePiece; ///< Forward declaration
/// \brief A collection of HighVariable objects that overlap
///
/// A HighVariable represents a variable or partial variable that is manipulated as a unit by the (de)compiler.
/// A formal Symbol may be manipulated using multiple HighVariables that in principal can overlap. For a set of
/// HighVariable objects that mutually overlap, a VariableGroup is a central access point for information about
/// the intersections. The information is used in particular to extend HighVariable Cover objects to take into
/// account the intersections.
class VariableGroup {
friend class VariablePiece;
/// \brief Compare two VariablePiece pointers by offset then by size
struct PieceCompareByOffset {
bool operator()(const VariablePiece *a,const VariablePiece *b) const; ///< Comparison operator
};
set<VariablePiece *,PieceCompareByOffset> pieceSet; ///< The set of VariablePieces making up \b this group
public:
bool empty(void) const { return pieceSet.empty(); } ///< Return \b true if \b this group has no pieces
void addPiece(VariablePiece *piece); ///< Add a new piece to \b this group
void removePiece(VariablePiece *piece); ///< Remove a piece from \b this group
};
/// \brief Information about how a HighVariable fits into a larger group or Symbol
///
/// This is an extension to a HighVariable object that is assigned if the HighVariable is part of a
/// group of mutually overlapping HighVariables. It describes the overlaps and how they affect the HighVariable Cover.
class VariablePiece {
friend class VariableGroup;
VariableGroup *group; ///< Group to which \b this piece belongs
HighVariable *high; ///< HighVariable owning \b this piece
int4 groupOffset; ///< Byte offset of \b this piece within the group
int4 size; ///< Number of bytes in \b this piece
mutable vector<const VariablePiece *> intersection; ///< List of VariablePieces \b this piece intersects with
mutable Cover cover; ///< Extended cover for the piece, taking into account intersections
public:
VariablePiece(HighVariable *h,int4 offset,HighVariable *grp=(HighVariable *)0);
~VariablePiece(void); ///< Destructor
HighVariable *getHigh(void) const { return high; } ///< Get the HighVariable associate with \b this piece
VariableGroup *getGroup(void) const { return group; } ///< Get the central group
int4 getOffset(void) const { return groupOffset; } ///< Get the offset of \b this within its group
int4 getSize(void) const { return size; } ///< Return the number of bytes in \b this piece.
const Cover &getCover(void) const { return cover; } ///< Get the cover associated with \b this piece.
int4 numIntersection(void) const { return intersection.size(); } ///< Get number of pieces \b this intersects with
const VariablePiece *getIntersection(int4 i) const { return intersection[i]; } ///< Get i-th piece \b this intersects with
void markIntersectionDirty(void) const; ///< Mark all pieces as needing intersection recalculation
void markExtendCoverDirty(void) const; ///< Mark all intersecting pieces as having a dirty extended cover
void updateIntersections(void) const; ///< Calculate intersections with other pieces in the group
void updateCover(void) const; ///< Calculate extended cover based on intersections
void adjustOffset(int4 amt); ///< Adjust every piece's offset by the given amount
void transferGroup(VariableGroup *newGroup); ///< Transfer \b this piece to another VariableGroup
void setHigh(HighVariable *newHigh) { high = newHigh; } ///< Move ownership of \b this to another HighVariable
void combineOtherGroup(VariablePiece *op2,vector<HighVariable *> &mergePairs); ///< Combine two VariableGroups
};
/// \brief A high-level variable modeled as a list of low-level variables, each written once
///
/// In the Static Single Assignment (SSA) representation of a function's data-flow, the Varnode
@ -57,23 +114,28 @@ public:
copy_in1 = 0x20, ///< There exists at least 1 COPY into \b this HighVariable from other HighVariables
copy_in2 = 0x40, ///< There exists at least 2 COPYs into \b this HighVariable from other HighVariables
type_finalized = 0x80, ///< Set if a final data-type is locked in and dirtying is disabled
unmerged = 0x100 ///< Set if part of a multi-entry Symbol but did not get merged with other SymbolEntrys
unmerged = 0x100, ///< Set if part of a multi-entry Symbol but did not get merged with other SymbolEntrys
intersectdirty = 0x200, ///< Set if intersections with other HighVariables needs to be recomputed
extendcoverdirty = 0x400 ///< Set if extended cover needs to be recomputed
};
private:
friend class Varnode;
friend class Merge;
friend class VariablePiece;
vector<Varnode *> inst; ///< The member Varnode objects making up \b this HighVariable
int4 numMergeClasses; ///< Number of different speculative merge classes in \b this
mutable uint4 highflags; ///< Dirtiness flags
mutable uint4 flags; ///< Boolean properties inherited from Varnode members
mutable Datatype *type; ///< The data-type for \b this
mutable Varnode *nameRepresentative; ///< The storage location used to generate a Symbol name
mutable Cover wholecover; ///< The ranges of code addresses covered by this HighVariable
mutable Cover internalCover; ///< The ranges of code addresses covered by this HighVariable
mutable VariablePiece *piece; ///< Additional info about intersections with other pieces (if non-null)
mutable Symbol *symbol; ///< The Symbol \b this HighVariable is tied to
mutable int4 symboloffset; ///< -1=perfect symbol match >=0, offset
int4 instanceIndex(const Varnode *vn) const; ///< Find the index of a specific Varnode member
void updateFlags(void) const; ///< (Re)derive boolean properties of \b this from the member Varnodes
void updateCover(void) const; ///< (Re)derive the cover of \b this from the member Varnodes
void updateInternalCover(void) const; ///< (Re)derive the internal cover of \b this from the member Varnodes
void updateCover(void) const; ///< (Re)derive the external cover of \b this, as a union of internal covers
void updateType(void) const; ///< (Re)derive the data-type for \b this from the member Varnodes
void updateSymbol(void) const; ///< (Re)derive the Symbol and offset for \b this from member Varnodes
void setCopyIn1(void) const { highflags |= copy_in1; } ///< Mark the existence of one COPY into \b this
@ -82,28 +144,34 @@ private:
bool hasCopyIn1(void) const { return ((highflags&copy_in1)!=0); } ///< Is there at least one COPY into \b this
bool hasCopyIn2(void) const { return ((highflags&copy_in2)!=0); } ///< Is there at least two COPYs into \b this
void remove(Varnode *vn); ///< Remove a member Varnode from \b this
void merge(HighVariable *tv2,bool isspeculative); ///< Merge another HighVariable into \b this
void mergeInternal(HighVariable *tv2,bool isspeculative); ///< Merge another HighVariable into \b this
void merge(HighVariable *tv2,bool isspeculative); ///< Merge with another HighVariable taking into account groups
void setSymbol(Varnode *vn) const; ///< Update Symbol information for \b this from the given member Varnode
void setSymbolReference(Symbol *sym,int4 off); ///< Attach a reference to a Symbol to \b this
void transferPiece(HighVariable *tv2); ///< Transfer ownership of another's VariablePiece to \b this
void flagsDirty(void) const { highflags |= flagsdirty | namerepdirty; } ///< Mark the boolean properties as \e dirty
void coverDirty(void) const { highflags |= coverdirty; } ///< Mark the cover as \e dirty
void coverDirty(void) const; ///< Mark the cover as \e dirty
void typeDirty(void) const { highflags |= typedirty; } ///< Mark the data-type as \e dirty
void symbolDirty(void) const { highflags |= symboldirty; } ///< Mark the symbol as \e dirty
void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems
bool isCoverDirty(void) const; ///< Is the cover returned by getCover() up-to-date
public:
HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode
~HighVariable(void); ///< Destructor
Datatype *getType(void) const { updateType(); return type; } ///< Get the data-type
const Cover &getCover(void) const; ///< Get cover data for \b this variable
Symbol *getSymbol(void) const { updateSymbol(); return symbol; } ///< Get the Symbol associated with \b this or null
SymbolEntry *getSymbolEntry(void) const; /// Get the SymbolEntry mapping to \b this or null
int4 getSymbolOffset(void) const { return symboloffset; } ///< Get the Symbol offset associated with \b this
int4 numInstances(void) const { return inst.size(); } ///< Get the number of member Varnodes \b this has
Varnode *getInstance(int4 i) const { return inst[i]; } ///< Get the i-th member Varnode
void finalizeDatatype(Datatype *tp); ///< Set a final datatype for \b this variable
void groupWith(int4 off,HighVariable *hi2); ///< Put \b this and another HighVariable in the same intersection group
/// \brief Print details of the cover for \b this (for debug purposes)
///
/// \param s is the output stream
void printCover(ostream &s) const { if ((highflags&HighVariable::coverdirty)==0) wholecover.print(s); else s << "Cover dirty"; }
void printCover(ostream &s) const { if ((highflags&HighVariable::coverdirty)==0) internalCover.print(s); else s << "Cover dirty"; }
void printInfo(ostream &s) const; ///< Print information about \b this HighVariable to stream
bool hasName(void) const; ///< Check if \b this HighVariable can be named
@ -147,4 +215,33 @@ public:
static int4 markExpression(Varnode *vn,vector<HighVariable *> &highList); ///< Mark and collect variables in expression
};
/// The internal cover is marked as dirty. If \b this is a piece of a VariableGroup, it and all the other
/// HighVariables it intersects with are marked as having a dirty extended cover.
inline void HighVariable::coverDirty(void) const
{
highflags |= coverdirty;
if (piece != (VariablePiece *)0)
piece->markExtendCoverDirty();
}
/// The cover could either by the internal one or the extended one if \b this is part of a Variable Group.
/// \return \b true if the cover needs to be recomputed.
inline bool HighVariable::isCoverDirty(void) const
{
return ((highflags & (coverdirty | extendcoverdirty)) != 0);
}
/// The returns the internal cover unless \b this is part of a VariableGroup, in which case the
/// extended cover is returned.
/// \return the cover associated with \b this variable
inline const Cover &HighVariable::getCover(void) const
{
if (piece == (VariablePiece *)0)
return internalCover;
return piece->getCover();
}
#endif

View file

@ -878,6 +878,135 @@ bool Varnode::copyShadow(const Varnode *op2) const
return false;
}
/// \brief Try to find a SUBPIECE operation producing the value in \b this from the given \b whole Varnode
///
/// The amount of truncation producing \b this must be known apriori. Allow for COPY and MULTIEQUAL operations
/// in the flow path from \b whole to \b this. This method will search recursively through branches
/// of MULTIEQUAL up to a maximum depth.
/// \param leastByte is the number of least significant bytes being truncated from \b whole to get \b this
/// \param whole is the given whole Varnode
/// \param recurse is the current depth of recursion
/// \return \b true if \b this and \b whole have the prescribed SUBPIECE relationship
bool Varnode::findSubpieceShadow(int4 leastByte,const Varnode *whole,int4 recurse) const
{
const Varnode *vn = this;
while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY)
vn = vn->getDef()->getIn(0);
if (!vn->isWritten()) {
if (vn->isConstant()) {
while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY)
whole = whole->getDef()->getIn(0);
if (!whole->isConstant()) return false;
uintb off = whole->getOffset() >> leastByte*8;
off &= calc_mask(vn->getSize());
return (off == vn->getOffset());
}
return false;
}
OpCode opc = vn->getDef()->code();
if (opc == CPUI_SUBPIECE) {
const Varnode *tmpvn = vn->getDef()->getIn(0);
int4 off = (int4)vn->getDef()->getIn(1)->getOffset();
if (off != leastByte || tmpvn->getSize() != whole->getSize())
return false;
if (tmpvn == whole) return true;
while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) {
tmpvn = tmpvn->getDef()->getIn(0);
if (tmpvn == whole) return true;
}
}
else if (opc == CPUI_MULTIEQUAL) {
recurse += 1;
if (recurse > 1) return false; // Truncate the recursion at maximum depth
while( whole->isWritten() && whole->getDef()->code() == CPUI_COPY)
whole = whole->getDef()->getIn(0);
if (!whole->isWritten()) return false;
const PcodeOp *bigOp = whole->getDef();
if (bigOp->code() != CPUI_MULTIEQUAL) return false;
const PcodeOp *smallOp = vn->getDef();
if (bigOp->getParent() != smallOp->getParent()) return false;
// Recurse search through all branches of the two MULTIEQUALs
for(int4 i=0;i<smallOp->numInput();++i) {
if (!smallOp->getIn(i)->findSubpieceShadow(leastByte, bigOp->getIn(i), recurse))
return false;
}
return true; // All branches were copy shadows
}
return false;
}
bool Varnode::findPieceShadow(int4 leastByte,const Varnode *piece) const
{
const Varnode *vn = this;
while( vn->isWritten() && vn->getDef()->code() == CPUI_COPY)
vn = vn->getDef()->getIn(0);
if (!vn->isWritten()) return false;
OpCode opc = vn->getDef()->code();
if (opc == CPUI_PIECE) {
const Varnode *tmpvn = vn->getDef()->getIn(1); // Least significant part
if (leastByte >= tmpvn->getSize()) {
leastByte -= tmpvn->getSize();
tmpvn = vn->getDef()->getIn(0);
}
else {
if (piece->getSize() + leastByte > tmpvn->getSize()) return false;
}
if (leastByte == 0 && tmpvn->getSize() == piece->getSize()) {
if (tmpvn == piece) return true;
while(tmpvn->isWritten() && tmpvn->getDef()->code() == CPUI_COPY) {
tmpvn = tmpvn->getDef()->getIn(0);
if (tmpvn == piece) return true;
}
return false;
}
// CPUI_PIECE input is too big, recursively search for another CPUI_PIECE
return tmpvn->findPieceShadow(leastByte, piece);
}
return false;
}
/// For \b this and another Varnode, establish that either:
/// - bigger = CONCAT(smaller,..) or
/// - smaller = SUBPIECE(bigger)
///
/// Check through COPY chains and verify that the form of the CONCAT or SUBPIECE matches
/// a given relative offset between the Varnodes.
/// \param op2 is the Varnode to compare to \b this
/// \param relOff is the putative relative byte offset of \b this to \b op2
/// \return \b true if one Varnode is contained, as a value, in the other
bool Varnode::partialCopyShadow(const Varnode *op2,int4 relOff) const
{
const Varnode *vn;
if (size < op2->size) {
vn = this;
}
else if (size > op2->size) {
vn = op2;
op2 = this;
relOff = -relOff;
}
else
return false;
if (relOff < 0)
return false; // Not proper containment
if (relOff + vn->getSize() > op2->getSize())
return false; // Not proper containment
bool bigEndian = getSpace()->isBigEndian();
int4 leastByte = bigEndian ? (op2->getSize() - vn->getSize()) - relOff : relOff;
if (vn->findSubpieceShadow(leastByte, op2, 0))
return true;
if (op2->findPieceShadow(leastByte, vn))
return true;
return false;
}
/// Compare term order of two Varnodes. Used in Term Rewriting strategies to order operands of commutative ops
/// \param op is the Varnode to order against \b this
/// \return -1 if \b this comes before \b op, 1 if op before this, or 0
@ -1487,6 +1616,44 @@ VarnodeLocSet::const_iterator VarnodeBank::endLoc(int4 s,const Address &addr,
return iter;
}
/// \brief Given start, return maximal range of overlapping Varnodes
///
/// Advance the iterator until no Varnodes after the iterator intersect any Varnodes
/// from the initial Varnode through the current iterator. The range is returned as pairs
/// of iterators to subranges. One subrange for each set of Varnodes with the same size and starting address.
/// A final iterator to the next Varnode after the overlapping set is also passed back.
/// \param iter is an iterator to the given start Varnode
/// \param bounds holds the array of iterator pairs passed back
/// \return the union of Varnode flags across the range
uint4 VarnodeBank::overlapLoc(VarnodeLocSet::const_iterator iter,vector<VarnodeLocSet::const_iterator> &bounds) const
{
Varnode *vn = *iter;
AddrSpace *spc = vn->getSpace();
uintb off = vn->getOffset();
uintb maxoff = off + (vn->getSize() - 1);
uint4 flags = vn->getFlags();
bounds.push_back(iter);
iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written);
bounds.push_back(iter);
while(iter != loc_tree.end()) {
vn = *iter;
if (vn->getSpace() != spc || vn->getOffset() > maxoff)
break;
if (vn->isFree()) {
iter = endLoc(vn->getSize(),vn->getAddr(),0);
continue;
}
maxoff = vn->getOffset() + (vn->getSize() - 1);
flags |= vn->getFlags();
bounds.push_back(iter);
iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written);
bounds.push_back(iter);
}
bounds.push_back(iter);
return flags;
}
/// \brief Beginning of varnodes with set definition property
///
/// Get an iterator to Varnodes in definition order restricted with the

View file

@ -331,6 +331,9 @@ public:
Datatype *getLocalType(bool &blockup) const; ///< Calculate type of Varnode based on local information
bool isBooleanValue(bool useAnnotation) const; ///< Does \b this Varnode hold a formal boolean value
bool copyShadow(const Varnode *op2) const; ///< Are \b this and \b op2 copied from the same source?
bool findSubpieceShadow(int4 leastByte,const Varnode *whole,int4 recurse) const;
bool findPieceShadow(int4 leastByte,const Varnode *piece) const;
bool partialCopyShadow(const Varnode *op2,int4 relOff) const; ///< Is one of \b this or \b op2 a partial copy of the other?
void encode(Encoder &encoder) const; ///< Encode a description of \b this to a stream
static bool comparePointers(const Varnode *a,const Varnode *b) { return (*a < *b); } ///< Compare Varnodes as pointers
static void printRaw(ostream &s,const Varnode *vn); ///< Print raw info about a Varnode to stream
@ -387,6 +390,7 @@ public:
VarnodeLocSet::const_iterator endLoc(int4 s,const Address &addr,uint4 fl) const;
VarnodeLocSet::const_iterator beginLoc(int4 s,const Address &addr,const Address &pc,uintm uniq) const;
VarnodeLocSet::const_iterator endLoc(int4 s,const Address &addr,const Address &pc,uintm uniq) const;
uint4 overlapLoc(VarnodeLocSet::const_iterator iter,vector<VarnodeLocSet::const_iterator> &bounds) const;
VarnodeDefSet::const_iterator beginDef(void) const { return def_tree.begin(); } ///< Beginning of Varnodes sorted by definition
VarnodeDefSet::const_iterator endDef(void) const { return def_tree.end(); } ///< End of Varnodes sorted by definition
VarnodeDefSet::const_iterator beginDef(uint4 fl) const;

View file

@ -0,0 +1,64 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived examples of propagating a partial varnode (a structure field) when
the whole varnode (the whole structure) is also being manipulated.
-->
<bytechunk space="ram" offset="0x1006a7" readonly="true">
8b1dc3ffffff48893d
bcffffff83c30a89d8c300008b05aeff
ffff01f048893da5ffffff83c00ac3
</bytechunk>
<bytechunk space="ram" offset="0x1006e6" readonly="true">
4883ec104889e766e893
008b1c244889e766e8890089d8c30000
8b1d6affffff488b3d63ffffff66e873
0089d8c34883ec10488b0551ffffff48
8904244889e766e85a008b5c24044889
e766e84f0089d84883c410c348893d2d
ffffff83fe0a7f0c66e838008b051eff
ffffeb0a66e8b8fd8b0512ffffff83c0
07c3
</bytechunk>
<symbol space="ram" offset="0x1006a7" name="readpartial"/>
<symbol space="ram" offset="0x1006bc" name="read_expr_interfere"/>
<symbol space="ram" offset="0x100700" name="readpartial_callinterfere"/>
<symbol space="ram" offset="0x100714" name="readpartialstack_inbetween"/>
<symbol space="ram" offset="0x10073c" name="partial_restore"/>
</binaryimage>
<script>
<com>parse line struct highlow { int4 a; int4 b; };</com>
<com>map addr r0x100670 highlow glob1</com>
<com>lo fu readpartial</com>
<com>map hash r0x1006a7 3f9001cf6a int4 a_simple</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu read_expr_interfere</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu readpartial_callinterfere</com>
<com>map hash r0x100700 3fc348d857 int4 a_call</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu readpartialstack_inbetween</com>
<com>map addr s0xfffffffffffffff0 highlow hilo_stack</com>
<com>map hash r0x10072a 3ff1b7c944 int4 b_between</com>
<com>decompile</com>
<com>print C</com>
<com>lo fu partial_restore</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Partial Merge #1" min="1" max="1">a_simple = glob1\.a;</stringmatch>
<stringmatch name="Partial Merge #2" min="1" max="1">return a_simple \+ 10;</stringmatch>
<stringmatch name="Partial Merge #3" min="0" max="0">return glob1\.a \+ 10;</stringmatch>
<stringmatch name="Partial Merge #4" min="1" max="1">param_2 = glob1\.a \+ param_2;</stringmatch>
<stringmatch name="Partial Merge #5" min="1" max="1">return param_2 \+ 10;</stringmatch>
<stringmatch name="Partial Merge #6" min="1" max="1">a_call = glob1\.a;</stringmatch>
<stringmatch name="Partial Merge #7" min="1" max="1">return a_call;</stringmatch>
<stringmatch name="Partial Merge #8" min="1" max="1">return a_call;</stringmatch>
<stringmatch name="Partial Merge #9" min="1" max="1">b_between = hilo_stack\.b;</stringmatch>
<stringmatch name="Partial Merge #10" min="1" max="1">return b_between;</stringmatch>
<stringmatch name="Partial Merge #11" min="1" max="1">return glob1\.a \+ 7;</stringmatch>
</decompilertest>