From 60cf8311f19ca538b7a608191b7a731dc59d0b39 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:00:42 +0000 Subject: [PATCH] GP-4095 Check for primitive data-type when triggering double precision --- .../src/decompile/cpp/coreaction.cc | 4 +- .../Decompiler/src/decompile/cpp/double.cc | 40 +++++++++++++++++-- .../Decompiler/src/decompile/cpp/double.hh | 1 + .../Decompiler/src/decompile/cpp/heritage.cc | 24 ++++++----- .../src/decompile/cpp/modelrules.cc | 4 +- .../src/decompile/cpp/modelrules.hh | 4 +- .../Decompiler/src/decompile/cpp/type.cc | 17 ++++++++ .../Decompiler/src/decompile/cpp/type.hh | 1 + 8 files changed, 76 insertions(+), 19 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index c814cccd7d..9dfd874934 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -1667,8 +1667,8 @@ int4 ActionParamDouble::apply(Funcdata &data) for(int4 i=0;igetType(); - type_metatype mt = tp->getMetatype(); - if ((mt==TYPE_ARRAY)||(mt==TYPE_STRUCT)) continue; // Not double precision objects + if (!tp->isPrimitiveWhole()) + continue; // Not double precision objects Varnode *vn = data.findVarnodeInput(tp->getSize(),param->getAddress()); if (vn == (Varnode *)0) continue; if (vn->getSize() < minDoubleSize) continue; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc index ff11d1e20c..840ed38365 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/double.cc @@ -693,6 +693,14 @@ PcodeOp *SplitVarnode::findOutExist(void) return findEarliestSplitPoint(); } +/// If the logical whole is a constant and is too big to be represented internally return \b true. +/// \return \b true if \b this is a constant and too big +bool SplitVarnode::exceedsConstPrecision(void) const + +{ + return isConstant() && (wholesize > sizeof(uintb)); +} + /// \brief Check if the values in the given Varnodes differ by the given size /// /// Return \b true, if the (possibly dynamic) value represented by the given \b vn1 plus \b size1 @@ -1543,6 +1551,8 @@ bool AddForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data return false; indoub.initPartial(in.getSize(),lo2,hi2); + if (indoub.exceedsConstPrecision()) + return false; outdoub.initPartial(in.getSize(),reslo,reshi); existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub); if (existop == (PcodeOp *)0) @@ -1636,6 +1646,8 @@ bool SubForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data return false; indoub.initPartial(in.getSize(),lo2,hi2); + if (indoub.exceedsConstPrecision()) + return false; outdoub.initPartial(in.getSize(),reslo,reshi); existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub); if (existop == (PcodeOp *)0) @@ -1757,6 +1769,8 @@ bool LogicalForm::applyRule(SplitVarnode &i,PcodeOp *lop,bool workishi,Funcdata outdoub.initPartial(in.getSize(),loop->getOut(),hiop->getOut()); indoub.initPartial(in.getSize(),lo2,hi2); + if (indoub.exceedsConstPrecision()) + return false; existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub); if (existop == (PcodeOp *)0) return false; @@ -1817,6 +1831,8 @@ bool Equal1Form::applyRule(SplitVarnode &i,PcodeOp *hop,bool workishi,Funcdata & ++iter3; in2.initPartial(in1.getSize(),lo2,hi2); + if (in2.exceedsConstPrecision()) + continue; if ((hibool->code() == CPUI_CBRANCH)&&(lobool->code()==CPUI_CBRANCH)) { // Branching form of the equal operation @@ -1958,8 +1974,10 @@ bool Equal2Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &d hixor = (PcodeOp *)0; hi2 = (Varnode *)0; if (fillOutFromOr(data)) { - SplitVarnode::replaceBoolOp(data,equalop,in,param2,equalop->code()); - return true; + if (!param2.exceedsConstPrecision()) { + SplitVarnode::replaceBoolOp(data,equalop,in,param2,equalop->code()); + return true; + } } } else { // We see an XOR @@ -1976,8 +1994,10 @@ bool Equal2Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &d if (orop->code() != CPUI_INT_OR) continue; orhislot = orop->getSlot(vn); if (fillOutFromOr(data)) { - SplitVarnode::replaceBoolOp(data,equalop,in,param2,equalop->code()); - return true; + if (!param2.exceedsConstPrecision()) { + SplitVarnode::replaceBoolOp(data,equalop,in,param2,equalop->code()); + return true; + } } } } @@ -2020,6 +2040,8 @@ bool Equal3Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &d return false; SplitVarnode in2(in.getSize(),calc_mask(in.getSize())); // Create the -1 value + if (in2.exceedsConstPrecision()) + return false; if (!SplitVarnode::prepareBoolOp(in,in2,compareop)) return false; SplitVarnode::replaceBoolOp(data,compareop,in,in2,compareop->code()); return true; @@ -2483,6 +2505,8 @@ bool LessThreeWay::applyRule(SplitVarnode &i,PcodeOp *loop,bool workishi,Funcdat if (!mapFromLow(loop)) return false; bool res = testReplace(); if (res) { + if (in2.exceedsConstPrecision()) + return false; if (hislot==0) SplitVarnode::createBoolOp(data,hilessbool,in,in2,finalopc); else @@ -2527,6 +2551,8 @@ bool LessConstForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata if (desc->code() != CPUI_CBRANCH) return false; constin.initPartial(in.getSize(),val); + if (constin.exceedsConstPrecision()) + return false; if (inslot==0) { if (SplitVarnode::prepareBoolOp(in,constin,op)) { @@ -2967,6 +2993,8 @@ bool MultForm::replace(Funcdata &data) { // We have matched a double precision multiply, now transform to logical variables outdoub.initPartial(in.getSize(),reslo,reshi); in2.initPartial(in.getSize(),lo2,hi2); + if (in2.exceedsConstPrecision()) + return false; existop = SplitVarnode::prepareBinaryOp(outdoub,in,in2); if (existop == (PcodeOp *)0) return false; @@ -3147,6 +3175,10 @@ int4 RuleDoubleIn::attemptMarking(Funcdata &data,Varnode *vn,PcodeOp *subpieceOp { Varnode *whole = subpieceOp->getIn(0); + if (whole->isTypeLock()) { + if (!whole->getType()->isPrimitiveWhole()) + return 0; // Don't mark for double precision if not a primitive type + } int4 offset = (int4)subpieceOp->getIn(1)->getOffset(); if (offset != vn->getSize()) return 0; if (offset * 2 != whole->getSize()) return 0; // Truncate exactly half diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh index 3463086523..175f6024ae 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh @@ -70,6 +70,7 @@ public: void buildHiFromWhole(Funcdata &data); ///< Rebuild the most significant piece as a CPUI_SUBPIECE of the \b whole PcodeOp *findEarliestSplitPoint(void); ///< Find the earliest definition point of the \b lo and \b hi pieces PcodeOp *findOutExist(void); ///< Find the point at which the output \b whole must exist + bool exceedsConstPrecision(void) const; ///< Check if \b this is a constant that exceeds precision limits static bool adjacentOffsets(Varnode *vn1,Varnode *vn2,uintb size1); static bool testContiguousPointers(PcodeOp *most,PcodeOp *least,PcodeOp *&first,PcodeOp *&second,AddrSpace *&spc); static bool isAddrTiedContiguous(Varnode *lo,Varnode *hi,Address &res); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc index f24fef0b65..30a8959dd3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/heritage.cc @@ -2065,11 +2065,9 @@ void Heritage::splitJoinRead(Varnode *vn,JoinRecord *joinrec) { PcodeOp *op = vn->loneDescend(); // vn isFree, so loneDescend must be non-null - bool preventConstCollapse = false; + bool isPrimitive = true; if (vn->isTypeLock()) { - type_metatype meta = vn->getType()->getMetatype(); - if (meta == TYPE_STRUCT || meta == TYPE_ARRAY) - preventConstCollapse = true; + isPrimitive = vn->getType()->isPrimitiveWhole(); } vector lastcombo; @@ -2090,10 +2088,13 @@ void Heritage::splitJoinRead(Varnode *vn,JoinRecord *joinrec) fd->opSetInput(concat,mosthalf,0); fd->opSetInput(concat,leasthalf,1); fd->opInsertBefore(concat,op); - if (preventConstCollapse) + if (isPrimitive) { + mosthalf->setPrecisHi(); // Set precision flags to trigger "double precision" rules + leasthalf->setPrecisLo(); + } + else { fd->opMarkNoCollapse(concat); - mosthalf->setPrecisHi(); // Set precision flags to trigger "double precision" rules - leasthalf->setPrecisLo(); + } op = concat; // Keep -op- as the earliest op in the concatenation construction } @@ -2118,6 +2119,9 @@ void Heritage::splitJoinWrite(Varnode *vn,JoinRecord *joinrec) { PcodeOp *op = vn->getDef(); // vn cannot be free, either it has def, or it is input BlockBasic *bb = (BlockBasic *)fd->getBasicBlocks().getBlock(0); + bool isPrimitive = true; + if (vn->isTypeLock()) + isPrimitive = vn->getType()->isPrimitiveWhole(); vector lastcombo; vector nextlev; @@ -2151,8 +2155,10 @@ void Heritage::splitJoinWrite(Varnode *vn,JoinRecord *joinrec) fd->opSetInput(split,curvn,0); fd->opSetInput(split,fd->newConstant(4,0),1); fd->opInsertAfter(split,op); - mosthalf->setPrecisHi(); // Make sure we set the precision flags to trigger "double precision" rules - leasthalf->setPrecisLo(); + if (isPrimitive) { + mosthalf->setPrecisHi(); // Make sure we set the precision flags to trigger "double precision" rules + leasthalf->setPrecisLo(); + } op = split; // Keep -op- as the latest op in the split construction } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc index 961d008c05..11012d5192 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.cc @@ -337,7 +337,7 @@ void DatatypeMatchFilter::decode(Decoder &decoder) /// /// Allocate the action object corresponding to the element and configure it. /// If the next element is not an action, throw an exception. -/// \param parser is the stream decoder +/// \param decoder is the stream decoder /// \param res is the resource set for the new action /// \return the new action AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandard *res) @@ -377,7 +377,7 @@ AssignAction *AssignAction::decodeAction(Decoder &decoder,const ParamListStandar /// /// Allocate the sideeffect object corresponding to the element and configure it. /// If the next element is not a sideeffect, throw an exception. -/// \param parser is the stream decoder +/// \param decoder is the stream decoder /// \param res is the resource set for the new sideeffect /// \return the new sideeffect AssignAction *AssignAction::decodeSideeffect(Decoder &decoder,const ParamListStandard *res) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh index 53c3e21769..3614b91bf4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/modelrules.hh @@ -286,7 +286,7 @@ class MultiSlotAssign : public AssignAction { void initializeEntries(void); ///< Cache specific ParamEntry needed by the action public: MultiSlotAssign(const ParamListStandard *res); ///< Constructor for use with decode - MultiSlotAssign(type_class store,bool stack,bool mostSig,bool align,bool justRight,const ParamListStandard *res); + MultiSlotAssign(type_class store,bool stack,bool mostSig,bool align,bool justRight,const ParamListStandard *res); ///< Constructor virtual AssignAction *clone(const ParamListStandard *newResource) const { return new MultiSlotAssign(resourceType,consumeFromStack,consumeMostSig,enforceAlignment,justifyRight,newResource); } virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist, @@ -304,7 +304,7 @@ class MultiMemberAssign : public AssignAction { bool consumeFromStack; ///< True if resources should be consumed from the stack bool consumeMostSig; ///< True if resources are consumed starting with most significant bytes public: - MultiMemberAssign(type_class store,bool stack,bool mostSig,const ParamListStandard *res); + MultiMemberAssign(type_class store,bool stack,bool mostSig,const ParamListStandard *res); ///< Constructor virtual AssignAction *clone(const ParamListStandard *newResource) const { return new MultiMemberAssign(resourceType,consumeFromStack,consumeMostSig,newResource); } virtual uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlist, diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 32f8348343..df6038d095 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -476,6 +476,23 @@ void Datatype::encodeRef(Encoder &encoder) const encode(encoder); } +/// If \b this has no component data-types, return \b true. +/// If \b this has only a single primitive component filling the whole data-type, also return \b true. +/// \return \b true if \b this data-type is made up of a single primitive +bool Datatype::isPrimitiveWhole(void) const + +{ + if (!isPieceStructured()) return true; + if (metatype == TYPE_ARRAY || metatype == TYPE_STRUCT) { + if (numDepend() > 0) { + Datatype *component = getDepend(0); + if (component->getSize() == getSize()) + return component->isPrimitiveWhole(); + } + } + return false; +} + /// Called only if the \b typedefImm field is non-null. Encode the data-type to the /// stream as a simple \ element including only the names and ids of \b this and /// the data-type it typedefs. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index d2061fcd7f..b1031829de 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -278,6 +278,7 @@ public: int4 typeOrderBool(const Datatype &op) const; ///< Order \b this with -op-, treating \e bool data-type as special 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 static uint4 encodeIntegerFormat(const string &val); static string decodeIntegerFormat(uint4 val); };