diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc index 27304436d4..046f60f9d9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc @@ -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; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.hh index 5df6bf1b90..153c4d6d57 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.hh @@ -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 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 &duplist); ///< Find any duplicates of \b basePointer void findInitialStores(vector &stores); static uint8 calcAddElements(Varnode *vn,vector &nonConst,int4 maxDepth); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index f1664ac3a3..560f74b0f1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -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;inumInput();++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;inumInput();++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 { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index d974875d7d..a9390971bf 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -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); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index c6529baea5..211e7daf6f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -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); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh index ab13b02522..dd24c7a331 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh @@ -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); }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc index 81df78ca91..d4632819f4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/float.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/float.hh index 06429dc7fa..44d5255634 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/float.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/float.hh @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc index f004922e9c..6ec19de290 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc @@ -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::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::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::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::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;inumInput();++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::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::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::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::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(); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index db85597a12..90bcd18b06 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 2232a823d5..bd0b58d7db 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -1656,15 +1656,40 @@ void Funcdata::coverVarnodes(SymbolEntry *entry,vector &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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc index c416820055..c9023e8efb 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/merge.cc @@ -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); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 9b92b3e2fe..c516890c10 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc index b191cdbd6b..d1e17ee800 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc @@ -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 inVarnodes; vector 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 ptrVarnodes; vector 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; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh index 6843eb77d8..1c87c941ed 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh @@ -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 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index e3601d3a99..81cd2392fe 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -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); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index a49726b0f4..591f724c65 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -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); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc index 3076dce7a6..0d7edd1f81 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.cc @@ -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. @@ -18,62 +18,114 @@ namespace ghidra { -/// The original parent must either be a union, a partial union, a structure with a single field, +/// The original data-type must either be a union, a partial union, a structure with a single field, /// an array with a single element, or a pointer to one of these data-types. /// The object is set up initially to resolve to the parent. -/// \param parent is the original parent data-type -ResolvedUnion::ResolvedUnion(Datatype *parent) +/// \param unresType is the original unresolved data-type +ResolvedUnion::ResolvedUnion(Datatype *unresType) { - baseType = parent; + baseType = unresType; if (baseType->getMetatype() == TYPE_PTR) baseType = ((TypePointer *)baseType)->getPtrTo(); - resolve = parent; + else if (baseType->getMetatype() == TYPE_PARTIALUNION) + baseType = ((TypePartialUnion *)baseType)->getParentUnion(); + resolve = unresType; fieldNum = -1; lock = false; } -/// The original parent must be a union or structure. -/// \param parent is the original parent -/// \param fldNum is the index of the particular field to resolve to (or -1 to resolve to parent) +/// \param unresType is the data-type that needs resolution +/// \param fldNum is the index of the particular field to resolve to (or -1 to resolve to \b unresType) /// \param typegrp is a TypeFactory used to construct the resolved data-type of the field -ResolvedUnion::ResolvedUnion(Datatype *parent,int4 fldNum,TypeFactory &typegrp) +ResolvedUnion::ResolvedUnion(Datatype *unresType,int4 fldNum,TypeFactory &typegrp) { - if (parent->getMetatype() == TYPE_PARTIALUNION) - parent = ((TypePartialUnion *)parent)->getParentUnion(); - baseType = parent; + baseType = unresType; fieldNum = fldNum; lock = false; - if (fldNum < 0) - resolve = parent; - else { - if (parent->getMetatype() == TYPE_PTR) { - TypePointer *pointer = (TypePointer *)parent; - Datatype *field = pointer->getPtrTo()->getDepend(fldNum); - resolve = typegrp.getTypePointer(parent->getSize(),field,pointer->getWordSize()); + if (unresType->getMetatype() == TYPE_PARTIALUNION) { + TypePartialUnion *partial = (TypePartialUnion *)unresType; + baseType = partial->getParentUnion(); + if (fldNum < 0) + resolve = unresType->getStripped(); + else { + const TypeField *field = partial->getParentUnion()->getField(fldNum); + resolve = typegrp.getExactPiece(field->type, partial->getOffset(), partial->getSize()); + if (resolve == (Datatype *)0) + resolve = partial->getStripped(); } + } + else if (unresType->getMetatype() == TYPE_PTR) { + TypePointer *pointer = (TypePointer *)unresType; + baseType = pointer->getPtrTo(); + if (fldNum < 0) + resolve = unresType; + else { + Datatype *field = pointer->getPtrTo()->getDepend(fldNum); + resolve = typegrp.getTypePointerStripArray(unresType->getSize(),field,pointer->getWordSize()); + } + } + else { + if (fldNum < 0) + resolve = unresType; else - resolve = parent->getDepend(fldNum); + resolve = unresType->getDepend(fldNum); } } -/// \param parent is a parent data-type that needs to be resolved -/// \param op is the PcodeOp reading/writing the \b parent data-type -/// \param slot is the slot (>=0 for input, -1 for output) accessing the \b parent -ResolveEdge::ResolveEdge(const Datatype *parent,const PcodeOp *op,int4 slot) +ResolvedUnion::ResolvedUnion(const ResolvedUnion &op,Datatype *res) + +{ + baseType = op.baseType; + fieldNum = op.fieldNum; + lock = op.lock; + resolve = res; +} + +/// \param op is the new resolution +/// \return \b true if any change is made +bool ResolvedUnion::update(const ResolvedUnion &op) + +{ + if (lock && fieldNum != op.fieldNum) + return false; // Lock protects the fieldNum resolution not the resolved data-type + if (fieldNum == op.fieldNum && resolve == op.resolve) + return false; + fieldNum = op.fieldNum; + resolve = op.resolve; + return true; +} + +/// \param unresType is the data-type that needs to be resolved +/// \param op is the PcodeOp reading/writing the data-type +/// \param slot is the slot (>=0 for input, -1 for output) accessing the data-type +ResolveEdge::ResolveEdge(const Datatype *unresType,const PcodeOp *op,int4 slot) { opTime = op->getTime(); encoding = slot; - if (parent->getMetatype() == TYPE_PTR) { - typeId = ((TypePointer *)parent)->getPtrTo()->getId(); // Strip pointer + if (unresType->getMetatype() == TYPE_PTR) { + typeId = ((TypePointer *)unresType)->getPtrTo()->getId(); // Strip pointer encoding += 0x1000; // Encode the fact that a pointer is getting accessed } - else if (parent->getMetatype() == TYPE_PARTIALUNION) - typeId = ((TypePartialUnion *)parent)->getParentUnion()->getId(); + else if (unresType->getMetatype() == TYPE_PARTIALUNION) + typeId = ((TypePartialUnion *)unresType)->getParentUnion()->getId(); else - typeId = parent->getId(); + typeId = unresType->getId(); +} + +ResolveEdge::ResolveEdge(const Datatype *unresType,const Address &addr,int4 slot) + +{ + opTime = addr.getOffset(); + encoding = 0x2000; + if (unresType->getMetatype() == TYPE_PTR) + typeId = ((TypePointer *)unresType)->getPtrTo()->getId(); // Strip pointer + else if (unresType->getMetatype() == TYPE_PARTIALUNION) + typeId = ((TypePartialUnion *)unresType)->getParentUnion()->getId(); + else + typeId = unresType->getId(); } const int4 ScoreUnionFields::threshold = 256; @@ -119,7 +171,7 @@ bool ScoreUnionFields::testArrayArithmetic(PcodeOp *op,int4 inslot) bool ScoreUnionFields::testSimpleCases(PcodeOp *op,int4 inslot,Datatype *parent) { - if (op->isMarker()) + if (op->isMarker() && inslot < 0) return true; // Propagate raw union across MULTIEQUAL and INDIRECT if (parent->getMetatype() == TYPE_PTR) { if (inslot < 0) @@ -127,13 +179,7 @@ bool ScoreUnionFields::testSimpleCases(PcodeOp *op,int4 inslot,Datatype *parent) if (testArrayArithmetic(op, inslot)) return true; } - if (op->code() != CPUI_COPY) - return false; // A more complicated case - if (inslot < 0) - return false; // Generally we don't want to propagate union backward thru COPY - if (op->getOut()->isTypeLock()) - return false; // Do the full scoring - return true; // Assume we don't have to extract a field if copying + return false; } /// A trial that encounters a locked data-type does not propagate through it but scores @@ -175,20 +221,17 @@ int4 ScoreUnionFields::scoreLockedType(Datatype *ct,Datatype *lockType) return score; } -/// Look up the call-specs for the given CALL. If the inputs are locked, find the corresponding +/// For the given function prototype, if the inputs are locked, find the corresponding /// parameter and score the trial data-type against it. /// \param ct is the trial data-type -/// \param callOp is the CALL +/// \param proto is the function prototype (may be null) /// \param paramSlot is the input slot of the trial data-type /// \return the score -int4 ScoreUnionFields::scoreParameter(Datatype *ct,const PcodeOp *callOp,int4 paramSlot) +int4 ScoreUnionFields::scoreParameter(Datatype *ct,const FuncProto *proto,int4 paramSlot) { - const Funcdata *fd = callOp->getParent()->getFuncdata(); - - FuncCallSpecs *fc = fd->getCallSpecs(callOp); - if (fc != (FuncCallSpecs *)0 && fc->isInputLocked() && fc->numParams() > paramSlot) { - return scoreLockedType(ct,fc->getParam(paramSlot)->getType()); + if (proto != (const FuncProto *)0 && proto->isInputLocked() && proto->numParams() > paramSlot) { + return scoreLockedType(ct,proto->getParam(paramSlot)->getType()); } type_metatype meta = ct->getMetatype(); if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE) @@ -196,19 +239,15 @@ int4 ScoreUnionFields::scoreParameter(Datatype *ct,const PcodeOp *callOp,int4 pa return 0; } -/// Look up the call-specs for the given CALL. If the output is locked, -/// score the trial data-type against it. +/// For the given function prototype, if the output is locked, score the trial data-type against it. /// \param ct is the trial data-type -/// \param callOp is the CALL +/// \param proto is the function prototype (may be null) /// \return the score -int4 ScoreUnionFields::scoreReturnType(Datatype *ct,const PcodeOp *callOp) +int4 ScoreUnionFields::scoreReturnType(Datatype *ct,const FuncProto *proto) { - const Funcdata *fd = callOp->getParent()->getFuncdata(); - - FuncCallSpecs *fc = fd->getCallSpecs(callOp); - if (fc != (FuncCallSpecs *)0 && fc->isOutputLocked()) { - return scoreLockedType(ct,fc->getOutputType()); + if (proto != (FuncProto *)0 && proto->isOutputLocked()) { + return scoreLockedType(ct,proto->getOutputType()); } type_metatype meta = ct->getMetatype(); if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE) @@ -224,46 +263,54 @@ int4 ScoreUnionFields::scoreReturnType(Datatype *ct,const PcodeOp *callOp) /// \param vn is the Varnode holding the value being loaded or stored /// \param score is used to pass back the score /// \return the data-type of the value or null -Datatype *ScoreUnionFields::derefPointer(Datatype *ct,Varnode *vn,int4 &score) +Datatype *ScoreUnionFields::derefPointer(const Trial &trial,Varnode *vn,int4 &score) { - Datatype *resType = (Datatype *)0; score = 0; - if (ct->getMetatype() == TYPE_PTR) { - Datatype *ptrto = ((TypePointer *)ct)->getPtrTo(); - while(ptrto != (Datatype *)0 && ptrto->getSize() > vn->getSize()) { + if (trial.fitType->getMetatype() == TYPE_PTR) { + Datatype *ptrto = ((TypePointer *)trial.fitType)->getPtrTo(); + Datatype *subType = ptrto; + while(subType != (Datatype *)0 && subType->getSize() > vn->getSize()) { int8 newoff; - ptrto = ptrto->getSubType(0, &newoff); + subType = subType->getSubType(0, &newoff); } - if (ptrto != (Datatype *)0 && ptrto->getSize() == vn->getSize()) { + if (subType != (Datatype *)0 && subType->getSize() == vn->getSize()) { score = 10; - resType = ptrto; + return subType; } + if (trial.maxLength != 0 && (vn->getSize() % ptrto->getSize()) == 0) { + score = -4; // Doesn't fit but could be multiple values at once + return typegrp.getTypeArray(vn->getSize() / ptrto->getSize(), ptrto); + } + score = -5; // Score to something but it doesn't fit } else - score = -10; - return resType; + score = -10; // Derefencing something that isn't even a pointer + return (Datatype *)0; } /// If the Varnode has already been visited, no new trials are created /// \param vn is the given Varnode /// \param ct is the data-type to associate with the trial /// \param scoreIndex is the field index to score the trial against -/// \param isArray is \b true if the data-type to fit is a pointer to an array -void ScoreUnionFields::newTrialsDown(Varnode *vn,Datatype *ct,int4 scoreIndex,bool isArray) +/// \param max is the biggest offset that can be added to the data-type as a pointer +void ScoreUnionFields::newTrialsDown(Varnode *vn,Datatype *ct,int4 scoreIndex,int4 max) { VisitMark mark(vn,scoreIndex); if (!visited.insert(mark).second) return; // Already visited this Varnode if (vn->isTypeLock()) { - scores[scoreIndex] += scoreLockedType(ct, vn->getType()); - return; // Don't propagate through locked Varnode + Datatype *lockType = vn->getTypeDefFacing(); + if (!lockType->needsResolution()) { + scores[scoreIndex] += scoreLockedType(ct, lockType); + return; // Don't propagate through locked Varnode + } } list::const_iterator piter; for(piter = vn->beginDescend();piter != vn->endDescend();++piter) { PcodeOp *op = *piter; - trialNext.emplace_back(op,op->getSlot(vn),ct,scoreIndex,isArray); + trialNext.emplace_back(op,op->getSlot(vn),ct,scoreIndex,max); } } @@ -272,8 +319,8 @@ void ScoreUnionFields::newTrialsDown(Varnode *vn,Datatype *ct,int4 scoreIndex,bo /// \param slot is the index of the given input slot /// \param ct is the data-type to associate with the trial /// \param scoreIndex is the field index to score the trial against -/// \param isArray is \b true if the data-type to fit is a pointer to an array -void ScoreUnionFields::newTrials(PcodeOp *op,int4 slot,Datatype *ct,int4 scoreIndex,bool isArray) +/// \param max is the biggest offset that can be added to the data-type as a pointer +void ScoreUnionFields::newTrials(PcodeOp *op,int4 slot,Datatype *ct,int4 scoreIndex,int4 max) { Varnode *vn = op->getIn(slot); @@ -281,17 +328,20 @@ void ScoreUnionFields::newTrials(PcodeOp *op,int4 slot,Datatype *ct,int4 scoreIn if (!visited.insert(mark).second) return; // Already visited this Varnode if (vn->isTypeLock()) { - scores[scoreIndex] += scoreLockedType(ct, vn->getType()); - return; // Don't propagate through locked Varnode + Datatype *lockType = vn->getTypeReadFacing(op); + if (!lockType->needsResolution()) { + scores[scoreIndex] += scoreLockedType(ct, lockType); + return; // Don't propagate through locked Varnode + } } - trialNext.emplace_back(vn,ct,scoreIndex,isArray); // Try to fit up + trialNext.emplace_back(vn,ct,scoreIndex,max); // Try to fit up list::const_iterator iter; for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { PcodeOp *readOp = *iter; int4 inslot = readOp->getSlot(vn); if (readOp == op && inslot == slot) continue; // Don't go down PcodeOp we came from - trialNext.emplace_back(readOp,inslot,ct, scoreIndex,isArray); + trialNext.emplace_back(readOp,inslot,ct, scoreIndex,max); } } @@ -308,6 +358,7 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) if (trial.direction == Trial::fit_up) return; // Trial doesn't push in this direction Datatype *resType = (Datatype *)0; // Assume by default we don't propagate + int4 maxLength = 0; type_metatype meta = trial.fitType->getMetatype(); int4 score = 0; switch(trial.op->code()) { @@ -315,23 +366,30 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) case CPUI_MULTIEQUAL: case CPUI_INDIRECT: resType = trial.fitType; // No score, but we can propagate + maxLength = trial.maxLength; break; case CPUI_LOAD: - resType = derefPointer(trial.fitType, trial.op->getOut(), score); + resType = derefPointer(trial, trial.op->getOut(), score); break; case CPUI_STORE: if (trial.inslot == 1) { - Datatype *ptrto = derefPointer(trial.fitType,trial.op->getIn(2),score); + Datatype *ptrto = derefPointer(trial,trial.op->getIn(2),score); if (ptrto != (Datatype*)0) { if (!lastLevel) - newTrials(trial.op,2,ptrto,trial.scoreIndex,trial.array); // Propagate to value being STOREd + newTrials(trial.op,2,ptrto,trial.scoreIndex,0); // Propagate to value being STOREd } } else if (trial.inslot == 2) { if (meta == TYPE_CODE) score = -5; - else + else { + Datatype *storePtr = trial.op->getIn(1)->getTypeReadFacing(trial.op); + if (!lastLevel && storePtr->getMetatype() == TYPE_PTR) { + Datatype *ptr = typegrp.getTypePointerStripArray(storePtr->getSize(), trial.fitType, ((TypePointer *)storePtr)->getWordSize()); + newTrials(trial.op,1,ptr,trial.scoreIndex,trial.fitType->getSize()); + } score = 1; + } } break; case CPUI_CBRANCH: @@ -350,7 +408,7 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) case CPUI_CALL: case CPUI_CALLOTHER: if (trial.inslot > 0) - score = scoreParameter(trial.fitType, trial.op, trial.inslot-1); + score = scoreParameter(trial.fitType, data.getCallSpecs(trial.op), trial.inslot-1); break; case CPUI_CALLIND: if (trial.inslot == 0) { @@ -365,13 +423,11 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) } } else { - score = scoreParameter(trial.fitType, trial.op, trial.inslot-1); + score = scoreParameter(trial.fitType, data.getCallSpecs(trial.op), trial.inslot-1); } break; case CPUI_RETURN: - // We could check for locked return data-type - if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE) - score = -1; + score = scoreReturnType(trial.fitType, &data.getFuncProto()); break; case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL: @@ -429,15 +485,28 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) Varnode *vn = trial.op->getIn(1-trial.inslot); if (vn->isConstant()) { TypePointer *baseType = (TypePointer *)trial.fitType; - int8 off = vn->getOffset(); - int8 parOff; - TypePointer *par; - resType = baseType->downChain(off,par,parOff,trial.array,typegrp); - if (resType != (Datatype*)0) + int8 off = sign_extend(vn->getOffset(),vn->getSize()*8-1); + if (trial.maxLength != 0) { + if (off < 0 || off >= trial.maxLength) { + baseType = (TypePointer *)0; + score = -1; // Offset doesn't land in allowed range + } + else + maxLength = trial.maxLength; + } + while(off != 0 && baseType != (Datatype *)0) { + int8 parOff; + TypePointer *par; + baseType = baseType->downChain(off,par,parOff,true,typegrp); + } + if (baseType != (Datatype*)0) { + resType = baseType; + maxLength = baseType->getPtrTo()->getSize(); score = 5; + } } else { - if (trial.array) { + if (trial.maxLength != 0) { score = 1; int4 elSize = 1; if (vn->isWritten()) { @@ -450,8 +519,12 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) } TypePointer *baseType = (TypePointer *)trial.fitType; if (baseType->getPtrTo()->getAlignSize() == elSize) { - score = 5; + if (elSize == trial.maxLength) + score = 2; // Only one element available, but variable index + else + score = 5; resType = trial.fitType; + maxLength = trial.maxLength; } } else @@ -459,10 +532,14 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) } } } - else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) - score = -5; - else + else if (meta == TYPE_INT || meta == TYPE_UINT || meta == TYPE_UNKNOWN) { score = 1; + resType = trial.fitType; + } + else if (meta == TYPE_BOOL) + score = -3; + else // TYPE_ARRAY TYPE_STRUCT TYPE_UNION TYPE_CODE TYPE_FLOAT + score = -5; break; case CPUI_INT_2COMP: if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) @@ -636,7 +713,7 @@ void ScoreUnionFields::scoreTrialDown(const Trial &trial,bool lastLevel) } scores[trial.scoreIndex] += score; if (resType != (Datatype *)0 && !lastLevel) - newTrialsDown(trial.op->getOut(), resType, trial.scoreIndex, trial.array); + newTrialsDown(trial.op->getOut(), resType, trial.scoreIndex, maxLength); } void ScoreUnionFields::scoreTrialUp(const Trial &trial,bool lastLevel) @@ -651,6 +728,7 @@ void ScoreUnionFields::scoreTrialUp(const Trial &trial,bool lastLevel) return; // Nothing to propagate up through } Datatype *resType = (Datatype *)0; // Assume by default we don't propagate + int4 maxLength = 0; int4 newslot = 0; type_metatype meta = trial.fitType->getMetatype(); PcodeOp *def = trial.vn->getDef(); @@ -659,16 +737,18 @@ void ScoreUnionFields::scoreTrialUp(const Trial &trial,bool lastLevel) case CPUI_MULTIEQUAL: case CPUI_INDIRECT: resType = trial.fitType; // No score, but we can propagate + maxLength = trial.maxLength; newslot = 0; break; case CPUI_LOAD: - resType = typegrp.getTypePointer(def->getIn(1)->getSize(),trial.fitType,1); + resType = typegrp.getTypePointerStripArray(def->getIn(1)->getSize(),trial.fitType,1); + maxLength = trial.fitType->getSize(); newslot = 1; // No score, but we can propagate break; case CPUI_CALL: case CPUI_CALLOTHER: case CPUI_CALLIND: - score = scoreReturnType(trial.fitType, def); + score = scoreReturnType(trial.fitType, data.getCallSpecs(def)); break; case CPUI_INT_EQUAL: case CPUI_INT_NOTEQUAL: @@ -699,11 +779,19 @@ void ScoreUnionFields::scoreTrialUp(const Trial &trial,bool lastLevel) case CPUI_INT_SUB: case CPUI_PTRSUB: if (meta == TYPE_PTR) { + score = 5; + if (trial.maxLength == 0 && def->getIn(1)->isConstant()) { + Datatype *ptrto = ((TypePointer *)trial.fitType)->getPtrTo(); + if (ptrto->getAlignSize() == (int4)def->getIn(1)->getOffset()) + score += 1; + } + } + else if (meta == TYPE_INT || meta == TYPE_UINT) { score = 5; // Don't try to back up further } else if (meta == TYPE_ARRAY || meta == TYPE_STRUCT || meta == TYPE_UNION || meta == TYPE_CODE || meta == TYPE_FLOAT) score = -5; - else + else // BOOL UNKNOWN score = 1; break; case CPUI_INT_2COMP: @@ -828,7 +916,7 @@ void ScoreUnionFields::scoreTrialUp(const Trial &trial,bool lastLevel) } scores[trial.scoreIndex] += score; if (resType != (Datatype *)0 && !lastLevel) { - newTrials(def, newslot, resType, trial.scoreIndex, trial.array); + newTrials(def, newslot, resType, trial.scoreIndex, maxLength); } } @@ -869,6 +957,16 @@ Datatype *ScoreUnionFields::scoreTruncation(Datatype *ct,Varnode *vn,int4 offset break; } } + else if (ct->getMetatype() == TYPE_ARRAY) { + Datatype *elType = ct->getDepend(0); + if (elType->getAlignSize() < vn->getSize()) { + if ((curOff + vn->getSize()) % elType->getAlignSize() != 0) + score = -5; // Varnode is unaligned with array elements + else + score = 1; // Varnode covers array elements + break; + } + } ct = ct->getSubType(curOff,&curOff); } if (ct == (Datatype *)0) @@ -895,9 +993,14 @@ void ScoreUnionFields::scoreConstantFit(const Trial &trial) score = -1; const FloatFormat *format = typegrp.getArch()->translate->getFloatFormat(size); if (format != (const FloatFormat *)0) { - int4 exp = format->extractExponentCode(val); - if (exp < 7 && exp > -4) // Check for common exponent range + FloatFormat::floatclass fClass = format->getClass(val); + if (fClass == FloatFormat::zero) score = 2; + else if (fClass == FloatFormat::normalized) { + int4 exp = format->getExponent(val); + if (exp < 7 && exp > -4) // Check for common exponent range + score = 2; + } } } else if (meta == TYPE_INT || meta == TYPE_UINT || meta == TYPE_PTR) { @@ -920,6 +1023,8 @@ void ScoreUnionFields::scoreConstantFit(const Trial &trial) } } } + else if (meta == TYPE_ARRAY) + score = 0; // Could be multiple values from an array else score = -2; scores[trial.scoreIndex] += score; @@ -983,12 +1088,12 @@ void ScoreUnionFields::run(void) /// /// The data-type must either be a union or a pointer to union. /// Set up the initial set of trials based on the given data-flow edge (PcodeOp and slot). -/// \param tgrp is the TypeFactory owning the data-types +/// \param fd is the function containing the data-flow /// \param parentType is the given data-type to score /// \param op is PcodeOp of the given data-flow edge /// \param slot is slot of the given data-flow edge -ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,Datatype *parentType,PcodeOp *op,int4 slot) - : typegrp(tgrp), result(parentType) +ScoreUnionFields::ScoreUnionFields(Funcdata &fd,Datatype *parentType,PcodeOp *op,int4 slot) + : data(fd), typegrp(*fd.getArch()->types), result(parentType) { if (testSimpleCases(op, slot, parentType)) return; @@ -1002,32 +1107,38 @@ ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,Datatype *parentType,PcodeO if (vn->getSize() != parentType->getSize()) scores[0] -= 10; // Data-type does not even match size of Varnode else - trialCurrent.emplace_back(vn,parentType,0,false); + trialCurrent.emplace_back(vn,parentType,0,0); } else { vn = op->getIn(slot); if (vn->getSize() != parentType->getSize()) scores[0] -= 10; else - trialCurrent.emplace_back(op,slot,parentType,0,false); + trialCurrent.emplace_back(op,slot,parentType,0,0); } fields[0] = parentType; + OpCode opc = op->code(); + // If the varnode is not a pointer or is not used in pointer arithmetic + if (wordSize == 0 || opc == CPUI_INT_ADD || opc == CPUI_PTRADD || opc == CPUI_PTRSUB || opc == CPUI_STORE || opc == CPUI_LOAD) + scores[0] -= 1; // Without any other information, prefer a resolution from a specific field visited.insert(VisitMark(vn,0)); for(int4 i=0;igetDepend(i); - bool isArray = false; - if (wordSize != 0) { - if (fieldType->getMetatype() == TYPE_ARRAY) - isArray = true; - fieldType = typegrp.getTypePointerStripArray(parentType->getSize(),fieldType,wordSize); + int4 maxLength = 0; + if (wordSize != 0) { // If this is a pointer + if (fieldType->getMetatype() == TYPE_ARRAY) // Array gets stripped + maxLength = fieldType->getSize(); // Keep track of original array size + else if (fieldType->getSize() < result.baseType->getSize()) + maxLength = fieldType->getSize(); + fieldType = typegrp.getTypePointerStripArray(parentType->getSize(),fieldType,wordSize); // Create pointer to field type } if (vn->getSize() != fieldType->getSize()) scores[i+1] -= 10; // Data-type does not even match size of Varnode, don't create trial else if (slot < 0) { - trialCurrent.emplace_back(vn,fieldType,i+1,isArray); + trialCurrent.emplace_back(vn,fieldType,i+1,maxLength); } else { - trialCurrent.emplace_back(op,slot,fieldType,i+1,isArray); + trialCurrent.emplace_back(op,slot,fieldType,i+1,maxLength); } fields[i+1] = fieldType; visited.insert(VisitMark(vn,i+1)); @@ -1043,12 +1154,12 @@ ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,Datatype *parentType,PcodeO /// If there is a good fit, the scoring for that field recurses into the given data-flow edge. /// This is only used where there is a SUBPIECE and the base scoring indicates the whole union is /// the best match for the input. -/// \param tgrp is the TypeFactory owning the data-types +/// \param fd is the function containing the data-flow /// \param unionType is the data-type to score, which must be a TypeUnion /// \param offset is the given starting offset of the truncation /// \param op is the SUBPIECE op -ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 offset,PcodeOp *op) - :typegrp(tgrp), result(unionType) +ScoreUnionFields::ScoreUnionFields(Funcdata &fd,TypeUnion *unionType,int4 offset,PcodeOp *op) + : data(fd), typegrp(*fd.getArch()->types), result(unionType) { Varnode *vn = op->getOut(); int numFields = unionType->numDepend(); @@ -1063,26 +1174,29 @@ ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 o scores[i+1] = -10; continue; } - newTrialsDown(vn, unionField->type, i+1, false); + newTrialsDown(vn, unionField->type, i+1, 0); } trialCurrent.swap(trialNext); if (trialCurrent.size() > 1) run(); computeBestIndex(); + result.resolve = typegrp.getExactPiece(result.resolve, offset, vn->getSize()); } /// \brief Score a union data-type against data-flow, where there is an implied truncation /// /// A truncation is fit to each union field before doing the fit against data-flow, starting with /// the given PcodeOp and input slot. -/// \param tgrp is the TypeFactory owning the data-types +/// \param fd is the function containing the data-flow /// \param unionType is the data-type to score, which must be a TypeUnion /// \param offset is the given starting offset of the truncation /// \param op is the PcodeOp initially reading/writing the union /// \param slot is the -1 if the op is writing, >= 0 if reading -ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 offset,PcodeOp *op,int4 slot) - :typegrp(tgrp), result(unionType) +ScoreUnionFields::ScoreUnionFields(Funcdata &fd,TypeUnion *unionType,int4 offset,PcodeOp *op,int4 slot) + :data(fd), typegrp(*fd.getArch()->types), result(unionType) { + if (testSimpleCases(op, slot, unionType)) + return; Varnode *vn = (slot < 0) ? op->getOut() : op->getIn(slot); int numFields = unionType->numDepend(); scores.resize(numFields + 1, 0); @@ -1096,15 +1210,84 @@ ScoreUnionFields::ScoreUnionFields(TypeFactory &tgrp,TypeUnion *unionType,int4 o Datatype *ct = scoreTruncation(unionField->type,vn,offset-unionField->offset,i+1); if (ct != (Datatype *)0) { if (slot < 0) - trialCurrent.emplace_back(vn,ct,i+1,false); // Try to flow backward + trialCurrent.emplace_back(vn,ct,i+1,0); // Try to flow backward else - trialCurrent.emplace_back(op,slot,ct,i+1,false); // Flow downward + trialCurrent.emplace_back(op,slot,ct,i+1,0); // Flow downward visited.insert(VisitMark(vn,i+1)); } } if (trialCurrent.size() > 1) run(); computeBestIndex(); + result.resolve = typegrp.getExactPiece(result.resolve, offset, vn->getSize()); +} + +/// If a resolution for the data-type exists along the given edge, it is cached. Otherwise there is no change. +/// \param key is the context id associated with the resolution +/// \param dt is the data-type needing resolution +/// \param op is the PcodeOp reading/writing the resolved data-type +/// \param slot is the edge index of the resolution +void ResolveCache::addResolution(int4 key,Datatype *dt,PcodeOp *op,int4 slot) + +{ + if (!dt->needsResolution()) return; + const ResolvedUnion *res = data.getUnionResolution(dt, op, slot); + if (res == (const ResolvedUnion *)0) return; + Datatype *parent = res->getBase(); + resolveList.emplace_back(key,parent,res->getFieldNum()); +} + +/// If the resolved data-type is in \b this cache, it is returned. +/// Otherwise the original data-type is returned. +/// \param key is the context id +/// \param dt is the given data-type needing resolution +/// \return the resolved form of the data-type +Datatype *ResolveCache::resolve(int4 key,Datatype *dt) const + +{ + list::const_iterator iter; + for(iter=resolveList.begin();iter!=resolveList.end();++iter) { + if ((*iter).match(key,dt)) { + if ((*iter).fieldNum < 0) + return dt; + return dt->getDepend((*iter).fieldNum); + } + } + return dt; +} + +/// \brief Associate a cached resolution with a new Varnode and read/write edge +/// +/// The data-type \b dt can either be the current data-type of the Varnode or it +/// can be a parent data-type, with \b off corresponding to the offset of the Varnode within the parent. +/// \param key is the context id +/// \param dt is the data-type needing resolution +/// \param off is the offset within the data-type of the Varnode +/// \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 ResolveCache::inheritResolution(int4 key,Datatype *dt,int4 off,Varnode *vn,PcodeOp *op,int4 slot) const + +{ + if (!dt->needsResolution()) return; + Datatype *parent = dt; + if (parent->getMetatype() == TYPE_PARTIALUNION) + parent = ((TypePartialUnion *)parent)->getParentUnion(); + else if (parent->getMetatype() == TYPE_PTR) + parent = ((TypePointer *)parent)->getPtrTo(); + list::const_iterator iter; + for(iter=resolveList.begin();iter!=resolveList.end();++iter) { + if ((*iter).match(key,parent)) { + TypeFactory *typegrp = data.getArch()->types; + dt = typegrp->getExactPiece(dt, off, vn->getSize()); + if (dt != (Datatype *)0) { + ResolvedUnion newRes(dt,(*iter).fieldNum,*typegrp); + data.setUnionField(dt, op, slot, newRes); + vn->updateType(dt); + } + return; + } + } } } // End namespace ghidra diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.hh index 8ceca302ba..b01db68c52 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/unionresolve.hh @@ -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 scores; ///< Score for each field, indexed by fieldNum + 1 (whole union is index=0) vector 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 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index 3c955754a9..93c19fe1ec 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -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; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java index 9ae3127742..1bcfce8e45 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/ClangFieldToken.java @@ -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; diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/ForceUnionAction.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/ForceUnionAction.java index 587097562c..dde3abb63e 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/ForceUnionAction.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/decompile/actions/ForceUnionAction.java @@ -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()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java index 5e40a6390d..ca5dcf1fed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java @@ -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)); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java index 9209d212d1..516717daf5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/UnionFacetSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/UnionFacetSymbol.java index 884ca384f2..5101cbb1b6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/UnionFacetSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/UnionFacetSymbol.java @@ -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'; } /**