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

@ -43,12 +43,29 @@ void VariableGroup::addPiece(VariablePiece *piece)
piece->group = this;
if (!pieceSet.insert(piece).second)
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)
{
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.
@ -134,17 +151,6 @@ void VariablePiece::updateCover(void) const
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 newGroup is the new VariableGroup to transfer \b this to
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
if (diff > 0)
op2->adjustOffset(diff);
op2->group->adjustOffsets(diff);
else if (diff < 0)
adjustOffset(-diff);
group->adjustOffsets(-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;
++iter;
set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator matchiter = group->pieceSet.find(piece);
if (matchiter != group->pieceSet.end()) {
mergePairs.push_back((*matchiter)->high);
@ -228,15 +235,8 @@ void HighVariable::setSymbol(Varnode *vn) const
}
}
symbol = entry->getSymbol();
if (vn->isProtoPartial()) {
Varnode *rootVn = PieceNode::findRoot(vn);
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();
if (vn->isProtoPartial() && piece != (VariablePiece *)0) {
symboloffset = piece->getOffset() + piece->getGroup()->getSymbolOffset();
}
else if (entry->isDynamic()) // Dynamic symbols (that aren't partials) match whole variable
symboloffset = -1;
@ -249,6 +249,8 @@ void HighVariable::setSymbol(Varnode *vn) const
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
}
@ -365,9 +367,17 @@ void HighVariable::updateType(void) const
vn = getTypeRepresentative();
type = vn->getType();
if (type->hasStripped() && type->getMetatype() != TYPE_PARTIALUNION)
type = type->getStripped();
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();
}
// Update lock flags
flags &= ~Varnode::typelock;
if (vn->isTypeLock())
@ -469,21 +479,6 @@ Varnode *HighVariable::getNameRepresentative(void) const
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.
/// \param vn is the given Varnode member to remove
void HighVariable::remove(Varnode *vn)
@ -551,7 +546,7 @@ void HighVariable::groupWith(int4 off,HighVariable *hi2)
else if (hi2->piece == (VariablePiece *)0) {
int4 hi2Off = piece->getOffset() - off;
if (hi2Off < 0) {
piece->adjustOffset(-hi2Off);
piece->getGroup()->adjustOffsets(-hi2Off);
hi2Off = 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.
/// \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
@ -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.
/// In all cases, the other HighVariable is deleted.
/// \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
void HighVariable::merge(HighVariable *tv2,bool isspeculative)
void HighVariable::merge(HighVariable *tv2,HighIntersectTest *testCache,bool isspeculative)
{
if (tv2 == this) return;
if (testCache != (HighIntersectTest *)0)
testCache->moveIntersectTests(this,tv2);
if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) {
mergeInternal(tv2,isspeculative);
return;
@ -638,16 +652,18 @@ void HighVariable::merge(HighVariable *tv2,bool 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();
if (isspeculative)
throw LowlevelError("Trying speculatively merge variables in separate groups");
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];
if (testCache != (HighIntersectTest *)0)
testCache->moveIntersectTests(high1, high2);
high1->mergeInternal(high2, isspeculative);
}
piece->markIntersectionDirty();
}
/// 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;
}
/// \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
void HighVariable::encode(Encoder &encoder) const
@ -780,6 +787,8 @@ void HighVariable::encode(Encoder &encoder) const
else if (!isPersist() && (symbol != (Symbol *)0)) {
if (symbol->getCategory() == Symbol::function_parameter)
encoder.writeString(ATTRIB_CLASS, "param");
else if (symbol->getScope()->isGlobal())
encoder.writeString(ATTRIB_CLASS, "global");
else
encoder.writeString(ATTRIB_CLASS, "local");
}
@ -883,3 +892,233 @@ void HighVariable::verifyCover(void) const
}
}
#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;
}