GP-2627 TypePartialStruct

This commit is contained in:
caheckman 2023-03-14 13:25:39 -04:00
parent cc35d57933
commit 2591c17f22
15 changed files with 636 additions and 433 deletions

View file

@ -44,6 +44,7 @@ src/decompile/datatests/offsetarray.xml||GHIDRA||||END|
src/decompile/datatests/packstructaccess.xml||GHIDRA||||END| src/decompile/datatests/packstructaccess.xml||GHIDRA||||END|
src/decompile/datatests/partialmerge.xml||GHIDRA||||END| src/decompile/datatests/partialmerge.xml||GHIDRA||||END|
src/decompile/datatests/partialunion.xml||GHIDRA||||END| src/decompile/datatests/partialunion.xml||GHIDRA||||END|
src/decompile/datatests/piecestruct.xml||GHIDRA||||END|
src/decompile/datatests/pointercmp.xml||GHIDRA||||END| src/decompile/datatests/pointercmp.xml||GHIDRA||||END|
src/decompile/datatests/pointerrel.xml||GHIDRA||||END| src/decompile/datatests/pointerrel.xml||GHIDRA||||END|
src/decompile/datatests/pointersub.xml||GHIDRA||||END| src/decompile/datatests/pointersub.xml||GHIDRA||||END|

View file

@ -4732,16 +4732,19 @@ void ActionInferTypes::buildLocaltypes(Funcdata &data)
Datatype *ct; Datatype *ct;
Varnode *vn; Varnode *vn;
VarnodeLocSet::const_iterator iter; VarnodeLocSet::const_iterator iter;
TypeFactory *typegrp = data.getArch()->types;
for(iter=data.beginLoc();iter!=data.endLoc();++iter) { for(iter=data.beginLoc();iter!=data.endLoc();++iter) {
vn = *iter; vn = *iter;
if (vn->isAnnotation()) continue; if (vn->isAnnotation()) continue;
if ((!vn->isWritten())&&(vn->hasNoDescend())) continue; if ((!vn->isWritten())&&(vn->hasNoDescend())) continue;
bool needsBlock = false; bool needsBlock = false;
if (vn->getSymbolEntry() != (SymbolEntry *)0) { SymbolEntry *entry = vn->getSymbolEntry();
ct = data.checkSymbolType(vn); if (entry != (SymbolEntry *)0 && !vn->isTypeLock() && entry->getSymbol()->isTypeLocked()) {
if (ct == (Datatype *)0) int4 curOff = (vn->getAddr().getOffset() - entry->getAddr().getOffset()) + entry->getOffset();
ct = vn->getLocalType(needsBlock); ct = typegrp->getExactPiece(entry->getSymbol()->getType(), curOff, vn->getSize());
if (ct == (Datatype *)0 || ct->getMetatype() == TYPE_UNKNOWN) // If we can't resolve, or resolve to UNKNOWN
ct = vn->getLocalType(needsBlock); // Let data-type float, even though parent symbol is type-locked
} }
else else
ct = vn->getLocalType(needsBlock); ct = vn->getLocalType(needsBlock);
@ -4956,13 +4959,9 @@ void ActionInferTypes::propagateRef(Funcdata &data,Varnode *vn,const Address &ad
if ((cursize!=lastsize)||(curoff!=lastoff)) { if ((cursize!=lastsize)||(curoff!=lastoff)) {
lastoff = curoff; lastoff = curoff;
lastsize = cursize; lastsize = cursize;
Datatype *cur = ct; lastct = typegrp->getExactPiece(ct, curoff, cursize);
do {
lastct = cur;
cur = cur->getSubType(curoff,&curoff);
} while(cur != (Datatype *)0);
} }
if (lastct->getSize() != cursize) continue; if (lastct == (Datatype *)0) continue;
// Try to propagate the reference type into a varnode that is pointed to by that reference // Try to propagate the reference type into a varnode that is pointed to by that reference
if (0>lastct->typeOrder(*curvn->getTempType())) { if (0>lastct->typeOrder(*curvn->getTempType())) {
@ -5314,6 +5313,7 @@ void ActionDatabase::universalAction(Architecture *conf)
actprop->addRule( new RuleAndDistribute("analysis") ); actprop->addRule( new RuleAndDistribute("analysis") );
actprop->addRule( new RuleAndCommute("analysis") ); actprop->addRule( new RuleAndCommute("analysis") );
actprop->addRule( new RuleAndPiece("analysis") ); actprop->addRule( new RuleAndPiece("analysis") );
actprop->addRule( new RuleAndZext("analysis") );
actprop->addRule( new RuleAndCompare("analysis") ); actprop->addRule( new RuleAndCompare("analysis") );
actprop->addRule( new RuleDoubleSub("analysis") ); actprop->addRule( new RuleDoubleSub("analysis") );
actprop->addRule( new RuleDoubleShift("analysis") ); actprop->addRule( new RuleDoubleShift("analysis") );

View file

@ -390,7 +390,6 @@ public:
bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const; bool onlyOpUse(const Varnode *invn,const PcodeOp *opmatch,const ParamTrial &trial,uint4 mainFlags) const;
bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,int4 offset,uint4 mainFlags) const; bool ancestorOpUse(int4 maxlevel,const Varnode *invn,const PcodeOp *op,ParamTrial &trial,int4 offset,uint4 mainFlags) const;
bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool updateDatatypes,bool unmappedAliasCheck); bool syncVarnodesWithSymbols(const ScopeLocal *lm,bool updateDatatypes,bool unmappedAliasCheck);
Datatype *checkSymbolType(Varnode *vn); ///< Check for any delayed symbol data-type information on the given Varnode
void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset); void transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset);
bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image bool fillinReadOnly(Varnode *vn); ///< Replace the given Varnode with its (constant) value in the load image
bool replaceVolatile(Varnode *vn); ///< Replace accesses of the given Varnode with \e volatile operations bool replaceVolatile(Varnode *vn); ///< Replace accesses of the given Varnode with \e volatile operations

View file

@ -869,24 +869,6 @@ bool Funcdata::syncVarnodesWithSymbols(const ScopeLocal *lm,bool updateDatatypes
return updateoccurred; return updateoccurred;
} }
/// If the Varnode is a partial of a Symbol with a \e union data-type component, we assign
/// a partial union data-type (TypePartialUnion) to the Varnode, so that facing resolutions
/// can be provided.
/// \param vn is the given Varnode
/// \return the partial data-type or null
Datatype *Funcdata::checkSymbolType(Varnode *vn)
{
if (vn->isTypeLock()) return vn->getType();
SymbolEntry *entry = vn->getSymbolEntry();
Symbol *sym = entry->getSymbol();
Datatype *curType = sym->getType();
if (curType->getSize() == vn->getSize())
return (Datatype *)0;
int4 curOff = (vn->getAddr().getOffset() - entry->getAddr().getOffset()) + entry->getOffset();
return glb->types->getExactPiece(curType, curOff, vn->getSize());
}
/// A Varnode overlaps the given SymbolEntry. Make sure the Varnode is part of the variable /// A Varnode overlaps the given SymbolEntry. Make sure the Varnode is part of the variable
/// underlying the Symbol. If not, remap things so that the Varnode maps to a distinct Symbol. /// underlying the Symbol. If not, remap things so that the Varnode maps to a distinct Symbol.
/// In either case, attach the appropriate Symbol to the Varnode /// In either case, attach the appropriate Symbol to the Varnode
@ -1037,9 +1019,11 @@ void Funcdata::linkProtoPartial(Varnode *vn)
Varnode *rootVn = PieceNode::findRoot(vn); Varnode *rootVn = PieceNode::findRoot(vn);
if (rootVn == vn) return; if (rootVn == vn) return;
Varnode *nameRep = rootVn->getHigh()->getNameRepresentative(); HighVariable *rootHigh = rootVn->getHigh();
Varnode *nameRep = rootHigh->getNameRepresentative();
Symbol *sym = linkSymbol(nameRep); Symbol *sym = linkSymbol(nameRep);
if (sym == (Symbol *)0) return; if (sym == (Symbol *)0) return;
rootHigh->establishGroupSymbolOffset();
SymbolEntry *entry = sym->getFirstWholeMap(); SymbolEntry *entry = sym->getFirstWholeMap();
vn->setSymbolEntry(entry); vn->setSymbolEntry(entry);
} }

View file

@ -114,6 +114,15 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in)
if (high_in->isAddrTied()) return false; if (high_in->isAddrTied()) return false;
if (high_in->isPersist()) return false; if (high_in->isPersist()) return false;
} }
if (high_in->piece != (VariablePiece *)0 && high_out->piece != (VariablePiece *)0) {
VariableGroup *groupIn = high_in->piece->getGroup();
VariableGroup *groupOut = high_out->piece->getGroup();
if (groupIn == groupOut)
return false;
// At least one of the pieces must represent its whole group
if (high_in->piece->getSize() != groupIn->getSize() && high_out->piece->getSize() != groupOut->getSize())
return false;
}
Symbol *symbolIn = high_in->getSymbol(); Symbol *symbolIn = high_in->getSymbol();
Symbol *symbolOut = high_out->getSymbol(); Symbol *symbolOut = high_out->getSymbol();
@ -123,20 +132,6 @@ bool Merge::mergeTestRequired(HighVariable *high_out,HighVariable *high_in)
if (high_in->getSymbolOffset() != high_out->getSymbolOffset()) if (high_in->getSymbolOffset() != high_out->getSymbolOffset())
return false; // Map to different parts of same symbol return false; // Map to different parts of same symbol
} }
if (high_out->piece != (VariablePiece *)0 || high_in->piece != (VariablePiece *)0) {
// 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;
if (symbolIn != symbolOut) { // If we know symbols are involved, and not both the same symbol
// Treat piece as if it were a separate symbol
if (symbolIn != (Symbol *)0 && high_out->piece != (VariablePiece *)0)
return false; // effectively different symbols
if (symbolOut != (Symbol *)0 && high_in->piece != (VariablePiece *)0)
return false; // effectively different symbols
}
}
return true; return true;
} }
@ -178,6 +173,10 @@ bool Merge::mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in)
if (symbol != (Symbol *)0) if (symbol != (Symbol *)0)
if (symbol->isIsolated()) if (symbol->isIsolated())
return false; return false;
// Currently don't allow speculative merging of variables that are in separate overlapping collections
if (high_out->piece != (VariablePiece *)0 && high_in->piece != (VariablePiece *)0)
return false;
return true; return true;
} }
@ -249,7 +248,7 @@ void Merge::mergeLinear(vector<HighVariable *> &highvec)
if (highvec.size() <= 1) return; if (highvec.size() <= 1) return;
for(initer=highvec.begin();initer!=highvec.end();++initer) for(initer=highvec.begin();initer!=highvec.end();++initer)
updateHigh(*initer); testCache.updateHigh(*initer);
sort(highvec.begin(),highvec.end(),compareHighByBlock); sort(highvec.begin(),highvec.end(),compareHighByBlock);
for(initer=highvec.begin();initer!=highvec.end();++initer) { for(initer=highvec.begin();initer!=highvec.end();++initer) {
high = *initer; high = *initer;
@ -927,11 +926,11 @@ void Merge::mergeMultiEntry(void)
} }
if (mergeList.empty()) continue; if (mergeList.empty()) continue;
HighVariable *high = mergeList[0]->getHigh(); HighVariable *high = mergeList[0]->getHigh();
updateHigh(high); testCache.updateHigh(high);
for(int4 i=0;i<mergeList.size();++i) { for(int4 i=0;i<mergeList.size();++i) {
HighVariable *newHigh = mergeList[i]->getHigh(); HighVariable *newHigh = mergeList[i]->getHigh();
if (newHigh == high) continue; // Varnodes already merged if (newHigh == high) continue; // Varnodes already merged
updateHigh(newHigh); testCache.updateHigh(newHigh);
if (!mergeTestRequired(high, newHigh)) { if (!mergeTestRequired(high, newHigh)) {
symbol->setMergeProblems(); symbol->setMergeProblems();
newHigh->setUnmerged(); newHigh->setUnmerged();
@ -1005,7 +1004,7 @@ void Merge::mergeAdjacent(void)
high_in = vn2->getHigh(); high_in = vn2->getHigh();
if (!mergeTestAdjacent(high_out,high_in)) continue; if (!mergeTestAdjacent(high_out,high_in)) continue;
if (!intersection(high_in,high_out)) // If no interval intersection if (!testCache.intersection(high_in,high_out)) // If no interval intersection
merge(high_out,high_in,true); merge(high_out,high_in,true);
} }
} }
@ -1232,7 +1231,7 @@ void Merge::buildDominantCopy(HighVariable *high,vector<PcodeOp *> &copy,int4 po
} }
} }
if (count > 0 && domCopyIsNew) { if (count > 0 && domCopyIsNew) {
high->merge(domVn->getHigh(),true); high->merge(domVn->getHigh(),(HighIntersectTest *)0,true);
} }
} }
@ -1437,8 +1436,9 @@ void Merge::markInternalCopies(void)
vector<HighVariable *> multiCopy; vector<HighVariable *> multiCopy;
list<PcodeOp *>::const_iterator iter; list<PcodeOp *>::const_iterator iter;
PcodeOp *op; PcodeOp *op;
HighVariable *h1,*h2,*h3; HighVariable *h1;
Varnode *v1,*v2,*v3; Varnode *v1,*v2,*v3;
VariablePiece *p1,*p2,*p3;
int4 val; int4 val;
for(iter=data.beginOpAlive();iter!=data.endOpAlive();++iter) { for(iter=data.beginOpAlive();iter!=data.endOpAlive();++iter) {
@ -1465,41 +1465,42 @@ void Merge::markInternalCopies(void)
} }
break; break;
case CPUI_PIECE: // Check if output is built out of pieces of itself case CPUI_PIECE: // Check if output is built out of pieces of itself
h1 = op->getOut()->getHigh(); v1 = op->getOut();
h2 = op->getIn(0)->getHigh(); v2 = op->getIn(0);
h3 = op->getIn(1)->getHigh(); v3 = op->getIn(1);
if (!h2->isPartialOrAddrTied()) break; p1 = v1->getHigh()->piece;
if (!h3->isPartialOrAddrTied()) break; p2 = v2->getHigh()->piece;
v2 = h2->getPartialOrAddrTied(); p3 = v3->getHigh()->piece;
v3 = h3->getPartialOrAddrTied(); if (p1 == (VariablePiece *)0) break;
if (v2->isAddrTied()) { if (p2 == (VariablePiece *)0) break;
if (!h1->isAddrTied()) break; if (p3 == (VariablePiece *)0) break;
v1 = h1->getTiedVarnode(); if (p1->getGroup() != p2->getGroup()) break;
if (p1->getGroup() != p3->getGroup()) break;
if (v1->getSpace()->isBigEndian()) {
if (p2->getOffset() != p1->getOffset()) break;
if (p3->getOffset() != p1->getOffset() + v2->getSize()) break;
} }
else { else {
if (op->getIn(0) != v2) break; if (p3->getOffset() != p1->getOffset()) break;
if (op->getIn(1) != v3) break; if (p2->getOffset() != p1->getOffset() + v3->getSize()) break;
v1 = op->getOut();
} }
if (v3->overlapJoin(*v1) != 0) break;
if (v2->overlapJoin(*v1) != v3->getSize()) break;
data.opMarkNonPrinting(op); data.opMarkNonPrinting(op);
break; break;
case CPUI_SUBPIECE: case CPUI_SUBPIECE:
h1 = op->getOut()->getHigh(); v1 = op->getOut();
h2 = op->getIn(0)->getHigh(); v2 = op->getIn(0);
if (!h1->isPartialOrAddrTied()) break; p1 = v1->getHigh()->piece;
v1 = h1->getPartialOrAddrTied(); p2 = v2->getHigh()->piece;
if (v1->isAddrTied()) { if (p1 == (VariablePiece *)0) break;
if (!h2->isAddrTied()) break; if (p2 == (VariablePiece *)0) break;
v2 = h2->getTiedVarnode(); if (p1->getGroup() != p2->getGroup()) break;
val = op->getIn(1)->getOffset();
if (v1->getSpace()->isBigEndian()) {
if (p2->getOffset() + (v2->getSize() - v1->getSize() - val) != p1->getOffset()) break;
} }
else { else {
if (!h1->sameGroup(h2)) break; if (p2->getOffset() + val != p1->getOffset()) break;
v2 = op->getIn(0);
} }
val = op->getIn(1)->getOffset();
if (v1->overlapJoin(*v2) != val) break;
data.opMarkNonPrinting(op); data.opMarkNonPrinting(op);
break; break;
default: default:
@ -1528,64 +1529,6 @@ void Merge::registerProtoPartialRoot(Varnode *vn)
protoPartial.push_back(vn->getDef()); protoPartial.push_back(vn->getDef());
} }
/// \brief Translate any intersection tests for \e high2 into tests for \e high1
///
/// 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)
{
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) );
map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) );
map<HighEdge,bool>::iterator iter;
for(iter=iterfirst;iter!=iterlast;++iter) {
HighVariable *b = (*iter).first.b;
if (b == high1) continue;
if ((*iter).second) // Save all high2's intersections
yesinter.push_back(b); // as they are still valid for the merge
else {
nointer.push_back(b);
b->setMark(); // Mark that high2 did not intersect
}
}
// Do a purge of all high2's tests
if (iterfirst != iterlast) { // Delete all the high2 tests
--iterlast; // Move back 1 to prevent deleting under the iterator
for(iter=iterfirst;iter!=iterlast;++iter)
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
++iterlast; // Restore original range (with possibly new open endpoint)
highedgemap.erase(iterfirst,iterlast);
}
iter = highedgemap.lower_bound( HighEdge(high1,(HighVariable *)0) );
while((iter!=highedgemap.end())&&((*iter).first.a == high1)) {
if (!(*iter).second) { // If test is intersection==false
if (!(*iter).first.b->isMark()) // and there was no test with high2
highedgemap.erase( iter++ ); // Delete the test
else
++iter;
}
else // Keep any intersection==true tests
++iter;
}
vector<HighVariable *>::iterator titer;
for(titer=nointer.begin();titer!=nointer.end();++titer)
(*titer)->clearMark();
// 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 /// \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 /// This routine only fails (returning \b false) if there is a Cover intersection between
@ -1600,197 +1543,25 @@ bool Merge::merge(HighVariable *high1,HighVariable *high2,bool isspeculative)
{ {
if (high1 == high2) return true; // Already merged if (high1 == high2) return true; // Already merged
if (intersection(high1,high2)) return false; if (testCache.intersection(high1,high2)) return false;
moveIntersectTests(high1, high2); high1->merge(high2,&testCache,isspeculative); // Do the actual merge
high1->merge(high2,isspeculative); // Do the actual merge
high1->updateCover(); // Update cover now so that updateHigh won't purge cached tests high1->updateCover(); // Update cover now so that updateHigh won't purge cached tests
return true; return true;
} }
/// As manipulations are made, Cover information gets out of date. A \e dirty flag is used to
/// indicate a particular HighVariable Cover is out-of-date. This routine checks the \e dirty
/// flag and updates the Cover information if it is set.
/// \param a is the HighVariable to update
/// \return \b true if the HighVariable was not originally dirty
bool Merge::updateHigh(HighVariable *a)
{
if (!a->isCoverDirty()) return true;
a->updateCover();
purgeHigh(a);
return false;
}
/// All tests for pairs where either the first or second HighVariable matches the given one
/// are removed.
/// \param high is the given HighVariable to purge
void Merge::purgeHigh(HighVariable *high)
{
map<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) );
map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high,(HighVariable *)~((uintp)0)) );
if (iterfirst == iterlast) return;
--iterlast; // Move back 1 to prevent deleting under the iterator
map<HighEdge,bool>::iterator iter;
for(iter=iterfirst;iter!=iterlast;++iter)
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
++iterlast; // Restore original range (with possibly new open endpoint)
highedgemap.erase(iterfirst,iterlast);
}
/// \brief Clear the any cached data from the last merge process /// \brief Clear the any cached data from the last merge process
/// ///
/// Free up resources used by cached intersection tests etc. /// Free up resources used by cached intersection tests etc.
void Merge::clear(void) void Merge::clear(void)
{ {
highedgemap.clear(); testCache.clear();
copyTrims.clear(); copyTrims.clear();
protoPartial.clear(); protoPartial.clear();
} }
/// \brief Test the intersection of two HighVariables and cache the result
///
/// If the Covers of the two variables intersect, this routine returns \b true. To avoid
/// expensive computation on the Cover objects themselves, the test result associated with
/// the pair of HighVariables is cached.
/// \param a is the first HighVariable
/// \param b is the second HighVariable
/// \return \b true if the variables intersect
bool Merge::intersection(HighVariable *a,HighVariable *b)
{
if (a==b) return false;
bool ares = updateHigh(a);
bool bres = updateHigh(b);
if (ares && bres) { // If neither high was dirty
map<HighEdge,bool>::iterator iter = highedgemap.find( HighEdge(a,b) );
if (iter != highedgemap.end()) // If previous test is present
return (*iter).second; // Use it
}
bool res = false;
int4 blk;
vector<int4> blockisect;
a->getCover().intersectList(blockisect,b->getCover(),2);
for(blk=0;blk<blockisect.size();++blk) {
if (blockIntersection(a,b,blockisect[blk])) {
res = true;
break;
}
}
highedgemap[ HighEdge(a,b) ] = res; // Cache the result
highedgemap[ HighEdge(b,a) ] = res;
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.
/// \param a is the first HighVariable
/// \param b is the second HighVariable
/// \param blk is the index of the BlockBasic on which to test intersection
/// \return \b true if an intersection occurs in the specified block
bool Merge::blockIntersection(HighVariable *a,HighVariable *b,int4 blk)
{
vector<Varnode *> blist;
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;
}
}
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;
}
/// \brief Inflate the Cover of a given Varnode with a HighVariable /// \brief Inflate the Cover of a given Varnode with a HighVariable
/// ///
/// An expression involving a HighVariable can be propagated to all the read sites of the /// An expression involving a HighVariable can be propagated to all the read sites of the
@ -1802,8 +1573,8 @@ bool Merge::blockIntersection(HighVariable *a,HighVariable *b,int4 blk)
void Merge::inflate(Varnode *a,HighVariable *high) void Merge::inflate(Varnode *a,HighVariable *high)
{ {
updateHigh(a->getHigh()); testCache.updateHigh(a->getHigh());
updateHigh(high); testCache.updateHigh(high);
for(int4 i=0;i<high->numInstances();++i) { for(int4 i=0;i<high->numInstances();++i) {
Varnode *b = high->getInstance(i); Varnode *b = high->getInstance(i);
a->cover->merge(*b->cover); a->cover->merge(*b->cover);
@ -1825,7 +1596,7 @@ bool Merge::inflateTest(Varnode *a,HighVariable *high)
{ {
HighVariable *ahigh = a->getHigh(); HighVariable *ahigh = a->getHigh();
updateHigh(high); testCache.updateHigh(high);
const Cover &highCover( high->internalCover ); // Only check for intersections with cover contributing to inflate const Cover &highCover( high->internalCover ); // Only check for intersections with cover contributing to inflate
for(int4 i=0;i<ahigh->numInstances();++i) { for(int4 i=0;i<ahigh->numInstances();++i) {
@ -1868,7 +1639,7 @@ bool Merge::mergeTest(HighVariable *high,vector<HighVariable *> &tmplist)
for(int4 i=0;i<tmplist.size();++i) { for(int4 i=0;i<tmplist.size();++i) {
HighVariable *a = tmplist[i]; HighVariable *a = tmplist[i];
if (intersection(a,high)) if (testCache.intersection(a,high))
return false; return false;
} }
tmplist.push_back(high); tmplist.push_back(high);

View file

@ -21,21 +21,6 @@
#include "op.hh" #include "op.hh"
/// \brief A record for caching a Cover intersection test between two HighVariable objects
///
/// This is just a pair of HighVariable objects that can be used as a map key. The main
/// Merge class uses it to cache intersection test results between the two variables in
/// a map.
class HighEdge {
friend class Merge;
HighVariable *a; ///< First HighVariable of the pair
HighVariable *b; ///< Second HighVariable of the pair
public:
/// \brief Comparator
bool operator<(const HighEdge &op2) const { if (a==op2.a) return (b<op2.b); return (a<op2.a); }
HighEdge(HighVariable *c,HighVariable *d) { a=c; b=d; } ///< Constructor
};
/// \brief Helper class associating a Varnode with the block where it is defined /// \brief Helper class associating a Varnode with the block where it is defined
/// ///
/// This class explicitly stores a Varnode with the index of the BlockBasic that defines it. /// This class explicitly stores a Varnode with the index of the BlockBasic that defines it.
@ -79,14 +64,9 @@ class Funcdata;
/// - Merging Varnodes that hold the same data-type /// - Merging Varnodes that hold the same data-type
class Merge { class Merge {
Funcdata &data; ///< The function containing the Varnodes to be merged Funcdata &data; ///< The function containing the Varnodes to be merged
map<HighEdge,bool> highedgemap; ///< A cache of intersection tests, sorted by HighVariable pair HighIntersectTest testCache; ///< Cached intersection tests
vector<PcodeOp *> copyTrims; ///< COPY ops inserted to facilitate merges vector<PcodeOp *> copyTrims; ///< COPY ops inserted to facilitate merges
vector<PcodeOp *> protoPartial; ///< Roots of unmapped CONCAT trees vector<PcodeOp *> protoPartial; ///< Roots of unmapped CONCAT trees
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 mergeTestRequired(HighVariable *high_out,HighVariable *high_in);
static bool mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in); static bool mergeTestAdjacent(HighVariable *high_out,HighVariable *high_in);
static bool mergeTestSpeculative(HighVariable *high_out,HighVariable *high_in); static bool mergeTestSpeculative(HighVariable *high_out,HighVariable *high_in);
@ -100,7 +80,6 @@ class Merge {
void collectCovering(vector<Varnode *> &vlist,HighVariable *high,PcodeOp *op); void collectCovering(vector<Varnode *> &vlist,HighVariable *high,PcodeOp *op);
bool collectCorrectable(const vector<Varnode *> &vlist,list<PcodeOp *> &oplist,vector<int4> &slotlist, bool collectCorrectable(const vector<Varnode *> &vlist,list<PcodeOp *> &oplist,vector<int4> &slotlist,
PcodeOp *op); PcodeOp *op);
void moveIntersectTests(HighVariable *high1,HighVariable *high2);
PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp); PcodeOp *allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trimOp);
void snipReads(Varnode *vn,list<PcodeOp *> &markedop); void snipReads(Varnode *vn,list<PcodeOp *> &markedop);
void snipIndirect(PcodeOp *indop); void snipIndirect(PcodeOp *indop);
@ -122,7 +101,6 @@ class Merge {
public: public:
Merge(Funcdata &fd) : data(fd) {} ///< Construct given a specific function Merge(Funcdata &fd) : data(fd) {} ///< Construct given a specific function
void clear(void); void clear(void);
bool intersection(HighVariable *a,HighVariable *b);
bool inflateTest(Varnode *a,HighVariable *high); bool inflateTest(Varnode *a,HighVariable *high);
void inflate(Varnode *a,HighVariable *high); void inflate(Varnode *a,HighVariable *high);
bool mergeTest(HighVariable *high,vector<HighVariable *> &tmplist); bool mergeTest(HighVariable *high,vector<HighVariable *> &tmplist);

View file

@ -1685,6 +1685,44 @@ int4 RuleAndPiece::applyOp(PcodeOp *op,Funcdata &data)
return 1; return 1;
} }
/// \class RuleAndZext
/// \brief Convert INT_AND to INT_ZEXT where appropriate: `sext(X) & 0xffff => zext(X)`
///
/// Similarly `concat(Y,X) & 0xffff => zext(X)`
void RuleAndZext::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_AND);
}
int4 RuleAndZext::applyOp(PcodeOp *op,Funcdata &data)
{
Varnode *cvn1 = op->getIn(1);
if (!cvn1->isConstant()) return 0;
if (!op->getIn(0)->isWritten()) return 0;
PcodeOp *otherop = op->getIn(0)->getDef();
OpCode opc = otherop->code();
Varnode *rootvn;
if (opc == CPUI_INT_SEXT)
rootvn = otherop->getIn(0);
else if (opc == CPUI_PIECE)
rootvn = otherop->getIn(1);
else
return 0;
uintb mask = calc_mask(rootvn->getSize());
if (mask != cvn1->getOffset())
return 0;
if (rootvn->isFree())
return 0;
if (rootvn->getSize() > sizeof(uintb)) // FIXME: Should be arbitrary precision
return 0;
data.opSetOpcode(op, CPUI_INT_ZEXT);
data.opRemoveInput(op, 1);
data.opSetInput(op, rootvn, 0);
return 1;
}
/// \class RuleAndCompare /// \class RuleAndCompare
/// \brief Simplify INT_ZEXT and SUBPIECE in masked comparison: `zext(V) & c == 0 => V & (c & mask) == 0` /// \brief Simplify INT_ZEXT and SUBPIECE in masked comparison: `zext(V) & c == 0 => V & (c & mask) == 0`
/// ///

View file

@ -374,6 +374,16 @@ public:
virtual void getOpList(vector<uint4> &oplist) const; virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data); virtual int4 applyOp(PcodeOp *op,Funcdata &data);
}; };
class RuleAndZext : public Rule {
public:
RuleAndZext(const string &g) : Rule(g, 0, "andzext") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleAndZext(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleAndCompare : public Rule { class RuleAndCompare : public Rule {
public: public:
RuleAndCompare(const string &g) : Rule(g, 0, "andcompare") {} ///< Constructor RuleAndCompare(const string &g) : Rule(g, 0, "andcompare") {} ///< Constructor

View file

@ -1894,6 +1894,67 @@ int4 TypeUnion::findCompatibleResolve(Datatype *ct) const
return -1; return -1;
} }
TypePartialStruct::TypePartialStruct(const TypePartialStruct &op)
: Datatype(op)
{
stripped = op.stripped;
container = op.container;
offset = op.offset;
}
TypePartialStruct::TypePartialStruct(Datatype *contain,int4 off,int4 sz,Datatype *strip)
: Datatype(sz,TYPE_PARTIALSTRUCT)
{
#ifdef CPUI_DEBUG
if (contain->getMetatype() != TYPE_STRUCT && contain->getMetatype() != TYPE_ARRAY)
throw new LowlevelError("Parent of partial struct is not a struture or array");
#endif
flags |= has_stripped;
stripped = strip;
container = contain;
offset = off;
}
void TypePartialStruct::printRaw(ostream &s) const
{
container->printRaw(s);
s << "[off=" << dec << offset << ",sz=" << size << ']';
}
Datatype *TypePartialStruct::getSubType(uintb off,uintb *newoff) const
{
off += offset;
return container->getSubType(off, newoff);
}
int4 TypePartialStruct::compare(const Datatype &op,int4 level) const
{
int4 res = Datatype::compare(op,level);
if (res != 0) return res;
// Both must be partial
TypePartialStruct *tp = (TypePartialStruct *) &op;
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
level -= 1;
if (level < 0) {
if (id == op.getId()) return 0;
return (id < op.getId()) ? -1 : 1;
}
return container->compare(*tp->container,level); // Compare the underlying union
}
int4 TypePartialStruct::compareDependency(const Datatype &op) const
{
if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1;
TypePartialStruct *tp = (TypePartialStruct *) &op; // Both must be partial
if (container != tp->container) return (container < tp->container) ? -1 : 1; // Compare absolute pointers
if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1;
return (op.getSize()-size);
}
TypePartialUnion::TypePartialUnion(const TypePartialUnion &op) TypePartialUnion::TypePartialUnion(const TypePartialUnion &op)
: Datatype(op) : Datatype(op)
{ {
@ -3370,6 +3431,14 @@ TypeStruct *TypeFactory::getTypeStruct(const string &n)
return (TypeStruct *) findAdd(tmp); return (TypeStruct *) findAdd(tmp);
} }
TypePartialStruct *TypeFactory::getTypePartialStruct(Datatype *contain,int4 off,int4 sz)
{
Datatype *strip = getBase(sz, TYPE_UNKNOWN);
TypePartialStruct tps(contain,off,sz,strip);
return (TypePartialStruct *) findAdd(tps);
}
/// The created union will be incomplete and have no fields. They must be added later. /// The created union will be incomplete and have no fields. They must be added later.
/// \param n is the name of the union /// \param n is the name of the union
/// \return the TypeUnion object /// \return the TypeUnion object
@ -3495,16 +3564,23 @@ TypePointer *TypeFactory::getTypePointerWithSpace(Datatype *ptrTo,AddrSpace *spc
Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size) Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size)
{ {
uintb newOff = offset; Datatype *lastType = (Datatype *)0;
while(ct != (Datatype *)0 && ct->getSize() > size && ct->getMetatype() != TYPE_UNION) { uintb curOff = offset;
ct = ct->getSubType(newOff, &newOff); do {
} if (ct->getSize() <= size) {
if (ct == (Datatype *)0 || ct->getSize() < size)
return (Datatype *)0;
if (ct->getSize() == size) if (ct->getSize() == size)
return ct; return ct; // Perfect size match
if (ct->getMetatype() == TYPE_UNION) // If we hit a containing union break;
return getTypePartialUnion((TypeUnion *)ct, newOff, size); }
else if (ct->getMetatype() == TYPE_UNION) {
return getTypePartialUnion((TypeUnion *)ct, curOff, size);
}
lastType = ct;
ct = ct->getSubType(curOff,&curOff);
} while(ct != (Datatype *)0);
// If we reach here, lastType is bigger than size
if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY)
return getTypePartialStruct(lastType, curOff, size);
return (Datatype *)0; return (Datatype *)0;
} }

View file

@ -99,23 +99,23 @@ enum sub_metatype {
SUB_VOID = 22, ///< Compare as a TYPE_VOID SUB_VOID = 22, ///< Compare as a TYPE_VOID
SUB_SPACEBASE = 21, ///< Compare as a TYPE_SPACEBASE SUB_SPACEBASE = 21, ///< Compare as a TYPE_SPACEBASE
SUB_UNKNOWN = 20, ///< Compare as a TYPE_UNKNOWN SUB_UNKNOWN = 20, ///< Compare as a TYPE_UNKNOWN
SUB_INT_CHAR = 19, ///< Signed 1-byte character, sub-type of TYPE_INT SUB_PARTIALSTRUCT = 19, ///< Compare as TYPE_PARTIALSTRUCT
SUB_UINT_CHAR = 18, ///< Unsigned 1-byte character, sub-type of TYPE_UINT SUB_INT_CHAR = 18, ///< Signed 1-byte character, sub-type of TYPE_INT
SUB_INT_PLAIN = 17, ///< Compare as a plain TYPE_INT SUB_UINT_CHAR = 17, ///< Unsigned 1-byte character, sub-type of TYPE_UINT
SUB_UINT_PLAIN = 16, ///< Compare as a plain TYPE_UINT SUB_INT_PLAIN = 16, ///< Compare as a plain TYPE_INT
SUB_INT_ENUM = 15, ///< Signed enum, sub-type of TYPE_INT SUB_UINT_PLAIN = 15, ///< Compare as a plain TYPE_UINT
SUB_UINT_ENUM = 14, ///< Unsigned enum, sub-type of TYPE_UINT SUB_INT_ENUM = 14, ///< Signed enum, sub-type of TYPE_INT
SUB_INT_UNICODE = 13, ///< Signed wide character, sub-type of TYPE_INT SUB_UINT_ENUM = 13, ///< Unsigned enum, sub-type of TYPE_UINT
SUB_UINT_UNICODE = 12, ///< Unsigned wide character, sub-type of TYPE_UINT SUB_INT_UNICODE = 12, ///< Signed wide character, sub-type of TYPE_INT
SUB_BOOL = 11, ///< Compare as TYPE_BOOL SUB_UINT_UNICODE = 11, ///< Unsigned wide character, sub-type of TYPE_UINT
SUB_CODE = 10, ///< Compare as TYPE_CODE SUB_BOOL = 10, ///< Compare as TYPE_BOOL
SUB_FLOAT = 9, ///< Compare as TYPE_FLOAT SUB_CODE = 9, ///< Compare as TYPE_CODE
SUB_PTRREL_UNK = 8, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR SUB_FLOAT = 8, ///< Compare as TYPE_FLOAT
SUB_PTR = 7, ///< Compare as TYPE_PTR SUB_PTRREL_UNK = 7, ///< Pointer to unknown field of struct, sub-type of TYPE_PTR
SUB_PTRREL = 6, ///< Pointer relative to another data-type, sub-type of TYPE_PTR SUB_PTR = 6, ///< Compare as TYPE_PTR
SUB_PTR_STRUCT = 5, ///< Pointer into struct, sub-type of TYPE_PTR SUB_PTRREL = 5, ///< Pointer relative to another data-type, sub-type of TYPE_PTR
SUB_ARRAY = 4, ///< Compare as TYPE_ARRAY SUB_PTR_STRUCT = 4, ///< Pointer into struct, sub-type of TYPE_PTR
SUB_PARTIALSTRUCT = 3, ///< Compare as TYPE_PARTIALSTRUCT SUB_ARRAY = 3, ///< Compare as TYPE_ARRAY
SUB_STRUCT = 2, ///< Compare as TYPE_STRUCT SUB_STRUCT = 2, ///< Compare as TYPE_STRUCT
SUB_UNION = 1, ///< Compare as TYPE_UNION SUB_UNION = 1, ///< Compare as TYPE_UNION
SUB_PARTIALUNION = 0 ///< Compare as a TYPE_PARTIALUNION SUB_PARTIALUNION = 0 ///< Compare as a TYPE_PARTIALUNION
@ -484,6 +484,24 @@ public:
virtual const TypeField *resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff); virtual const TypeField *resolveTruncation(int4 offset,PcodeOp *op,int4 slot,int4 &newoff);
}; };
/// \brief A data-type that holds \e part of a TypeStruct or TypeArray
class TypePartialStruct : public Datatype {
friend class TypeFactory;
Datatype *stripped; ///< The \e undefined data-type to use if a formal data-type is required.
Datatype *container; ///< Parent structure or array of which \b this is a part
int4 offset; ///< Byte offset within the parent where \b this starts
public:
TypePartialStruct(const TypePartialStruct &op); ///< Construct from another TypePartialStruct
TypePartialStruct(Datatype *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor
Datatype *getParent(void) const { return container; } ///< Get the data-type containing \b this piece
virtual void printRaw(ostream &s) const;
virtual Datatype *getSubType(uintb off,uintb *newoff) const;
virtual int4 compare(const Datatype &op,int4 level) const;
virtual int4 compareDependency(const Datatype &op) const;
virtual Datatype *clone(void) const { return new TypePartialStruct(*this); }
virtual Datatype *getStripped(void) const { return stripped; }
};
/// \brief An internal data-type for holding information about a variable's relative position within a union data-type /// \brief An internal data-type for holding information about a variable's relative position within a union data-type
/// ///
/// This is a data-type that can be assigned to a Varnode offset into a Symbol, where either the Symbol itself or /// This is a data-type that can be assigned to a Varnode offset into a Symbol, where either the Symbol itself or
@ -500,7 +518,7 @@ public:
TypePartialUnion(const TypePartialUnion &op); ///< Construct from another TypePartialUnion TypePartialUnion(const TypePartialUnion &op); ///< Construct from another TypePartialUnion
TypePartialUnion(TypeUnion *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor TypePartialUnion(TypeUnion *contain,int4 off,int4 sz,Datatype *strip); ///< Constructor
TypeUnion *getParentUnion(void) const { return container; } ///< Get the union which \b this is part of TypeUnion *getParentUnion(void) const { return container; } ///< Get the union which \b this is part of
virtual void printRaw(ostream &s) const; ///< Print a description of the type to stream virtual void printRaw(ostream &s) const;
virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const; virtual const TypeField *findTruncation(int4 off,int4 sz,const PcodeOp *op,int4 slot,int4 &newoff) const;
virtual int4 numDepend(void) const; virtual int4 numDepend(void) const;
virtual Datatype *getDepend(int4 index) const; virtual Datatype *getDepend(int4 index) const;
@ -681,6 +699,7 @@ public:
TypePointer *getTypePointerNoDepth(int4 s,Datatype *pt,uint4 ws); ///< Construct a depth limited pointer data-type TypePointer *getTypePointerNoDepth(int4 s,Datatype *pt,uint4 ws); ///< Construct a depth limited pointer data-type
TypeArray *getTypeArray(int4 as,Datatype *ao); ///< Construct an array data-type TypeArray *getTypeArray(int4 as,Datatype *ao); ///< Construct an array data-type
TypeStruct *getTypeStruct(const string &n); ///< Create an (empty) structure TypeStruct *getTypeStruct(const string &n); ///< Create an (empty) structure
TypePartialStruct *getTypePartialStruct(Datatype *contain,int4 off,int4 sz); ///< Create a partial structure
TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union
TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union
TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration

View file

@ -43,12 +43,29 @@ void VariableGroup::addPiece(VariablePiece *piece)
piece->group = this; piece->group = this;
if (!pieceSet.insert(piece).second) if (!pieceSet.insert(piece).second)
throw LowlevelError("Duplicate VariablePiece"); throw LowlevelError("Duplicate VariablePiece");
int4 pieceMax = piece->getOffset() + piece->getSize();
if (pieceMax > size)
size = pieceMax;
}
/// The adjustment amount must be positive, and this effectively increases the size of the group.
/// \param amt is the given amount to add to offsets
void VariableGroup::adjustOffsets(int4 amt)
{
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter;
for(iter=pieceSet.begin();iter!=pieceSet.end();++iter) {
(*iter)->groupOffset += amt;
}
size += amt;
} }
void VariableGroup::removePiece(VariablePiece *piece) void VariableGroup::removePiece(VariablePiece *piece)
{ {
pieceSet.erase(piece); pieceSet.erase(piece);
// We currently don't adjust size here as removePiece is currently only called during clean up
} }
/// Construct piece given a HighVariable and its position within the whole. /// Construct piece given a HighVariable and its position within the whole.
@ -134,17 +151,6 @@ void VariablePiece::updateCover(void) const
high->highflags &= ~(uint4)HighVariable::extendcoverdirty; 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. /// If there are no remaining references to the old VariableGroup it is deleted.
/// \param newGroup is the new VariableGroup to transfer \b this to /// \param newGroup is the new VariableGroup to transfer \b this to
void VariablePiece::transferGroup(VariableGroup *newGroup) void VariablePiece::transferGroup(VariableGroup *newGroup)
@ -169,13 +175,14 @@ void VariablePiece::combineOtherGroup(VariablePiece *op2,vector<HighVariable *>
{ {
int4 diff = groupOffset - op2->groupOffset; // Add to op2, or subtract from this int4 diff = groupOffset - op2->groupOffset; // Add to op2, or subtract from this
if (diff > 0) if (diff > 0)
op2->adjustOffset(diff); op2->group->adjustOffsets(diff);
else if (diff < 0) else if (diff < 0)
adjustOffset(-diff); group->adjustOffsets(-diff);
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->group->pieceSet.begin(); set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->group->pieceSet.begin();
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator enditer = op2->group->pieceSet.end(); set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator enditer = op2->group->pieceSet.end();
while(iter != enditer) { while(iter != enditer) {
VariablePiece *piece = *iter; VariablePiece *piece = *iter;
++iter;
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator matchiter = group->pieceSet.find(piece); set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator matchiter = group->pieceSet.find(piece);
if (matchiter != group->pieceSet.end()) { if (matchiter != group->pieceSet.end()) {
mergePairs.push_back((*matchiter)->high); mergePairs.push_back((*matchiter)->high);
@ -228,15 +235,8 @@ void HighVariable::setSymbol(Varnode *vn) const
} }
} }
symbol = entry->getSymbol(); symbol = entry->getSymbol();
if (vn->isProtoPartial()) { if (vn->isProtoPartial() && piece != (VariablePiece *)0) {
Varnode *rootVn = PieceNode::findRoot(vn); symboloffset = piece->getOffset() + piece->getGroup()->getSymbolOffset();
if (rootVn == vn)
throw LowlevelError("Partial varnode does not match symbol");
symboloffset = vn->getAddr().overlapJoin(0,rootVn->getAddr(),rootVn->getSize());
SymbolEntry *entry = rootVn->getSymbolEntry();
if (entry != (SymbolEntry *)0)
symboloffset += entry->getOffset();
} }
else if (entry->isDynamic()) // Dynamic symbols (that aren't partials) match whole variable else if (entry->isDynamic()) // Dynamic symbols (that aren't partials) match whole variable
symboloffset = -1; symboloffset = -1;
@ -249,6 +249,8 @@ void HighVariable::setSymbol(Varnode *vn) const
symboloffset = vn->getAddr().overlapJoin(0,entry->getAddr(),symbol->getType()->getSize()) + entry->getOffset(); symboloffset = vn->getAddr().overlapJoin(0,entry->getAddr(),symbol->getType()->getSize()) + entry->getOffset();
} }
if (type != (Datatype *)0 && type->getMetatype() == TYPE_PARTIALUNION)
highflags |= typedirty;
highflags &= ~((uint4)symboldirty); // We are no longer dirty highflags &= ~((uint4)symboldirty); // We are no longer dirty
} }
@ -365,9 +367,17 @@ void HighVariable::updateType(void) const
vn = getTypeRepresentative(); vn = getTypeRepresentative();
type = vn->getType(); type = vn->getType();
if (type->hasStripped() && type->getMetatype() != TYPE_PARTIALUNION) if (type->hasStripped()) {
if (type->getMetatype() == TYPE_PARTIALUNION) {
if (symbol != (Symbol *)0 && symboloffset != -1) {
type_metatype meta = symbol->getType()->getMetatype();
if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol
type = type->getStripped(); // strip the partial union
}
}
else
type = type->getStripped(); type = type->getStripped();
}
// Update lock flags // Update lock flags
flags &= ~Varnode::typelock; flags &= ~Varnode::typelock;
if (vn->isTypeLock()) if (vn->isTypeLock())
@ -469,21 +479,6 @@ Varnode *HighVariable::getNameRepresentative(void) const
return nameRepresentative; return nameRepresentative;
} }
/// Find the first member that is either address tied or marked as a proto partial.
/// \return a member Varnode acting as partial storage or null if none exist
Varnode *HighVariable::getPartialOrAddrTied(void) const
{
int4 i;
for(i=0;i<inst.size();++i) {
Varnode *vn = inst[i];
if (vn->isAddrTied() || vn->isProtoPartial())
return vn;
}
return (Varnode *)0;
}
/// Search for the given Varnode and cut it out of the list, marking all properties as \e dirty. /// Search for the given Varnode and cut it out of the list, marking all properties as \e dirty.
/// \param vn is the given Varnode member to remove /// \param vn is the given Varnode member to remove
void HighVariable::remove(Varnode *vn) void HighVariable::remove(Varnode *vn)
@ -551,7 +546,7 @@ void HighVariable::groupWith(int4 off,HighVariable *hi2)
else if (hi2->piece == (VariablePiece *)0) { else if (hi2->piece == (VariablePiece *)0) {
int4 hi2Off = piece->getOffset() - off; int4 hi2Off = piece->getOffset() - off;
if (hi2Off < 0) { if (hi2Off < 0) {
piece->adjustOffset(-hi2Off); piece->getGroup()->adjustOffsets(-hi2Off);
hi2Off = 0; hi2Off = 0;
} }
if ((highflags & intersectdirty) == 0) if ((highflags & intersectdirty) == 0)
@ -564,6 +559,22 @@ void HighVariable::groupWith(int4 off,HighVariable *hi2)
} }
} }
/// If \b this is part of a larger group and has had its \b symboloffset set, it can be used
/// to calculate the \b symboloffset of other HighVariables in the same group, by writing it
/// to the common VariableGroup object.
void HighVariable::establishGroupSymbolOffset(void)
{
VariableGroup *group = piece->getGroup();
int4 off = symboloffset;
if (off < 0)
off = 0;
off -= piece->getOffset();
if (off < 0)
throw LowlevelError("Symbol offset is incompatible with VariableGroup");
group->setSymbolOffset(off);
}
/// The lists of members are merged and the other HighVariable is deleted. /// The lists of members are merged and the other HighVariable is deleted.
/// \param tv2 is the other HighVariable to merge into \b this /// \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 /// \param isspeculative is \b true to keep the new members in separate \e merge classes
@ -614,12 +625,15 @@ void HighVariable::mergeInternal(HighVariable *tv2,bool isspeculative)
/// the groups are combined into one, which may induce additional HighVariable pairs within the group to be merged. /// 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. /// In all cases, the other HighVariable is deleted.
/// \param tv2 is the other HighVariable to merge into \b this /// \param tv2 is the other HighVariable to merge into \b this
/// \param testCache if non-null is a cache of intersection tests that must be updated to reflect the merge
/// \param isspeculative is \b true to keep the new members in separate \e merge classes /// \param isspeculative is \b true to keep the new members in separate \e merge classes
void HighVariable::merge(HighVariable *tv2,bool isspeculative) void HighVariable::merge(HighVariable *tv2,HighIntersectTest *testCache,bool isspeculative)
{ {
if (tv2 == this) return; if (tv2 == this) return;
if (testCache != (HighIntersectTest *)0)
testCache->moveIntersectTests(this,tv2);
if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) { if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) {
mergeInternal(tv2,isspeculative); mergeInternal(tv2,isspeculative);
return; return;
@ -638,16 +652,18 @@ void HighVariable::merge(HighVariable *tv2,bool isspeculative)
return; return;
} }
// Reaching here both HighVariables are part of a group // Reaching here both HighVariables are part of a group
throw LowlevelError("Merging variables in separate groups not supported"); if (isspeculative)
// vector<HighVariable *> mergePairs; throw LowlevelError("Trying speculatively merge variables in separate groups");
// piece->combineOtherGroup(tv2->piece, mergePairs); vector<HighVariable *> mergePairs;
// for(int4 i=0;i<mergePairs.size();i+=2) { piece->combineOtherGroup(tv2->piece, mergePairs);
// HighVariable *high1 = mergePairs[i]; for(int4 i=0;i<mergePairs.size();i+=2) {
// HighVariable *high2 = mergePairs[i+1]; HighVariable *high1 = mergePairs[i];
// // Need to deal with cached intersect tests herev HighVariable *high2 = mergePairs[i+1];
// high1->mergeInternal(high2, isspeculative); if (testCache != (HighIntersectTest *)0)
// } testCache->moveIntersectTests(high1, high2);
// piece->markIntersectionDirty(); high1->mergeInternal(high2, isspeculative);
}
piece->markIntersectionDirty();
} }
/// All Varnode objects are assigned a HighVariable, including those that don't get names like /// All Varnode objects are assigned a HighVariable, including those that don't get names like
@ -755,15 +771,6 @@ int4 HighVariable::instanceIndex(const Varnode *vn) const
return -1; return -1;
} }
/// \param op2 is the other HighVariable to compare with \b this
/// \return \b true if they are in the same group
bool HighVariable::sameGroup(const HighVariable *op2) const
{
if (piece == (VariablePiece *)0 || op2->piece == (VariablePiece *)0) return false;
return (piece->getGroup() == op2->piece->getGroup());
}
/// \param encoder is the stream encoder /// \param encoder is the stream encoder
void HighVariable::encode(Encoder &encoder) const void HighVariable::encode(Encoder &encoder) const
@ -780,6 +787,8 @@ void HighVariable::encode(Encoder &encoder) const
else if (!isPersist() && (symbol != (Symbol *)0)) { else if (!isPersist() && (symbol != (Symbol *)0)) {
if (symbol->getCategory() == Symbol::function_parameter) if (symbol->getCategory() == Symbol::function_parameter)
encoder.writeString(ATTRIB_CLASS, "param"); encoder.writeString(ATTRIB_CLASS, "param");
else if (symbol->getScope()->isGlobal())
encoder.writeString(ATTRIB_CLASS, "global");
else else
encoder.writeString(ATTRIB_CLASS, "local"); encoder.writeString(ATTRIB_CLASS, "local");
} }
@ -883,3 +892,233 @@ void HighVariable::verifyCover(void) const
} }
} }
#endif #endif
/// \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 HighIntersectTest::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 HighIntersectTest::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.
/// \param a is the first HighVariable
/// \param b is the second HighVariable
/// \param blk is the index of the BlockBasic on which to test intersection
/// \return \b true if an intersection occurs in the specified block
bool HighIntersectTest::blockIntersection(HighVariable *a,HighVariable *b,int4 blk)
{
vector<Varnode *> blist;
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;
}
}
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;
}
/// All tests for pairs where either the first or second HighVariable matches the given one
/// are removed.
/// \param high is the given HighVariable to purge
void HighIntersectTest::purgeHigh(HighVariable *high)
{
map<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) );
map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high,(HighVariable *)~((uintp)0)) );
if (iterfirst == iterlast) return;
--iterlast; // Move back 1 to prevent deleting under the iterator
map<HighEdge,bool>::iterator iter;
for(iter=iterfirst;iter!=iterlast;++iter)
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
++iterlast; // Restore original range (with possibly new open endpoint)
highedgemap.erase(iterfirst,iterlast);
}
/// \brief Translate any intersection tests for \e high2 into tests for \e high1
///
/// 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 HighIntersectTest::moveIntersectTests(HighVariable *high1,HighVariable *high2)
{
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) );
map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) );
map<HighEdge,bool>::iterator iter;
for(iter=iterfirst;iter!=iterlast;++iter) {
HighVariable *b = (*iter).first.b;
if (b == high1) continue;
if ((*iter).second) // Save all high2's intersections
yesinter.push_back(b); // as they are still valid for the merge
else {
nointer.push_back(b);
b->setMark(); // Mark that high2 did not intersect
}
}
// Do a purge of all high2's tests
if (iterfirst != iterlast) { // Delete all the high2 tests
--iterlast; // Move back 1 to prevent deleting under the iterator
for(iter=iterfirst;iter!=iterlast;++iter)
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
++iterlast; // Restore original range (with possibly new open endpoint)
highedgemap.erase(iterfirst,iterlast);
}
iter = highedgemap.lower_bound( HighEdge(high1,(HighVariable *)0) );
while((iter!=highedgemap.end())&&((*iter).first.a == high1)) {
if (!(*iter).second) { // If test is intersection==false
if (!(*iter).first.b->isMark()) // and there was no test with high2
highedgemap.erase( iter++ ); // Delete the test
else
++iter;
}
else // Keep any intersection==true tests
++iter;
}
vector<HighVariable *>::iterator titer;
for(titer=nointer.begin();titer!=nointer.end();++titer)
(*titer)->clearMark();
// 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;
}
}
/// As manipulations are made, Cover information gets out of date. A \e dirty flag is used to
/// indicate a particular HighVariable Cover is out-of-date. This routine checks the \e dirty
/// flag and updates the Cover information if it is set.
/// \param a is the HighVariable to update
/// \return \b true if the HighVariable was not originally dirty
bool HighIntersectTest::updateHigh(HighVariable *a)
{
if (!a->isCoverDirty()) return true;
a->updateCover();
purgeHigh(a);
return false;
}
/// \brief Test the intersection of two HighVariables and cache the result
///
/// If the Covers of the two variables intersect, this routine returns \b true. To avoid
/// expensive computation on the Cover objects themselves, the test result associated with
/// the pair of HighVariables is cached.
/// \param a is the first HighVariable
/// \param b is the second HighVariable
/// \return \b true if the variables intersect
bool HighIntersectTest::intersection(HighVariable *a,HighVariable *b)
{
if (a==b) return false;
bool ares = updateHigh(a);
bool bres = updateHigh(b);
if (ares && bres) { // If neither high was dirty
map<HighEdge,bool>::iterator iter = highedgemap.find( HighEdge(a,b) );
if (iter != highedgemap.end()) // If previous test is present
return (*iter).second; // Use it
}
bool res = false;
int4 blk;
vector<int4> blockisect;
a->getCover().intersectList(blockisect,b->getCover(),2);
for(blk=0;blk<blockisect.size();++blk) {
if (blockIntersection(a,b,blockisect[blk])) {
res = true;
break;
}
}
highedgemap[ HighEdge(a,b) ] = res; // Cache the result
highedgemap[ HighEdge(b,a) ] = res;
return res;
}

View file

@ -48,10 +48,17 @@ class VariableGroup {
}; };
set<VariablePiece *,PieceCompareByOffset> pieceSet; ///< The set of VariablePieces making up \b this group set<VariablePiece *,PieceCompareByOffset> pieceSet; ///< The set of VariablePieces making up \b this group
int4 size; ///< Number of contiguous bytes covered by the whole group
int4 symbolOffset; ///< Byte offset of \b this group within its containing Symbol
public: public:
VariableGroup(void) { size = 0; symbolOffset = 0; }
bool empty(void) const { return pieceSet.empty(); } ///< Return \b true if \b this group has no pieces 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 addPiece(VariablePiece *piece); ///< Add a new piece to \b this group
void adjustOffsets(int4 amt); ///< Adjust offset for every piece by the given amount
void removePiece(VariablePiece *piece); ///< Remove a piece from \b this group void removePiece(VariablePiece *piece); ///< Remove a piece from \b this group
int4 getSize(void) const { return size; } ///< Get the number of bytes \b this group covers
void setSymbolOffset(int4 val) { symbolOffset = val; } ///< Cache the symbol offset for the group
int4 getSymbolOffset(void) const { return symbolOffset; } ///< Get offset of \b this group within its Symbol
}; };
/// \brief Information about how a HighVariable fits into a larger group or Symbol /// \brief Information about how a HighVariable fits into a larger group or Symbol
@ -80,12 +87,13 @@ public:
void markExtendCoverDirty(void) const; ///< Mark all intersecting pieces as having a dirty extended cover 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 updateIntersections(void) const; ///< Calculate intersections with other pieces in the group
void updateCover(void) const; ///< Calculate extended cover based on intersections 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 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 setHigh(HighVariable *newHigh) { high = newHigh; } ///< Move ownership of \b this to another HighVariable
void combineOtherGroup(VariablePiece *op2,vector<HighVariable *> &mergePairs); ///< Combine two VariableGroups void combineOtherGroup(VariablePiece *op2,vector<HighVariable *> &mergePairs); ///< Combine two VariableGroups
}; };
class HighIntersectTest;
/// \brief A high-level variable modeled as a list of low-level variables, each written once /// \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 /// In the Static Single Assignment (SSA) representation of a function's data-flow, the Varnode
@ -122,6 +130,7 @@ private:
friend class Varnode; friend class Varnode;
friend class Merge; friend class Merge;
friend class VariablePiece; friend class VariablePiece;
friend class HighIntersectTest;
vector<Varnode *> inst; ///< The member Varnode objects making up \b this HighVariable vector<Varnode *> inst; ///< The member Varnode objects making up \b this HighVariable
int4 numMergeClasses; ///< Number of different speculative merge classes in \b this int4 numMergeClasses; ///< Number of different speculative merge classes in \b this
mutable uint4 highflags; ///< Dirtiness flags mutable uint4 highflags; ///< Dirtiness flags
@ -145,7 +154,7 @@ private:
bool hasCopyIn2(void) const { return ((highflags&copy_in2)!=0); } ///< Is there at least two COPYs 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 remove(Varnode *vn); ///< Remove a member Varnode from \b this
void mergeInternal(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 merge(HighVariable *tv2,HighIntersectTest *testCache,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 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 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 transferPiece(HighVariable *tv2); ///< Transfer ownership of another's VariablePiece to \b this
@ -167,6 +176,7 @@ public:
Varnode *getInstance(int4 i) const { return inst[i]; } ///< Get the i-th member Varnode 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 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 void groupWith(int4 off,HighVariable *hi2); ///< Put \b this and another HighVariable in the same intersection group
void establishGroupSymbolOffset(void); ///< Transfer \b symbol offset of \b this to the VariableGroup
/// \brief Print details of the cover for \b this (for debug purposes) /// \brief Print details of the cover for \b this (for debug purposes)
/// ///
@ -179,7 +189,6 @@ public:
Varnode *getInputVarnode(void) const; ///< Find (the) input member Varnode Varnode *getInputVarnode(void) const; ///< Find (the) input member Varnode
Varnode *getTypeRepresentative(void) const; ///< Get a member Varnode with the strongest data-type Varnode *getTypeRepresentative(void) const; ///< Get a member Varnode with the strongest data-type
Varnode *getNameRepresentative(void) const; ///< Get a member Varnode that dictates the naming of \b this HighVariable Varnode *getNameRepresentative(void) const; ///< Get a member Varnode that dictates the naming of \b this HighVariable
Varnode *getPartialOrAddrTied(void) const; ///< Find the first member that can act as partial symbol storage
int4 getNumMergeClasses(void) const { return numMergeClasses; } ///< Get the number of speculative merges for \b this int4 getNumMergeClasses(void) const { return numMergeClasses; } ///< Get the number of speculative merges for \b this
bool isMapped(void) const { updateFlags(); return ((flags&Varnode::mapped)!=0); } ///< Return \b true if \b this is mapped bool isMapped(void) const { updateFlags(); return ((flags&Varnode::mapped)!=0); } ///< Return \b true if \b this is mapped
bool isPersist(void) const { updateFlags(); return ((flags&Varnode::persist)!=0); } ///< Return \b true if \b this is a global variable bool isPersist(void) const { updateFlags(); return ((flags&Varnode::persist)!=0); } ///< Return \b true if \b this is a global variable
@ -191,7 +200,6 @@ public:
bool isUnaffected(void) const { updateFlags(); return ((flags&Varnode::unaffected)!=0); } ///< Return \b true if \b this is an \e unaffected register bool isUnaffected(void) const { updateFlags(); return ((flags&Varnode::unaffected)!=0); } ///< Return \b true if \b this is an \e unaffected register
bool isExtraOut(void) const { updateFlags(); return ((flags&(Varnode::indirect_creation|Varnode::addrtied))==Varnode::indirect_creation); } ///< Return \b true if \b this is an extra output bool isExtraOut(void) const { updateFlags(); return ((flags&(Varnode::indirect_creation|Varnode::addrtied))==Varnode::indirect_creation); } ///< Return \b true if \b this is an extra output
bool isProtoPartial(void) const { updateFlags(); return ((flags&Varnode::proto_partial)!=0); } ///< Return \b true if \b this is a piece concatenated into a larger whole bool isProtoPartial(void) const { updateFlags(); return ((flags&Varnode::proto_partial)!=0); } ///< Return \b true if \b this is a piece concatenated into a larger whole
bool isPartialOrAddrTied(void) const { updateFlags(); return ((flags&(Varnode::addrtied|Varnode::proto_partial))!=0); } ///< Return \b true if \b this is potential partial symbol
void setMark(void) const { flags |= Varnode::mark; } ///< Set the mark on this variable void setMark(void) const { flags |= Varnode::mark; } ///< Set the mark on this variable
void clearMark(void) const { flags &= ~Varnode::mark; } ///< Clear the mark on this variable void clearMark(void) const { flags &= ~Varnode::mark; } ///< Clear the mark on this variable
bool isMark(void) const { return ((flags&Varnode::mark)!=0); } ///< Return \b true if \b this is marked bool isMark(void) const { return ((flags&Varnode::mark)!=0); } ///< Return \b true if \b this is marked
@ -208,7 +216,6 @@ public:
bool isUnattached(void) const { return inst.empty(); } ///< Return \b true if \b this has no member Varnode bool isUnattached(void) const { return inst.empty(); } ///< Return \b true if \b this has no member Varnode
bool isTypeLock(void) const { updateType(); return ((flags & Varnode::typelock)!=0); } ///< Return \b true if \b this is \e typelocked bool isTypeLock(void) const { updateType(); return ((flags & Varnode::typelock)!=0); } ///< Return \b true if \b this is \e typelocked
bool isNameLock(void) const { updateFlags(); return ((flags & Varnode::namelock)!=0); } ///< Return \b true if \b this is \e namelocked bool isNameLock(void) const { updateFlags(); return ((flags & Varnode::namelock)!=0); } ///< Return \b true if \b this is \e namelocked
bool sameGroup(const HighVariable *op2) const; ///< Return \b true if \b and other variable are parts of the same variable
void encode(Encoder &encoder) const; ///< Encode \b this variable to stream as a \<high> element void encode(Encoder &encoder) const; ///< Encode \b this variable to stream as a \<high> element
#ifdef MERGEMULTI_DEBUG #ifdef MERGEMULTI_DEBUG
void verifyCover(void) const; void verifyCover(void) const;
@ -219,6 +226,42 @@ public:
static int4 markExpression(Varnode *vn,vector<HighVariable *> &highList); ///< Mark and collect variables in expression static int4 markExpression(Varnode *vn,vector<HighVariable *> &highList); ///< Mark and collect variables in expression
}; };
/// \brief A record for caching a Cover intersection test between two HighVariable objects
///
/// This is just a pair of HighVariable objects that can be used as a map key. The HighIntersectTest
/// class uses it to cache intersection test results between the two variables in a map.
class HighEdge {
friend class HighIntersectTest;
HighVariable *a; ///< First HighVariable of the pair
HighVariable *b; ///< Second HighVariable of the pair
public:
/// \brief Comparator
bool operator<(const HighEdge &op2) const { if (a==op2.a) return (b<op2.b); return (a<op2.a); }
HighEdge(HighVariable *c,HighVariable *d) { a=c; b=d; } ///< Constructor
};
/// \brief A cache of Cover intersection tests for HighVariables
///
/// An test is performed by calling the intersect() method, which returns the result of a full
/// Cover intersection test, taking into account, overlapping pieces, shadow Varnodes etc. The
/// results of the test are cached in \b this object, so repeated calls do not need to perform the
/// full calculation. The cache examines HighVariable dirtiness flags to determine if its Cover
/// and cached tests are stale. The Cover can be externally updated, without performing a test,
/// and still keeping the cached tests accurate, by calling the updateHigh() method. If two HighVariables
/// to be merged, the cached tests can be updated by calling moveIntersectTest() before merging.
class HighIntersectTest {
map<HighEdge,bool> highedgemap; ///< A cache of intersection tests, sorted by HighVariable pair
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);
void purgeHigh(HighVariable *high); ///< Remove cached intersection tests for a given HighVariable
public:
void moveIntersectTests(HighVariable *high1,HighVariable *high2);
bool updateHigh(HighVariable *a); ///< Make sure given HighVariable's Cover is up-to-date
bool intersection(HighVariable *a,HighVariable *b);
void clear(void) { highedgemap.clear(); }
};
/// The internal cover is marked as dirty. If \b this is a piece of a VariableGroup, it and all the other /// 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. /// HighVariables it intersects with are marked as having a dirty extended cover.
inline void HighVariable::coverDirty(void) const inline void HighVariable::coverDirty(void) const

View file

@ -978,6 +978,9 @@ void MapState::gatherVarnodes(const Funcdata &fd)
if (vn->isFree()) continue; if (vn->isFree()) continue;
uintb start = vn->getOffset(); uintb start = vn->getOffset();
Datatype *ct = vn->getType(); Datatype *ct = vn->getType();
// Assume parents are present so partials aren't needed
if (ct->getMetatype() == TYPE_PARTIALSTRUCT) continue;
if (ct->getMetatype() == TYPE_PARTIALUNION) continue;
// Do not force Varnode flags on the entry // Do not force Varnode flags on the entry
// as the flags were inherited from the previous // as the flags were inherited from the previous
// (now obsolete) entry // (now obsolete) entry
@ -1008,6 +1011,7 @@ void MapState::gatherHighs(const Funcdata &fd)
varvec.push_back(high); varvec.push_back(high);
uintb start = vn->getOffset(); uintb start = vn->getOffset();
Datatype *ct = high->getType(); // Get type from high Datatype *ct = high->getType(); // Get type from high
if (ct->getMetatype() == TYPE_PARTIALUNION) continue;
addRange(start,ct,0,RangeHint::fixed,-1); addRange(start,ct,0,RangeHint::fixed,-1);
} }
for(int4 i=0;i<varvec.size();++i) for(int4 i=0;i<varvec.size();++i)

View file

@ -1674,20 +1674,22 @@ uint4 VarnodeBank::overlapLoc(VarnodeLocSet::const_iterator iter,vector<VarnodeL
Varnode *vn = *iter; Varnode *vn = *iter;
AddrSpace *spc = vn->getSpace(); AddrSpace *spc = vn->getSpace();
uintb off = vn->getOffset(); uintb off = vn->getOffset();
uintb maxoff = off + (vn->getSize() - 1); uintb maxOff = off + (vn->getSize() - 1);
uint4 flags = vn->getFlags(); uint4 flags = vn->getFlags();
bounds.push_back(iter); bounds.push_back(iter);
iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written);
bounds.push_back(iter); bounds.push_back(iter);
while(iter != loc_tree.end()) { while(iter != loc_tree.end()) {
vn = *iter; vn = *iter;
if (vn->getSpace() != spc || vn->getOffset() > maxoff) if (vn->getSpace() != spc || vn->getOffset() > maxOff)
break; break;
if (vn->isFree()) { if (vn->isFree()) {
iter = endLoc(vn->getSize(),vn->getAddr(),0); iter = endLoc(vn->getSize(),vn->getAddr(),0);
continue; continue;
} }
maxoff = vn->getOffset() + (vn->getSize() - 1); uintb endOff = vn->getOffset() + (vn->getSize() - 1);
if (endOff > maxOff)
maxOff = endOff;
flags |= vn->getFlags(); flags |= vn->getFlags();
bounds.push_back(iter); bounds.push_back(iter);
iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written); iter = endLoc(vn->getSize(),vn->getAddr(),Varnode::written);

View file

@ -0,0 +1,39 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Example of logically independent values being concatenated together
before being written into a structure, assigning to multiple fields simultaneously
-->
<bytechunk space="ram" offset="0x100740" readonly="true">
554889e54883ec4089c84589c24589c8
89f966894ddc89f166894dd88855d488
45d04489d08845cc4489c08845c86448
8b042528000000488945f831c00fbf45
d88945ecc165ec100fb745dc0fb7c009
45ec8b45ec8945f00fbe45c825ff0000
008945ecc165ec080fbe45cc0fb6c009
45ecc165ec080fbe45d00fb6c00945ec
c165ec080fbe45d40fb6c00945ec8b45
ec8945f4488d45f04889c7e81affffff
90488b45f86448330425280000007405
e8bbfdffffc9c3
</bytechunk>
<symbol space="ram" offset="0x100740" name="assign"/>
<symbol space="ram" offset="0x1006fa" name="print"/>
</binaryimage>
<script>
<com>parse line struct mypiece { int2 a; int2 b; char arr[4]; };</com>
<com>parse line extern void assign(int2 a,int2 b,char c,char d,char e,char f);</com>
<com>parse line extern void print(mypiece *ptr);</com>
<com>lo fu assign</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Piece Structure #1" min="1" max="1">Stack_18\.a = a;</stringmatch>
<stringmatch name="Piece Structure #2" min="1" max="1">Stack_18\.b = b;</stringmatch>
<stringmatch name="Piece Structure #3" min="1" max="1">Stack_18\.arr\[0\] = c;</stringmatch>
<stringmatch name="Piece Structure #4" min="1" max="1">Stack_18\.arr\[1\] = d;</stringmatch>
<stringmatch name="Piece Structure #5" min="1" max="1">Stack_18\.arr\[2\] = e;</stringmatch>
<stringmatch name="Piece Structure #6" min="1" max="1">Stack_18\.arr\[3\] = f;</stringmatch>
</decompilertest>