Merge remote-tracking branch 'origin/GP-7126_caheckman_SmallFields'

This commit is contained in:
Ryan Kurtz
2026-08-11 06:16:26 -04:00
8 changed files with 112 additions and 3 deletions
@@ -6075,6 +6075,7 @@ void ActionDatabase::universalAction(Architecture *conf)
actcleanup->addRule( new RulePtrsubCharConstant("cleanup") );
actcleanup->addRule( new RuleExtensionPush("cleanup") );
actcleanup->addRule( new RulePieceStructure("cleanup") );
actcleanup->addRule( new RuleAndStructure("cleanup") );
actcleanup->addRule( new RuleSplitCopy("splitcopy") );
actcleanup->addRule( new RuleSplitLoad("splitpointer") );
actcleanup->addRule( new RuleSplitStore("splitpointer") );
@@ -7685,6 +7685,63 @@ int4 RulePieceStructure::applyOp(PcodeOp *op,Funcdata &data)
return 1;
}
/// \class RuleAndStructure
/// \brief Convert `and(x,#mask) => zext(sub(x,#c)` when the mask corresponds to a field access
void RuleAndStructure::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_AND);
}
int4 RuleAndStructure::applyOp(PcodeOp *op,Funcdata &data)
{
Varnode *cvn = op->getIn(1);
if (!cvn->isConstant()) return 0;
Varnode *vn = op->getIn(0);
Datatype *baseType = vn->getTypeReadFacing(op);
type_metatype meta = baseType->getMetatype();
if (meta != TYPE_STRUCT && meta != TYPE_PARTIALSTRUCT && meta != TYPE_ARRAY)
return 0;
uintb off = cvn->getOffset();
int4 count = sizeof(uintb)*8 - count_leading_zeros(off);
int4 size = count / 8; // Number of bytes being masked off
if (count % 8 != 0)
size += 1;
int8 structOff = vn->getSpace()->isBigEndian() ? vn->getSize() - size : 0;
Datatype *ct = baseType->findSmallestContainer(structOff, size, &structOff);
if (ct->getSize() >= vn->getSize() || structOff != 0)
return 0;
PcodeOp *subOp = data.newOp(2, op->getAddr());
data.opSetOpcode(subOp, CPUI_SUBPIECE);
data.opSetInput(subOp,data.newConstant(4, 0),1);
data.opMarkSpecialPrint(subOp); // Mark SUBPIECE as a field extraction
Address addr = op->getOut()->getAddr();
if (addr.isBigEndian())
addr = addr + (vn->getSize() - ct->getSize());
addr.renormalize(vn->getSize()); // Allow for possible join address
Varnode *outvn = data.newVarnodeOut(ct->getSize(), addr, subOp);
outvn->updateType(ct);
if (calc_mask(size) == off) {
data.opRemoveInput(op, 1);
data.opSetInput(op, outvn, 0);
data.opSetOpcode(op, CPUI_INT_ZEXT);
data.opSetInput(subOp,vn,0);
data.opInsertBefore(subOp, op);
}
else {
PcodeOp *newZext = data.newOp(1, op->getAddr());
data.opSetOpcode(newZext, CPUI_INT_ZEXT);
Varnode *outzext = data.newUniqueOut(vn->getSize(), newZext);
data.opSetInput(op, outzext, 0);
data.opSetInput(subOp, vn, 0);
data.opSetInput(newZext,outvn,0);
data.opInsertBefore(newZext,op);
data.opInsertBefore(subOp,newZext);
}
return 1;
}
/// \class RuleSubNormal
/// \brief Pull-back SUBPIECE through INT_RIGHT and INT_SRIGHT
///
@@ -1212,6 +1212,17 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleAndStructure : public Rule {
public:
RuleAndStructure(const string &g) : Rule( g, 0, "andstructure") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleAndStructure(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleSubNormal : public Rule {
public:
RuleSubNormal(const string &g) : Rule( g, 0, "subnormal") {} ///< Constructor
@@ -3032,7 +3032,22 @@ int4 RuleDumptyHumpLate::applyOp(PcodeOp *op,Funcdata &data)
Varnode *vn = op->getIn(0);
if (!vn->isWritten()) return 0;
PcodeOp *pieceOp = vn->getDef();
if (pieceOp->code() != CPUI_PIECE) return 0;
OpCode opc = pieceOp->code();
if (opc == CPUI_SUBPIECE) {
// SUB(SUB(base,#c),#d) => SUB(base,#c+#d)
Varnode *base = pieceOp->getIn(0);
data.opSetInput(op,base,0);
uintb trunc = op->getIn(1)->getOffset() + pieceOp->getIn(1)->getOffset();
if (trunc != op->getIn(1)->getOffset())
data.opSetInput(op,data.newConstant(4, trunc),1);
if (vn->hasNoDescend() && !vn->isAutoLive()) {
vector<PcodeOp *> scratch;
data.opDestroyRecursive(pieceOp, scratch);
}
return 1;
}
else if (opc != CPUI_PIECE)
return 0;
Varnode *out = op->getOut();
int4 outSize = out->getSize();
int4 trunc = (int4)op->getIn(1)->getOffset();
@@ -360,6 +360,7 @@ public:
/// Simplify expressions like:
/// - `sub( concat(V,W), 0) => W`
/// - `sub( concat(V,W), c) => V`
/// - `sub( sub(V, c), d) => SUB(V,#c+#d)`
///
/// preserving the data-types and removing the SUBPIECE and PIECE operations that are discarded.
class RuleDumptyHumpLate : public Rule {
@@ -589,6 +589,29 @@ bool Datatype::testForArraySlack(int8 off)
return nearestArrayedComponentBackward(off, comp);
}
/// Return the component data-type, which may be \b this if there is no smaller component, and pass back
/// the relative offset of the start of the range into the returned component.
/// \param off is the starting byte offset of the given range, within \b this
/// \param sz is the number of bytes in the range
/// \param newoff is used to pass back the relative offset into the returned component
/// \return the component data-type contain the range
Datatype *Datatype::findSmallestContainer(int8 off,int8 sz,int8 *newoff)
{
Datatype *res = this;
Datatype *next = res;
int8 curOff = off;
for(;;) {
next = next->getSubType(curOff, &curOff);
if (next == (Datatype *)0) break; // No smaller component
if (curOff + sz > next->getSize()) break; // Next component down does not contain
res = next;
off = curOff;
}
*newoff = off;
return res;
}
/// Called only if the \b typedefImm field is non-null. Encode the data-type to the
/// stream as a simple \<typedef> element including only the names and ids of \b this and
/// the data-type it typedefs.
@@ -312,6 +312,7 @@ public:
bool isPrimitiveWhole(void) const; ///< Is \b this made up of a single primitive
bool nearestArrayedComponent(int8 off,uint4 arrayHint,int8 *newoff) const;
bool testForArraySlack(int8 off); ///< Test if an \e out-of-bounds offset makes sense as array slack
Datatype *findSmallestContainer(int8 off,int8 sz,int8 *newoff); ///< Find the smallest component containing the given range
static uint4 encodeIntegerFormat(const string &val);
static string decodeIntegerFormat(uint4 val);
@@ -308,8 +308,8 @@ void HighVariable::stripType(void) const
if (meta == TYPE_PARTIALUNION || meta == TYPE_PARTIALSTRUCT) {
if (symbol != (Symbol *)0 && symboloffset != -1) { // If there is a bigger backing symbol
type_metatype submeta = symbol->getType()->getMetatype();
if (submeta == TYPE_STRUCT || submeta == TYPE_UNION)
return; // Don't strip the partial union
if (submeta == TYPE_STRUCT || submeta == TYPE_UNION || submeta == TYPE_ARRAY)
return; // Don't strip the partial
}
}
else if (type->isEnumType()) {