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