diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index 8e7f2fab8c..68656f784e 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -25,6 +25,7 @@ src/decompile/datatests/divopt.xml||GHIDRA||||END| src/decompile/datatests/doublemove.xml||GHIDRA||||END| src/decompile/datatests/dupptr.xml||GHIDRA||||END| src/decompile/datatests/elseif.xml||GHIDRA||||END| +src/decompile/datatests/enum.xml||GHIDRA||||END| src/decompile/datatests/floatcast.xml||GHIDRA||||END| src/decompile/datatests/floatconv.xml||GHIDRA||||END| src/decompile/datatests/floatprint.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 2596d8629a..afaf942d4c 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 Rule2Comp2Sub("cleanup") ); actcleanup->addRule( new RuleSubRight("cleanup") ); actcleanup->addRule( new RuleFloatSignCleanup("cleanup") ); + actcleanup->addRule( new RuleExpandLoad("cleanup") ); actcleanup->addRule( new RulePtrsubCharConstant("cleanup") ); actcleanup->addRule( new RuleExtensionPush("cleanup") ); actcleanup->addRule( new RulePieceStructure("cleanup") ); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.cc index 697888ee26..1077bada52 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.cc @@ -3112,7 +3112,7 @@ int grammarerror(const char *str) Datatype *parse_type(istream &s,string &name,Architecture *glb) { - CParse parser(glb,1000); + CParse parser(glb,4096); if (!parser.parseStream(s,CParse::doc_parameter_declaration)) throw ParseError(parser.getError()); @@ -3131,7 +3131,7 @@ Datatype *parse_type(istream &s,string &name,Architecture *glb) void parse_protopieces(PrototypePieces &pieces, istream &s,Architecture *glb) { - CParse parser(glb,1000); + CParse parser(glb,4096); if (!parser.parseStream(s,CParse::doc_declaration)) throw ParseError(parser.getError()); @@ -3151,7 +3151,7 @@ void parse_protopieces(PrototypePieces &pieces, void parse_C(Architecture *glb,istream &s) { // Load type data straight into datastructures - CParse parser(glb,1000); + CParse parser(glb,4096); if (!parser.parseStream(s,CParse::doc_declaration)) throw ParseError(parser.getError()); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y b/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y index 85591b2357..298843cc1b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/grammar.y @@ -1366,7 +1366,7 @@ int grammarerror(const char *str) Datatype *parse_type(istream &s,string &name,Architecture *glb) { - CParse parser(glb,1000); + CParse parser(glb,4096); if (!parser.parseStream(s,CParse::doc_parameter_declaration)) throw ParseError(parser.getError()); @@ -1385,7 +1385,7 @@ Datatype *parse_type(istream &s,string &name,Architecture *glb) void parse_protopieces(PrototypePieces &pieces, istream &s,Architecture *glb) { - CParse parser(glb,1000); + CParse parser(glb,4096); if (!parser.parseStream(s,CParse::doc_declaration)) throw ParseError(parser.getError()); @@ -1405,7 +1405,7 @@ void parse_protopieces(PrototypePieces &pieces, void parse_C(Architecture *glb,istream &s) { // Load type data straight into datastructures - CParse parser(glb,1000); + CParse parser(glb,4096); if (!parser.parseStream(s,CParse::doc_declaration)) throw ParseError(parser.getError()); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 0e595426ac..8aa9019d16 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -959,7 +959,7 @@ void PrintC::opPtrsub(const PcodeOp *op) if (ct->getMetatype() == TYPE_STRUCT || ct->getMetatype() == TYPE_UNION) { int8 suboff = (int4)in1const; // How far into container if (ptrel != (TypePointerRel *)0) { - suboff += ptrel->getPointerOffset(); + suboff += ptrel->getAddressOffset(); suboff &= calc_mask(ptype->getSize()); if (suboff == 0) { // Special case where we do not print a field @@ -1666,22 +1666,23 @@ void PrintC::pushCharConstant(uintb val,const Datatype *ct,tagtype tag,const Var void PrintC::pushEnumConstant(uintb val,const TypeEnum *ct,tagtype tag, const Varnode *vn,const PcodeOp *op) { - vector valnames; + TypeEnum::Representation rep; - bool complement = ct->getMatches(val,valnames); - if (valnames.size() > 0) { - if (complement) + ct->getMatches(val,rep); + if (rep.matchname.size() > 0) { + if (rep.shiftAmount != 0) + pushOp(&shift_right,op); + if (rep.complement) pushOp(&bitwise_not,op); - for(int4 i=valnames.size()-1;i>0;--i) + for(int4 i=rep.matchname.size()-1;i>0;--i) pushOp(&enum_cat,op); - for(int4 i=0;igetSize(),false,tag,vn,op); - // ostringstream s; - // s << "BAD_ENUM(0x" << hex << val << ")"; - // pushAtom(Atom(s.str(),vartoken,EmitMarkup::const_color,op,vn)); } } @@ -1793,8 +1794,11 @@ void PrintC::pushConstant(uintb val,const Datatype *ct,tagtype tag, case TYPE_SPACEBASE: case TYPE_CODE: case TYPE_ARRAY: + case TYPE_ENUM_INT: + case TYPE_ENUM_UINT: case TYPE_STRUCT: case TYPE_UNION: + case TYPE_PARTIALENUM: case TYPE_PARTIALSTRUCT: case TYPE_PARTIALUNION: break; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 1ca1e8625b..009570af71 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -15,7 +15,6 @@ */ #include "ruleaction.hh" #include "coreaction.hh" -#include "subflow.hh" #include "rangeutil.hh" #include "multiprecision.hh" @@ -5679,7 +5678,7 @@ void AddTreeState::clear(void) nonmultsum = 0; biggestNonMultCoeff = 0; if (pRelType != (const TypePointerRel *)0) { - nonmultsum = ((TypePointerRel *)ct)->getPointerOffset(); + nonmultsum = ((TypePointerRel *)ct)->getAddressOffset(); nonmultsum &= ptrmask; } multiple.clear(); @@ -5733,7 +5732,7 @@ AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot) if (ct->isFormalPointerRel()) { pRelType = (const TypePointerRel *)ct; baseType = pRelType->getParent(); - nonmultsum = pRelType->getPointerOffset(); + nonmultsum = pRelType->getAddressOffset(); nonmultsum &= ptrmask; } if (baseType->isVariableLength()) @@ -6012,7 +6011,7 @@ void AddTreeState::calcSubtype(void) extra = AddrSpace::byteToAddressInt(extra, ct->getWordSize()); // Convert back to address units offset = (offset - extra) & ptrmask; correct = (correct - extra) & ptrmask; - if (pRelType != (TypePointerRel *)0 && offset == pRelType->getPointerOffset()) { + if (pRelType != (TypePointerRel *)0 && offset == pRelType->getAddressOffset()) { // offset falls within basic ptrto if (!pRelType->evaluateThruParent(0)) { // If we are not representing offset 0 through parent valid = false; // Use basic (alternate) form @@ -6031,7 +6030,7 @@ void AddTreeState::calcSubtype(void) valid = false; // There is substructure we don't know about } if (pRelType != (const TypePointerRel *)0) { - int4 ptrOff = ((TypePointerRel *)ct)->getPointerOffset(); + int4 ptrOff = ((TypePointerRel *)ct)->getAddressOffset(); offset = (offset - ptrOff) & ptrmask; correct = (correct - ptrOff) & ptrmask; } @@ -6398,8 +6397,7 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data) baseType = ptRel->getParent(); if (baseType->getMetatype() != TYPE_STRUCT) return 0; - int8 iOff = ptRel->getPointerOffset(); - iOff = AddrSpace::addressToByteInt(iOff, ptRel->getWordSize()); + int8 iOff = ptRel->getByteOffset(); if (iOff >= baseType->getSize()) return 0; offset = iOff; @@ -6864,7 +6862,6 @@ int4 RuleAddUnsigned::applyOp(PcodeOp *op,Funcdata &data) Datatype *dt = constvn->getTypeReadFacing(op); if (dt->getMetatype() != TYPE_UINT) return 0; if (dt->isCharPrint()) return 0; // Only change integer forms - if (dt->isEnumType()) return 0; uintb val = constvn->getOffset(); uintb mask = calc_mask(constvn->getSize()); int4 sa = constvn->getSize() * 6; // 1/4 less than full bitsize @@ -6877,8 +6874,14 @@ int4 RuleAddUnsigned::applyOp(PcodeOp *op,Funcdata &data) return 0; // Dont transform a named equate } } + uintb negatedVal = (-val) & mask; + if (dt->isEnumType()) { + TypeEnum *enumType = (TypeEnum *)dt; + if (!enumType->hasNamedValue(negatedVal) && enumType->hasNamedValue((~val)&mask)) + return 0; + } data.opSetOpcode(op,CPUI_INT_SUB); - Varnode *cvn = data.newConstant(constvn->getSize(), (-val) & mask); + Varnode *cvn = data.newConstant(constvn->getSize(), negatedVal); cvn->copySymbol(constvn); data.opSetInput(op,cvn,1); return 1; @@ -7369,86 +7372,6 @@ int4 RulePieceStructure::applyOp(PcodeOp *op,Funcdata &data) return 1; } -/// \class RuleSplitCopy -/// \brief Split COPY ops based on TypePartialStruct -/// -/// If more than one logical component of a structure or array is copied at once, -/// rewrite the COPY operator as multiple COPYs. -void RuleSplitCopy::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_COPY); -} - -int4 RuleSplitCopy::applyOp(PcodeOp *op,Funcdata &data) - -{ - Datatype *inType = op->getIn(0)->getTypeReadFacing(op); - Datatype *outType = op->getOut()->getTypeDefFacing(); - type_metatype metain = inType->getMetatype(); - type_metatype metaout = outType->getMetatype(); - if (metain != TYPE_PARTIALSTRUCT && metaout != TYPE_PARTIALSTRUCT && - metain != TYPE_ARRAY && metaout != TYPE_ARRAY && - metain != TYPE_STRUCT && metaout != TYPE_STRUCT) - return false; - SplitDatatype splitter(data); - if (splitter.splitCopy(op, inType, outType)) - return 1; - return 0; -} - -/// \class RuleSplitLoad -/// \brief Split LOAD ops based on TypePartialStruct -/// -/// If more than one logical component of a structure or array is loaded at once, -/// rewrite the LOAD operator as multiple LOADs. -void RuleSplitLoad::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_LOAD); -} - -int4 RuleSplitLoad::applyOp(PcodeOp *op,Funcdata &data) - -{ - Datatype *inType = SplitDatatype::getValueDatatype(op, op->getOut()->getSize(), data.getArch()->types); - if (inType == (Datatype *)0) - return 0; - type_metatype metain = inType->getMetatype(); - if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT) - return 0; - SplitDatatype splitter(data); - if (splitter.splitLoad(op, inType)) - return 1; - return 0; -} - -/// \class RuleSplitStore -/// \brief Split STORE ops based on TypePartialStruct -/// -/// If more than one logical component of a structure or array is stored at once, -/// rewrite the STORE operator as multiple STOREs. -void RuleSplitStore::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_STORE); -} - -int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data) - -{ - Datatype *outType = SplitDatatype::getValueDatatype(op, op->getIn(2)->getSize(), data.getArch()->types); - if (outType == (Datatype *)0) - return 0; - type_metatype metain = outType->getMetatype(); - if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT) - return 0; - SplitDatatype splitter(data); - if (splitter.splitStore(op, outType)) - return 1; - return 0; -} - /// \class RuleSubNormal /// \brief Pull-back SUBPIECE through INT_RIGHT and INT_SRIGHT /// @@ -8779,138 +8702,6 @@ int4 RuleSegment::applyOp(PcodeOp *op,Funcdata &data) return 0; } -/// \class RuleSubvarAnd -/// \brief Perform SubVariableFlow analysis triggered by INT_AND -void RuleSubvarAnd::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_INT_AND); -} - -int4 RuleSubvarAnd::applyOp(PcodeOp *op,Funcdata &data) - -{ - if (!op->getIn(1)->isConstant()) return 0; - Varnode *vn = op->getIn(0); - Varnode *outvn = op->getOut(); - // if (vn->getSize() != 1) return 0; // Only for bitsize variables - if (outvn->getConsume() != op->getIn(1)->getOffset()) return 0; - if ((outvn->getConsume() & 1)==0) return 0; - uintb cmask; - if (outvn->getConsume() == (uintb)1) - cmask = (uintb)1; - else { - cmask = calc_mask(vn->getSize()); - cmask >>=8; - while(cmask != 0) { - if (cmask == outvn->getConsume()) break; - cmask >>=8; - } - } - if (cmask == 0) return 0; - // if (vn->getConsume() == 0) return 0; - // if ((vn->getConsume() & 0xff)==0xff) return 0; - // if (op->getIn(1)->getOffset() != (uintb)1) return 0; - if (op->getOut()->hasNoDescend()) return 0; - SubvariableFlow subflow(&data,vn,cmask,false,false,false); - if (!subflow.doTrace()) return 0; - subflow.doReplacement(); - return 1; -} - -/// \class RuleSubvarSubpiece -/// \brief Perform SubVariableFlow analysis triggered by SUBPIECE -void RuleSubvarSubpiece::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_SUBPIECE); -} - -int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data) - -{ - Varnode *vn = op->getIn(0); - Varnode *outvn = op->getOut(); - int4 flowsize = outvn->getSize(); - uintb mask = calc_mask( flowsize ); - mask <<= 8*((int4)op->getIn(1)->getOffset()); - bool aggressive = outvn->isPtrFlow(); - if (!aggressive) { - if ((vn->getConsume() & mask) != vn->getConsume()) return 0; - if (op->getOut()->hasNoDescend()) return 0; - } - bool big = false; - if (flowsize >= 8 && vn->isInput()) { - // Vector register inputs getting truncated to what actually gets used - // happens occasionally. We let SubvariableFlow deal with this special case - // to avoid overlapping inputs - // TODO: ActionLaneDivide should be handling this - if (vn->loneDescend() == op) - big = true; - } - SubvariableFlow subflow(&data,vn,mask,aggressive,false,big); - if (!subflow.doTrace()) return 0; - subflow.doReplacement(); - return 1; -} - -/// \class RuleSplitFlow -/// \brief Try to detect and split artificially joined Varnodes -/// -/// Look for SUBPIECE coming from a PIECE that has come through INDIRECTs and/or MULTIEQUAL -/// Then: check if the input to SUBPIECE can be viewed as two independent pieces -/// If so: split the pieces into independent data-flows -void RuleSplitFlow::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_SUBPIECE); -} - -int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data) - -{ - int4 loSize = (int4)op->getIn(1)->getOffset(); - if (loSize == 0) // Make sure SUBPIECE doesn't take least significant part - return 0; - Varnode *vn = op->getIn(0); - if (!vn->isWritten()) - return 0; - if (vn->isPrecisLo() || vn->isPrecisHi()) - return 0; - if (op->getOut()->getSize() + loSize != vn->getSize()) - return 0; // Make sure SUBPIECE is taking most significant part - PcodeOp *concatOp = (PcodeOp *)0; - PcodeOp *multiOp = vn->getDef(); - while(multiOp->code() == CPUI_INDIRECT) { // PIECE may come through INDIRECT - Varnode *tmpvn = multiOp->getIn(0); - if (!tmpvn->isWritten()) return 0; - multiOp = tmpvn->getDef(); - } - if (multiOp->code() == CPUI_PIECE) { - if (vn->getDef() != multiOp) - concatOp = multiOp; - } - else if (multiOp->code() == CPUI_MULTIEQUAL) { // Otherwise PIECE comes through MULTIEQUAL - for(int4 i=0;inumInput();++i) { - Varnode *invn = multiOp->getIn(i); - if (!invn->isWritten()) continue; - PcodeOp *tmpOp = invn->getDef(); - if (tmpOp->code() == CPUI_PIECE) { - concatOp = tmpOp; - break; - } - } - } - if (concatOp == (PcodeOp *)0) // Didn't find the concatenate - return 0; - if (concatOp->getIn(1)->getSize() != loSize) - return 0; - SplitFlow splitFlow(&data,vn,loSize); - if (!splitFlow.doTrace()) return 0; - splitFlow.apply(); - return 1; -} - /// \class RulePtrFlow /// \brief Mark Varnode and PcodeOp objects that are carrying or operating on pointers /// @@ -9111,178 +8902,6 @@ int4 RulePtrFlow::applyOp(PcodeOp *op,Funcdata &data) return madeChange; } -/// \class RuleSubvarCompZero -/// \brief Perform SubvariableFlow analysis triggered by testing of a single bit -/// -/// Given a comparison (INT_EQUAL or INT_NOTEEQUAL_ to a constant, -/// check that input has only 1 bit that can possibly be non-zero -/// and that the constant is testing this. This then triggers -/// the full SubvariableFlow analysis. -void RuleSubvarCompZero::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_INT_NOTEQUAL); - oplist.push_back(CPUI_INT_EQUAL); -} - -int4 RuleSubvarCompZero::applyOp(PcodeOp *op,Funcdata &data) - -{ - if (!op->getIn(1)->isConstant()) return 0; - Varnode *vn = op->getIn(0); - uintb mask = vn->getNZMask(); - int4 bitnum = leastsigbit_set(mask); - if (bitnum == -1) return 0; - if ((mask >> bitnum) != 1) return 0; // Check if only one bit active - - // Check if the active bit is getting tested - if ((op->getIn(1)->getOffset()!=mask)&& - (op->getIn(1)->getOffset()!=0)) - return 0; - - if (op->getOut()->hasNoDescend()) return 0; - // We do a basic check that the stream from which it looks like - // the bit is getting pulled is not fully consumed - if (vn->isWritten()) { - PcodeOp *andop = vn->getDef(); - if (andop->numInput()==0) return 0; - Varnode *vn0 = andop->getIn(0); - switch(andop->code()) { - case CPUI_INT_AND: - case CPUI_INT_OR: - case CPUI_INT_RIGHT: - { - if (vn0->isConstant()) return 0; - uintb mask0 = vn0->getConsume() & vn0->getNZMask(); - uintb wholemask = calc_mask(vn0->getSize()) & mask0; - // We really need a popcnt here - // We want: if the number of bits that are both consumed - // and not known to be zero are "big" then don't continue - // because it doesn't look like a few bits getting manipulated - // within a status register - if ((wholemask & 0xff)==0xff) return 0; - if ((wholemask & 0xff00)==0xff00) return 0; - } - break; - default: - break; - } - } - - SubvariableFlow subflow(&data,vn,mask,false,false,false); - if (!subflow.doTrace()) { - return 0; - } - subflow.doReplacement(); - return 1; -} - -/// \class RuleSubvarShift -/// \brief Perform SubvariableFlow analysis triggered by INT_RIGHT -/// -/// If the INT_RIGHT input has only 1 bit that can possibly be non-zero -/// and it is getting shifted into the least significant bit position, -/// trigger the full SubvariableFlow analysis. -void RuleSubvarShift::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_INT_RIGHT); -} - -int4 RuleSubvarShift::applyOp(PcodeOp *op,Funcdata &data) - -{ - Varnode *vn = op->getIn(0); - if (vn->getSize() != 1) return 0; - if (!op->getIn(1)->isConstant()) return 0; - int4 sa = (int4)op->getIn(1)->getOffset(); - uintb mask = vn->getNZMask(); - if ((mask >> sa) != (uintb)1) return 0; // Pulling out a single bit - mask = (mask >> sa) << sa; - if (op->getOut()->hasNoDescend()) return 0; - - SubvariableFlow subflow(&data,vn,mask,false,false,false); - if (!subflow.doTrace()) return 0; - subflow.doReplacement(); - return 1; -} - -/// \class RuleSubvarZext -/// \brief Perform SubvariableFlow analysis triggered by INT_ZEXT -void RuleSubvarZext::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_INT_ZEXT); -} - -int4 RuleSubvarZext::applyOp(PcodeOp *op,Funcdata &data) - -{ - Varnode *vn = op->getOut(); - Varnode *invn = op->getIn(0); - uintb mask = calc_mask(invn->getSize()); - - SubvariableFlow subflow(&data,vn,mask,invn->isPtrFlow(),false,false); - if (!subflow.doTrace()) return 0; - subflow.doReplacement(); - return 1; -} - -/// \class RuleSubvarSext -/// \brief Perform SubvariableFlow analysis triggered by INT_SEXT -void RuleSubvarSext::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_INT_SEXT); -} - -int4 RuleSubvarSext::applyOp(PcodeOp *op,Funcdata &data) - -{ - Varnode *vn = op->getOut(); - Varnode *invn = op->getIn(0); - uintb mask = calc_mask(invn->getSize()); - - SubvariableFlow subflow(&data,vn,mask,isaggressive,true,false); - if (!subflow.doTrace()) return 0; - subflow.doReplacement(); - return 1; -} - -void RuleSubvarSext::reset(Funcdata &data) - -{ - isaggressive = data.getArch()->aggressive_ext_trim; -} - -/// \class RuleSubfloatConvert -/// \brief Perform SubfloatFlow analysis triggered by FLOAT_FLOAT2FLOAT -void RuleSubfloatConvert::getOpList(vector &oplist) const - -{ - oplist.push_back(CPUI_FLOAT_FLOAT2FLOAT); -} - -int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data) - -{ - Varnode *invn = op->getIn(0); - Varnode *outvn = op->getOut(); - int4 insize = invn->getSize(); - int4 outsize = outvn->getSize(); - if (outsize > insize) { - SubfloatFlow subflow(&data,outvn,insize); - if (!subflow.doTrace()) return 0; - subflow.apply(); - } - else { - SubfloatFlow subflow(&data,invn,outsize); - if (!subflow.doTrace()) return 0; - subflow.apply(); - } - return 1; -} - /// \class RuleNegateNegate /// \brief Simplify INT_NEGATE chains: `~~V => V` void RuleNegateNegate::getOpList(vector &oplist) const @@ -10926,4 +10545,145 @@ int4 RuleOrCompare::applyOp(PcodeOp *op,Funcdata &data) return 1; } +/// \brief Check that all uses of given Varnode are of the form `(V & C) == D` +/// +/// \param vn is the given Varnode +/// \return \b true if all uses match the INT_AND form +bool RuleExpandLoad::checkAndComparison(Varnode *vn) + +{ + list::const_iterator iter; + for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) { + PcodeOp *op = *iter; + if (op->code() != CPUI_INT_AND) return false; + if (!op->getIn(1)->isConstant()) return false; + PcodeOp *compOp = op->getOut()->loneDescend(); + if (compOp == (PcodeOp *)0) return false; + OpCode opc = compOp->code(); + if (opc != CPUI_INT_EQUAL && opc != CPUI_INT_NOTEQUAL) return false; + if (!compOp->getIn(1)->isConstant()) return false; + } + return true; +} + +/// \brief Expand the constants in the previously scanned forms: `(V & C) == D` +/// +/// The method checkAndComparison() must have returned \b true for \b oldVn. Change the size and +/// data-type of all the constants in these expressions. +/// \param data is the function containing the expressions +/// \param oldVn is incoming variable in all the expressions +/// \param newVn is the new bigger variable +/// \param dt is the data-type to associate with the constants +/// \param offset is the number of least significant (zero) bytes to add to each constant +void RuleExpandLoad::modifyAndComparison(Funcdata &data,Varnode *oldVn,Varnode *newVn,Datatype *dt,int4 offset) + +{ + offset = 8*offset; // Convert to shift amount + list::const_iterator iter = oldVn->beginDescend(); + while(iter != oldVn->endDescend()) { + PcodeOp *andOp = *iter; + ++iter; // Advance iterator before modifying op + PcodeOp *compOp = andOp->getOut()->loneDescend(); + uintb newOff = andOp->getIn(1)->getOffset(); + newOff <<= offset; + Varnode *vn = data.newConstant(dt->getSize(), newOff); + vn->updateType(dt, false, false); + data.opSetInput(andOp, newVn, 0); + data.opSetInput(andOp, vn, 1); + newOff = compOp->getIn(1)->getOffset(); + newOff <<= offset; + vn = data.newConstant(dt->getSize(), newOff); + vn->updateType(dt, false, false); + data.opSetInput(compOp,vn,1); + } +} + +/// \class RuleExpandLoad +/// \brief Convert LOAD size to match pointer data-type +/// +/// Change LOAD output to a larger size if used for INT_AND comparisons or if a truncation is natural +void RuleExpandLoad::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_LOAD); +} + +int4 RuleExpandLoad::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *outVn = op->getOut(); + int4 outSize = outVn->getSize(); + Varnode *rootPtr = op->getIn(1); + PcodeOp *addOp = (PcodeOp *)0; + int4 offset = 0; + Datatype *elType; + if (rootPtr->isWritten()) { + PcodeOp *defOp = rootPtr->getDef(); + if (defOp->code() == CPUI_INT_ADD && defOp->getIn(1)->isConstant()) { + addOp = defOp; + rootPtr = defOp->getIn(0); + offset = defOp->getIn(1)->getOffset(); + if (offset > 16) return 0; // INT_ADD offset must be small + if (defOp->getOut()->loneDescend() == (PcodeOp *)0) return 0; // INT_ADD must be used only once + elType = rootPtr->getTypeReadFacing(defOp); + } + else + elType = rootPtr->getTypeReadFacing(op); + } + else + elType = rootPtr->getTypeReadFacing(op); + if (elType->getMetatype() != TYPE_PTR) return 0; + elType = ((TypePointer *)elType)->getPtrTo(); + if (elType->getSize() <= outSize) return 0; // Pointer data-type must be bigger than LOAD + if (elType->getSize() < outSize + offset) return 0; + + type_metatype meta = elType->getMetatype(); + if (meta == TYPE_UNKNOWN) return 0; + bool addForm = checkAndComparison(outVn); + AddrSpace *spc = op->getIn(0)->getSpaceFromConst(); + int4 lsbCut = 0; + if (addForm) { + if (spc->isBigEndian()) { + lsbCut = elType->getSize() - outSize - offset; + } + else + lsbCut = offset; + } + else { + // Check for natural integer truncation + if (meta != TYPE_INT && meta != TYPE_UINT) return 0; + type_metatype outMeta = outVn->getTypeDefFacing()->getMetatype(); + if (outMeta != TYPE_INT && outMeta != TYPE_UINT && outMeta != TYPE_UNKNOWN && outMeta != TYPE_BOOL) + return false; + // Check that LOAD is grabbing least significant bytes + if (spc->isBigEndian()) { + if (outSize + offset != elType->getSize()) return 0; + } + else { + if (offset != 0) return 0; + } + } + // Modify the LOAD + Varnode *newOut = data.newUnique(elType->getSize(), elType); + data.opSetOutput(op, newOut); + if (addOp != (PcodeOp *)0) { + data.opSetInput(op, rootPtr, 1); + data.opDestroy(addOp); + } + if (addForm) { + if (meta != TYPE_INT && meta != TYPE_UINT) + elType = data.getArch()->types->getBase(elType->getSize(), TYPE_UINT); + modifyAndComparison(data, outVn, newOut, elType, lsbCut); + } + else { + PcodeOp *subOp = data.newOp(2,op->getAddr()); + data.opSetOpcode(subOp, CPUI_SUBPIECE); + data.opSetInput(subOp,newOut,0); // Truncate new bigger LOAD output + data.opSetInput(subOp, data.newConstant(4, 0), 1); + data.opSetOutput(subOp, outVn); // Original LOAD output is now defined by SUBPIECE + data.opInsertAfter(subOp, op); + } + return 1; +} + } // End namespace ghidra diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index e225e0e908..4e9c31a5a8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -1181,39 +1181,6 @@ public: virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; -class RuleSplitCopy : public Rule { -public: - RuleSplitCopy(const string &g) : Rule( g, 0, "splitcopy") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSplitCopy(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSplitLoad : public Rule { -public: - RuleSplitLoad(const string &g) : Rule( g, 0, "splitload") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSplitLoad(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSplitStore : public Rule { -public: - RuleSplitStore(const string &g) : Rule( g, 0, "splitstore") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSplitStore(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 @@ -1398,39 +1365,6 @@ public: virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; -class RuleSubvarAnd : public Rule { -public: - RuleSubvarAnd(const string &g) : Rule( g, 0, "subvar_and") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubvarAnd(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSubvarSubpiece : public Rule { -public: - RuleSubvarSubpiece(const string &g) : Rule( g, 0, "subvar_subpiece") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubvarSubpiece(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSplitFlow : public Rule { -public: - RuleSplitFlow(const string &g) : Rule( g, 0, "splitflow") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSplitFlow(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - class RulePtrFlow : public Rule { Architecture *glb; ///< The address space manager bool hasTruncations; ///< \b true if this architecture needs truncated pointers @@ -1448,63 +1382,6 @@ public: virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; -class RuleSubvarCompZero : public Rule { -public: - RuleSubvarCompZero(const string &g) : Rule( g, 0, "subvar_compzero") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubvarCompZero(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSubvarShift : public Rule { -public: - RuleSubvarShift(const string &g) : Rule( g, 0, "subvar_shift") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubvarShift(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSubvarZext : public Rule { -public: - RuleSubvarZext(const string &g) : Rule( g, 0, "subvar_zext") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubvarZext(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - -class RuleSubvarSext : public Rule { - int4 isaggressive; ///< Is it guaranteed the root is a sub-variable needing to be trimmed -public: - RuleSubvarSext(const string &g) : Rule( g, 0, "subvar_sext") { isaggressive = false; } ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubvarSext(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); - virtual void reset(Funcdata &data); -}; - -class RuleSubfloatConvert : public Rule { -public: - RuleSubfloatConvert(const string &g) : Rule( g, 0, "subfloat_convert") {} ///< Constructor - virtual Rule *clone(const ActionGroupList &grouplist) const { - if (!grouplist.contains(getGroup())) return (Rule *)0; - return new RuleSubfloatConvert(getGroup()); - } - virtual void getOpList(vector &oplist) const; - virtual int4 applyOp(PcodeOp *op,Funcdata &data); -}; - class RuleNegateNegate : public Rule { public: RuleNegateNegate(const string &g) : Rule( g, 0, "negatenegate") {} ///< Constructor @@ -1695,5 +1572,18 @@ public: virtual int4 applyOp(PcodeOp *op,Funcdata &data); }; +class RuleExpandLoad : public Rule { + static bool checkAndComparison(Varnode *vn); + static void modifyAndComparison(Funcdata &data,Varnode *oldVn,Varnode *newVn,Datatype *dt,int4 offset); +public: + RuleExpandLoad(const string &g) : Rule( g, 0, "expandload") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleExpandLoad(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + } // End namespace ghidra #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc index 801c430938..880d198bbb 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.cc @@ -14,6 +14,7 @@ * limitations under the License. */ #include "subflow.hh" +#include "funcdata.hh" namespace ghidra { @@ -1506,6 +1507,204 @@ void SubvariableFlow::doReplacement(void) } } +void RuleSubvarAnd::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_AND); +} + +int4 RuleSubvarAnd::applyOp(PcodeOp *op,Funcdata &data) + +{ + if (!op->getIn(1)->isConstant()) return 0; + Varnode *vn = op->getIn(0); + Varnode *outvn = op->getOut(); + // if (vn->getSize() != 1) return 0; // Only for bitsize variables + if (outvn->getConsume() != op->getIn(1)->getOffset()) return 0; + if ((outvn->getConsume() & 1)==0) return 0; + uintb cmask; + if (outvn->getConsume() == (uintb)1) + cmask = (uintb)1; + else { + cmask = calc_mask(vn->getSize()); + cmask >>=8; + while(cmask != 0) { + if (cmask == outvn->getConsume()) break; + cmask >>=8; + } + } + if (cmask == 0) return 0; + // if (vn->getConsume() == 0) return 0; + // if ((vn->getConsume() & 0xff)==0xff) return 0; + // if (op->getIn(1)->getOffset() != (uintb)1) return 0; + if (op->getOut()->hasNoDescend()) return 0; + SubvariableFlow subflow(&data,vn,cmask,false,false,false); + if (!subflow.doTrace()) return 0; + subflow.doReplacement(); + return 1; +} + +void RuleSubvarSubpiece::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_SUBPIECE); +} + +int4 RuleSubvarSubpiece::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *vn = op->getIn(0); + Varnode *outvn = op->getOut(); + int4 flowsize = outvn->getSize(); + uintb mask = calc_mask( flowsize ); + mask <<= 8*((int4)op->getIn(1)->getOffset()); + bool aggressive = outvn->isPtrFlow(); + if (!aggressive) { + if ((vn->getConsume() & mask) != vn->getConsume()) return 0; + if (op->getOut()->hasNoDescend()) return 0; + } + bool big = false; + if (flowsize >= 8 && vn->isInput()) { + // Vector register inputs getting truncated to what actually gets used + // happens occasionally. We let SubvariableFlow deal with this special case + // to avoid overlapping inputs + // TODO: ActionLaneDivide should be handling this + if (vn->loneDescend() == op) + big = true; + } + SubvariableFlow subflow(&data,vn,mask,aggressive,false,big); + if (!subflow.doTrace()) return 0; + subflow.doReplacement(); + return 1; +} + +void RuleSubvarCompZero::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_NOTEQUAL); + oplist.push_back(CPUI_INT_EQUAL); +} + +int4 RuleSubvarCompZero::applyOp(PcodeOp *op,Funcdata &data) + +{ + if (!op->getIn(1)->isConstant()) return 0; + Varnode *vn = op->getIn(0); + uintb mask = vn->getNZMask(); + int4 bitnum = leastsigbit_set(mask); + if (bitnum == -1) return 0; + if ((mask >> bitnum) != 1) return 0; // Check if only one bit active + + // Check if the active bit is getting tested + if ((op->getIn(1)->getOffset()!=mask)&& + (op->getIn(1)->getOffset()!=0)) + return 0; + + if (op->getOut()->hasNoDescend()) return 0; + // We do a basic check that the stream from which it looks like + // the bit is getting pulled is not fully consumed + if (vn->isWritten()) { + PcodeOp *andop = vn->getDef(); + if (andop->numInput()==0) return 0; + Varnode *vn0 = andop->getIn(0); + switch(andop->code()) { + case CPUI_INT_AND: + case CPUI_INT_OR: + case CPUI_INT_RIGHT: + { + if (vn0->isConstant()) return 0; + uintb mask0 = vn0->getConsume() & vn0->getNZMask(); + uintb wholemask = calc_mask(vn0->getSize()) & mask0; + // We really need a popcnt here + // We want: if the number of bits that are both consumed + // and not known to be zero are "big" then don't continue + // because it doesn't look like a few bits getting manipulated + // within a status register + if ((wholemask & 0xff)==0xff) return 0; + if ((wholemask & 0xff00)==0xff00) return 0; + } + break; + default: + break; + } + } + + SubvariableFlow subflow(&data,vn,mask,false,false,false); + if (!subflow.doTrace()) { + return 0; + } + subflow.doReplacement(); + return 1; +} + +void RuleSubvarShift::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_RIGHT); +} + +int4 RuleSubvarShift::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *vn = op->getIn(0); + if (vn->getSize() != 1) return 0; + if (!op->getIn(1)->isConstant()) return 0; + int4 sa = (int4)op->getIn(1)->getOffset(); + uintb mask = vn->getNZMask(); + if ((mask >> sa) != (uintb)1) return 0; // Pulling out a single bit + mask = (mask >> sa) << sa; + if (op->getOut()->hasNoDescend()) return 0; + + SubvariableFlow subflow(&data,vn,mask,false,false,false); + if (!subflow.doTrace()) return 0; + subflow.doReplacement(); + return 1; +} + +void RuleSubvarZext::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_ZEXT); +} + +int4 RuleSubvarZext::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *vn = op->getOut(); + Varnode *invn = op->getIn(0); + uintb mask = calc_mask(invn->getSize()); + + SubvariableFlow subflow(&data,vn,mask,invn->isPtrFlow(),false,false); + if (!subflow.doTrace()) return 0; + subflow.doReplacement(); + return 1; +} + +void RuleSubvarSext::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_INT_SEXT); +} + +int4 RuleSubvarSext::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *vn = op->getOut(); + Varnode *invn = op->getIn(0); + uintb mask = calc_mask(invn->getSize()); + + SubvariableFlow subflow(&data,vn,mask,isaggressive,true,false); + if (!subflow.doTrace()) return 0; + subflow.doReplacement(); + return 1; +} + +void RuleSubvarSext::reset(Funcdata &data) + +{ + isaggressive = data.getArch()->aggressive_ext_trim; +} + /// \brief Find or build the placeholder objects for a Varnode that needs to be split /// /// Mark the Varnode so it doesn't get revisited. @@ -1797,6 +1996,57 @@ bool SplitFlow::doTrace(void) return true; } +void RuleSplitFlow::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_SUBPIECE); +} + +int4 RuleSplitFlow::applyOp(PcodeOp *op,Funcdata &data) + +{ + int4 loSize = (int4)op->getIn(1)->getOffset(); + if (loSize == 0) // Make sure SUBPIECE doesn't take least significant part + return 0; + Varnode *vn = op->getIn(0); + if (!vn->isWritten()) + return 0; + if (vn->isPrecisLo() || vn->isPrecisHi()) + return 0; + if (op->getOut()->getSize() + loSize != vn->getSize()) + return 0; // Make sure SUBPIECE is taking most significant part + PcodeOp *concatOp = (PcodeOp *)0; + PcodeOp *multiOp = vn->getDef(); + while(multiOp->code() == CPUI_INDIRECT) { // PIECE may come through INDIRECT + Varnode *tmpvn = multiOp->getIn(0); + if (!tmpvn->isWritten()) return 0; + multiOp = tmpvn->getDef(); + } + if (multiOp->code() == CPUI_PIECE) { + if (vn->getDef() != multiOp) + concatOp = multiOp; + } + else if (multiOp->code() == CPUI_MULTIEQUAL) { // Otherwise PIECE comes through MULTIEQUAL + for(int4 i=0;inumInput();++i) { + Varnode *invn = multiOp->getIn(i); + if (!invn->isWritten()) continue; + PcodeOp *tmpOp = invn->getDef(); + if (tmpOp->code() == CPUI_PIECE) { + concatOp = tmpOp; + break; + } + } + } + if (concatOp == (PcodeOp *)0) // Didn't find the concatenate + return 0; + if (concatOp->getIn(1)->getSize() != loSize) + return 0; + SplitFlow splitFlow(&data,vn,loSize); + if (!splitFlow.doTrace()) return 0; + splitFlow.apply(); + return 1; +} + /// If \b pointer Varnode is written by a COPY, INT_ADD, PTRSUB, or PTRADD from another pointer to a /// - structure /// - array OR @@ -2628,8 +2878,7 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto if (ptrType->isPointerRel()) { TypePointerRel *ptrRel = (TypePointerRel *)ptrType; resType = ptrRel->getParent(); - baseOffset = ptrRel->getPointerOffset(); - baseOffset = AddrSpace::addressToByteInt(baseOffset, ptrRel->getWordSize()); + baseOffset = ptrRel->getByteOffset(); } else { resType = ((TypePointer *)ptrType)->getPtrTo(); @@ -2649,6 +2898,71 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto return (Datatype *)0; } +void RuleSplitCopy::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_COPY); +} + +int4 RuleSplitCopy::applyOp(PcodeOp *op,Funcdata &data) + +{ + Datatype *inType = op->getIn(0)->getTypeReadFacing(op); + Datatype *outType = op->getOut()->getTypeDefFacing(); + type_metatype metain = inType->getMetatype(); + type_metatype metaout = outType->getMetatype(); + if (metain != TYPE_PARTIALSTRUCT && metaout != TYPE_PARTIALSTRUCT && + metain != TYPE_ARRAY && metaout != TYPE_ARRAY && + metain != TYPE_STRUCT && metaout != TYPE_STRUCT) + return false; + SplitDatatype splitter(data); + if (splitter.splitCopy(op, inType, outType)) + return 1; + return 0; +} + +void RuleSplitLoad::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_LOAD); +} + +int4 RuleSplitLoad::applyOp(PcodeOp *op,Funcdata &data) + +{ + Datatype *inType = SplitDatatype::getValueDatatype(op, op->getOut()->getSize(), data.getArch()->types); + if (inType == (Datatype *)0) + return 0; + type_metatype metain = inType->getMetatype(); + if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT) + return 0; + SplitDatatype splitter(data); + if (splitter.splitLoad(op, inType)) + return 1; + return 0; +} + +void RuleSplitStore::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_STORE); +} + +int4 RuleSplitStore::applyOp(PcodeOp *op,Funcdata &data) + +{ + Datatype *outType = SplitDatatype::getValueDatatype(op, op->getIn(2)->getSize(), data.getArch()->types); + if (outType == (Datatype *)0) + return 0; + type_metatype metain = outType->getMetatype(); + if (metain != TYPE_STRUCT && metain != TYPE_ARRAY && metain != TYPE_PARTIALSTRUCT) + return 0; + SplitDatatype splitter(data); + if (splitter.splitStore(op, outType)) + return 1; + return 0; +} + /// 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 @@ -3062,6 +3376,32 @@ bool SubfloatFlow::doTrace(void) return true; } +void RuleSubfloatConvert::getOpList(vector &oplist) const + +{ + oplist.push_back(CPUI_FLOAT_FLOAT2FLOAT); +} + +int4 RuleSubfloatConvert::applyOp(PcodeOp *op,Funcdata &data) + +{ + Varnode *invn = op->getIn(0); + Varnode *outvn = op->getOut(); + int4 insize = invn->getSize(); + int4 outsize = outvn->getSize(); + if (outsize > insize) { + SubfloatFlow subflow(&data,outvn,insize); + if (!subflow.doTrace()) return 0; + subflow.apply(); + } + else { + SubfloatFlow subflow(&data,invn,outsize); + if (!subflow.doTrace()) return 0; + subflow.apply(); + } + return 1; +} + /// \brief Find or build the placeholder objects for a Varnode that needs to be split into lanes /// /// The Varnode is split based on the given subset of the lane description. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh index b7650fd851..ff719eca39 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh @@ -18,7 +18,8 @@ #ifndef __SUBFLOW_HH__ #define __SUBFLOW_HH__ -#include "funcdata.hh" +#include "ruleaction.hh" +#include "transform.hh" namespace ghidra { @@ -128,6 +129,89 @@ public: void doReplacement(void); ///< Perform the discovered transform, making logical values explicit }; +/// \brief Perform SubVariableFlow analysis triggered by INT_AND +class RuleSubvarAnd : public Rule { +public: + RuleSubvarAnd(const string &g) : Rule( g, 0, "subvar_and") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubvarAnd(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Perform SubVariableFlow analysis triggered by SUBPIECE +class RuleSubvarSubpiece : public Rule { +public: + RuleSubvarSubpiece(const string &g) : Rule( g, 0, "subvar_subpiece") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubvarSubpiece(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Perform SubvariableFlow analysis triggered by testing of a single bit +/// +/// Given a comparison (INT_EQUAL or INT_NOTEEQUAL_ to a constant, +/// check that input has only 1 bit that can possibly be non-zero +/// and that the constant is testing this. This then triggers +/// the full SubvariableFlow analysis. +class RuleSubvarCompZero : public Rule { +public: + RuleSubvarCompZero(const string &g) : Rule( g, 0, "subvar_compzero") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubvarCompZero(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Perform SubvariableFlow analysis triggered by INT_RIGHT +/// +/// If the INT_RIGHT input has only 1 bit that can possibly be non-zero +/// and it is getting shifted into the least significant bit position, +/// trigger the full SubvariableFlow analysis. +class RuleSubvarShift : public Rule { +public: + RuleSubvarShift(const string &g) : Rule( g, 0, "subvar_shift") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubvarShift(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Perform SubvariableFlow analysis triggered by INT_ZEXT +class RuleSubvarZext : public Rule { +public: + RuleSubvarZext(const string &g) : Rule( g, 0, "subvar_zext") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubvarZext(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Perform SubvariableFlow analysis triggered by INT_SEXT +class RuleSubvarSext : public Rule { + int4 isaggressive; ///< Is it guaranteed the root is a sub-variable needing to be trimmed +public: + RuleSubvarSext(const string &g) : Rule( g, 0, "subvar_sext") { isaggressive = false; } ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubvarSext(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); + virtual void reset(Funcdata &data); +}; + /// \brief Class for splitting up Varnodes that hold 2 logical variables /// /// Starting from a \e root Varnode provided to the constructor, \b this class looks for data-flow @@ -147,6 +231,22 @@ public: bool doTrace(void); ///< Trace split through data-flow, constructing transform }; +/// \brief Try to detect and split artificially joined Varnodes +/// +/// Look for SUBPIECE coming from a PIECE that has come through INDIRECTs and/or MULTIEQUAL +/// Then: check if the input to SUBPIECE can be viewed as two independent pieces +/// If so: split the pieces into independent data-flows +class RuleSplitFlow : public Rule { +public: + RuleSplitFlow(const string &g) : Rule( g, 0, "splitflow") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSplitFlow(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + /// \brief Split a p-code COPY, LOAD, or STORE op based on underlying composite data-type /// /// During the cleanup phase, if a COPY, LOAD, or STORE occurs on a partial structure or array @@ -208,6 +308,51 @@ public: static Datatype *getValueDatatype(PcodeOp *loadStore,int4 size,TypeFactory *tlst); }; +/// \brief Split COPY ops based on TypePartialStruct +/// +/// If more than one logical component of a structure or array is copied at once, +/// rewrite the COPY operator as multiple COPYs. +class RuleSplitCopy : public Rule { +public: + RuleSplitCopy(const string &g) : Rule( g, 0, "splitcopy") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSplitCopy(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Split LOAD ops based on TypePartialStruct +/// +/// If more than one logical component of a structure or array is loaded at once, +/// rewrite the LOAD operator as multiple LOADs. +class RuleSplitLoad : public Rule { +public: + RuleSplitLoad(const string &g) : Rule( g, 0, "splitload") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSplitLoad(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + +/// \brief Split STORE ops based on TypePartialStruct +/// +/// If more than one logical component of a structure or array is stored at once, +/// rewrite the STORE operator as multiple STOREs. +class RuleSplitStore : public Rule { +public: + RuleSplitStore(const string &g) : Rule( g, 0, "splitstore") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSplitStore(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 @@ -242,6 +387,18 @@ public: bool doTrace(void); ///< Trace logical value as far as possible }; +/// \brief Perform SubfloatFlow analysis triggered by FLOAT_FLOAT2FLOAT +class RuleSubfloatConvert : public Rule { +public: + RuleSubfloatConvert(const string &g) : Rule( g, 0, "subfloat_convert") {} ///< Constructor + virtual Rule *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Rule *)0; + return new RuleSubfloatConvert(getGroup()); + } + virtual void getOpList(vector &oplist) const; + virtual int4 applyOp(PcodeOp *op,Funcdata &data); +}; + /// \brief Class for splitting data-flow on \e laned registers /// /// From a root Varnode and a description of its \e lanes, trace data-flow as far as diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc index c8e43af58a..1f3614f5e6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc @@ -113,6 +113,24 @@ void FunctionTestCollection::clear(void) console->reset(); } +/// Remove any carriage return character as well. +/// \param ref is the string to strip +/// \return the stripped string +string FunctionTestCollection::stripNewlines(const string &ref) + +{ + string res; + + for(int4 i=0;i tag void FunctionTestCollection::restoreXmlCommands(const Element *el) @@ -122,7 +140,7 @@ void FunctionTestCollection::restoreXmlCommands(const Element *el) for(iter=list.begin();iter!=list.end();++iter) { const Element *subel = *iter; - commands.push_back(subel->getContent()); + commands.push_back(stripNewlines(subel->getContent())); } } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh index 2b7cc6b3d4..121032661d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.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. @@ -77,6 +77,7 @@ class FunctionTestCollection { mutable int4 numTestsApplied; ///< Count of tests that were executed mutable int4 numTestsSucceeded; ///< Count of tests that passed void clear(void); ///< Clear any previous architecture and function + static string stripNewlines(const string &ref); ///< Convert any \e newline character to a \e space void restoreXmlCommands(const Element *el); ///< Reconstruct commands from an XML tag void buildProgram(DocumentStorage &store); ///< Build program (Architecture) from \ tag void startTests(void) const; ///< Initialize each FunctionTestProperty diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 8eb8d42bc8..fd0ab26fb4 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -20,16 +20,17 @@ namespace ghidra { /// The base propagation ordering associated with each meta-type. /// The array elements correspond to the ordering of #type_metatype. -sub_metatype Datatype::base2sub[15] = { - SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UNION, SUB_STRUCT, SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE, - SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, SUB_SPACEBASE, SUB_VOID +sub_metatype Datatype::base2sub[18] = { + SUB_PARTIALUNION, SUB_PARTIALSTRUCT, SUB_UINT_ENUM, SUB_UNION, SUB_STRUCT, SUB_INT_ENUM, SUB_UINT_ENUM, + SUB_ARRAY, SUB_PTRREL, SUB_PTR, SUB_FLOAT, SUB_CODE, SUB_BOOL, SUB_UINT_PLAIN, SUB_INT_PLAIN, SUB_UNKNOWN, + SUB_SPACEBASE, SUB_VOID }; AttributeId ATTRIB_ALIGNMENT = AttributeId("alignment",47); AttributeId ATTRIB_ARRAYSIZE = AttributeId("arraysize",48); AttributeId ATTRIB_CHAR = AttributeId("char",49); AttributeId ATTRIB_CORE = AttributeId("core",50); -AttributeId ATTRIB_ENUM = AttributeId("enum",51); +//AttributeId ATTRIB_ENUM = AttributeId("enum",51); // deprecated AttributeId ATTRIB_INCOMPLETE = AttributeId("incomplete",52); //AttributeId ATTRIB_ENUMSIZE = AttributeId("enumsize",53); // deprecated //AttributeId ATTRIB_INTSIZE = AttributeId("intsize",54); // deprecated @@ -250,12 +251,21 @@ void metatype2string(type_metatype metatype,string &res) case TYPE_ARRAY: res = "array"; break; + case TYPE_PARTIALENUM: + res = "partenum"; + break; case TYPE_PARTIALSTRUCT: res = "partstruct"; break; case TYPE_PARTIALUNION: res = "partunion"; break; + case TYPE_ENUM_INT: + res = "enum_int"; + break; + case TYPE_ENUM_UINT: + res = "enum_uint"; + break; case TYPE_STRUCT: res = "struct"; break; @@ -309,6 +319,12 @@ type_metatype string2metatype(const string &metastring) if (metastring=="array") return TYPE_ARRAY; break; + case 'e': + if (metastring=="enum_int") + return TYPE_ENUM_INT; + else if (metastring == "enum_uint") + return TYPE_ENUM_UINT; + break; case 's': if (metastring=="struct") return TYPE_STRUCT; @@ -1083,6 +1099,12 @@ TypePointer *TypePointer::downChain(int8 &off,TypePointer *&par,int8 &parOff,boo } } + if (ptrto->isEnumType()) { + // Go "into" the enumeration + Datatype *tmp = typegrp.getBase(1, TYPE_UINT); + off = 0; + return typegrp.getTypePointer(size,tmp,wordsize); + } type_metatype meta = ptrto->getMetatype(); bool isArray = (meta == TYPE_ARRAY); if (isArray || meta == TYPE_STRUCT) { @@ -1325,81 +1347,22 @@ TypeEnum::TypeEnum(const TypeEnum &op) : TypeBase(op) { namemap = op.namemap; - masklist = op.masklist; - flags |= (op.flags&poweroftwo)|enumtype; } -/// Set the map. Calculate the independent bit-fields within the named values of the enumeration -/// Two bits are in the same bit-field if there is a name in the map whose value -/// has those two bits set. Bit-fields must be a contiguous range of bits. -void TypeEnum::setNameMap(const map &nmap) +/// \param val is the given value to test +/// \return \b true if \b this enumeration has a name with the value +bool TypeEnum::hasNamedValue(uintb val) const { - map::const_iterator iter; - uintb curmask,lastmask; - int4 maxbit; - int4 curmaxbit; - bool fieldisempty; - - namemap = nmap; - masklist.clear(); - - flags &= ~((uint4)poweroftwo); - - maxbit = 8 * size - 1; - - curmaxbit = 0; - while(curmaxbit <= maxbit) { - curmask = 1; - curmask <<= curmaxbit; - lastmask = 0; - fieldisempty = true; - while(curmask != lastmask) { // Repeat until there is no change in the current mask - lastmask = curmask; // Note changes from last time through - - for(iter=namemap.begin();iter!=namemap.end();++iter) { // For every named enumeration value - uintb val = (*iter).first; - if ((val & curmask) != 0) { // If the value shares ANY bits in common with the current mask - curmask |= val; // Absorb ALL defined bits of the value into the current mask - fieldisempty = false; - } - } - - // Fill in any holes in the mask (bit field must consist of contiguous bits - int4 lsb = leastsigbit_set(curmask); - int4 msb = mostsigbit_set(curmask); - if (msb > curmaxbit) - curmaxbit = msb; - - uintb mask1 = 1; - mask1 = (mask1 << lsb) - 1; // every bit below lsb is set to 1 - uintb mask2 = 1; - mask2 <<= msb; - mask2 <<= 1; - mask2 -= 1; // every bit below or equal to msb is set to 1 - curmask = mask1 ^ mask2; - } - if (fieldisempty) { // If no value hits this bit - if (!masklist.empty()) - masklist.back() |= curmask; // Include the bit with the previous mask - else - masklist.push_back(curmask); - } - else - masklist.push_back(curmask); - curmaxbit += 1; - } - if (masklist.size() > 1) - flags |= poweroftwo; + return (namemap.find(val) != namemap.end()); } /// Given a specific value of the enumeration, calculate the named representation of that value. /// The representation is returned as a list of names that must logically ORed and possibly complemented. /// If no representation is possible, no names will be returned. /// \param val is the value to find the representation for -/// \param valnames will hold the returned list of names -/// \return true if the representation needs to be complemented -bool TypeEnum::getMatches(uintb val,vector &valnames) const +/// \param rep will contain the individual names in the representation and other transforms +void TypeEnum::getMatches(uintb val,Representation &rep) const { map::const_iterator iter; @@ -1407,33 +1370,47 @@ bool TypeEnum::getMatches(uintb val,vector &valnames) const for(count=0;count<2;++count) { bool allmatch = true; - if (val == 0) { // Zero handled specially, it crosses all masks + if (val == 0) { // Zero handled specially iter = namemap.find(val); if (iter != namemap.end()) - valnames.push_back( (*iter).second ); + rep.matchname.push_back( (*iter).second ); else allmatch = false; } else { - for(int4 i=0;i= bitsleft) break; // Could not match most significant bit of bitsleft + if ((curval & diff) == 0) { + // Found a named value that matches at least most significant bit of bitsleft + rep.matchname.push_back( (*iter).second ); // Accept the name + bitsleft ^= curval; // Remove the bits from bitsleft + target = bitsleft; // Continue searching for named value that match the new bitsleft + } + else { + // Not all the (one) bits of curval match into bitsleft, but we can restrict a further search. + // Bits above diff in curval are the maximum we can hope to match with one named value. + // Zero out bits below this and prepare to search at or below this value + target = curval & ~diff; } } + allmatch = (bitsleft == 0); + } + if (allmatch) { // If we have a complete representation + rep.complement = (count==1); // Set whether we represented original value or complement + return; } - if (allmatch) // If we have a complete representation - return (count==1); // Return whether we represented original value or complement val = val ^ calc_mask(size); // Switch value we are trying to represent (to complement) - valnames.clear(); // Clear out old attempt + rep.matchname.clear(); // Clear out old attempt } - return false; // If we reach here, no representation was possible, -valnames- is empty + // If we reach here, no representation was possible, -matchname- is empty } int4 TypeEnum::compare(const Datatype &op,int4 level) const @@ -1475,8 +1452,7 @@ void TypeEnum::encode(Encoder &encoder) const return; } encoder.openElement(ELEM_TYPE); - encodeBasic(metatype,-1,encoder); - encoder.writeString(ATTRIB_ENUM, "true"); + encodeBasic((metatype == TYPE_INT) ? TYPE_ENUM_INT : TYPE_ENUM_UINT,-1,encoder); map::const_iterator iter; for(iter=namemap.begin();iter!=namemap.end();++iter) { encoder.openElement(ELEM_VAL); @@ -1496,7 +1472,7 @@ string TypeEnum::decode(Decoder &decoder,TypeFactory &typegrp) { // uint4 elemId = decoder.openElement(); decodeBasic(decoder); - submeta = (metatype == TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; + metatype = (metatype == TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; // Use TYPE_INT or TYPE_UINT internally map nmap; string warning; @@ -2265,6 +2241,81 @@ void TypeUnion::assignFieldOffsets(vector &list,int4 &newSize,int4 &n } } +TypePartialEnum::TypePartialEnum(const TypePartialEnum &op) + : TypeEnum(op) +{ + stripped = op.stripped; + parent = op.parent; + offset = op.offset; +} + +TypePartialEnum::TypePartialEnum(TypeEnum *par,int4 off,int4 sz,Datatype *strip) + : TypeEnum(sz, TYPE_PARTIALENUM) +{ + flags |= has_stripped; + stripped = strip; + parent = par; + offset = off; +} + +void TypePartialEnum::printRaw(ostream &s) const + +{ + parent->printRaw(s); + s << "[off=" << dec << offset << ",sz=" << size << ']'; +} + +bool TypePartialEnum::hasNamedValue(uintb val) const + +{ + val <<= 8*offset; + return parent->hasNamedValue(val); +} + +void TypePartialEnum::getMatches(uintb val,Representation &rep) const + +{ + val <<= 8*offset; + rep.shiftAmount = offset * 8; + parent->getMatches(val,rep); +} + +int4 TypePartialEnum::compare(const Datatype &op,int4 level) const + +{ + int4 res = Datatype::compare(op,level); + if (res != 0) return res; + // Both must be partial + TypePartialEnum *tp = (TypePartialEnum *) &op; + if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1; + level -= 1; + if (level < 0) { + if (id == op.getId()) return 0; + return (id < op.getId()) ? -1 : 1; + } + return parent->compare(*tp->parent,level); // Compare the underlying union +} + +int4 TypePartialEnum::compareDependency(const Datatype &op) const + +{ + if (submeta != op.getSubMeta()) return (submeta < op.getSubMeta()) ? -1 : 1; + TypePartialEnum *tp = (TypePartialEnum *) &op; // Both must be partial + if (parent != tp->parent) return (parent < tp->parent) ? -1 : 1; // Compare absolute pointers + if (offset != tp->offset) return (offset < tp->offset) ? -1 : 1; + return (op.getSize()-size); +} + +void TypePartialEnum::encode(Encoder &encoder) const + +{ + encoder.openElement(ELEM_TYPE); + encodeBasic(TYPE_PARTIALENUM,-1,encoder); + encoder.writeSignedInteger(ATTRIB_OFFSET, offset); + parent->encodeRef(encoder); + encoder.closeElement(ELEM_TYPE); +} + TypePartialStruct::TypePartialStruct(const TypePartialStruct &op) : Datatype(op) { @@ -3111,7 +3162,7 @@ void TypeFactory::setupSizes(void) setDefaultAlignmentMap(); if (enumsize == 0) { enumsize = glb->getDefaultSize(); - enumtype = TYPE_UINT; + enumtype = TYPE_ENUM_UINT; } } @@ -3866,6 +3917,11 @@ TypeStruct *TypeFactory::getTypeStruct(const string &n) return (TypeStruct *) findAdd(tmp); } +/// Create a data-type representing storage of part of an \e array or \e structure. +/// \param contain is the parent \e array or \e structure data-type that we are taking a part of. +/// \param off is the offset (in bytes) within the parent that the partial data-type starts at +/// \param sz is the number of bytes in the partial data-type +/// \return the TypePartialStruct object TypePartialStruct *TypeFactory::getTypePartialStruct(Datatype *contain,int4 off,int4 sz) { @@ -3887,6 +3943,11 @@ TypeUnion *TypeFactory::getTypeUnion(const string &n) return (TypeUnion *) findAdd(tmp); } +/// Create a data-type representing storage of part of a \e union data-type. +/// \param contain is the parent \e union data-type that we are taking a part of. +/// \param off is the offset (in bytes) within the parent that the partial data-type starts at +/// \param sz is the number of bytes in the partial data-type +/// \return the TypePartialUnion object TypePartialUnion *TypeFactory::getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz) { @@ -3907,6 +3968,19 @@ TypeEnum *TypeFactory::getTypeEnum(const string &n) return (TypeEnum *) findAdd(tmp); } +/// Create a data-type representing storage of part of an \e enumeration. +/// \param contain is the parent \e enumeration data-type that we are taking a part of. +/// \param off is the offset (in bytes) within the parent that the partial data-type starts at +/// \param sz is the number of bytes in the partial data-type +/// \return the TypePartialEnum object +TypePartialEnum *TypeFactory::getTypePartialEnum(TypeEnum *contain,int4 off,int4 sz) + +{ + Datatype *strip = getBase(sz, TYPE_UNKNOWN); + TypePartialEnum tpe(contain,off,sz,strip); + return (TypePartialEnum *) findAdd(tpe); +} + /// Creates the special TypeSpacebase with an associated address space and scope /// \param id is the address space /// \param addr specifies the function scope, or isInvalid() for global scope @@ -4032,6 +4106,8 @@ Datatype *TypeFactory::getExactPiece(Datatype *ct,int4 offset,int4 size) // If we reach here, lastType is bigger than size if (lastType->getMetatype() == TYPE_STRUCT || lastType->getMetatype() == TYPE_ARRAY) return getTypePartialStruct(lastType, lastOff, size); + else if (lastType->isEnumType() && !lastType->hasStripped()) + return getTypePartialEnum((TypeEnum *)lastType, lastOff, size); } return (Datatype *)0; } @@ -4238,7 +4314,7 @@ Datatype *TypeFactory::decodeTypedef(Decoder &decoder) Datatype *TypeFactory::decodeEnum(Decoder &decoder,bool forcecore) { - TypeEnum te(1,TYPE_INT); // size and metatype are replaced + TypeEnum te(1,TYPE_ENUM_INT); // metatype and size are replaced string warning = te.decode(decoder,*this); if (forcecore) te.flags |= Datatype::coretype; @@ -4399,6 +4475,10 @@ Datatype *TypeFactory::decodeTypeNoRef(Decoder &decoder,bool forcecore) ct = findAdd(ta); } break; + case TYPE_ENUM_INT: + case TYPE_ENUM_UINT: + ct = decodeEnum(decoder,forcecore); + break; case TYPE_STRUCT: ct = decodeStruct(decoder,forcecore); break; @@ -4438,12 +4518,6 @@ Datatype *TypeFactory::decodeTypeNoRef(Decoder &decoder,bool forcecore) decoder.closeElement(elemId); return ct; } - else if (attribId == ATTRIB_ENUM && decoder.readBool()) { - decoder.rewindAttributes(); - ct = decodeEnum(decoder, forcecore); - decoder.closeElement(elemId); - return ct; - } else if (attribId == ATTRIB_UTF && decoder.readBool()) { TypeUnicode tu; decoder.rewindAttributes(); @@ -4587,9 +4661,9 @@ void TypeFactory::parseEnumConfig(Decoder &decoder) uint4 elemId = decoder.openElement(ELEM_ENUM); enumsize = decoder.readSignedInteger(ATTRIB_SIZE); if (decoder.readBool(ATTRIB_SIGNED)) - enumtype = TYPE_INT; + enumtype = TYPE_ENUM_INT; else - enumtype = TYPE_UINT; + enumtype = TYPE_ENUM_UINT; decoder.closeElement(elemId); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index 7ee40540fd..95ceaf9bb1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -27,7 +27,7 @@ extern AttributeId ATTRIB_ALIGNMENT; ///< Marshaling attribute "alignment" extern AttributeId ATTRIB_ARRAYSIZE; ///< Marshaling attribute "arraysize" extern AttributeId ATTRIB_CHAR; ///< Marshaling attribute "char" extern AttributeId ATTRIB_CORE; ///< Marshaling attribute "core" -extern AttributeId ATTRIB_ENUM; ///< Marshaling attribute "enum" +//extern AttributeId ATTRIB_ENUM; ///< Marshaling attribute "enum" deprecated extern AttributeId ATTRIB_INCOMPLETE; ///< Marshaling attribute "incomplete" //extern AttributeId ATTRIB_ENUMSIZE; ///< Marshaling attribute "enumsize" deprecated //extern AttributeId ATTRIB_INTSIZE; ///< Marshaling attribute "intsize" deprecated @@ -77,20 +77,23 @@ extern void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseadd /// The core meta-types supported by the decompiler. These are sizeless templates /// for the elements making up the type algebra. Index is important for Datatype::base2sub array. enum type_metatype { - TYPE_VOID = 14, ///< Standard "void" type, absence of type - TYPE_SPACEBASE = 13, ///< Placeholder for symbol/type look-up calculations - TYPE_UNKNOWN = 12, ///< An unknown low-level type. Treated as an unsigned integer. - TYPE_INT = 11, ///< Signed integer. Signed is considered less specific than unsigned in C - TYPE_UINT = 10, ///< Unsigned integer - TYPE_BOOL = 9, ///< Boolean - TYPE_CODE = 8, ///< Data is actual executable code - TYPE_FLOAT = 7, ///< Floating-point + TYPE_VOID = 17, ///< Standard "void" type, absence of type + TYPE_SPACEBASE = 16, ///< Placeholder for symbol/type look-up calculations + TYPE_UNKNOWN = 15, ///< An unknown low-level type. Treated as an unsigned integer. + TYPE_INT = 14, ///< Signed integer. Signed is considered less specific than unsigned in C + TYPE_UINT = 13, ///< Unsigned integer + TYPE_BOOL = 12, ///< Boolean + TYPE_CODE = 11, ///< Data is actual executable code + TYPE_FLOAT = 10, ///< Floating-point - TYPE_PTR = 6, ///< Pointer data-type - TYPE_PTRREL = 5, ///< Pointer relative to another data-type (specialization of TYPE_PTR) - TYPE_ARRAY = 4, ///< Array data-type, made up of a sequence of "element" datatype - TYPE_STRUCT = 3, ///< Structure data-type, made up of component datatypes - TYPE_UNION = 2, ///< An overlapping union of multiple datatypes + TYPE_PTR = 9, ///< Pointer data-type + TYPE_PTRREL = 8, ///< Pointer relative to another data-type (specialization of TYPE_PTR) + TYPE_ARRAY = 7, ///< Array data-type, made up of a sequence of "element" datatype + TYPE_ENUM_UINT = 6, ///< Unsigned enumeration data-type (specialization of TYPE_UINT) + TYPE_ENUM_INT = 5, ///< Signed enumeration data-type (specialization of TYPE_INT) + TYPE_STRUCT = 4, ///< Structure data-type, made up of component datatypes + TYPE_UNION = 3, ///< An overlapping union of multiple datatypes + TYPE_PARTIALENUM = 2, ///< Part of an enumerated value (specialization of TYPE_UINT) TYPE_PARTIALSTRUCT = 1, ///< Part of a structure, stored separately from the whole TYPE_PARTIALUNION = 0 ///< Part of a union }; @@ -160,7 +163,7 @@ struct DatatypeCompare; /// Used for symbols, function prototypes, type propagation etc. class Datatype { protected: - static sub_metatype base2sub[15]; + static sub_metatype base2sub[18]; /// Boolean properties of datatypes enum { coretype = 1, ///< This is a basic type which will never be redefined @@ -213,7 +216,6 @@ public: bool isCoreType(void) const { return ((flags&coretype)!=0); } ///< Is this a core data-type bool isCharPrint(void) const { return ((flags&(chartype|utf16|utf32|opaque_string))!=0); } ///< Does this print as a 'char' bool isEnumType(void) const { return ((flags&enumtype)!=0); } ///< Is this an enumerated type - bool isPowerOfTwo(void) const { return ((flags&poweroftwo)!=0); } ///< Is this a flag-based enumeration bool isASCII(void) const { return ((flags&chartype)!=0); } ///< Does this print as an ASCII 'char' bool isUTF16(void) const { return ((flags&utf16)!=0); } ///< Does this print as UTF16 'wchar' bool isUTF32(void) const { return ((flags&utf32)!=0); } ///< Does this print as UTF32 'wchar' @@ -466,24 +468,33 @@ public: /// This supports combinations of the enumeration values (using logical OR and bit-wise complement) /// by defining independent \b bit-fields. class TypeEnum : public TypeBase { +public: + /// \brief Class describing how a particular enumeration value is constructed using tokens + class Representation { + public: + vector matchname; ///< Name tokens that are ORed together + bool complement; ///< If \b true, bitwise complement value after ORing + int4 shiftAmount; ///< Number of bits to left-shift final value + Representation(void) { complement = false; shiftAmount = 0; } ///< Constructor + }; protected: friend class TypeFactory; map namemap; ///< Map from integer to name - vector masklist; ///< Masks for each bitfield within the enum - void setNameMap(const map &nmap); ///< Establish the value -> name map + void setNameMap(const map &nmap) { namemap = nmap; } ///< Establish the value -> name map string decode(Decoder &decoder,TypeFactory &typegrp); ///< Restore \b this enum data-type from a stream public: /// Construct from another TypeEnum TypeEnum(const TypeEnum &op); /// Construct from a size and meta-type (TYPE_INT or TYPE_UINT) TypeEnum(int4 s,type_metatype m) : TypeBase(s,m) { - flags |= enumtype; submeta = (m==TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; } + flags |= enumtype; metatype = (m==TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; } /// Construct from a size, meta-type, and name TypeEnum(int4 s,type_metatype m,const string &nm) : TypeBase(s,m,nm) { - flags |= enumtype; submeta = (m==TYPE_INT) ? SUB_INT_ENUM : SUB_UINT_ENUM; } + flags |= enumtype; metatype = (m==TYPE_ENUM_INT) ? TYPE_INT : TYPE_UINT; } map::const_iterator beginEnum(void) const { return namemap.begin(); } ///< Beginning of name map map::const_iterator endEnum(void) const { return namemap.end(); } ///< End of name map - bool getMatches(uintb val,vector &matchname) const; ///< Recover the named representation + virtual bool hasNamedValue(uintb val) const; ///< Does \b this have a (single) name for the given value + virtual void getMatches(uintb val,Representation &rep) const; ///< Recover the named representation virtual int4 compare(const Datatype &op,int4 level) const; virtual int4 compareDependency(const Datatype &op) const; virtual Datatype *clone(void) const { return new TypeEnum(*this); } @@ -553,6 +564,27 @@ public: static void assignFieldOffsets(vector &list,int4 &newSize,int4 &newAlign,TypeUnion *tu); ///< Assign field offsets }; +/// \brief A data-type thats holds part of a TypeEnum and possible additional padding +class TypePartialEnum : public TypeEnum { + friend class TypeFactory; + Datatype *stripped; ///< The \e undefined data-type to use if a formal data-type is required. + TypeEnum *parent; ///< The enumeration data-type \b this is based on + int4 offset; ///< Byte offset with the parent enum where \b this starts +public: + TypePartialEnum(const TypePartialEnum &op); ///< Construct from another TypePartialEnum + TypePartialEnum(TypeEnum *par,int4 off,int4 sz,Datatype *strip); ///< Constructor + int4 getOffset(void) const { return offset; } ///< Get the byte offset into the containing data-type + Datatype *getParent(void) const { return parent; } ///< Get the enumeration containing \b this piece + virtual void printRaw(ostream &s) const; + virtual bool hasNamedValue(uintb val) const; + virtual void getMatches(uintb val,Representation &rep) const; + virtual int4 compare(const Datatype &op,int4 level) const; + virtual int4 compareDependency(const Datatype &op) const; + virtual Datatype *clone(void) const { return new TypePartialEnum(*this); } + virtual void encode(Encoder &encoder) const; + virtual Datatype *getStripped(void) const { return stripped; } +}; + /// \brief A data-type that holds \e part of a TypeStruct or TypeArray class TypePartialStruct : public Datatype { friend class TypeFactory; @@ -634,7 +666,12 @@ public: /// \brief Get offset of \b this pointer relative to start of the containing data-type /// /// \return the offset value in \e address \e units - int4 getPointerOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); } + int4 getAddressOffset(void) const { return AddrSpace::byteToAddressInt(offset, wordsize); } + + /// \brief Get offset of \b this pointer relative to start of the containing data-type + /// + /// \return the offset value in \e byte units + int4 getByteOffset(void) const { return offset; } virtual void printRaw(ostream &s) const; virtual int4 compare(const Datatype &op,int4 level) const; virtual int4 compareDependency(const Datatype &op) const; @@ -803,6 +840,7 @@ public: TypeUnion *getTypeUnion(const string &n); ///< Create an (empty) union TypePartialUnion *getTypePartialUnion(TypeUnion *contain,int4 off,int4 sz); ///< Create a partial union TypeEnum *getTypeEnum(const string &n); ///< Create an (empty) enumeration + TypePartialEnum *getTypePartialEnum(TypeEnum *contain,int4 off,int4 sz); ///< Create a partial enumeration TypeSpacebase *getTypeSpacebase(AddrSpace *id,const Address &addr); ///< Create a "spacebase" type TypeCode *getTypeCode(const PrototypePieces &proto); ///< Create a "function" datatype Datatype *getTypedef(Datatype *ct,const string &name,uint8 id,uint4 format); ///< Create a new \e typedef data-type diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc index 541cb2462d..01ecc71d4f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.cc @@ -175,10 +175,11 @@ OpCode TypeOp::floatSignManipulation(PcodeOp *op) return CPUI_MAX; } -/// \brief Propagate a dereferenced data-type up to its pointer data-type +/// \brief Propagate a dereferenced data-type up to its pointer data-type through a LOAD or STORE /// /// Don't create more than a depth of 1, i.e. ptr->ptr -/// \param pt is the pointed-to data-type +/// \param t is the TypeFactory containing the data-types +/// \param dt is the pointed-to data-type /// \param sz is the size of the pointer /// \param wordsz is the wordsize associated with the pointer /// \return the TypePointer object @@ -196,6 +197,36 @@ Datatype *TypeOp::propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wo return t->getTypePointer(sz,dt,wordsz); } +/// \brief Propagate a pointer data-type down to its element data-type through a LOAD or STORE +/// +/// \param t is the TypeFactory containing the data-types +/// \param dt is the pointer data-type +/// \param sz is the size of the dereferenced pointer +/// \return the dereferenced data-type +Datatype *TypeOp::propagateFromPointer(TypeFactory *t,Datatype *dt,int4 sz) + +{ + if (dt->getMetatype() != TYPE_PTR) + return (Datatype *)0; + Datatype *ptrto = ((TypePointer *)dt)->getPtrTo(); + if (ptrto->isVariableLength()) + return (Datatype *)0; + if (ptrto->getSize() == sz) + return ptrto; + // If we reach here, there is a size mismatch between the pointer data-type and the dereferenced value. + // We only propagate (partial) enumerations in this case. + if (dt->isPointerRel()) { + TypePointerRel *ptrrel = (TypePointerRel *)dt; + Datatype *res = t->getExactPiece(ptrrel->getParent(), ptrrel->getByteOffset(), sz); + if (res != (Datatype *)0 && res->isEnumType()) + return res; + } + else if (ptrto->isEnumType() && !ptrto->hasStripped()) { + return t->getTypePartialEnum((TypeEnum *)ptrto, 0, sz); + } + return (Datatype *)0; +} + /// \param t is the TypeFactory used to construct data-types /// \param opc is the op-code value the new object will represent /// \param n is the display name that will represent the op-code @@ -463,13 +494,8 @@ Datatype *TypeOpLoad::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn, AddrSpace *spc = op->getIn(0)->getSpaceFromConst(); newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize()); } - else if (alttype->getMetatype()==TYPE_PTR) { - newtype = ((TypePointer *)alttype)->getPtrTo(); - if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength()) // Size must be appropriate - newtype = (Datatype *)0; - } else - newtype = (Datatype *)0; // Don't propagate anything + newtype = propagateFromPointer(tlst, alttype, outvn->getSize()); return newtype; } @@ -538,13 +564,8 @@ Datatype *TypeOpStore::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn AddrSpace *spc = op->getIn(0)->getSpaceFromConst(); newtype = propagateToPointer(tlst,alttype,outvn->getSize(),spc->getWordSize()); } - else if (alttype->getMetatype()==TYPE_PTR) { - newtype = ((TypePointer *)alttype)->getPtrTo(); - if (newtype->getSize() != outvn->getSize() || newtype->isVariableLength()) - newtype = (Datatype *)0; - } else - newtype = (Datatype *)0; // Don't propagate anything + newtype = propagateFromPointer(tlst, alttype, outvn->getSize()); return newtype; } @@ -933,7 +954,7 @@ Datatype *TypeOpEqual::propagateAcrossCompare(Datatype *alttype,TypeFactory *typ } else if (alttype->isPointerRel() && !outvn->isConstant()) { TypePointerRel *relPtr = (TypePointerRel *)alttype; - if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getPointerOffset() >= 0) { + if (relPtr->getParent()->getMetatype() == TYPE_STRUCT && relPtr->getByteOffset() >= 0) { // If we know the pointer is in the middle of a structure, don't propagate across comparison operators // as the two sides of the operator are likely to be different types , and the other side can also // get data-type information from the structure pointer @@ -1383,7 +1404,7 @@ Datatype *TypeOpIntXor::getOutputToken(const PcodeOp *op,CastStrategy *castStrat Datatype *TypeOpIntXor::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn, int4 inslot,int4 outslot) { - if (!alttype->isPowerOfTwo()) { + if (!alttype->isEnumType()) { if (alttype->getMetatype() != TYPE_FLOAT) return (Datatype *)0; if (floatSignManipulation(op) == CPUI_MAX) @@ -1416,7 +1437,7 @@ Datatype *TypeOpIntAnd::getOutputToken(const PcodeOp *op,CastStrategy *castStrat Datatype *TypeOpIntAnd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn, int4 inslot,int4 outslot) { - if (!alttype->isPowerOfTwo()) { + if (!alttype->isEnumType()) { if (alttype->getMetatype() != TYPE_FLOAT) return (Datatype *)0; if (floatSignManipulation(op) == CPUI_MAX) @@ -1449,7 +1470,7 @@ Datatype *TypeOpIntOr::getOutputToken(const PcodeOp *op,CastStrategy *castStrate Datatype *TypeOpIntOr::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn, int4 inslot,int4 outslot) { - if (!alttype->isPowerOfTwo()) return (Datatype *)0; // Only propagate flag enums + if (!alttype->isEnumType()) return (Datatype *)0; // Only propagate enums Datatype *newtype; if (invn->isSpacebase()) { AddrSpace *spc = tlst->getArch()->getDefaultDataSpace(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh index 72517448b5..51b20cc7e8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/typeop.hh @@ -181,6 +181,7 @@ public: /// \brief Return the floating-point operation associated with the \e sign bit manipulation by the given PcodeOp static OpCode floatSignManipulation(PcodeOp *op); static Datatype *propagateToPointer(TypeFactory *t,Datatype *dt,int4 sz,int4 wordsz); + static Datatype *propagateFromPointer(TypeFactory *t,Datatype *dt,int4 sz); }; // Major classes of operations diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc index 08f48726de..8e226258c7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.cc @@ -297,6 +297,27 @@ void HighVariable::transferPiece(HighVariable *tv2) tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty); } +/// Except in specific circumstances, convert \b type into its stripped form. +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 + } + } + else if (type->isEnumType()) { + if (inst.size() != 1 || !inst[0]->isConstant()) // Only preserve partial enum on a constant + type = type->getStripped(); + } + else + type = type->getStripped(); +} + /// Only update if the cover is marked as \e dirty. /// Merge the covers of all Varnode instances. void HighVariable::updateInternalCover(void) const @@ -386,17 +407,7 @@ void HighVariable::updateType(void) const vn = getTypeRepresentative(); type = vn->getType(); - if (type->hasStripped()) { - 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 - } - } - else - type = type->getStripped(); - } + stripType(); // Update lock flags flags &= ~Varnode::typelock; if (vn->isTypeLock()) @@ -549,17 +560,7 @@ void HighVariable::finalizeDatatype(TypeFactory *typeFactory) if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN) return; type = tp; - if (type->hasStripped()) { - if (type->getMetatype() == TYPE_PARTIALUNION) { - if (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 - } - } - else - type = type->getStripped(); - } + stripType(); highflags |= type_finalized; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh index 5c5f621528..5e833b633a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/variable.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/variable.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. @@ -167,6 +167,7 @@ private: void symbolDirty(void) const { highflags |= symboldirty; } ///< Mark the symbol as \e dirty void setUnmerged(void) const { highflags |= unmerged; } ///< Mark \b this as having merge problems bool isCoverDirty(void) const; ///< Is the cover returned by getCover() up-to-date + void stripType(void) const; ///< Take the stripped form of the current data-type. public: HighVariable(Varnode *vn); ///< Construct a HighVariable with a single member Varnode ~HighVariable(void); ///< Destructor diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml new file mode 100644 index 0000000000..61d4937427 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/enum.xml @@ -0,0 +1,108 @@ + + + + +4883ec184889e7e8f40f0000f6442408 +4175054883c418c3bf00000000e8e60f +0000ebef4883ec184889e7e8d00f0000 +f744240c0003000075054883c418c3bf +00000000e8bf0f0000ebef + + + f647082c7501c34883ec08 +bf02000000e88e0f00004883c408c3f7 +470c571300007501c34883ec08bf0300 +0000e8710f00004883c408c3 + + + 48817f08000810007401c34883 +ec08bf06000000e81c0f00004883c408 +c3 + + + + + + + + + +Stack_18\.flagfield & \(FLAG_40\|FLAG_1\)\) .= 0\) +Stack_18\.flagfield\._4_4_ & \(HIGH_200\|HIGH_100\) >> 0x20\) .= 0\) +ptr->flagfield & \(FLAG_20\|FLAG_8\|FLAG_4\)\) .= 0\) +if \(\(ptrhigh->flagfield & \(HIGH_1000\|HIGH_200\|HIGH_100\|HIGH_40\|HIGH_10\|HIGH_4\|HIGH_2\|HIGH_1\)\) .= 0 +if \(ptrequal->flagfield .= \(FLAG_100000\|FLAG_800\)\) + diff --git a/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc b/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc index a25b36c7ac..542d39267e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.cc +++ b/Ghidra/Features/Decompiler/src/decompile/unittests/testtypes.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. @@ -212,4 +212,52 @@ TEST(cast_integertoken) { ASSERT(!longPrinted(CPUI_INT_RIGHT,parse("uint8"),0x100000000)); } +TEST(enum_matching) { + TypeTestEnvironment::build(); + TypeEnum *enum3 = (TypeEnum *)parse("enum enum3 { ZERO=0, ONE=1, TWO=2, FOUR=4, EIGHT=8 }"); + TypeEnum::Representation rep; + enum3->getMatches(5, rep); + ASSERT(rep.matchname.size() == 2); + ASSERT(rep.matchname[0] == "FOUR"); + ASSERT(rep.matchname[1] == "ONE"); + ASSERT(!rep.complement); + rep.matchname.clear(); + enum3->getMatches(0xfffffffffffffff7,rep); + ASSERT(rep.matchname.size() == 1); + ASSERT(rep.matchname[0] == "EIGHT"); + ASSERT(rep.complement); + rep.matchname.clear(); + rep.complement = false; + enum3->getMatches(0,rep); + ASSERT(rep.matchname.size() == 1); + ASSERT(rep.matchname[0] == "ZERO"); + ASSERT(!rep.complement); + rep.matchname.clear(); + enum3->getMatches(0x10, rep); + ASSERT(rep.matchname.size() == 0); + ASSERT(!rep.complement); +} + +TEST(enum_matching2) { + TypeTestEnvironment::build(); + TypeEnum *enum4 = (TypeEnum *)parse("enum enum4 { ZERO=0, ONE=1, TWO=2, FOUR=4, SIX=6, EIGHT=8, ELEVEN=11 }"); + TypeEnum::Representation rep; + enum4->getMatches(12,rep); + ASSERT(rep.matchname.size()==2); + ASSERT(rep.matchname[0] == "EIGHT"); + ASSERT(rep.matchname[1] == "FOUR"); + ASSERT(!rep.complement); + rep.matchname.clear(); + enum4->getMatches(7,rep); + ASSERT(rep.matchname.size()==2); + ASSERT(rep.matchname[0] == "SIX"); + ASSERT(rep.matchname[1] == "ONE"); + ASSERT(!rep.complement); + rep.matchname.clear(); + enum4->getMatches(11,rep); + ASSERT(rep.matchname.size()==1); + ASSERT(rep.matchname[0] == "ELEVEN"); + ASSERT(!rep.complement); +} + } // End namespace ghidra diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java index 0945439e71..dcb4b13113 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AttributeId.java @@ -114,7 +114,7 @@ public record AttributeId(String name, int id) { public static final AttributeId ATTRIB_ARRAYSIZE = new AttributeId("arraysize", 48); public static final AttributeId ATTRIB_CHAR = new AttributeId("char", 49); public static final AttributeId ATTRIB_CORE = new AttributeId("core", 50); - public static final AttributeId ATTRIB_ENUM = new AttributeId("enum", 51); +// public static final AttributeId ATTRIB_ENUM = new AttributeId("enum", 51); // deprecated public static final AttributeId ATTRIB_INCOMPLETE = new AttributeId("incomplete", 52); // public static final AttributeId ATTRIB_ENUMSIZE = new AttributeId("enumsize", 53); // deprecated // public static final AttributeId ATTRIB_INTSIZE = new AttributeId("intsize", 54); // deprecated diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java index 84cf11b85b..93e7ee86e8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java @@ -278,6 +278,13 @@ public class PcodeDataTypeManager { decoder.closeElement(el); return new PartialUnion(progDataTypes, dt, offset, size); } + else if (meta.equals("partenum")) { + int size = (int) decoder.readSignedInteger(ATTRIB_SIZE); +// int offset = (int) decoder.readSignedInteger(ATTRIB_OFFSET); +// DataType dt = decodeDataType(decoder); + decoder.closeElementSkipping(el); + return AbstractIntegerDataType.getUnsignedDataType(size, progDataTypes); + } else { // We typically reach here if the decompiler invents a new type // probably an unknown with a non-standard size int size = (int) decoder.readSignedInteger(ATTRIB_SIZE); @@ -541,11 +548,10 @@ public class PcodeDataTypeManager { private void encodeEnum(Encoder encoder, Enum type, int size) throws IOException { encoder.openElement(ELEM_TYPE); encodeNameIdAttributes(encoder, type); - String metatype = type.isSigned() ? "int" : "uint"; + String metatype = type.isSigned() ? "enum_int" : "enum_uint"; String[] names = type.getNames(); encoder.writeString(ATTRIB_METATYPE, metatype); encoder.writeSignedInteger(ATTRIB_SIZE, type.getLength()); - encoder.writeBool(ATTRIB_ENUM, true); for (String name : names) { encoder.openElement(ELEM_VAL); encoder.writeString(ATTRIB_NAME, name);