GP-6492 Adjustments to union scoring

This commit is contained in:
caheckman
2026-02-26 19:53:42 +00:00
parent 7928bef769
commit e12d821d86
25 changed files with 952 additions and 333 deletions
@@ -205,7 +205,13 @@ StringSequence::StringSequence(Funcdata &fdata,Datatype *ct,SymbolEntry *ent,Pco
break;
arrayType = parentType;
lastOff = off;
parentType = parentType->getSubType(off, &off);
if (parentType->needsResolution()) {
const TypeField *field = parentType->resolveTruncation(off, root, -1, off); // Resolve thru the specific COPY
if (field == (const TypeField *)0) break;
parentType = field->type;
}
else
parentType = parentType->getSubType(off, &off);
} while(parentType != (Datatype *)0);
if (parentType != ct || arrayType == (Datatype *)0 || arrayType->getMetatype() != TYPE_ARRAY)
return;
@@ -296,8 +302,20 @@ Varnode *StringSequence::constructTypedPointer(PcodeOp *insertPoint)
if (baseType->getMetatype() == TYPE_ARRAY)
elSize = ((TypeArray *)baseType)->getBase()->getAlignSize();
int8 newOff;
baseType = baseType->getSubType(curOff, &newOff );
if (baseType == (Datatype *)0) break;
if (baseType->needsResolution()) {
const TypeField *field = baseType->resolveTruncation(curOff, insertPoint, -1, newOff);
if (field != (const TypeField *)0) {
baseType = field->type;
curOff = newOff;
continue; // Do not create PTRSUB for union resolution here
}
else
break;
}
else {
baseType = baseType->getSubType(curOff, &newOff );
if (baseType == (Datatype *)0) break;
}
curOff -= newOff;
baseOff = AddrSpace::byteToAddress(curOff, spc->getWordSize());
if (elSize >= 0) {
@@ -318,6 +336,8 @@ Varnode *StringSequence::constructTypedPointer(PcodeOp *insertPoint)
data.opSetInput(ptrsub,data.newConstant(spacePtr->getSize(), baseOff), 1);
}
data.opSetInput(ptrsub,spacePtr,0);
if (curType->needsResolution())
data.inheritUnionFieldPtr(curType, ptrsub, 0, insertPoint, -1);
spacePtr = data.newUniqueOut(spacePtr->getSize(), ptrsub);
data.opInsertBefore(ptrsub, insertPoint);
curType = types->getTypePointerStripArray(spacePtr->getSize(), baseType, spc->getWordSize());
@@ -364,6 +384,8 @@ PcodeOp *StringSequence::buildStringCopy(void)
Varnode *destPtr = constructTypedPointer(insertPoint);
data.opSetInput(copyOp, destPtr, 1);
data.opSetInput(copyOp, srcPtr, 2);
if (destPtr->getType()->needsResolution())
data.inheritUnionFieldPtr(destPtr->getType(), copyOp, 1, insertPoint, -1);
Varnode *lenVn = data.newConstant(4,index);
lenVn->updateType(copyOp->inputTypeLocal(3));
data.opSetInput(copyOp, lenVn, 3);
@@ -460,12 +482,12 @@ bool StringSequence::transform(void)
return true;
}
/// From a starting pointer, backtrack through PTRADDs and COPYs to a putative root Varnode pointer.
/// \param initPtr is pointer Varnode into the root STORE
void HeapSequence::findBasePointer(Varnode *initPtr)
/// From a starting pointer to \b rootOp, backtrack through PTRADDs and COPYs to a putative root Varnode pointer.
void HeapSequence::findBasePointer(void)
{
basePointer = initPtr;
basePointer = rootOp->getIn(1);
immedRead = rootOp;
while(basePointer->isWritten()) {
PcodeOp *op = basePointer->getDef();
OpCode opc = op->code();
@@ -476,6 +498,7 @@ void HeapSequence::findBasePointer(Varnode *initPtr)
else if (opc != CPUI_COPY)
break;
basePointer = op->getIn(0);
immedRead = op;
}
}
@@ -745,6 +768,8 @@ PcodeOp *HeapSequence::buildStringCopy(void)
data.opSetInput(ptrAdd,data.newConstant(basePointer->getSize(), charType->getAlignSize()),2);
destPtr->updateType(charPtrType);
data.opInsertBefore(ptrAdd, insertPoint);
if (basePointer->getType()->needsResolution())
data.inheritUnionField(basePointer->getType(), ptrAdd, 0, immedRead, immedRead->getSlot(basePointer));
}
int4 index;
uint4 builtInId = selectStringCopyFunction(index);
@@ -758,6 +783,8 @@ PcodeOp *HeapSequence::buildStringCopy(void)
lenVn->updateType(copyOp->inputTypeLocal(3));
data.opSetInput(copyOp, lenVn, 3);
data.opInsertBefore(copyOp, insertPoint);
if (destPtr->getType()->needsResolution())
data.inheritUnionField(destPtr->getType(), copyOp, 1, immedRead, immedRead->getSlot(destPtr));
return copyOp;
}
@@ -910,7 +937,7 @@ HeapSequence::HeapSequence(Funcdata &fdata,Datatype *ct,PcodeOp *root)
baseOffset = 0;
storeSpace = root->getIn(0)->getSpaceFromConst();
ptrAddMult = AddrSpace::byteToAddressInt(charType->getAlignSize(), storeSpace->getWordSize());
findBasePointer(rootOp->getIn(1));
findBasePointer();
if (!collectStoreOps())
return;
if (!checkInterference())
@@ -956,7 +983,7 @@ int4 RuleStringCopy::applyOp(PcodeOp *op,Funcdata &data)
{
if (!op->getIn(0)->isConstant()) return 0; // Constant
Varnode *outvn = op->getOut();
Datatype *ct = outvn->getType();
Datatype *ct = outvn->getTypeDefFacing();
if (!ct->isCharPrint()) return 0; // Copied to a "char" data-type Varnode
if (ct->isOpaqueString()) return 0;
if (!outvn->isAddrTied()) return 0;
@@ -95,11 +95,12 @@ class HeapSequence : public ArraySequence {
static bool compareOutput(const IndirectPair *a,const IndirectPair *b); ///< Compare pairs by output storage
};
Varnode *basePointer; ///< Pointer that sequence is stored to
PcodeOp *immedRead; ///< Op immediately reading basePointer
uint8 baseOffset; ///< Offset relative to pointer to root STORE
AddrSpace *storeSpace; ///< Address space being STOREed to
int4 ptrAddMult; ///< Required multiplier for PTRADD ops
vector<Varnode *> nonConstAdds; ///< non-constant Varnodes being added into pointer calculation
void findBasePointer(Varnode *initPtr); ///< Find the base pointer for the sequence
void findBasePointer(void); ///< Find the base pointer for the sequence
void findDuplicateBases(vector<Varnode *> &duplist); ///< Find any duplicates of \b basePointer
void findInitialStores(vector<PcodeOp *> &stores);
static uint8 calcAddElements(Varnode *vn,vector<Varnode *> &nonConst,int4 maxDepth);
@@ -2454,23 +2454,54 @@ bool ActionSetCasts::testStructOffset0(Datatype *reqtype,Datatype *curtype,CastS
return (castStrategy->castStandard(reqtype, curtype, true, true) == (Datatype *)0);
}
/// \brief Try to adjust the input and output Varnodes to eliminate a CAST
/// \brief Try to eliminate a CAST for a data-type needing resolution by matching the data-type needed by the p-code op
///
/// Merging can cause the high data-type to become unresolved even though the underlying data-type requires a specific
/// resolution. This forces the resolution onto the high, if possible, in the context of the specific read or write.
/// \param dt is the data-type needed by the p-code op
/// \param op is the p-code op
/// \param slot is the index of the slot to test for resolution (-1 for output, >= 0 for input)
/// \return \b true if the resolution was successfully changed and a CAST is not needed
bool ActionSetCasts::tryResolutionAdjustment(Datatype *dt,PcodeOp *op,int4 slot,Funcdata &data)
{
if (dt->needsResolution()) return false;
Varnode *vn = (slot < 0) ? op->getOut() : op->getIn(slot);
Datatype *curType = vn->getHigh()->getType();
if (!curType->needsResolution()) return false;
if (slot < 0 && curType->getMetatype() == TYPE_PTR) return false; // Cannot take field of pointer during assignment
int4 fieldNum = curType->findCompatibleResolve(dt);
if (fieldNum < 0) return false;
TypeFactory *typegrp = data.getArch()->types;
ResolvedUnion resolve(curType,fieldNum,*typegrp);
if (!data.setUnionField(curType, op, slot, resolve))
return false;
if (slot >=0 && curType->getMetatype() == TYPE_PTR) {
if (vn->isWritten() && (vn->getDef()->code() != CPUI_PTRSUB || vn->getDef()->getIn(0)->getType() != curType)) {
// For pointers, if not already inserted, insert a PTRSUB representing the field access
PcodeOp *ptrsub = insertPtrsubZero(op,slot,resolve.getDatatype(),data);
data.setUnionField(curType, ptrsub,-1,resolve); // Attach the resolution to the PTRSUB
}
}
return true;
}
/// \brief Try to adjust the input and output Varnodes to a COPY, in order to eliminate a CAST
///
/// If input/output data-types are different, it may be due to late merges. For
/// unions, the CAST can sometimes be eliminated by adjusting the data-type resolutions
/// of the Varnodes relative to the PcodeOp
/// \param op is the PcodeOp reading the input Varnode and writing the output Varnode
/// \param slot is the index of the input Varnode
/// \param op is the COPY
/// \param data is the function
/// \return \b true if an adjustment is made so that a CAST is no longer needed
bool ActionSetCasts::tryResolutionAdjustment(PcodeOp *op,int4 slot,Funcdata &data)
bool ActionSetCasts::tryResolutionCopy(PcodeOp *op,Funcdata &data)
{
Varnode *outvn = op->getOut();
if (outvn == (Varnode *)0)
return false;
Datatype *outType = outvn->getHigh()->getType();
Datatype *inType = op->getIn(slot)->getHigh()->getType();
Datatype *inType = op->getIn(0)->getHigh()->getType();
if (!inType->needsResolution() && !outType->needsResolution()) return false;
int4 inResolve = -1;
int4 outResolve = -1;
@@ -2478,7 +2509,7 @@ bool ActionSetCasts::tryResolutionAdjustment(PcodeOp *op,int4 slot,Funcdata &dat
inResolve = inType->findCompatibleResolve(outType);
if (inResolve < 0) return false;
}
if (outType->needsResolution()) {
if (outType->needsResolution() && outType->getMetatype() != TYPE_PTR) {
if (inResolve >= 0)
outResolve = outType->findCompatibleResolve(inType->getDepend(inResolve));
else
@@ -2487,12 +2518,12 @@ bool ActionSetCasts::tryResolutionAdjustment(PcodeOp *op,int4 slot,Funcdata &dat
}
TypeFactory *typegrp = data.getArch()->types;
if (inType->needsResolution()) {
if (inResolve >= 0) {
ResolvedUnion resolve(inType,inResolve,*typegrp);
if (!data.setUnionField(inType, op, slot, resolve))
if (!data.setUnionField(inType, op, 0, resolve))
return false;
}
if (outType->needsResolution()) {
if (outResolve >= 0) {
ResolvedUnion resolve(outType,outResolve,*typegrp);
if (!data.setUnionField(outType, op, -1, resolve))
return false;
@@ -2537,10 +2568,12 @@ int4 ActionSetCasts::resolveUnion(PcodeOp *op,int4 slot,Funcdata &data,CastStrat
Datatype *dt = vn->getHigh()->getType();
if (!dt->needsResolution())
return 0;
if (dt != vn->getType())
const ResolvedUnion *resUnion = data.getUnionField(dt, op, slot);
if (resUnion == (ResolvedUnion *)0) {
dt->resolveInFlow(op, slot); // Last chance to resolve data-type based on flow
const ResolvedUnion *resUnion = data.getUnionField(dt, op,slot);
if (resUnion != (ResolvedUnion*)0 && resUnion->getFieldNum() >= 0) {
resUnion = data.getUnionField(dt, op, slot);
}
if (resUnion != (ResolvedUnion *)0 && resUnion->getFieldNum() >= 0) {
if (dt->getMetatype() == TYPE_PTR) {
// Test if a cast is still needed even after resolution
Datatype *reqtype = vn->getTypeReadFacing(op);
@@ -2577,12 +2610,11 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
Datatype *outct,*ct,*tokenct;
Varnode *vn,*outvn;
PcodeOp *newop;
Datatype *outHighType;
bool force=false;
tokenct = op->getOpcode()->getOutputToken(op,castStrategy);
outvn = op->getOut();
outHighType = outvn->getHigh()->getType();
Datatype *outHighType = outvn->getHigh()->getType();
if (tokenct == outHighType) {
if (tokenct->needsResolution()) {
// operation copies directly to outvn AS a union
@@ -2592,12 +2624,7 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
// Short circuit more sophisticated casting tests. If they are the same type, there is no cast
return 0;
}
Datatype *outHighResolve = outHighType;
if (outHighType->needsResolution()) {
if (outHighType != outvn->getType())
outHighType->resolveInFlow(op, -1); // Last chance to resolve data-type based on flow
outHighResolve = outHighType->findResolve(op, -1); // Finish fetching DefFacing data-type
}
Datatype *outHighResolve = outvn->getHighTypeDefFacing();
if (outvn->isImplied()) {
// implied varnode must have parse type
if (outvn->isTypeLock()) {
@@ -2632,6 +2659,8 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
ct = castStrategy->castStandard(outct,tokenct,false,true);
if (ct == (Datatype *)0) return 0;
}
if (tryResolutionAdjustment(tokenct, op, -1, data))
return 0;
}
// Generate the cast op
vn = data.newUnique(outvn->getSize());
@@ -2652,7 +2681,7 @@ int4 ActionSetCasts::castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStr
if (tokenct->needsResolution())
data.forceFacingType(tokenct, -1, newop, 0);
if (outHighType->needsResolution())
data.inheritResolution(outHighType, newop, -1, op, -1); // Inherit write resolution
data.inheritUnionField(outHighType, newop, -1, op, -1); // Inherit write resolution
return 1;
}
@@ -2735,10 +2764,12 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy
// Insert a PTRSUB(vn,#0) instead of a CAST
newop = insertPtrsubZero(op, slot, ct, data);
if (vn->getHigh()->getType()->needsResolution())
data.inheritResolution(vn->getHigh()->getType(),newop, 0, op, slot);
data.inheritUnionField(vn->getHigh()->getType(),newop, 0, op, slot);
return 1;
}
else if (tryResolutionAdjustment(op, slot, data)) {
else if (op->code() != CPUI_COPY && tryResolutionAdjustment(ct, op, slot, data))
return 1;
else if (op->code() == CPUI_COPY && tryResolutionCopy(op, data)) {
return 1;
}
newop = data.newOp(1,op->getAddr());
@@ -2756,7 +2787,7 @@ int4 ActionSetCasts::castInput(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy
data.forceFacingType(ct, -1, newop, -1);
}
if (vn->getHigh()->getType()->needsResolution()) {
data.inheritResolution(vn->getHigh()->getType(),newop, 0, op, slot);
data.inheritUnionField(vn->getHigh()->getType(),newop, 0, op, slot);
}
return 1;
}
@@ -2796,9 +2827,18 @@ int4 ActionSetCasts::apply(Funcdata &data)
data.opSetOpcode(op, CPUI_INT_ADD);
}
}
// Do input casts first, as output may depend on input
// Allow unresolved high data-types to resolve
for(int4 i=0;i<op->numInput();++i) {
count += resolveUnion(op, i, data, castStrategy);
}
Varnode *vn = op->getOut();
if (vn != (Varnode *)0) {
Datatype *outHighType = vn->getHigh()->getType();
if (outHighType->needsResolution())
outHighType->resolveInFlow(op, -1); // Last chance to resolve data-type based on flow
}
// Do input casts first, as output may depend on input
for(int4 i=0;i<op->numInput();++i) {
count += castInput(op,i,data,castStrategy);
}
if (opc == CPUI_LOAD) {
@@ -2807,9 +2847,8 @@ int4 ActionSetCasts::apply(Funcdata &data)
else if (opc == CPUI_STORE) {
checkPointerIssues(op, op->getIn(2), data);
}
Varnode *vn = op->getOut();
if (vn == (Varnode *)0) continue;
count += castOutput(op,data,castStrategy);
if (vn != (Varnode *)0)
count += castOutput(op,data,castStrategy);
}
}
return 0; // Indicate full completion
@@ -5127,13 +5166,15 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i
{
Varnode *invn,*outvn;
if (inslot == outslot) return false; // don't backtrack
invn = (inslot==-1) ? op->getOut() : op->getIn(inslot);
Datatype *alttype = invn->getTempType();
if (alttype->needsResolution()) {
// Always give incoming data-type a chance to resolve, even if it would not otherwise propagate
alttype = alttype->resolveInFlow(op, inslot);
Datatype *resType = alttype->resolveInFlow(op, inslot);
if (!op->isMarker())
alttype = resType;
}
if (inslot == outslot) return false; // don't backtrack
if (outslot < 0)
outvn = op->getOut();
else {
@@ -320,7 +320,8 @@ public:
class ActionSetCasts : public Action {
static void checkPointerIssues(PcodeOp *op,Varnode *vn,Funcdata &data);
static bool testStructOffset0(Datatype *reqtype,Datatype *curtype,CastStrategy *castStrategy);
static bool tryResolutionAdjustment(PcodeOp *op,int4 slot,Funcdata &data);
static bool tryResolutionAdjustment(Datatype *dt,PcodeOp *op,int4 slot,Funcdata &data);
static bool tryResolutionCopy(PcodeOp *op,Funcdata &data);
static bool isOpIdentical(Datatype *ct1,Datatype *ct2);
static int4 resolveUnion(PcodeOp *op,int4 slot,Funcdata &data,CastStrategy *castStrategy);
static int4 castOutput(PcodeOp *op,Funcdata &data,CastStrategy *castStrategy);
@@ -688,11 +688,13 @@ void EquateSymbol::decode(Decoder &decoder)
/// \param nm is the name of the symbol
/// \param unionDt is the union data-type being forced
/// \param fldNum is the particular field to force (-1 indicates the whole union)
UnionFacetSymbol::UnionFacetSymbol(Scope *sc,const string &nm,Datatype *unionDt,int4 fldNum)
/// \param isAddr is \b true for facets that apply to all ops at the address
UnionFacetSymbol::UnionFacetSymbol(Scope *sc,const string &nm,Datatype *unionDt,int4 fldNum,bool isAddr)
: Symbol(sc, nm, unionDt)
{
fieldNum = fldNum;
category = union_facet;
addrBased = isAddr;
}
void UnionFacetSymbol::encode(Encoder &encoder) const
@@ -701,6 +703,7 @@ void UnionFacetSymbol::encode(Encoder &encoder) const
encoder.openElement(ELEM_FACETSYMBOL);
encodeHeader(encoder);
encoder.writeSignedInteger(ATTRIB_FIELD, fieldNum);
encoder.writeBool(ATTRIB_ADDRTIED, true);
encodeBody(encoder);
encoder.closeElement(ELEM_FACETSYMBOL);
}
@@ -711,6 +714,7 @@ void UnionFacetSymbol::decode(Decoder &decoder)
uint4 elemId = decoder.openElement(ELEM_FACETSYMBOL);
decodeHeader(decoder);
fieldNum = decoder.readSignedInteger(ATTRIB_FIELD);
addrBased = decoder.readBool(ATTRIB_ADDRTIED);
decodeBody(decoder);
decoder.closeElement(elemId);
@@ -318,10 +318,12 @@ public:
/// different symbols attached. The Symbol's associated data-type will be the desired \e union to force.
class UnionFacetSymbol : public Symbol {
int4 fieldNum; ///< Particular field to associate with Symbol access
bool addrBased; ///< Set to \b true if facet matches any PcodeOp at the address
public:
UnionFacetSymbol(Scope *sc,const string &nm,Datatype *unionDt,int4 fldNum); ///< Constructor from components
UnionFacetSymbol(Scope *sc) : Symbol(sc) { fieldNum = -1; category = union_facet; } ///< Constructor for decode
UnionFacetSymbol(Scope *sc,const string &nm,Datatype *unionDt,int4 fldNum,bool isAddr=false); ///< Constructor from components
UnionFacetSymbol(Scope *sc) : Symbol(sc) { fieldNum = -1; category = union_facet; addrBased = false; } ///< Constructor for decode
int4 getFieldNumber(void) const { return fieldNum; } ///< Get the particular field associate with \b this
bool isAddrBased(void) const { return addrBased; } ///< Return \b true if facet matches any PcodeOp at the address
virtual void encode(Encoder &encoder) const;
virtual void decode(Decoder &decoder);
};
@@ -222,6 +222,25 @@ void FloatFormat::calcPrecision(void)
decimalMaxPrecision = (int4)ceil((frac_size + 1) * 0.30103) + 1;
}
/// \param encoding is the encoded floating-point value
/// \return either \e zero, \e infinity, \e denormalized, \e nan, or \e normalized
FloatFormat::floatclass FloatFormat::getClass(uintb encoding) const
{
int4 exp = extractExponentCode(encoding);
if (exp == 0) {
if ( extractFractionalCode(encoding) == 0 )
return zero;
return denormalized;
}
if (exp == maxexponent) {
if ( extractFractionalCode(encoding) == 0 )
return infinity;
return nan;
}
return normalized;
}
/// \param encoding is the encoding value
/// \param type points to the floating-point class, which is passed back
/// \return the equivalent double value
@@ -64,6 +64,7 @@ private:
public:
FloatFormat(int4 sz); ///< Construct default IEEE 754 standard settings
int4 getSize(void) const { return size; } ///< Get the size of the encoding in bytes
floatclass getClass(uintb encoding) const; ///< Get the class of an encoding
double getHostFloat(uintb encoding,floatclass *type) const; ///< Convert an encoding into host's double
uintb getEncoding(double host) const; ///< Convert host's double into \b this encoding
uintb convertEncoding(uintb encoding,const FloatFormat *formin) const; ///< Convert between two different formats
@@ -71,6 +72,7 @@ public:
uintb extractFractionalCode(uintb x) const; ///< Extract the fractional part of the encoding
bool extractSign(uintb x) const; ///< Extract the sign bit from the encoding
int4 extractExponentCode(uintb x) const; ///< Extract the exponent from the encoding
int4 getExponent(uintb x) const { return extractExponentCode(x) - bias; } ///< Get the exponent of an encoding
string printDecimal(double host,bool forcesci) const; ///< Print given value as a decimal string
@@ -907,44 +907,87 @@ void PcodeEmitFd::dump(const Address &addr,OpCode opc,VarnodeData *outvar,Varnod
}
}
/// \brief Get the resolved union field associated with the given edge
/// \brief Get the resolution (union field) associated with an unresolved data-type and the given edge
///
/// If there is no field associated with the edge, null is returned
/// \param parent is the data-type being resolved
/// If there is no field associated with the edge, or the resolved data-type is not
/// the same size as the given data-type, null is returned.
/// \param unresType is the given data-type being resolved
/// \param op is the PcodeOp component of the given edge
/// \param slot is the slot component of the given edge
/// \return the associated field as a ResolvedUnion or null
const ResolvedUnion *Funcdata::getUnionField(const Datatype *parent,const PcodeOp *op,int4 slot) const
const ResolvedUnion *Funcdata::getUnionField(const Datatype *unresType,const PcodeOp *op,int4 slot) const
{
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
ResolveEdge edge(parent,op,slot);
ResolveEdge edge(unresType,op,slot);
iter = unionMap.find(edge);
if (iter != unionMap.end()) {
const ResolvedUnion *res = &(*iter).second;
Datatype *dt = res->getDatatype();
// Check that the Varnode size matches the data-type
if (unresType->getSize() == dt->getSize())
return res;
}
return (const ResolvedUnion *)0;
}
/// \brief Get the resolution (union field) associated with an unresolved data-type and the given edge
///
/// The size of the resolved data-type does not have to match the given data-type.
/// If there is no field associated with the edge, null is returned.
/// \param unresType is the unresolved data-type
/// \param op is the PcodeOp component of the given edge
/// \param slot is the slot component of the given edge
/// \return the associated field as a ResolvedUnion or null
const ResolvedUnion *Funcdata::getUnionResolution(const Datatype *unresType,const PcodeOp *op,int4 slot) const
{
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
ResolveEdge edge(unresType,op,slot);
iter = unionMap.find(edge);
if (iter != unionMap.end()) {
const ResolvedUnion *res = &(*iter).second;
return res;
}
return (const ResolvedUnion *)0;
}
/// \brief Get any resolution (union field) associated with an unresolved data-type and the given address
///
/// If there is no associated field, null is returned
/// \param unresType is the unresolved data-type
/// \param addr is the given address
/// \param slot is the slot component the resolution matches
/// \return the associated field as a ResolvedUnion or null
const ResolvedUnion *Funcdata::getAddressBasedUnionField(const Datatype *unresType,const Address &addr,int4 slot) const
{
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
ResolveEdge edge(unresType,addr,slot);
iter = unionMap.find(edge);
if (iter != unionMap.end())
return &(*iter).second;
return (const ResolvedUnion *)0;
}
/// \brief Associate a union field with the given edge
/// \brief Associate a resolution (union field) with an unresolved data-type and the given edge
///
/// If there was a previous association, it is overwritten unless it was \e locked.
/// The method returns \b true except in this case where a previous locked association exists.
/// \param parent is the parent union data-type
/// \param unresType is the unresolved data-type
/// \param op is the PcodeOp component of the given edge
/// \param slot is the slot component of the given edge
/// \param resolve is the resolved union
/// \return \b true unless there was a locked association
bool Funcdata::setUnionField(const Datatype *parent,const PcodeOp *op,int4 slot,const ResolvedUnion &resolve)
/// \return \b true unless the resolution was locked
bool Funcdata::setUnionField(const Datatype *unresType,const PcodeOp *op,int4 slot,const ResolvedUnion &resolve)
{
ResolveEdge edge(parent,op,slot);
ResolveEdge edge(unresType,op,slot);
pair<map<ResolveEdge,ResolvedUnion>::iterator,bool> res;
res = unionMap.emplace(edge,resolve);
if (!res.second) {
if ((*res.first).second.isLocked()) {
return false;
}
(*res.first).second = resolve;
if (!(*res.first).second.update(resolve))
return !(*res.first).second.isLocked();
}
if (op->code() == CPUI_MULTIEQUAL && slot >= 0) {
// Data-type propagation doesn't happen between MULTIEQUAL input slots holding the same Varnode
@@ -953,54 +996,124 @@ bool Funcdata::setUnionField(const Datatype *parent,const PcodeOp *op,int4 slot,
for(int4 i=0;i<op->numInput();++i) {
if (i == slot) continue;
if (op->getIn(i) != vn) continue; // Check that different input slot holds same Varnode
ResolveEdge dupedge(parent,op,i);
ResolveEdge dupedge(unresType,op,i);
res = unionMap.emplace(dupedge,resolve);
if (!res.second) {
if (!(*res.first).second.isLocked())
(*res.first).second = resolve;
(*res.first).second.update(resolve);
}
}
}
return true;
}
/// \brief Force a specific union field resolution for the given edge
/// \brief Associate a resolution (union field) with an unresolved data-type and the given address
///
/// The \b parent data-type is taken directly from the given Varnode.
/// \param parent is the parent data-type
/// If there was a previous association, it is overwritten unless it was \e locked.
/// The method returns \b true except in this case where a previous locked association exists.
/// \param unresType is the unresolved data-type
/// \param addr is the given address
/// \param slot is the slot component the resolution matches
/// \param resolve is the resolved union
/// \return \b true unless there was a locked association
bool Funcdata::setAddressBasedUnionField(const Datatype *unresType,const Address &addr,int4 slot,const ResolvedUnion &resolve)
{
ResolveEdge edge(unresType,addr,slot);
pair<map<ResolveEdge,ResolvedUnion>::iterator,bool> res;
res = unionMap.emplace(edge,resolve);
if (!res.second) {
if ((*res.first).second.isLocked()) {
return false;
}
(*res.first).second = resolve;
}
return true;
}
/// \brief Update the resolution data-type associated with an unresolved data-type for a given edge
///
/// If further resolution of the cached data-type has happened, update the cache with the new data-type.
/// \param unresType is the unresolved data-type
/// \param op is the PcodeOp component of the given edge
/// \param slot is the slot component of the given edge
/// \param resType is the new resolution data-type
/// \return \b true unless there was a locked association
bool Funcdata::updateUnionField(const Datatype *unresType,const PcodeOp *op,int4 slot,Datatype *resType)
{
map<ResolveEdge,ResolvedUnion>::iterator iter;
ResolveEdge edge(unresType,op,slot);
iter = unionMap.find(edge);
if (iter != unionMap.end()) {
(*iter).second.setResolve(resType);
return true;
}
return false;
}
/// \brief Force a specific resolution (union field) resolution for an unresolved data-type and the given edge
///
/// \param unresType is the unresolved data-type
/// \param fieldNum is the index of the field to force
/// \param op is PcodeOp of the edge
/// \param slot is -1 for the write edge or >=0 indicating the particular read edge
void Funcdata::forceFacingType(Datatype *parent,int4 fieldNum,PcodeOp *op,int4 slot)
void Funcdata::forceFacingType(Datatype *unresType,int4 fieldNum,PcodeOp *op,int4 slot)
{
Datatype *baseType = parent;
Datatype *baseType = unresType;
if (baseType->getMetatype() == TYPE_PTR)
baseType = ((TypePointer *)baseType)->getPtrTo();
if (parent->isPointerRel()) {
if (unresType->isPointerRel()) {
// Don't associate a relative pointer with the resolution, but convert to a standard pointer
parent = glb->types->getTypePointer(parent->getSize(), baseType, ((TypePointer *)parent)->getWordSize());
unresType = glb->types->getTypePointer(unresType->getSize(), baseType, ((TypePointer *)unresType)->getWordSize());
}
ResolvedUnion resolve(parent,fieldNum,*glb->types);
setUnionField(parent, op, slot, resolve);
ResolvedUnion resolve(unresType,fieldNum,*glb->types);
setUnionField(unresType, op, slot, resolve);
}
/// \brief Copy a read/write facing resolution for a specific data-type from one PcodeOp to another
/// \brief Copy a read/write facing resolution for an unresolved data-type from one PcodeOp to another
///
/// \param parent is the data-type that needs resolution
/// \param unresType is the unresolved data-type
/// \param op is the new reading PcodeOp
/// \param slot is the new slot (-1 for write, >=0 for read)
/// \param oldOp is the PcodeOp to inherit the resolution from
/// \param oldSlot is the old slot (-1 for write, >=0 for read)
int4 Funcdata::inheritResolution(Datatype *parent,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot)
/// \return the field number that was inherited
int4 Funcdata::inheritUnionField(Datatype *unresType,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot)
{
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
if (slot < 0 && oldOp->isMarker())
slot = 0;
ResolveEdge edge(unresType,oldOp,oldSlot);
iter = unionMap.find(edge);
if (iter == unionMap.end())
return -1;
setUnionField(unresType,op,slot,(*iter).second);
return (*iter).second.getFieldNum();
}
/// \brief Create a resolution for an unresolved pointer data-type based on earlier non-pointer resolution
///
/// \param unresPtr is the unresolved pointer data-type
/// \param op is the new reading PcodeOp
/// \param slot is the new slot (-1 for write, >=0 for read)
/// \param oldOp is the PcodeOp to inherit the resolution from
/// \param oldSlot is the old slot (-1 for write, >=0 for read)
/// \return the field number that was inherited
int4 Funcdata::inheritUnionFieldPtr(Datatype *unresPtr,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot)
{
Datatype *parent = unresPtr->getDepend(0);
if (slot < 0 && oldOp->isMarker())
slot = 0;
map<ResolveEdge,ResolvedUnion>::const_iterator iter;
ResolveEdge edge(parent,oldOp,oldSlot);
iter = unionMap.find(edge);
if (iter == unionMap.end())
return -1;
setUnionField(parent,op,slot,(*iter).second);
ResolvedUnion ptrres(unresPtr,(*iter).second.getFieldNum(),*glb->types);
setUnionField(unresPtr,op,slot,ptrres);
return (*iter).second.getFieldNum();
}
@@ -529,11 +529,15 @@ public:
bool moveRespectingCover(PcodeOp *op,PcodeOp *lastOp); ///< Move given op past \e lastOp respecting covers if possible
const ResolvedUnion *getUnionField(const Datatype *parent,const PcodeOp *op,int4 slot) const;
bool setUnionField(const Datatype *parent,const PcodeOp *op,int4 slot,const ResolvedUnion &resolve);
void forceFacingType(Datatype *parent,int4 fieldNum,PcodeOp *op,int4 slot);
int4 inheritResolution(Datatype *parent,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot);
const ResolvedUnion *getUnionField(const Datatype *unresType,const PcodeOp *op,int4 slot) const;
const ResolvedUnion *getAddressBasedUnionField(const Datatype *unresType,const Address &addr,int4 slot) const;
const ResolvedUnion *getUnionResolution(const Datatype *unresType,const PcodeOp *op,int4 slot) const;
bool setUnionField(const Datatype *unresType,const PcodeOp *op,int4 slot,const ResolvedUnion &resolve);
bool setAddressBasedUnionField(const Datatype *unresType,const Address &addr,int4 slot,const ResolvedUnion &resolve);
bool updateUnionField(const Datatype *unresType,const PcodeOp *op,int4 slot,Datatype *resType);
void forceFacingType(Datatype *unresType,int4 fieldNum,PcodeOp *op,int4 slot);
int4 inheritUnionField(Datatype *unresType,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot);
int4 inheritUnionFieldPtr(Datatype *unresPtr,const PcodeOp *op,int4 slot,PcodeOp *oldOp,int4 oldSlot);
// Jumptable routines
JumpTable *linkJumpTable(PcodeOp *op); ///< Link jump-table with a given BRANCHIND
JumpTable *findJumpTable(const PcodeOp *op) const; ///< Find a jump-table associated with a given BRANCHIND
@@ -1656,15 +1656,40 @@ void Funcdata::coverVarnodes(SymbolEntry *entry,vector<Varnode *> &list)
bool Funcdata::applyUnionFacet(SymbolEntry *entry,DynamicHash &dhash)
{
Symbol *sym = entry->getSymbol();
UnionFacetSymbol *sym = (UnionFacetSymbol *)entry->getSymbol();
if (sym->isAddrBased()) {
ResolvedUnion resolve(sym->getType(), sym->getFieldNumber(), *glb->types);
resolve.setLock(true);
int4 slot = DynamicHash::getSlotFromHash(entry->getHash());
return setAddressBasedUnionField(sym->getType(), entry->getFirstUseAddress(), slot, resolve);
}
PcodeOp *op = dhash.findOp(this, entry->getFirstUseAddress(), entry->getHash());
if (op == (PcodeOp *)0)
return false;
int4 slot = DynamicHash::getSlotFromHash(entry->getHash());
int4 fldNum = ((UnionFacetSymbol *)sym)->getFieldNumber();
ResolvedUnion resolve(sym->getType(), fldNum, *glb->types);
const ResolvedUnion *res = getUnionResolution(sym->getType(), op, slot);
if (res != (const ResolvedUnion *)0 && res->getFieldNum() == sym->getFieldNumber())
return false;
Varnode *vn = (slot < 0) ? op->getOut() : op->getIn(slot);
Datatype *unresType = sym->getType();
Datatype *dt = vn->getType();
if (dt->getMetatype() == TYPE_PTR) {
if (((TypePointer *)dt)->getPtrTo() == unresType) {
unresType = dt;
}
}
else if (dt->getMetatype() == TYPE_PARTIALSTRUCT) {
if (((TypePartialStruct *)dt)->getParent() == unresType)
unresType = dt;
}
else if (dt->getMetatype() == TYPE_PARTIALUNION) {
if (((TypePartialUnion *)dt)->getParentUnion() == unresType)
unresType = dt;
}
ResolvedUnion resolve(unresType,sym->getFieldNumber(), *glb->types);
resolve.setLock(true);
return setUnionField(sym->getType(),op,slot,resolve);
setUnionField(unresType,op,slot,resolve);
return true;
}
/// Search for \e addrtied Varnodes whose storage falls in the global Scope, then
@@ -416,7 +416,7 @@ PcodeOp *Merge::allocateCopyTrim(Varnode *inVn,const Address &addr,PcodeOp *trim
Datatype *ct = inVn->getType();
if (ct->needsResolution()) { // If the data-type needs resolution
if (inVn->isWritten()) {
int4 fieldNum = data.inheritResolution(ct, copyOp, -1, inVn->getDef(), -1);
int4 fieldNum = data.inheritUnionField(ct, copyOp, -1, inVn->getDef(), -1);
data.forceFacingType(ct, fieldNum, copyOp, 0);
}
else {
@@ -669,7 +669,7 @@ void Merge::trimOpOutput(PcodeOp *op)
copyop = data.newOp(1,op->getAddr());
data.opSetOpcode(copyop,CPUI_COPY);
if (ct->needsResolution()) {
int4 fieldNum = data.inheritResolution(ct, copyop, -1, op, -1);
int4 fieldNum = data.inheritUnionField(ct, copyop, -1, op, -1);
data.forceFacingType(ct, fieldNum, copyop, 0);
if (ct->getMetatype() == TYPE_PARTIALUNION)
ct = vn->getTypeDefFacing();
@@ -871,7 +871,7 @@ void Merge::mergeIndirect(PcodeOp *indop)
PcodeOp *newop = allocateCopyTrim(invn0, indop->getAddr(), indop);
SymbolEntry *entry = outvn->getSymbolEntry();
if (entry != (SymbolEntry *)0 && entry->getSymbol()->getType()->needsResolution()) {
data.inheritResolution(entry->getSymbol()->getType(), newop, -1, indop, -1);
data.inheritUnionField(entry->getSymbol()->getType(), newop, -1, indop, -1);
}
data.opSetInput(indop,newop->getOut(),0);
data.opInsertBefore(newop,indop);
@@ -6517,8 +6517,12 @@ void AddTreeState::buildTree(void)
// Create PTRADD portion of operation
if (multNode != (Varnode *)0) {
newop = data.newOpBefore(baseOp,CPUI_PTRADD,ptr,multNode,data.newConstant(ptrsize,size));
if (ptr->getType()->needsResolution())
data.inheritResolution(ptr->getType(),newop, 0, baseOp, baseSlot);
if (ptr->getType()->needsResolution()) {
if (((TypePointer *)ptr->getType())->getPtrTo()->getSize() == baseType->getSize())
data.forceFacingType(ptr->getType(),-1,newop, 0); // Force type not to resolve before the index is applied
else
data.inheritUnionField(ptr->getType(), newop, 0, baseOp, baseSlot);
}
if (data.isTypeRecoveryExceeded())
assignPropagatedType(newop);
multNode = newop->getOut();
@@ -6530,7 +6534,7 @@ void AddTreeState::buildTree(void)
if (isSubtype) {
newop = data.newOpBefore(baseOp,CPUI_PTRSUB,multNode,data.newConstant(ptrsize,offset));
if (multNode->getType()->needsResolution())
data.inheritResolution(multNode->getType(),newop, 0, baseOp, baseSlot);
data.inheritUnionField(multNode->getType(),newop, 0, baseOp, baseSlot);
if (data.isTypeRecoveryExceeded())
assignPropagatedType(newop);
if (size != 0)
@@ -6731,7 +6735,7 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data)
// Create pointer up to parent
PcodeOp *newop = data.newOpBefore(op,CPUI_PTRSUB,ptrVn,data.newConstant(ptrVn->getSize(),offset));
if (ptrVn->getType()->needsResolution())
data.inheritResolution(ptrVn->getType(),newop, 0, op, 1);
data.inheritUnionField(ptrVn->getType(),newop, 0, op, 1);
newop->setStopTypePropagation();
if (newoff != 0) {
// Add newoff in to get back to zero total offset
@@ -6769,7 +6773,7 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data)
PcodeOp *newop = data.newOpBefore(op,CPUI_PTRSUB,ptrVn,data.newConstant(ptrVn->getSize(),0));
if (ptrVn->getType()->needsResolution())
data.inheritResolution(ptrVn->getType(),newop, 0, op, 1);
data.inheritUnionField(ptrVn->getType(),newop, 0, op, 1);
newop->setStopTypePropagation();
data.opSetInput(op,newop->getOut(),1);
return 1;
@@ -7100,10 +7104,11 @@ int8 RulePtrsubUndo::removeLocalAdds(Varnode *vn,Funcdata &data)
{
int8 extra = 0;
PcodeOp *op = vn->loneDescend();
Varnode *nextVn = vn;
while(op != (PcodeOp *)0) {
OpCode opc = op->code();
if (opc == CPUI_INT_ADD) {
int4 slot = op->getSlot(vn);
int4 slot = op->getSlot(nextVn);
if (slot == 0 && op->getIn(1)->isConstant()) {
extra += (int8)op->getIn(1)->getOffset();
data.opRemoveInput(op, 1);
@@ -7120,7 +7125,7 @@ int8 RulePtrsubUndo::removeLocalAdds(Varnode *vn,Funcdata &data)
data.opSetOpcode(op, CPUI_COPY);
}
else if (opc == CPUI_PTRADD) {
if (op->getIn(0) != vn) break;
if (op->getIn(0) != nextVn) break;
// The PTRADD should be converted to an INT_ADD or COPY
// as it is associated with the invalid PTRSUB
int8 ptraddmult = op->getIn(2)->getOffset();
@@ -7139,9 +7144,11 @@ int8 RulePtrsubUndo::removeLocalAdds(Varnode *vn,Funcdata &data)
else {
break;
}
vn = op->getOut();
op = vn->loneDescend();
nextVn = op->getOut();
op = nextVn->loneDescend();
}
if (nextVn != vn)
vn->updateType(nextVn->getType());
return extra;
}
@@ -7561,7 +7568,7 @@ bool RulePieceStructure::convertZextToPiece(PcodeOp *zext,Datatype *ct,int4 offs
data.opSetOpcode(zext, CPUI_PIECE);
data.opInsertInput(zext, zerovn, 0);
if (invn->getType()->needsResolution())
data.inheritResolution(invn->getType(), zext, 1, zext, 0); // Transfer invn's resolution to slot 1
data.inheritUnionField(invn->getType(), zext, 1, zext, 0); // Transfer invn's resolution to slot 1
return true;
}
@@ -7692,7 +7699,7 @@ int4 RulePieceStructure::applyOp(PcodeOp *op,Funcdata &data)
data.opInsertBefore(copyOp, node.getOp());
if (vn->getType()->needsResolution()) {
// Inherit PIECE's read resolution for COPY's read
data.inheritResolution(vn->getType(), copyOp, 0, node.getOp(), node.getSlot());
data.inheritUnionField(vn->getType(), copyOp, 0, node.getOp(), node.getSlot());
}
if (newType->needsResolution()) {
newType->resolveInFlow(copyOp, -1); // If the piece represents part of a union, resolve it
@@ -2141,7 +2141,7 @@ bool SplitDatatype::RootPointer::backUpPointer(Datatype *impliedBase)
/// \param op is the LOAD or STORE
/// \param valueType is the specific data-type to match
/// \return \b true if the root pointer is found
bool SplitDatatype::RootPointer::find(PcodeOp *op,Datatype *valueType)
bool SplitDatatype::RootPointer::find(PcodeOp *op,Datatype *valueType,ResolveCache &resolver)
{
Datatype *impliedBase = (Datatype *)0;
@@ -2151,12 +2151,14 @@ bool SplitDatatype::RootPointer::find(PcodeOp *op,Datatype *valueType)
valueType = ((TypeArray *)valueType)->getBase();
impliedBase = valueType; // we allow an implied array (pointer to element) as a match
}
int4 key = (op->code() == CPUI_LOAD) ? 0 : 1;
loadStore = op;
baseOffset = 0;
firstPointer = pointer = op->getIn(1);
Datatype *ct = pointer->getTypeReadFacing(op);
if (ct->getMetatype() != TYPE_PTR)
return false;
resolver.addResolution(key, pointer->getType(), op, 1);
ptrType = (TypePointer *)ct;
if (ptrType->getPtrTo() != valueType) {
if (impliedBase != (Datatype *)0)
@@ -2169,8 +2171,10 @@ bool SplitDatatype::RootPointer::find(PcodeOp *op,Datatype *valueType)
// The required pointer is found. We try to back up to pointers to containing structures or arrays
for(int4 i=0;i<3;++i) {
if (pointer->isAddrTied() || pointer->loneDescend() == (PcodeOp *)0) break;
Varnode *lastVn = pointer;
if (!backUpPointer(impliedBase))
break;
resolver.addResolution(key, pointer->getType(), lastVn->getDef(), 0);
}
return true;
}
@@ -2639,9 +2643,15 @@ void SplitDatatype::buildPointers(Varnode *rootVn,TypePointer *ptrType,int4 base
newOff = 0;
}
}
if (tmpType == newType || tmpType->getMetatype() == TYPE_ARRAY) {
Datatype *resType;
if (newType->needsResolution())
resType = resolver.resolve(isInput ? 0 : 1, newType);
else
resType = newType;
if (tmpType == resType || tmpType->getMetatype() == TYPE_ARRAY) {
int8 finalOffset = curOff - newOff;
int4 sz = newType->getSize(); // Element size in bytes
int4 sz = resType->getSize(); // Element size in bytes
finalOffset = finalOffset / sz; // Number of elements
sz = AddrSpace::byteToAddressInt(sz, ptrType->getWordSize());
newOp = data.newOp(3,followOp->getAddr());
@@ -2660,11 +2670,12 @@ void SplitDatatype::buildPointers(Varnode *rootVn,TypePointer *ptrType,int4 base
data.opSetInput(newOp, inPtr, 0);
data.opSetInput(newOp, data.newConstant(inPtr->getSize(), finalOffset), 1);
}
resolver.inheritResolution(isInput ? 0 : 1, inPtr, newOp, 0);
inPtr = data.newUniqueOut(inPtr->getSize(), newOp);
Datatype *tmpPtr = types->getTypePointerStripArray(ptrType->getSize(), newType, ptrType->getWordSize());
inPtr->updateType(tmpPtr);
data.opInsertBefore(newOp, followOp);
tmpType = newType;
tmpType = resType;
curOff = newOff;
} while(tmpType->getSize() > matchType->getSize());
ptrVarnodes.push_back(inPtr);
@@ -2699,7 +2710,7 @@ bool SplitDatatype::isArithmeticOutput(Varnode *vn)
}
SplitDatatype::SplitDatatype(Funcdata &func)
: data(func)
: data(func), resolver(func)
{
Architecture *glb = func.getArch();
types = glb->types;
@@ -2729,6 +2740,8 @@ bool SplitDatatype::splitCopy(PcodeOp *copyOp,Datatype *inType,Datatype *outType
return false;
vector<Varnode *> inVarnodes;
vector<Varnode *> outVarnodes;
Datatype *unresOutType = outVn->getType();
resolver.addResolution(0,unresOutType, copyOp, -1);
if (inVn->isConstant())
buildInConstants(inVn,inVarnodes,outVn->getSpace()->isBigEndian());
else
@@ -2741,6 +2754,7 @@ bool SplitDatatype::splitCopy(PcodeOp *copyOp,Datatype *inType,Datatype *outType
data.opSetInput(newCopyOp,inVarnodes[i],0);
data.opSetOutput(newCopyOp,outVarnodes[i]);
data.opInsertBefore(newCopyOp, copyOp);
resolver.inheritResolution(0,unresOutType, dataTypePieces[i].offset, outVarnodes[i], newCopyOp, -1);
}
data.opDestroy(copyOp);
return true;
@@ -2776,7 +2790,7 @@ bool SplitDatatype::splitLoad(PcodeOp *loadOp,Datatype *inType)
if (isArithmeticInput(outVn)) // Sanity check on output
return false;
RootPointer root;
if (!root.find(loadOp,inType))
if (!root.find(loadOp,inType,resolver))
return false;
vector<Varnode *> ptrVarnodes;
vector<Varnode *> outVarnodes;
@@ -2839,12 +2853,12 @@ bool SplitDatatype::splitStore(PcodeOp *storeOp,Datatype *outType)
return false;
RootPointer storeRoot;
if (!storeRoot.find(storeOp,outType))
if (!storeRoot.find(storeOp,outType,resolver))
return false;
RootPointer loadRoot;
if (loadOp != (PcodeOp *)0) {
if (!loadRoot.find(loadOp,inType))
if (!loadRoot.find(loadOp,inType,resolver))
return false;
}
@@ -20,6 +20,7 @@
#include "ruleaction.hh"
#include "transform.hh"
#include "unionresolve.hh"
namespace ghidra {
@@ -277,13 +278,14 @@ class SplitDatatype {
int4 baseOffset; ///< Offset of the LOAD or STORE relative to root pointer
bool backUpPointer(Datatype *impliedBase); ///< Follow flow of \b pointer back thru INT_ADD or PTRSUB
public:
bool find(PcodeOp *op,Datatype *valueType); ///< Locate root pointer for underlying LOAD or STORE
bool find(PcodeOp *op,Datatype *valueType,ResolveCache &resolver); ///< Locate root pointer for underlying LOAD or STORE
void duplicateToTemp(Funcdata &data,PcodeOp *followOp); ///< COPY the root varnode into a temp register
void freePointerChain(Funcdata &data); ///< Remove unused pointer calculations
};
Funcdata &data; ///< The containing function
TypeFactory *types; ///< The data-type container
vector<Component> dataTypePieces; ///< Sequence of all data-type pairs being copied
ResolveCache resolver; ///< Resolved data-types encountered
bool splitStructures; ///< Whether or not structures should be split
bool splitArrays; ///< Whether or not arrays should be split
bool isLoadStore; ///< True if trying to split LOAD or STORE
@@ -1317,9 +1317,15 @@ Datatype *TypePointer::resolveInFlow(PcodeOp *op,int4 slot)
if (ptrto->getMetatype() == TYPE_UNION) {
Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(this,op,slot);
if (res != (ResolvedUnion*)0)
if (res != (ResolvedUnion *)0)
return res->getDatatype();
ScoreUnionFields scoreFields(*fd->getArch()->types,this,op,slot);
res = fd->getAddressBasedUnionField(this, op->getAddr(), slot);
if (res != (ResolvedUnion *)0) {
ResolvedUnion resolve(this,res->getFieldNum(),*fd->getArch()->types);
fd->setUnionField(this,op,slot,resolve);
return resolve.getDatatype();
}
ScoreUnionFields scoreFields(*fd,this,op,slot);
fd->setUnionField(this,op,slot,scoreFields.getResult());
return scoreFields.getResult().getDatatype();
}
@@ -1338,6 +1344,15 @@ Datatype* TypePointer::findResolve(const PcodeOp *op,int4 slot)
return this;
}
int4 TypePointer::findCompatibleResolve(Datatype *ct) const
{
if (ct->getMetatype() == TYPE_PTR) {
return ptrto->findCompatibleResolve(((TypePointer *)ct)->ptrto);
}
return -1;
}
void TypeArray::printRaw(ostream &s) const
{
@@ -2530,7 +2545,13 @@ Datatype *TypeUnion::resolveInFlow(PcodeOp *op,int4 slot)
const ResolvedUnion *res = fd->getUnionField(this, op, slot);
if (res != (ResolvedUnion *)0)
return res->getDatatype();
ScoreUnionFields scoreFields(*fd->getArch()->types,this,op,slot);
res = fd->getAddressBasedUnionField(this, op->getAddr(), slot);
if (res != (ResolvedUnion *)0) {
ResolvedUnion resolve(this,res->getFieldNum(),*fd->getArch()->types);
fd->setUnionField(this, op, slot, *res);
return resolve.getDatatype();
}
ScoreUnionFields scoreFields(*fd,this,op,slot);
fd->setUnionField(this, op, slot, scoreFields.getResult());
return scoreFields.getResult().getDatatype();
}
@@ -2549,7 +2570,13 @@ const TypeField *TypeUnion::resolveTruncation(int8 offset,PcodeOp *op,int4 slot,
{
Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(this, op, slot);
const ResolvedUnion *res = fd->getUnionResolution(this, op, slot);
if (res == (ResolvedUnion *)0) {
res = fd->getAddressBasedUnionField(this, op->getAddr(), slot);
if (res != (ResolvedUnion *)0) {
fd->setUnionField(this, op, slot, *res);
}
}
if (res != (ResolvedUnion *)0) {
if (res->getFieldNum() >= 0) {
const TypeField *field = getField(res->getFieldNum());
@@ -2558,7 +2585,7 @@ const TypeField *TypeUnion::resolveTruncation(int8 offset,PcodeOp *op,int4 slot,
}
}
else if (op->code() == CPUI_SUBPIECE && slot == 1) { // The slot is artificial in this case
ScoreUnionFields scoreFields(*fd->getArch()->types,this,offset,op);
ScoreUnionFields scoreFields(*fd,this,offset,op);
fd->setUnionField(this, op, slot, scoreFields.getResult());
if (scoreFields.getResult().getFieldNum() >= 0) {
newoff = 0;
@@ -2566,7 +2593,7 @@ const TypeField *TypeUnion::resolveTruncation(int8 offset,PcodeOp *op,int4 slot,
}
}
else {
ScoreUnionFields scoreFields(*fd->getArch()->types,this,offset,op,slot);
ScoreUnionFields scoreFields(*fd,this,offset,op,slot);
fd->setUnionField(this, op, slot, scoreFields.getResult());
if (scoreFields.getResult().getFieldNum() >= 0) {
const TypeField *field = getField(scoreFields.getResult().getFieldNum());
@@ -2588,7 +2615,7 @@ const TypeField *TypeUnion::findTruncation(int8 offset,int4 sz,const PcodeOp *op
{
// No new scoring is done, but if a cached result is available, return it.
const Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(this, op, slot);
const ResolvedUnion *res = fd->getUnionResolution(this, op, slot);
if (res != (ResolvedUnion *)0 && res->getFieldNum() >= 0) {
const TypeField *field = getField(res->getFieldNum());
newoff = offset - field->offset;
@@ -2908,38 +2935,50 @@ void TypePartialUnion::encode(Encoder &encoder) const
Datatype *TypePartialUnion::resolveInFlow(PcodeOp *op,int4 slot)
{
Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(this, op, slot);
if (res != (ResolvedUnion *)0)
return res->getDatatype();
Datatype *curType = container;
int8 curOff = offset;
while(curType != (Datatype *)0 && curType->getSize() > size) {
if (curType->getMetatype() == TYPE_UNION) {
const TypeField *field = curType->resolveTruncation(curOff, op, slot, curOff);
curType = (field == (const TypeField *)0) ? (Datatype *)0 : field->type;
if (curType->getMetatype() == TYPE_PARTIALUNION) {
TypePartialUnion *curPartial = (TypePartialUnion *)curType;
curOff += curPartial->getOffset();
int8 newOff;
const TypeField *field = curPartial->getParentUnion()->resolveTruncation(curOff, op, slot, newOff);
Architecture *glb = op->getParent()->getFuncdata()->getArch();
curType = (Datatype *)0;
if (field != (const TypeField *)0)
curType = glb->types->getExactPiece(field->type, curOff, size);
curOff = 0;
}
else {
curType = curType->getSubType(curOff, &curOff);
else if (curType->getMetatype() == TYPE_UNION) {
const TypeField *field = curType->resolveTruncation(curOff, op, slot, curOff);
Architecture *glb = op->getParent()->getFuncdata()->getArch();
curType = (Datatype *)0;
if (field != (const TypeField *)0)
curType = glb->types->getExactPiece(field->type, curOff, size);
curOff = 0;
}
else { // Should never reach here
curType = (Datatype *)0;
break;
}
}
if (curType != (Datatype *)0 && curType->getSize() == size)
return curType;
return stripped;
if (curType == (Datatype *)0 || curType->getSize() != size)
curType = stripped;
fd->updateUnionField(this, op, slot, curType);
return curType;
}
Datatype* TypePartialUnion::findResolve(const PcodeOp *op,int4 slot)
{
Datatype *curType = container;
int8 curOff = offset;
while(curType != (Datatype *)0 && curType->getSize() > size) {
if (curType->getMetatype() == TYPE_UNION) {
Datatype *newType = curType->findResolve(op, slot);
curType = (newType == curType) ? (Datatype *)0 : newType;
}
else {
curType = curType->getSubType(curOff, &curOff);
}
}
if (curType != (Datatype *)0 && curType->getSize() == size)
return curType;
const Funcdata *fd = op->getParent()->getFuncdata();
const ResolvedUnion *res = fd->getUnionField(this, op, slot);
if (res != (ResolvedUnion *)0)
return res->getDatatype();
return stripped;
}
@@ -4535,9 +4574,6 @@ Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size)
}
if (ct->getSize() == size)
return ct; // Perfect size match
if (ct->getMetatype() == TYPE_UNION) {
return getTypePartialUnion((TypeUnion *)ct, curOff, size);
}
lastType = ct;
lastOff = curOff;
ct = ct->getSubType(curOff,&curOff);
@@ -4547,6 +4583,12 @@ Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size)
type_metatype meta = lastType->getMetatype();
if (meta == TYPE_STRUCT || meta == TYPE_ARRAY || meta == TYPE_PARTIALSTRUCT)
return getTypePartialStruct(lastType, lastOff, size);
else if (meta == TYPE_UNION)
return getTypePartialUnion((TypeUnion *)lastType, lastOff, size);
else if (meta == TYPE_PARTIALUNION) { // Truncate to smaller partial union
TypePartialUnion *partial = (TypePartialUnion *)lastType;
return getTypePartialUnion(partial->getParentUnion(),lastOff + partial->getOffset(), size);
}
else if (lastType->isEnumType() && !lastType->hasStripped())
return getTypePartialEnum((TypeEnum *)lastType, lastOff, size);
}
@@ -293,7 +293,7 @@ public:
virtual int4 findCompatibleResolve(Datatype *ct) const; ///< Find a resolution compatible with the given data-type
virtual const TypeField *resolveTruncation(int8 offset,PcodeOp *op,int4 slot,int8 &newoff);
int4 typeOrder(const Datatype &op) const { if (this==&op) return 0; return compare(op,10); } ///< Order this with -op- datatype
int4 typeOrderBool(const Datatype &op) const; ///< Order \b this with -op-, treating \e bool data-type as special
int4 typeOrderFormal(const Datatype &op) const; ///< Order \b this with \b op for selecting a formal high-level data-type
void encodeRef(Encoder &encoder) const; ///< Encode a reference of \b this to a stream
bool isPieceStructured(void) const; ///< Does \b this data-type consist of separate pieces?
bool isPrimitiveWhole(void) const; ///< Is \b this made up of a single primitive
@@ -479,6 +479,7 @@ public:
virtual bool isPtrsubMatching(int8 off,int8 extra,int8 multiplier) const;
virtual Datatype *resolveInFlow(PcodeOp *op,int4 slot);
virtual Datatype* findResolve(const PcodeOp *op,int4 slot);
virtual int4 findCompatibleResolve(Datatype *ct) const; ///< Find a resolution compatible with the given data-type
};
/// \brief Datatype object representing an array of elements
@@ -978,17 +979,19 @@ inline uint8 Datatype::getUnsizedId(void) const
return id;
}
/// Order data-types, with special handling of the \e bool data-type. Data-types are compared
/// using the normal ordering, but \e bool is ordered after all other data-types. A return value
/// of 0 indicates the data-types are the same, -1 indicates that \b this is prefered (ordered earlier),
/// Order data-types, preferring the most specialized, except deemphasize \e partial data-types,
/// which can't be formal, and \e bool, which can be over specialized. A return value
/// of 0 indicates the data-types are the same, -1 indicates that \b this is preferred (ordered earlier),
/// and 1 indicates \b this is ordered later.
/// \param op is the other data-type to compare with \b this
/// \return -1, 0, or 1
inline int4 Datatype::typeOrderBool(const Datatype &op) const
inline int4 Datatype::typeOrderFormal(const Datatype &op) const
{
if (this == &op) return 0;
if (metatype == TYPE_BOOL) return 1; // Never prefer bool over other data-types
if (metatype == TYPE_PARTIALUNION) return 1; // Prefer partials the least
if (op.metatype == TYPE_PARTIALUNION) return -1;
if (metatype == TYPE_BOOL) return 1; // Prefer bool less than integers
if (op.metatype == TYPE_BOOL) return -1;
return compare(op,10);
}
File diff suppressed because it is too large Load Diff
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -39,16 +39,19 @@ namespace ghidra {
class ResolvedUnion {
friend class ScoreUnionFields;
Datatype *resolve; ///< The resolved data-type
Datatype *baseType; ///< Union or Structure being resolved
Datatype *baseType; ///< Union or Structure being resolved (pointers and partials stripped)
int4 fieldNum; ///< Index of field referenced by \b resolve
bool lock; ///< If \b true, resolution cannot be overridden
public:
ResolvedUnion(Datatype *parent); ///< Construct a data-type that resolves to itself
ResolvedUnion(Datatype *parent,int4 fldNum,TypeFactory &typegrp); ///< Construct a reference to a field
ResolvedUnion(Datatype *unresType); ///< Construct a data-type that resolves to itself
ResolvedUnion(Datatype *unresType,int4 fldNum,TypeFactory &typegrp); ///< Construct a resolution with a specific field number
ResolvedUnion(const ResolvedUnion &op,Datatype *res); ///< Copy constructor with updated resolve data-type
Datatype *getDatatype(void) const { return resolve; } ///< Get the resolved data-type
Datatype *getBase(void) const { return baseType; } ///< Get the union or structure being referenced
int4 getFieldNum(void) const { return fieldNum; } ///< Get the index of the resolved field or -1
bool isLocked(void) const { return lock; } ///< Is \b this locked against overrides
bool isLocked(void) const { return lock; } ///< Return \b true if the field resolution is locked
bool update(const ResolvedUnion &op); ///< Update a resolution with a new data-type
void setResolve(Datatype *res) { resolve = res; } ///< Update the resolution data-type (without changing field resolution)
void setLock(bool val) { lock = val; } ///< Set whether \b this resolution is locked against overrides
};
@@ -62,7 +65,8 @@ class ResolveEdge {
uintm opTime; ///< Id of PcodeOp edge
int4 encoding; ///< Encoding of the slot and pointer-ness
public:
ResolveEdge(const Datatype *parent,const PcodeOp *op,int4 slot); ///< Construct from components
ResolveEdge(const Datatype *unresType,const PcodeOp *op,int4 slot); ///< Construct from components
ResolveEdge(const Datatype *unresType,const Address &addr,int4 slot); ///< Construct address based resolve
bool operator<(const ResolveEdge &op2) const; ///< Compare two edges
};
@@ -92,7 +96,7 @@ class ScoreUnionFields {
PcodeOp *op; ///< The PcodeOp reading the Varnode (or null)
int4 inslot; ///< The slot reading the Varnode (or -1)
dir_type direction; ///< Direction to push fit. 0=down 1=up
bool array; ///< Field can be accessed as an array
int4 maxLength; ///< Maximum byte offset that can be added to \b fitType as a pointer
Datatype *fitType; ///< The putative data-type of the Varnode
int4 scoreIndex; ///< The original field being scored by \b this trial
public:
@@ -102,18 +106,18 @@ class ScoreUnionFields {
/// \param slot is the input slot being read
/// \param ct is the trial data-type to fit
/// \param index is the scoring index
/// \param isArray is \b true if the data-type to fit is a pointer to an array
Trial(PcodeOp *o,int4 slot,Datatype *ct,int4 index,bool isArray) {
op = o; inslot = slot; direction = fit_down; fitType = ct; scoreIndex = index; vn = o->getIn(slot); array=isArray; }
/// \param max is the biggest offset that can be added to this data-type as a pointer (or 0 if there is no known restriction)
Trial(PcodeOp *o,int4 slot,Datatype *ct,int4 index,int4 max) {
op = o; inslot = slot; direction = fit_down; fitType = ct; scoreIndex = index; vn = o->getIn(slot); maxLength = max; }
/// \brief Construct an upward trial for a Varnode
///
/// \param v is the Varnode to fit
/// \param ct is the trial data-type to fit
/// \param index is the scoring index
/// \param isArray is \b true if the data-type to fit is a pointer to an array
Trial(Varnode *v,Datatype *ct,int4 index,bool isArray) {
vn = v; op = (PcodeOp *)0; inslot=-1; direction = fit_up; fitType = ct; scoreIndex = index; array=isArray; }
/// \param max is the biggest offset that can be added to this data-type as a pointer (or 0 if there is no known restriction)
Trial(Varnode *v,Datatype *ct,int4 index,int4 max) {
vn = v; op = (PcodeOp *)0; inslot=-1; direction = fit_up; fitType = ct; scoreIndex = index; maxLength = max; }
};
/// \brief A mark accumulated when a given Varnode is visited with a specific field index
@@ -133,6 +137,7 @@ class ScoreUnionFields {
return (index < op2.index);
}
};
Funcdata &data; ///< Function containing data-flow being scored
TypeFactory &typegrp; ///< The factory containing data-types
vector<int4> scores; ///< Score for each field, indexed by fieldNum + 1 (whole union is index=0)
vector<Datatype *> fields; ///< Field corresponding to each score
@@ -147,11 +152,11 @@ class ScoreUnionFields {
bool testArrayArithmetic(PcodeOp *op,int4 inslot); ///< Check if given PcodeOp is operating on array with union elements
bool testSimpleCases(PcodeOp *op,int4 inslot,Datatype *parent); ///< Preliminary checks before doing full scoring
int4 scoreLockedType(Datatype *ct,Datatype *lockType); ///< Score trial data-type against a locked data-type
int4 scoreParameter(Datatype *ct,const PcodeOp *callOp,int4 paramSlot); ///< Score trial data-type against a parameter
int4 scoreReturnType(Datatype *ct,const PcodeOp *callOp); ///< Score trial data-type against return data-type of function
Datatype *derefPointer(Datatype *ct,Varnode *vn,int4 &score); ///< Score trial data-type as a pointer to LOAD/STORE
void newTrialsDown(Varnode *vn,Datatype *ct,int4 scoreIndex,bool isArray); ///< Create new trials based an reads of given Varnode
void newTrials(PcodeOp *op,int4 slot,Datatype *ct,int4 scoreIndex,bool isArray); ///< Create new trials based on given input slot
int4 scoreParameter(Datatype *ct,const FuncProto *proto,int4 paramSlot); ///< Score trial data-type against a parameter
int4 scoreReturnType(Datatype *ct,const FuncProto *proto); ///< Score trial data-type against return data-type of function
Datatype *derefPointer(const Trial &trial,Varnode *vn,int4 &score); ///< Score trial as a pointer to LOAD/STORE
void newTrialsDown(Varnode *vn,Datatype *ct,int4 scoreIndex,int4 max); ///< Create new trials based an reads of given Varnode
void newTrials(PcodeOp *op,int4 slot,Datatype *ct,int4 scoreIndex,int4 max); ///< Create new trials based on given input slot
void scoreTrialDown(const Trial &trial,bool lastLevel); ///< Try to fit the given trial following data-flow down
void scoreTrialUp(const Trial &trial,bool lastLevel); ///< Try to fit the given trial following data-flow up
Datatype *scoreTruncation(Datatype *ct,Varnode *vn,int4 offset,int4 scoreIndex); ///< Score a truncation in the data-flow
@@ -160,12 +165,47 @@ class ScoreUnionFields {
void computeBestIndex(void); ///< Assuming scoring is complete, compute the best index
void run(void); ///< Calculate best fitting field
public:
ScoreUnionFields(TypeFactory &tgrp,Datatype *parentType,PcodeOp *op,int4 slot);
ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 offset,PcodeOp *op);
ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 offset,PcodeOp *op,int4 slot);
ScoreUnionFields(Funcdata &fd,Datatype *parentType,PcodeOp *op,int4 slot);
ScoreUnionFields(Funcdata &fd,TypeUnion *unionType,int4 offset,PcodeOp *op);
ScoreUnionFields(Funcdata &fd,TypeUnion *unionType,int4 offset,PcodeOp *op,int4 slot);
const ResolvedUnion &getResult(void) const { return result; } ///< Get the resulting best field resolution
};
/// \brief A collection of specific union data-type and how they resolve in a given context
///
/// Union resolutions are collected before a transform via addResolution(). Then afterwards Varnodes
/// can inherit the the same resolutions via the inheritResolution() methods.
/// Resolutions for multiple contexts can stored and retrieved by providing an id for the context
/// in the \b key parameter.
class ResolveCache {
/// \brief A specific resolution of a union data-type
class Record {
public:
Datatype *baseType; ///< The union or structure needing resolution (pointers and partials stripped)
int4 fieldNum; ///< Index of the specific resolution
int4 key; ///< Context key
Record(int4 k,Datatype *dt,int4 fldNum) { baseType = dt; fieldNum = fldNum; key = k; } ///< Constructor
bool match(int4 k,Datatype *dt) const { return (key == k && baseType == dt); } ///< Does the given data-type match \b this record
};
Funcdata &data; ///< Function to which resolutions apply
list<Record> resolveList; ///< List of known resolutions
public:
ResolveCache(Funcdata &fd) : data(fd) {} ///< Constructor
void addResolution(int4 key,Datatype *dt,PcodeOp *op,int4 slot); ///< Add a specific resolution to the cache
Datatype *resolve(int4 key,Datatype *dt) const; ///< Look up the in context resolution for the given data-type
void inheritResolution(int4 key,Datatype *dt,int4 off,Varnode *vn,PcodeOp *op,int4 slot) const;
/// \brief Associate a cached resolution with a Varnode and its new read/write edge
///
/// \param key is the context id
/// \param vn is the new Varnode inheriting the resolution
/// \param op is the PcodeOp reading/writing the Varnode
/// \param slot is the read/write edge
void inheritResolution(int4 key,Varnode *vn,PcodeOp *op,int4 slot) const {
inheritResolution(key,vn->getType(),0,vn,op,slot);
}
};
/// Compare based on the data-type, the \b slot, and the PcodeOp's unique id.
/// \param op2 is the other edge to compare with \b this
/// \return \b true if \b this should be ordered before the other edge
@@ -389,7 +389,7 @@ Varnode *HighVariable::getTypeRepresentative(void) const
if (vn->isTypeLock())
rep = vn;
}
else if (0>vn->getType()->typeOrderBool(*rep->getType()))
else if (0>vn->getType()->typeOrderFormal(*rep->getType()))
rep = vn;
}
return rep;
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -47,6 +47,11 @@ public class ClangFieldToken extends ClangToken {
return offset;
}
@Override
public Varnode getVarnode() {
return op.getOutput();
}
@Override
public PcodeOp getPcodeOp() {
return op;
@@ -55,6 +55,40 @@ public class ForceUnionAction extends AbstractDecompilerAction {
// setKeyBindingData(new KeyBindingData(KeyEvent.VK_L, 0));
}
/**
* Find any union data-type associated with the token and return it. Otherwise return null.
* @param tokenAtCursor is the decompiler token
* @return the union data-type or null
*/
private static Union findUnion(ClangToken tokenAtCursor) {
if (tokenAtCursor instanceof ClangFieldToken) {
Composite composite = getCompositeDataType(tokenAtCursor);
if (composite instanceof Union) {
return (Union) composite;
}
}
Varnode vn = tokenAtCursor.getVarnode();
if (vn != null) {
DataType dt = vn.getHigh().getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Pointer) {
dt = ((Pointer) dt).getDataType();
}
else if (dt instanceof PartialUnion) {
dt = ((PartialUnion) dt).getParent();
}
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (dt instanceof Union)
return (Union) dt;
}
return null;
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
@@ -63,11 +97,7 @@ public class ForceUnionAction extends AbstractDecompilerAction {
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (!(tokenAtCursor instanceof ClangFieldToken)) {
return false;
}
Composite composite = getCompositeDataType(tokenAtCursor);
return (composite instanceof Union);
return findUnion(tokenAtCursor) != null;
}
/**
@@ -147,26 +177,39 @@ public class ForceUnionAction extends AbstractDecompilerAction {
}
return;
}
}
else {
parentDt = null;
for (accessSlot = 0; accessSlot < accessOp.getNumInputs(); ++accessSlot) {
accessVn = accessOp.getInput(accessSlot);
parentDt = typeIsUnionRelated(accessVn);
if (parentDt != null) {
break;
}
}
parentDt = typeIsUnionRelated(accessOp.getOutput()); // Its possible the output of PTRSUB is the union
if (parentDt != null) {
if (opcode == PcodeOp.SUBPIECE && accessSlot == 0 &&
!(parentDt instanceof Pointer)) {
// SUBPIECE acts directly as resolution operator
// Choose field based on output varnode, even though it isn't the union data-type
accessSlot = -1;
accessVn = accessOp.getOutput();
accessVn = accessOp.getOutput();
accessSlot = -1;
PcodeOp readOp = accessVn.getLoneDescend();
if (readOp != null && readOp.getOpcode() != PcodeOp.MULTIEQUAL &&
readOp.getOpcode() != PcodeOp.INDIRECT) {
accessOp = readOp;
accessSlot = accessOp.getSlot(accessVn); // Transfer to lone reading Op
return;
}
return;
}
accessOp = null; // Give up. Could not find union type.
return;
}
parentDt = null;
for (accessSlot = 0; accessSlot < accessOp.getNumInputs(); ++accessSlot) {
accessVn = accessOp.getInput(accessSlot);
parentDt = typeIsUnionRelated(accessVn);
if (parentDt != null) {
break;
}
}
if (parentDt != null) {
if (opcode == PcodeOp.SUBPIECE && accessSlot == 0 &&
!(parentDt instanceof Pointer)) {
// SUBPIECE acts directly as resolution operator
// Choose field based on output varnode, even though it isn't the union data-type
accessSlot = -1;
accessVn = accessOp.getOutput();
}
return;
}
accessSlot = -1;
accessVn = accessOp.getOutput();
@@ -262,7 +305,7 @@ public class ForceUnionAction extends AbstractDecompilerAction {
Program program = context.getProgram();
ClangToken tokenAtCursor = context.getTokenAtCursor();
HighFunction highFunction = context.getHighFunction();
unionDt = (Union) getCompositeDataType(tokenAtCursor);
unionDt = findUnion(tokenAtCursor);
determineFacet(tokenAtCursor);
if (accessOp == null || accessVn == null) {
Msg.showError(this, null, "Force Union failed", "Could not recover p-code op");
@@ -280,7 +323,7 @@ public class ForceUnionAction extends AbstractDecompilerAction {
int transaction = program.startTransaction("Force Union");
try {
HighFunctionDBUtil.writeUnionFacet(function, parentDt, fieldNumber, pcAddr,
dhash.getHash(), SourceType.USER_DEFINED);
dhash.getHash(), true, SourceType.USER_DEFINED);
}
catch (DuplicateNameException e) {
Msg.showError(this, null, "Force Union failed", e.getMessage());
@@ -877,42 +877,53 @@ public class HighFunctionDBUtil {
/**
* Write a union facet to the database (UnionFacetSymbol). Parameters provide the
* pieces for building the dynamic LocalVariable. This method clears out any preexisting
* union facet with the same dynamic hash and firstUseOffset.
* union facet with the same dynamic hash and firstUseOffset. The new facet can optionally
* be "address based", meaning that all reads/writes of the union at the address are
* controlled by this single facet.
* @param function is the function affected by the union facet
* @param dt is the parent data-type; a union, a pointer to a union, or a partial union
* @param fieldNum is the ordinal of the desired union field
* @param addr is the first use address of the facet
* @param hash is the dynamic hash
* @param isAddr is true if the new facet is address based
* @param source is the SourceType for the LocalVariable
* @throws InvalidInputException if the LocalVariable cannot be created
* @throws DuplicateNameException if the (auto-generated) name is used elsewhere
*/
public static void writeUnionFacet(Function function, DataType dt, int fieldNum, Address addr,
long hash, SourceType source) throws InvalidInputException, DuplicateNameException {
if (dt instanceof PartialUnion) {
dt = ((PartialUnion) dt).getParent();
}
long hash, boolean isAddr, SourceType source)
throws InvalidInputException, DuplicateNameException {
int firstUseOffset = (int) addr.subtract(function.getEntryPoint());
String symbolName = UnionFacetSymbol.buildSymbolName(fieldNum, addr);
boolean nameCollision = false;
Variable[] localVariables =
function.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER);
// Clean out any facet symbols with bad data-types
for (int i = 0; i < localVariables.length; ++i) {
Variable var = localVariables[i];
if (var.getName().startsWith(UnionFacetSymbol.BASENAME)) {
if (!UnionFacetSymbol.isUnionType(var.getDataType())) {
if (!UnionFacetSymbol.isUnionType(var.getDataType())) { // Clean out any facet symbols with bad data-types
function.removeVariable(var);
localVariables[i] = null;
}
}
else {
localVariables[i] = null; // Ignore symbols that aren't facets
}
}
String symbolName = UnionFacetSymbol.buildSymbolName(fieldNum, addr, isAddr);
Variable preexistingVar = null;
for (Variable var : localVariables) {
if (var == null) {
continue;
}
if (var.getFirstUseOffset() == firstUseOffset &&
if (isAddr && var.getFirstUseOffset() == firstUseOffset) {
if (preexistingVar == null) {
preexistingVar = var;
}
else {
function.removeVariable(var); // Address based facets override all other facets at the same address
}
}
else if (!isAddr && var.getFirstUseOffset() == firstUseOffset &&
var.getFirstStorageVarnode().getOffset() == hash) {
preexistingVar = var;
}
@@ -920,6 +931,12 @@ public class HighFunctionDBUtil {
nameCollision = true;
}
}
if (dt instanceof PartialUnion) {
dt = ((PartialUnion) dt).getParent();
}
if (isAddr && dt instanceof Pointer) {
dt = ((Pointer) dt).getDataType();
}
if (nameCollision) { // Uniquify the name if necessary
symbolName = symbolName + '_' + Integer.toHexString(DynamicHash.getComparable(hash));
}
@@ -461,8 +461,7 @@ public class LocalSymbolMap {
}
HighSymbol sym;
if (DynamicHash.getMethodFromHash(hash) > 3 && UnionFacetSymbol.isUnionType(dt)) {
int fieldNum = UnionFacetSymbol.extractFieldNumber(nm);
sym = new UnionFacetSymbol(id, nm, dt, fieldNum, func);
sym = new UnionFacetSymbol(id, nm, dt, func);
}
else {
sym = new HighSymbol(id, nm, dt, func);
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -35,11 +35,13 @@ import ghidra.program.model.data.*;
public class UnionFacetSymbol extends HighSymbol {
public static String BASENAME = "unionfacet";
private int fieldNumber; // Ordinal of field within union being selected
private boolean isAddrBased; // Controls facets for any op at the address
public UnionFacetSymbol(long uniqueId, String nm, DataType dt, int fldNum, HighFunction func) {
public UnionFacetSymbol(long uniqueId, String nm, DataType dt, HighFunction func) {
super(uniqueId, nm, dt, func);
category = 2;
fieldNumber = fldNum;
fieldNumber = extractFieldNumber(nm);
isAddrBased = extractAddressBased(nm);
}
@Override
@@ -47,6 +49,7 @@ public class UnionFacetSymbol extends HighSymbol {
encoder.openElement(ELEM_FACETSYMBOL);
encodeHeader(encoder);
encoder.writeSignedInteger(ATTRIB_FIELD, fieldNumber);
encoder.writeBool(ATTRIB_ADDRTIED, isAddrBased);
dtmanage.encodeTypeRef(encoder, type, getSize());
encoder.closeElement(ELEM_FACETSYMBOL);
}
@@ -55,11 +58,16 @@ public class UnionFacetSymbol extends HighSymbol {
* Generate an automatic symbol name, given a field number and address
* @param fldNum is the field number
* @param addr is the Address
* @param isAddr is true if the facet should be address based
* @return the name
*/
public static String buildSymbolName(int fldNum, Address addr) {
public static String buildSymbolName(int fldNum, Address addr, boolean isAddr) {
StringBuilder buffer = new StringBuilder();
buffer.append(BASENAME).append(fldNum + 1).append('_');
buffer.append(BASENAME);
if (isAddr) {
buffer.append('a');
}
buffer.append(fldNum + 1).append('_');
buffer.append(Long.toHexString(addr.getOffset()));
return buffer.toString();
}
@@ -74,11 +82,28 @@ public class UnionFacetSymbol extends HighSymbol {
if (pos < 0) {
return -1;
}
pos += BASENAME.length();
if (nm.length() > pos && nm.charAt(pos) == 'a') {
pos = pos + 1;
}
int endpos = nm.indexOf('_', pos);
if (endpos < 0) {
return -1;
}
return Integer.decode(nm.substring(pos + BASENAME.length(), endpos)) - 1;
return Integer.decode(nm.substring(pos, endpos)) - 1;
}
/**
* First character of 'a' after BASENAME indicates an address based facet
* @param nm is the symbol name
* @return true if the facet is address based
*/
public static boolean extractAddressBased(String nm) {
int pos = nm.indexOf(BASENAME);
if (pos < 0 || nm.length() <= pos) {
return false;
}
return nm.charAt(pos + BASENAME.length()) == 'a';
}
/**