diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index 971b72967e..ccb91abf11 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -15,6 +15,7 @@ src/decompile/cpp/Makefile||GHIDRA||||END| src/decompile/datatests/boolless.xml||GHIDRA||||END| src/decompile/datatests/ccmp.xml||GHIDRA||||END| src/decompile/datatests/concat.xml||GHIDRA||||END| +src/decompile/datatests/concatsplit.xml||GHIDRA||||END| src/decompile/datatests/condconst.xml||GHIDRA||||END| src/decompile/datatests/condmulti.xml||GHIDRA||||END| src/decompile/datatests/convert.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc index 14d63871fc..da27695088 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/cast.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/cast.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. @@ -46,13 +46,14 @@ bool CastStrategy::markExplicitUnsigned(PcodeOp *op,int4 slot) const if (!vn->isConstant()) return false; Datatype *dt = vn->getHighTypeReadFacing(op); type_metatype meta = dt->getMetatype(); - if ((meta != TYPE_UINT)&&(meta != TYPE_UNKNOWN)) return false; + if (meta != TYPE_UINT && meta != TYPE_UNKNOWN && meta != TYPE_PARTIALSTRUCT && meta != TYPE_PARTIALUNION) + return false; if (dt->isCharPrint()) return false; if (dt->isEnumType()) return false; if ((op->numInput() == 2) && !inheritsFirstParamOnly) { Varnode *firstvn = op->getIn(1-slot); meta = firstvn->getHighTypeReadFacing(op)->getMetatype(); - if ((meta == TYPE_UINT)||(meta == TYPE_UNKNOWN)) + if (meta == TYPE_UINT || meta == TYPE_UNKNOWN || meta == TYPE_PARTIALSTRUCT || meta == TYPE_PARTIALUNION) return false; // Other side of the operation will force the unsigned } // Check if type is going to get forced anyway @@ -85,7 +86,9 @@ bool CastStrategy::markExplicitLongSize(PcodeOp *op,int4 slot) const if (vn->getSize() <= promoteSize) return false; Datatype *dt = vn->getHigh()->getType(); type_metatype meta = dt->getMetatype(); - if ((meta != TYPE_UINT)&&(meta != TYPE_INT)&&(meta != TYPE_UNKNOWN)) return false; + if (meta != TYPE_UINT && meta != TYPE_INT && meta != TYPE_UNKNOWN && + meta != TYPE_PARTIALSTRUCT && meta != TYPE_PARTIALUNION) + return false; uintb off = vn->getOffset(); if (meta == TYPE_INT && signbit_negative(off, vn->getSize())) { off = uintb_negate(off, vn->getSize()); @@ -138,8 +141,9 @@ int4 CastStrategyC::localExtensionType(const Varnode *vn,const PcodeOp *op) cons { type_metatype meta = vn->getHighTypeReadFacing(op)->getMetatype(); - int4 natural; // 1= natural zero extension, 2= natural sign extension - if ((meta == TYPE_UINT)||(meta == TYPE_BOOL)||(meta == TYPE_UNKNOWN)) + int4 natural; + if (meta == TYPE_UINT || meta == TYPE_BOOL || meta == TYPE_UNKNOWN || + meta == TYPE_PARTIALSTRUCT || meta == TYPE_PARTIALUNION) natural = UNSIGNED_EXTENSION; else if (meta == TYPE_INT) natural = SIGNED_EXTENSION; @@ -331,20 +335,23 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype, } switch(reqbase->getMetatype()) { case TYPE_UNKNOWN: + case TYPE_PARTIALSTRUCT: // As they are ultimately stripped, treat partials as undefined + case TYPE_PARTIALUNION: return (Datatype *)0; case TYPE_UINT: if (!care_uint_int) { type_metatype meta = curbase->getMetatype(); // Note: meta can be TYPE_UINT if curbase is typedef/enumerated - if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL)) + if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL || + meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION) return (Datatype *)0; } else { type_metatype meta = curbase->getMetatype(); if ((meta == TYPE_UINT)||(meta==TYPE_BOOL)) // Can be TYPE_UINT for typedef/enumerated return (Datatype *)0; - if (isptr && (meta==TYPE_UNKNOWN)) // Don't cast pointers to unknown - return (Datatype *)0; + if (isptr && (meta==TYPE_UNKNOWN || meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION)) + return (Datatype *)0; // Don't cast pointers to unknown } if ((!care_ptr_uint)&&(curbase->getMetatype()==TYPE_PTR)) return (Datatype *)0; @@ -353,15 +360,16 @@ Datatype *CastStrategyC::castStandard(Datatype *reqtype,Datatype *curtype, if (!care_uint_int) { type_metatype meta = curbase->getMetatype(); // Note: meta can be TYPE_INT if curbase is an enumerated type - if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL)) + if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL || + meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION) return (Datatype *)0; } else { type_metatype meta = curbase->getMetatype(); if ((meta == TYPE_INT)||(meta == TYPE_BOOL)) return (Datatype *)0; // Can be TYPE_INT for typedef/enumerated/char - if (isptr && (meta==TYPE_UNKNOWN)) // Don't cast pointers to unknown - return (Datatype *)0; + if (isptr && (meta==TYPE_UNKNOWN || meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION)) + return (Datatype *)0; // Don't cast pointers to unknown } break; case TYPE_CODE: @@ -402,24 +410,19 @@ bool CastStrategyC::isSubpieceCast(Datatype *outtype,Datatype *intype,uint4 offs { if (offset != 0) return false; type_metatype inmeta = intype->getMetatype(); - if ((inmeta!=TYPE_INT)&& - (inmeta!=TYPE_UINT)&& - (inmeta!=TYPE_UNKNOWN)&& - (inmeta!=TYPE_PTR)) + if (inmeta!=TYPE_INT && inmeta!=TYPE_UINT && inmeta!=TYPE_UNKNOWN && inmeta!=TYPE_PTR && + inmeta!=TYPE_PARTIALSTRUCT && inmeta!=TYPE_PARTIALUNION) return false; type_metatype outmeta = outtype->getMetatype(); - if ((outmeta!=TYPE_INT)&& - (outmeta!=TYPE_UINT)&& - (outmeta!=TYPE_UNKNOWN)&& - (outmeta!=TYPE_PTR)&& - (outmeta!=TYPE_FLOAT)) + if (outmeta!=TYPE_INT && outmeta!=TYPE_UINT && outmeta!=TYPE_UNKNOWN && + outmeta!=TYPE_PTR && outmeta!=TYPE_FLOAT) return false; if (inmeta==TYPE_PTR) { if (outmeta == TYPE_PTR) { if (outtype->getSize() < intype->getSize()) return true; // Cast from far pointer to near pointer } - if ((outmeta!=TYPE_INT) && (outmeta!=TYPE_UINT)) + if (outmeta!=TYPE_INT && outmeta!=TYPE_UINT) return false; //other casts don't make sense for pointers } return true; @@ -477,12 +480,15 @@ Datatype *CastStrategyJava::castStandard(Datatype *reqtype,Datatype *curtype, if (reqbase->getSize() != curbase->getSize()) return reqtype; // Always cast change in size switch(reqbase->getMetatype()) { case TYPE_UNKNOWN: + case TYPE_PARTIALSTRUCT: // As they are ultimately stripped, treat partials as undefined + case TYPE_PARTIALUNION: return (Datatype *)0; case TYPE_UINT: if (!care_uint_int) { type_metatype meta = curbase->getMetatype(); // Note: meta can be TYPE_UINT if curbase is typedef/enumerated - if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL)) + if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL || + meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION) return (Datatype *)0; } else { @@ -495,7 +501,8 @@ Datatype *CastStrategyJava::castStandard(Datatype *reqtype,Datatype *curtype, if (!care_uint_int) { type_metatype meta = curbase->getMetatype(); // Note: meta can be TYPE_INT if curbase is an enumerated type - if ((meta==TYPE_UNKNOWN)||(meta==TYPE_INT)||(meta==TYPE_UINT)||(meta==TYPE_BOOL)) + if (meta==TYPE_UNKNOWN || meta==TYPE_INT || meta==TYPE_UINT || meta==TYPE_BOOL || + meta==TYPE_PARTIALSTRUCT || meta==TYPE_PARTIALUNION) return (Datatype *)0; } else { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc index 8cfd9cd1b2..d9a4f03216 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/constseq.cc @@ -807,33 +807,6 @@ void HeapSequence::gatherIndirectPairs(vector &indirects,vectorclearMark(); } -/// \brief Remove the given PcodeOp and any other ops that uniquely produce its inputs -/// -/// The given PcodeOp is always removed. PcodeOps are recursively removed, if the only data-flow -/// path of their output is to the given op, and they are not a CALL or are otherwise special. -/// \param op is the given PcodeOp to remove -/// \param scratch is scratch space for holding -void HeapSequence::removeRecursive(PcodeOp *op,vector &scratch) - -{ - scratch.clear(); - scratch.push_back(op); - int4 pos = 0; - while(pos < scratch.size()) { - op = scratch[pos]; - pos += 1; - for(int4 i=0;inumInput();++i) { - Varnode *vn = op->getIn(i); - if (!vn->isWritten() || vn->isAutoLive()) continue; - if (vn->loneDescend() == (PcodeOp *)0) continue; - PcodeOp *defOp = vn->getDef(); - if (defOp->isCall() || defOp->isIndirectSource()) continue; - scratch.push_back(defOp); - } - data.opDestroy(op); - } -} - /// If the STORE pointer no longer has any other uses, remove the PTRADD producing it, recursively, /// up to the base pointer. INDIRECT ops surrounding any STORE that is removed are replaced with /// INDIRECTs around the user-op replacing the STOREs. @@ -847,7 +820,7 @@ void HeapSequence::removeStoreOps(PcodeOp *replaceOp) gatherIndirectPairs(indirects, indirectPairs); for(int4 i=0;i &indirects,vector &pairs); - void removeRecursive(PcodeOp *op,vector &scratch); void removeStoreOps(PcodeOp *replaceOp); ///< Remove all STORE ops from the basic block public: HeapSequence(Funcdata &fdata,Datatype *ct,PcodeOp *root); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 436b8431e9..bed14fc3ea 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -5600,6 +5600,7 @@ void ActionDatabase::universalAction(Architecture *conf) actcleanup->addRule( new RuleMultNegOne("cleanup") ); actcleanup->addRule( new RuleAddUnsigned("cleanup") ); actcleanup->addRule( new Rule2Comp2Sub("cleanup") ); + actcleanup->addRule( new RuleDumptyHumpLate("cleanup") ); actcleanup->addRule( new RuleSubRight("cleanup") ); actcleanup->addRule( new RuleFloatSignCleanup("cleanup") ); actcleanup->addRule( new RuleExpandLoad("cleanup") ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh index a04df11427..50b0645ce7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.hh @@ -464,6 +464,7 @@ public: void opUninsert(PcodeOp *op); ///< Remove the given PcodeOp from its basic block void opUnlink(PcodeOp *op); ///< Unset inputs/output and remove given PcodeOP from its basic block void opDestroy(PcodeOp *op); ///< Remove given PcodeOp and destroy its Varnode operands + void opDestroyRecursive(PcodeOp *op,vector &scratch); ///< Remove a PcodeOp and recursively remove ops producing its inputs void opDestroyRaw(PcodeOp *op); ///< Remove the given \e raw PcodeOp void opDeadAndGone(PcodeOp *op) { obank.destroy(op); } ///< Free resources for the given \e dead PcodeOp void opSetAllInput(PcodeOp *op,const vector &vvec); ///< Set all input Varnodes for the given PcodeOp simultaneously diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc index 2bc19508f1..b231fd8649 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc @@ -221,6 +221,31 @@ void Funcdata::opDestroy(PcodeOp *op) } } +/// The given PcodeOp is always removed. PcodeOps are recursively removed, if the only data-flow +/// path of their output is to the given op, and they are not a CALL or are otherwise special. +/// \param op is the given PcodeOp to remove +/// \param scratch is scratch space for holding PcodeOps being examined +void Funcdata::opDestroyRecursive(PcodeOp *op,vector &scratch) + +{ + scratch.clear(); + scratch.push_back(op); + int4 pos = 0; + while(pos < scratch.size()) { + op = scratch[pos]; + pos += 1; + for(int4 i=0;inumInput();++i) { + Varnode *vn = op->getIn(i); + if (!vn->isWritten() || vn->isAutoLive()) continue; + if (vn->loneDescend() == (PcodeOp *)0) continue; + PcodeOp *defOp = vn->getDef(); + if (defOp->isCall() || defOp->isIndirectSource()) continue; + scratch.push_back(defOp); + } + opDestroy(op); + } +} + /// This is a specialized routine for deleting an op during flow generation that has /// been replaced by something else. The op is expected to be \e dead with none of its inputs /// or outputs linked to anything else. Both the PcodeOp and all the input/output Varnodes are destroyed. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 8aa9019d16..dfdc6b4bed 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -849,6 +849,16 @@ void PrintC::opSubpiece(const PcodeOp *op) if (ct->isPieceStructured()) { int8 offset; int8 byteOff = TypeOpSubpiece::computeByteOffsetForComposite(op); + Symbol *sym = vn->getHigh()->getSymbol(); + if (sym != (Symbol *)0 && vn->isExplicit()) { + int4 sz = op->getOut()->getSize(); + int4 suboff = vn->getHigh()->getSymbolOffset(); + if (suboff > 0) + byteOff += suboff; + int4 slot = ct->needsResolution() ? 1 : 0; // Use artificial slot for initial resolution + pushPartialSymbol(sym, byteOff, sz, op->getOut(), op, slot, true); + return; + } const TypeField *field = ct->findTruncation(byteOff,op->getOut()->getSize(),op,1,offset); // Use artificial slot if (field != (const TypeField*)0 && offset == 0) { // A formal structure field pushOp(&object_member,op); @@ -856,16 +866,6 @@ void PrintC::opSubpiece(const PcodeOp *op) pushAtom(Atom(field->name,fieldtoken,EmitMarkup::no_color,ct,field->ident,op)); return; } - else if (vn->isExplicit() && vn->getHigh()->getSymbolOffset() == -1) { // An explicit, entire, structured object - Symbol *sym = vn->getHigh()->getSymbol(); - if (sym != (Symbol *)0) { - int4 sz = op->getOut()->getSize(); - int4 off = (int4)op->getIn(1)->getOffset(); - off = vn->getSpace()->isBigEndian() ? vn->getSize() - (sz + off) : off; - pushPartialSymbol(sym, off, sz, vn, op, -1); - return; - } - } // Fall thru to functional printing } } @@ -1089,7 +1089,7 @@ void PrintC::opPtrsub(const PcodeOp *op) // we can't use a cast in its description, so turn off // casting when printing the partial symbol // Datatype *exttype = ((mods & print_store_value)!=0) ? (Datatype *)0 : ct; - pushPartialSymbol(symbol,off,0,(Varnode *)0,op,-1); + pushPartialSymbol(symbol,off,0,(Varnode *)0,op,-1,false); } } if (arrayvalue) @@ -1883,7 +1883,7 @@ void PrintC::pushAnnotation(const Varnode *vn,const PcodeOp *op) pushSymbol(entry->getSymbol(),vn,op); else { int4 symboloff = vn->getOffset() - entry->getFirst(); - pushPartialSymbol(entry->getSymbol(),symboloff,size,vn,op,-1); + pushPartialSymbol(entry->getSymbol(),symboloff,size,vn,op,-1,false); } } else { @@ -1946,7 +1946,7 @@ void PrintC::pushUnnamedLocation(const Address &addr, void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz, const Varnode *vn,const PcodeOp *op, - int4 inslot) + int4 slot,bool allowCast) { // We need to print "bottom up" in order to get parentheses right // I.e. we want to print globalstruct.arrayfield[0], rather than @@ -1965,12 +1965,12 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz, bool succeeded = false; if (ct->getMetatype()==TYPE_STRUCT) { if (ct->needsResolution() && ct->getSize() == sz) { - Datatype *outtype = ct->findResolve(op, inslot); + Datatype *outtype = ct->findResolve(op, slot); if (outtype == ct) break; // Turns out we don't resolve to the field } const TypeField *field; - field = ct->findTruncation(off,sz,op,inslot,newoff); + field = ct->findTruncation(off,sz,op,slot,newoff); if (field != (const TypeField *)0) { off = newoff; stack.emplace_back(); @@ -2000,7 +2000,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz, } else if (ct->getMetatype() == TYPE_UNION) { const TypeField *field; - field = ct->findTruncation(off,sz,op,inslot,newoff); + field = ct->findTruncation(off,sz,op,slot,newoff); if (field != (const TypeField*)0) { off = newoff; stack.emplace_back(); @@ -2015,7 +2015,7 @@ void PrintC::pushPartialSymbol(const Symbol *sym,int4 off,int4 sz, else if (ct->getSize() == sz) break; // Turns out we don't need to resolve the field } - else if (inslot >= 0) { + else if (allowCast) { Datatype *outtype = vn->getHigh()->getType(); AddrSpace *spc = sym->getFirstWholeMap()->getAddr().getSpace(); if (spc == (AddrSpace *)0) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh index 380624335a..d6ed9931b0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh @@ -211,7 +211,7 @@ protected: virtual void pushUnnamedLocation(const Address &addr, const Varnode *vn,const PcodeOp *op); virtual void pushPartialSymbol(const Symbol *sym,int4 off,int4 sz, - const Varnode *vn,const PcodeOp *op,int4 inslot); + const Varnode *vn,const PcodeOp *op,int4 slot,bool allowCast); virtual void pushMismatchSymbol(const Symbol *sym,int4 off,int4 sz, const Varnode *vn,const PcodeOp *op); virtual void pushImpliedField(const Varnode *vn,const PcodeOp *op); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc index cee8630e70..79642ef234 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.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. @@ -254,7 +254,7 @@ void PrintLanguage::pushSymbolDetail(const Varnode *vn,const PcodeOp *op,bool is } if (symboloff + vn->getSize() <= sym->getType()->getSize()) { int4 inslot = isRead ? op->getSlot(vn) : -1; - pushPartialSymbol(sym,symboloff,vn->getSize(),vn,op,inslot); + pushPartialSymbol(sym,symboloff,vn->getSize(),vn,op,inslot,isRead); } else pushMismatchSymbol(sym,symboloff,vn->getSize(),vn,op); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh index 7cdb7069cf..af6e87cb04 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printlanguage.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. @@ -365,14 +365,20 @@ protected: /// \brief Push a variable that represents only part of a symbol onto the RPN stack /// /// Generally \e member syntax specifying a field within a structure gets emitted. + /// Nested structures may result in multiple fields being emitted to get to the final size. + /// If the final size requires truncating a data-type that is not a structure, this method + /// can optionally emit a final cast to represent the truncation, otherwise an artificial + /// field representing the truncation is emitted. Any \e union encountered is resolved using + /// the given PcodeOp and slot. /// \param sym is the root Symbol /// \param off is the byte offset, within the Symbol, of the partial variable /// \param sz is the number of bytes in the partial variable /// \param vn is the Varnode holding the partial value /// \param op is a PcodeOp associate with the Varnode - /// \param inslot is the input slot of \b vn with \b op, or -1 if \b op writes \b vn + /// \param slot is the slot to use (relative to \b op) for any data-type requiring resolution + /// \param allowCast is \b true if a final truncation should be printed as a cast virtual void pushPartialSymbol(const Symbol *sym,int4 off,int4 sz, - const Varnode *vn,const PcodeOp *op,int4 inslot)=0; + const Varnode *vn,const PcodeOp *op,int4 slot,bool allowCast)=0; /// \brief Push an identifier for a variable that mismatches with its Symbol /// diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc index d3122b3bcd..c0deb4ca80 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc @@ -2983,6 +2983,70 @@ int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data) return 0; } +void RuleDumptyHumpLate::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_SUBPIECE); +} + +int4 RuleDumptyHumpLate::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *vn = op->getIn(0); + if (!vn->isWritten()) return 0; + PcodeOp *pieceOp = vn->getDef(); + if (pieceOp->code() != CPUI_PIECE) return 0; + Varnode *out = op->getOut(); + int4 outSize = out->getSize(); + int4 trunc = (int4)op->getIn(1)->getOffset(); + for(;;) { + // Try to backtrack thru PIECE to the component vn is being truncated from + Varnode *trialVn = pieceOp->getIn(1); // Assume the least significant component + int4 trialTrunc = trunc; + if (trunc >= trialVn->getSize()) { // Test for truncation from the most significant part + trialTrunc -= trialVn->getSize(); // How much is truncated + trialVn = pieceOp->getIn(0); + } + if (outSize + trialTrunc > trialVn->getSize()) + break; // vn crosses both components + vn = trialVn; // Commit to this component + trunc = trialTrunc; + if (vn->getSize() == outSize) + break; // Found matching component + if (!vn->isWritten()) + break; + pieceOp = vn->getDef(); + if (pieceOp->code() != CPUI_PIECE) + break; + } + if (vn == op->getIn(0)) + return 0; // Didn't backtrack thru any PIECE + if (vn->isWritten() && vn->getDef()->code() == CPUI_COPY) + vn = vn->getDef()->getIn(0); + PcodeOp *removeOp; + if (outSize != vn->getSize()) { // Component does not match size exactly. Preserve SUBPIECE. + removeOp = op->getIn(0)->getDef(); + if (op->getIn(1)->getOffset() != trunc) + data.opSetInput(op, data.newConstant(4, trunc), 1); + data.opSetInput(op, vn, 0); + } + else if (out->isAutoLive()) { // Exact match but output address fixed. Change SUBPIECE to COPY. + removeOp = op->getIn(0)->getDef(); + data.opRemoveInput(op, 1); + data.opSetOpcode(op, CPUI_COPY); + data.opSetInput(op, vn, 0); + } + else { // Exact match. Completely replace output with component. + removeOp = op; + data.totalReplace(out, vn); + } + if (removeOp->getOut()->hasNoDescend() && !removeOp->getOut()->isAutoLive()) { + vector scratch; + data.opDestroyRecursive(removeOp, scratch); + } + return 1; +} + /// This method distinguishes between a floating-point variable with \e full precision, where all the /// storage can vary (or is unknown), versus a value that is extended from a floating-point variable with /// smaller storage. Within the data-flow above the given Varnode, we search for the maximum diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh index ff719eca39..d389e8979d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh @@ -353,6 +353,24 @@ public: virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; +/// \brief Simplify join and break apart based on data-types +/// +/// Simplify expressions like: +/// - `sub( concat(V,W), 0) => W` +/// - `sub( concat(V,W), c) => V` +/// +/// preserving the data-types and removing the SUBPIECE and PIECE operations that are discarded. +class RuleDumptyHumpLate : public Rule { +public: + RuleDumptyHumpLate(const string &g) : Rule( g, 0, "dumptyhumplate") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleDumptyHumpLate(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + /// \brief Class for tracing changes of precision in floating point variables /// /// It follows the flow of a logical lower precision value stored in higher precision locations diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index 8e226258c7..f064ddd256 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -303,19 +303,19 @@ void HighVariable::stripType(void) const { if (!type->hasStripped()) return; - if (type->getMetatype() == TYPE_PARTIALUNION) { - if (symbol != (Symbol *)0 && symboloffset != -1) { - type_metatype meta = symbol->getType()->getMetatype(); - if (meta != TYPE_STRUCT && meta != TYPE_UNION) // If partial union does not have a bigger backing symbol - type = type->getStripped(); // strip the partial union + type_metatype meta = type->getMetatype(); + if (meta == TYPE_PARTIALUNION || meta == TYPE_PARTIALSTRUCT) { + if (symbol != (Symbol *)0 && symboloffset != -1) { // If there is a bigger backing symbol + type_metatype submeta = symbol->getType()->getMetatype(); + if (submeta == TYPE_STRUCT || submeta == TYPE_UNION) + return; // Don't strip the partial union } } else if (type->isEnumType()) { - if (inst.size() != 1 || !inst[0]->isConstant()) // Only preserve partial enum on a constant - type = type->getStripped(); + if (inst.size() == 1 && inst[0]->isConstant()) // Only preserve partial enum on a constant + return; } - else - type = type->getStripped(); + type = type->getStripped(); } /// Only update if the cover is marked as \e dirty. diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/concatsplit.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/concatsplit.xml new file mode 100644 index 0000000000..d14812276f --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/concatsplit.xml @@ -0,0 +1,32 @@ + + + + +660f6ec266410f6ec8660f6ed94883ec +2866410f6ed1660f62c348897c240866 +0f62ca660f6cc10f29442410e82f0000 +00488b442408660f6f4424108b542430 +0f1140148950244883c428c3 + + + + + +create\(&out->base,&in->base\); +out->a = A; +out->b = B; +out->c = C; +out->d = D; +out->e = E; +CONCAT +