mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-2845_PartialMerging'
This commit is contained in:
commit
a04f7fbb03
9 changed files with 818 additions and 77 deletions
|
@ -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|
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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©_in1)!=0); } ///< Is there at least one COPY into \b this
|
||||
bool hasCopyIn2(void) const { return ((highflags©_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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue