diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index fb3123c178..1155b22c03 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -984,6 +984,20 @@ void Architecture::parseDeadcodeDelay(const Element *el) throw LowlevelError("Bad tag"); } +/// Alter the range of addresses for which a pointer is allowed to be inferred. +void Architecture::parseInferPtrBounds(const Element *el) + +{ + const List &list(el->getChildren()); + List::const_iterator iter; + for(iter=list.begin();iter!=list.end();++iter) { + const Element *subel = *iter; + Range range; + range.restoreXml(subel,this); + setInferPtrBounds(range); + } +} + /// Pull information from a \ tag. Turn on alignment analysis of /// function pointers, some architectures have aligned function pointers /// and encode extra information in the unused bits. @@ -1109,6 +1123,9 @@ void Architecture::parseProcessorConfig(DocumentStorage &store) throw LowlevelError("Undefined space: "+spaceName); setDefaultDataSpace(spc->getIndex()); } + else if (elname == "inferptrbounds") { + parseInferPtrBounds(*iter); + } else if (elname == "segmented_address") { } else if (elname == "default_symbols") { @@ -1183,6 +1200,8 @@ void Architecture::parseCompilerConfig(DocumentStorage &store) parseFuncPtrAlign(*iter); else if (elname == "deadcodedelay") parseDeadcodeDelay(*iter); + else if (elname == "inferptrbounds") + parseInferPtrBounds(*iter); } // tags instantiate the base symbol table // They need to know about all spaces, so it must come diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index 496db81809..bfee675511 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -274,6 +274,7 @@ protected: void parseLaneSizes(const Element *el); ///< Apply lane size configuration void parseStackPointer(const Element *el); ///< Apply stack pointer configuration void parseDeadcodeDelay(const Element *el); ///< Apply dead-code delay configuration + void parseInferPtrBounds(const Element *el); ///< Apply pointer inference bounds void parseFuncPtrAlign(const Element *el); ///< Apply function pointer alignment configuration void parseSpacebase(const Element *el); ///< Create an additional indexed space void parseNoHighPtr(const Element *el); ///< Apply memory alias configuration diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index a4c5bfc399..53d4693f71 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -1077,6 +1077,8 @@ SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op // Make sure the constant is in the expected range for a pointer if (spc->getPointerLowerBound() > vn->getOffset()) return (SymbolEntry *)0; + if (spc->getPointerUpperBound() < vn->getOffset()) + return (SymbolEntry *)0; // Check if the constant looks like a single bit or mask if (bit_transitions(vn->getOffset(),vn->getSize()) < 3) return (SymbolEntry *)0; @@ -1205,7 +1207,6 @@ int4 ActionDeindirect::apply(Funcdata &data) // We use isInputLocked as a test of whether the // function pointer prototype has been applied before fc->forceSet(data,*fp); - data.updateOpFromSpec(fc); count += 1; } } @@ -2158,7 +2159,6 @@ int4 ActionDefaultParams::apply(Funcdata &data) fc->setInternal(evalfp,data.getArch()->types->getTypeVoid()); } fc->insertPcode(data); // Insert any necessary pcode - data.updateOpFromSpec(fc); } return 0; // Indicate success } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.cc index 42019c7ea3..7d6e899b16 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.cc @@ -37,8 +37,6 @@ void CPoolRecord::saveXml(ostream &s) const a_v(s,"tag","classref"); else a_v(s,"tag","primitive"); - if (hasThisPointer()) - a_v_b(s,"hasthis",true); if (isConstructor()) a_v_b(s,"constructor",true); if (isDestructor()) @@ -100,10 +98,6 @@ void CPoolRecord::restoreXml(const Element *el,TypeFactory &typegrp) else if (tagstring == "classref") tag = class_reference; } - else if (attr == "hasthis") { - if (xml_readbool(el->getAttributeValue(i))) - flags |= CPoolRecord::has_thisptr; - } else if (attr == "constructor") { if (xml_readbool(el->getAttributeValue(i))) flags |= CPoolRecord::is_constructor; @@ -145,10 +139,9 @@ void CPoolRecord::restoreXml(const Element *el,TypeFactory &typegrp) throw LowlevelError("Bad constant pool record: missing "); subel = *iter; if (flags != 0) { - bool hasThisPtr = ((flags & has_thisptr)!=0); bool isConstructor = ((flags & is_constructor)!=0); bool isDestructor = ((flags & is_destructor)!=0); - type = typegrp.restoreXmlTypeWithCodeFlags(subel,hasThisPtr,isConstructor,isDestructor); + type = typegrp.restoreXmlTypeWithCodeFlags(subel,isConstructor,isDestructor); } else type = typegrp.restoreXmlType(subel); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.hh index 9fd4c1fb1a..7a90f6e9f8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/cpool.hh @@ -55,9 +55,8 @@ public: check_cast=7 ///< Pointer to object, new name in \b token, new data-type in \b type }; enum { - has_thisptr = 0x1, ///< Referenced method has a \b this pointer - is_constructor = 0x2, ///< Referenced method is a constructor - is_destructor = 0x4 ///< Referenced method is a destructor + is_constructor = 0x1, ///< Referenced method is a constructor + is_destructor = 0x2 ///< Referenced method is a destructor }; private: friend class ConstantPool; @@ -77,7 +76,6 @@ public: int4 getByteDataLength(void) const { return byteDataLen; } ///< Number of bytes of string literal data Datatype *getType(void) const { return type; } ///< Get the data-type associated with \b this uintb getValue(void) const { return value; } ///< Get the constant value associated with \b this - bool hasThisPointer(void) const { return ((flags & has_thisptr)!=0); } ///< Is object a method with a \b this pointer bool isConstructor(void) const { return ((flags & is_constructor)!=0); } ///< Is object a constructor method bool isDestructor(void) const { return ((flags & is_destructor)!=0); } ///< Is object a destructor method void saveXml(ostream &s) const; ///< Save object to an XML stream diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index 82c1cab05c..74f18a1846 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -224,6 +224,16 @@ void Symbol::checkSizeTypeLock(void) dispflags |= size_typelock; } +/// \param val is \b true if we are the "this" pointer +void Symbol::setThisPointer(bool val) + +{ + if (val) + dispflags |= is_this_ptr; + else + dispflags &= ~((uint4)is_this_ptr); +} + /// The name for a Symbol can be unspecified. See ScopeInternal::buildUndefinedName /// \return \b true if the name of \b this is undefined bool Symbol::isNameUndefined(void) const diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh index 1bd6731fdd..c651d2ecb2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh @@ -173,6 +173,7 @@ protected: virtual ~Symbol(void) {} ///< Destructor void setDisplayFormat(uint4 val); ///< Set the display format for \b this Symbol void checkSizeTypeLock(void); ///< Calculate if \b size_typelock property is on + void setThisPointer(bool val); ///< Toggle whether \b this is the "this" pointer for a class method public: /// \brief Possible display (dispflag) properties for a Symbol enum { @@ -701,6 +702,7 @@ public: void saveXmlRecursive(ostream &s,bool onlyGlobal) const; ///< Save all contained scopes as an XML stream void overrideSizeLockType(Symbol *sym,Datatype *ct); ///< Change the data-type of a Symbol that is \e sizelocked void resetSizeLockType(Symbol *sym); ///< Clear a Symbol's \e size-locked data-type + void setThisPointer(Symbol *sym,bool val) { sym->setThisPointer(val); } ///< Toggle the given Symbol as the "this" pointer bool isSubScope(const Scope *scp) const; ///< Is this a sub-scope of the given Scope string getFullName(void) const; ///< Get the full name of \b this Scope void getNameSegments(vector &vec) const; ///< Get the fullname of \b this in segments diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index b3be7ca4a5..42adedf4ba 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -540,7 +540,7 @@ void ParamListStandard::assignMap(const vector &proto,bool isinput,T if (isinput) { if (res.size()==2) { // Check for hidden parameters defined by the output list res.back().addr = assignAddress(res.back().type,status); // Reserve first param for hidden ret value - res.back().flags |= Varnode::hiddenretparm; + res.back().flags |= ParameterPieces::hiddenretparm; if (res.back().addr.isInvalid()) throw ParamUnassignedError("Cannot assign parameter address for " + res.back().type->getName()); } @@ -556,7 +556,7 @@ void ParamListStandard::assignMap(const vector &proto,bool isinput,T Datatype *pointertp = typefactory.getTypePointer(pointersize,proto[i],wordsize); res.back().addr = assignAddress(pointertp,status); res.back().type = pointertp; - res.back().flags = Varnode::indirectstorage; + res.back().flags = ParameterPieces::indirectstorage; } else res.back().addr = assignAddress(proto[i],status); @@ -1107,12 +1107,12 @@ void ParamListStandardOut::assignMap(const vector &proto,bool isinpu if (res.back().addr.isInvalid()) throw ParamUnassignedError("Cannot assign return value as a pointer"); res.back().type = pointertp; - res.back().flags = Varnode::indirectstorage; + res.back().flags = ParameterPieces::indirectstorage; res.push_back(ParameterPieces()); // Add extra storage location in the input params res.back().type = pointertp; // that holds a pointer to where the return value should be stored // leave its address invalid, to be filled in by the input list assignMap - res.back().flags = Varnode::hiddenretparm; // Mark it as special + res.back().flags = ParameterPieces::hiddenretparm; // Mark it as special } } @@ -1759,6 +1759,7 @@ ProtoModel::ProtoModel(Architecture *g) glb = g; input = (ParamList *)0; output = (ParamList *)0; + compatModel = (const ProtoModel *)0; extrapop=0; injectUponEntry = -1; injectUponReturn = -1; @@ -1797,6 +1798,9 @@ ProtoModel::ProtoModel(const string &nm,const ProtoModel &op2) stackgrowsnegative = op2.stackgrowsnegative; hasThis = op2.hasThis; isConstruct = op2.isConstruct; + if (name == "__thiscall") + hasThis = true; + compatModel = &op2; } ProtoModel::~ProtoModel(void) @@ -1808,6 +1812,19 @@ ProtoModel::~ProtoModel(void) delete output; } +/// Test whether one ProtoModel can substituted for another during FuncCallSpecs::deindirect +/// Currently this can only happen if one model is a copy of the other except for the +/// hasThis boolean property. +/// \param op2 is the other ProtoModel to compare with \b this +/// \return \b true if the two models are compatible +bool ProtoModel::isCompatible(const ProtoModel *op2) const + +{ + if (this == op2 || compatModel == op2 || op2->compatModel == this) + return true; + return false; +} + /// \brief Calculate input and output storage locations given a function prototype /// /// The data-types of the function prototype are passed in as an ordered list, with the @@ -2266,21 +2283,30 @@ void ParameterBasic::setTypeLock(bool val) { if (val) { - flags |= Varnode::typelock; + flags |= ParameterPieces::typelock; if (type->getMetatype() == TYPE_UNKNOWN) // Check if we are locking TYPE_UNKNOWN - flags |= Varnode::mark; // If so, set Varnode::mark to indicate the sizelock + flags |= ParameterPieces::sizelock; } else - flags &= ~((uint4)(Varnode::typelock|Varnode::mark)); + flags &= ~((uint4)(ParameterPieces::typelock|ParameterPieces::sizelock)); } void ParameterBasic::setNameLock(bool val) { if (val) - flags |= Varnode::namelock; + flags |= ParameterPieces::namelock; else - flags &= ~((uint4)Varnode::namelock); + flags &= ~((uint4)ParameterPieces::namelock); +} + +void ParameterBasic::setThisPointer(bool val) + +{ + if (val) + flags |= ParameterPieces::isthis; + else + flags &= ~((uint4)ParameterPieces::isthis); } void ParameterBasic::overrideSizeLockType(Datatype *ct) @@ -2352,6 +2378,12 @@ bool ParameterSymbol::isSizeTypeLocked(void) const return sym->isSizeTypeLocked(); } +bool ParameterSymbol::isThisPointer(void) const + +{ + return sym->isThisPointer(); +} + bool ParameterSymbol::isIndirectStorage(void) const { @@ -2393,6 +2425,13 @@ void ParameterSymbol::setNameLock(bool val) scope->clearAttribute(sym,Varnode::namelock); } +void ParameterSymbol::setThisPointer(bool val) + +{ + Scope *scope = sym->getScope(); + scope->setThisPointer(sym, val); +} + void ParameterSymbol::overrideSizeLockType(Datatype *ct) { @@ -2471,6 +2510,8 @@ ProtoParameter *ProtoStoreSymbol::setInput(int4 i, const string &nm,const Parame SymbolEntry *entry; Address usepoint; + bool isindirect = (pieces.flags & ParameterPieces::indirectstorage) != 0; + bool ishidden = (pieces.flags & ParameterPieces::hiddenretparm) != 0; if (res->sym != (Symbol *)0) { entry = res->sym->getFirstWholeMap(); if ((entry->getAddr() != pieces.addr)||(entry->getSize() != pieces.type->getSize())) { @@ -2483,18 +2524,24 @@ ProtoParameter *ProtoStoreSymbol::setInput(int4 i, const string &nm,const Parame usepoint = restricted_usepoint; res->sym = scope->addSymbol(nm,pieces.type,pieces.addr,usepoint)->getSymbol(); scope->setCategory(res->sym,0,i); - if ((pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm)) != 0) - scope->setAttribute(res->sym,pieces.flags & (Varnode::indirectstorage|Varnode::hiddenretparm)); + if (isindirect || ishidden) { + uint4 mirror = 0; + if (isindirect) + mirror |= Varnode::indirectstorage; + if (ishidden) + mirror |= Varnode::hiddenretparm; + scope->setAttribute(res->sym,mirror); + } return res; } - if ((res->sym->getFlags() & Varnode::indirectstorage) != (pieces.flags & Varnode::indirectstorage)) { - if ((pieces.flags & Varnode::indirectstorage)!=0) + if (res->sym->isIndirectStorage() != isindirect) { + if (isindirect) scope->setAttribute(res->sym,Varnode::indirectstorage); else scope->clearAttribute(res->sym,Varnode::indirectstorage); } - if ((res->sym->getFlags() & Varnode::hiddenretparm) != (pieces.flags & Varnode::hiddenretparm)) { - if ((pieces.flags & Varnode::hiddenretparm)!=0) + if (res->sym->isHiddenReturn() != ishidden) { + if (ishidden) scope->setAttribute(res->sym,Varnode::hiddenretparm); else scope->clearAttribute(res->sym,Varnode::hiddenretparm); @@ -2737,6 +2784,8 @@ void ProtoStoreInternal::saveXml(ostream &s) const a_v_b(s,"typelock",true); if (param->isNameLocked()) a_v_b(s,"namelock",true); + if (param->isThisPointer()) + a_v_b(s,"thisptr",true); if (param->isIndirectStorage()) a_v_b(s,"indirectstorage",true); if (param->isHiddenReturn()) @@ -2759,47 +2808,50 @@ void ProtoStoreInternal::restoreXml(const Element *el,ProtoModel *model) List::const_iterator iter; vector pieces; vector namelist; - vector typelocklist; - vector namelocklist; bool addressesdetermined = true; pieces.push_back( ParameterPieces() ); // Push on placeholder for output pieces namelist.push_back("ret"); - typelocklist.push_back(outparam->isTypeLocked()); - namelocklist.push_back(false); pieces.back().type = outparam->getType(); pieces.back().flags = 0; + if (outparam->isTypeLocked()) + pieces.back().flags |= ParameterPieces::typelock; if (outparam->isIndirectStorage()) - pieces.back().flags |= Varnode::indirectstorage; + pieces.back().flags |= ParameterPieces::indirectstorage; if (outparam->getAddress().isInvalid()) addressesdetermined = false; for(iter=list.begin();iter!=list.end();++iter) { // This is only the input params const Element *subel = *iter; string name; - bool typelock = false; - bool namelock = false; uint4 flags = 0; for(int4 i=0;igetNumAttributes();++i) { const string &attr( subel->getAttributeName(i) ); if (attr == "name") name = subel->getAttributeValue(i); - else if (attr == "typelock") - typelock = xml_readbool(subel->getAttributeValue(i)); - else if (attr == "namelock") - namelock = xml_readbool(subel->getAttributeValue(i)); + else if (attr == "typelock") { + if (xml_readbool(subel->getAttributeValue(i))) + flags |= ParameterPieces::typelock; + } + else if (attr == "namelock") { + if (xml_readbool(subel->getAttributeValue(i))) + flags |= ParameterPieces::namelock; + } + else if (attr == "thisptr") { + if (xml_readbool(subel->getAttributeValue(i))) + flags |= ParameterPieces::isthis; + } else if (attr == "indirectstorage") { if (xml_readbool(subel->getAttributeValue(i))) - flags |= Varnode::indirectstorage; + flags |= ParameterPieces::indirectstorage; } else if (attr == "hiddenretparm") { if (xml_readbool(subel->getAttributeValue(i))) - flags |= Varnode::hiddenretparm; + flags |= ParameterPieces::hiddenretparm; } } - namelist.push_back(name); - typelocklist.push_back(typelock); - namelocklist.push_back(namelock); + if ((flags & ParameterPieces::hiddenretparm) == 0) + namelist.push_back(name); pieces.push_back(ParameterPieces()); ParameterPieces &curparam( pieces.back() ); const List &sublist(subel->getChildren()); @@ -2811,8 +2863,6 @@ void ProtoStoreInternal::restoreXml(const Element *el,ProtoModel *model) curparam.flags = flags; if (curparam.addr.isInvalid()) addressesdetermined = false; - typelocklist.push_back(typelock); - namelocklist.push_back(namelock); } ProtoParameter *curparam; if (!addressesdetermined) { @@ -2821,28 +2871,53 @@ void ProtoStoreInternal::restoreXml(const Element *el,ProtoModel *model) vector typelist; for(int4 i=0;iassignParameterStorage(typelist,pieces,true); + vector addrPieces; + model->assignParameterStorage(typelist,addrPieces,true); + addrPieces.swap(pieces); + uint4 k = 0; + for(uint4 i=0;isetTypeLock(typelocklist[0]); + curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0); } uint4 j=1; for(uint4 i=1;isetTypeLock(typelocklist[0]); // Has output's typelock + curparam->setTypeLock((pieces[0].flags & ParameterPieces::typelock)!=0); // Has output's typelock continue; // increment i but not j } curparam = setInput(i-1,namelist[j],pieces[i]); - curparam->setTypeLock(typelocklist[j]); - curparam->setNameLock(namelocklist[j]); + curparam->setTypeLock((pieces[i].flags & ParameterPieces::typelock)!=0); + curparam->setNameLock((pieces[i].flags & ParameterPieces::namelock)!=0); j = j + 1; } } +/// This is called after a new prototype is established (via restoreXml or updateAllTypes) +/// It makes sure that if the ProtoModel calls for a "this" parameter, then the appropriate parameter +/// is explicitly marked as the "this". +void FuncProto::updateThisPointer(void) + +{ + if (!model->hasThisPointer()) return; + int4 numInputs = store->getNumInputs(); + if (numInputs == 0) return; + ProtoParameter *param = store->getInput(0); + if (param->isHiddenReturn()) { + if (numInputs < 2) return; + param = store->getInput(1); + } + param->setThisPointer(true); +} + /// Prepend the indicated number of input parameters to \b this. /// The new parameters have a data-type of xunknown4. If they were /// originally locked, the existing parameters are preserved. @@ -2893,7 +2968,7 @@ void FuncProto::paramShift(int4 paramshift) store->setOutput(pieces[0]); uint4 j=1; for(uint4 i=1;isetInput(i-1,"rethidden",pieces[i]); continue; // increment i but not j } @@ -3219,6 +3294,7 @@ void FuncProto::updateInputTypes(Funcdata &data,const vector &trialli } for(int4 i=0;iclearMark(); + updateThisPointer(); } /// \brief Update input parameters based on Varnode trials, but do not store the data-type @@ -3346,7 +3422,7 @@ void FuncProto::updateAllTypes(const vector &namelist,const vectorsetOutput(pieces[0]); uint4 j=1; for(uint4 i=1;isetInput(i-1,"rethidden",pieces[i]); continue; // increment i but not j } @@ -3357,6 +3433,7 @@ void FuncProto::updateAllTypes(const vector &namelist,const vectorisCompatible(op2.model)) return false; if (op2.isOutputLocked()) { if (isOutputLocked()) { ProtoParameter *out1 = store->getOutput(); @@ -3939,7 +4016,7 @@ void FuncProto::restoreXml(const Element *el,Architecture *glb) if ((outparam->getType()->getMetatype()!=TYPE_VOID)&&outparam->getAddress().isInvalid()) { throw LowlevelError(" tag must include a valid storage address"); } - + updateThisPointer(); } /// \brief Calculate the stack offset of \b this call site diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index 3fc240280e..303c39b373 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -292,6 +292,14 @@ public: /// \brief Basic elements of a parameter: address, data-type, properties struct ParameterPieces { + enum { + isthis = 1, ///< Parameter is "this" pointer + hiddenretparm = 2, ///< Parameter is hidden pointer to return value, mirrors Varnode::hiddenretparm + indirectstorage = 4, ///< Parameter is indirect pointer to true parameter, mirrors Varnode::indirectstorage + namelock = 8, ///< Parameter's name is locked, mirrors Varnode::namelock + typelock = 16, ///< Parameter's data-type is locked, mirrors Varnode::typelock + sizelock = 32 ///< Size of the parameter is locked (but not the data-type) + }; Address addr; ///< Storage address of the parameter Datatype *type; ///< The datatype of the parameter uint4 flags; ///< additional attributes of the parameter @@ -619,6 +627,7 @@ class ProtoModel { int4 extrapop; ///< Extra bytes popped from stack ParamList *input; ///< Resource model for input parameters ParamList *output; ///< Resource model for output parameters + const ProtoModel *compatModel; ///< The model \b this is a copy of vector effectlist; ///< List of side-effects vector likelytrash; ///< Storage locations potentially carrying \e trash values int4 injectUponEntry; ///< Id of injection to perform at beginning of function (-1 means not used) @@ -645,6 +654,7 @@ public: void setExtraPop(int4 ep) { extrapop = ep; } ///< Set the stack-pointer \e extrapop int4 getInjectUponEntry(void) const { return injectUponEntry; } ///< Get the inject \e uponentry id int4 getInjectUponReturn(void) const { return injectUponReturn; } ///< Get the inject \e uponreturn id + bool isCompatible(const ProtoModel *op2) const; ///< Return \b true if other given model can be substituted for \b this /// \brief Given a list of input \e trials, derive the most likely input prototype /// @@ -911,11 +921,13 @@ public: virtual bool isTypeLocked(void) const=0; ///< Is the parameter data-type locked virtual bool isNameLocked(void) const=0; ///< Is the parameter name locked virtual bool isSizeTypeLocked(void) const=0; ///< Is the size of the parameter locked + virtual bool isThisPointer(void) const=0; ///< Is \b this the "this" pointer for a class method virtual bool isIndirectStorage(void) const=0; ///< Is \b this really a pointer to the true parameter virtual bool isHiddenReturn(void) const=0; ///< Is \b this a pointer to storage for a return value virtual bool isNameUndefined(void) const=0; ///< Is the name of \b this parameter undefined virtual void setTypeLock(bool val)=0; ///< Toggle the lock on the data-type virtual void setNameLock(bool val)=0; ///< Toggle the lock on the name + virtual void setThisPointer(bool val)=0; ///< Toggle whether \b this is the "this" pointer for a class method /// \brief Change (override) the data-type of a \e size-locked parameter. /// @@ -965,7 +977,7 @@ class ParameterBasic : public ProtoParameter { string name; ///< The name of the parameter, "" for undefined or return value parameters Address addr; ///< Storage address of the parameter Datatype *type; ///< Data-type of the parameter - uint4 flags; ///< Lock properties. Varnode::mark is co-opted to hold the \e size-lock flag + uint4 flags; ///< Lock and other properties from ParameterPieces flags public: ParameterBasic(const string &nm,const Address &ad,Datatype *tp,uint4 fl) { name = nm; addr = ad; type = tp; flags=fl; } ///< Construct from components @@ -973,14 +985,16 @@ public: virtual Datatype *getType(void) const { return type; } virtual Address getAddress(void) const { return addr; } virtual int4 getSize(void) const { return type->getSize(); } - virtual bool isTypeLocked(void) const { return ((flags&Varnode::typelock)!=0); } - virtual bool isNameLocked(void) const { return ((flags&Varnode::namelock)!=0); } - virtual bool isSizeTypeLocked(void) const { return ((flags&Varnode::mark)!=0); } - virtual bool isIndirectStorage(void) const { return ((flags&Varnode::indirectstorage)!=0); } - virtual bool isHiddenReturn(void) const { return ((flags&Varnode::hiddenretparm)!=0); } + virtual bool isTypeLocked(void) const { return ((flags&ParameterPieces::typelock)!=0); } + virtual bool isNameLocked(void) const { return ((flags&ParameterPieces::namelock)!=0); } + virtual bool isSizeTypeLocked(void) const { return ((flags&ParameterPieces::sizelock)!=0); } + virtual bool isThisPointer(void) const { return ((flags&ParameterPieces::isthis)!=0); } + virtual bool isIndirectStorage(void) const { return ((flags&ParameterPieces::indirectstorage)!=0); } + virtual bool isHiddenReturn(void) const { return ((flags&ParameterPieces::hiddenretparm)!=0); } virtual bool isNameUndefined(void) const { return (name.size()==0); } virtual void setTypeLock(bool val); virtual void setNameLock(bool val); + virtual void setThisPointer(bool val); virtual void overrideSizeLockType(Datatype *ct); virtual void resetSizeLockType(TypeFactory *factory); virtual ProtoParameter *clone(void) const; @@ -1061,11 +1075,13 @@ public: virtual bool isTypeLocked(void) const; virtual bool isNameLocked(void) const; virtual bool isSizeTypeLocked(void) const; + virtual bool isThisPointer(void) const; virtual bool isIndirectStorage(void) const; virtual bool isHiddenReturn(void) const; virtual bool isNameUndefined(void) const; virtual void setTypeLock(bool val); virtual void setNameLock(bool val); + virtual void setThisPointer(bool val); virtual void overrideSizeLockType(Datatype *ct); virtual void resetSizeLockType(TypeFactory *factory); virtual ProtoParameter *clone(void) const; @@ -1169,6 +1185,7 @@ class FuncProto { vector likelytrash; ///< Locations that may contain \e trash values int4 injectid; ///< (If non-negative) id of p-code snippet that should replace this function int4 returnBytesConsumed; ///< Number of bytes of return value that are consumed by callers (0 = all bytes) + void updateThisPointer(void); ///< Make sure any "this" parameter is properly marked protected: void paramShift(int4 paramshift); ///< Add parameters to the front of the input parameter list bool isParamshiftApplied(void) const { return ((flags¶mshift_applied)!=0); } ///< Has a parameter shift been applied @@ -1240,11 +1257,6 @@ public: /// \brief Is \b this a prototype for a class method, taking a \e this pointer. bool hasThisPointer(void) const { return ((flags & has_thisptr)!=0); } - /// \brief Toggle the \e this-call setting for \b this prototype - /// - /// \param val is \b true if \b this prototype uses a \e this pointer - void setThisPointer(bool val) { flags = val ? (flags|has_thisptr) : (flags & ~((uint4)has_thisptr)); } - /// \brief Is \b this prototype for a class constructor method bool isConstructor(void) const { return ((flags & is_constructor)!=0); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc index a2390354a9..c6dcac6380 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc @@ -421,29 +421,6 @@ FuncCallSpecs *Funcdata::getCallSpecs(const PcodeOp *op) const return (FuncCallSpecs *)0; } -/// \brief Update CALL PcodeOp properties based on its corresponding call specification -/// -/// As call specifications for a particular call site are updated, this routine pushes -/// back properties to the particular CALL op that are relevant for analysis. -/// \param fc is the call specification -void Funcdata::updateOpFromSpec(FuncCallSpecs *fc) - -{ - PcodeOp *op = fc->getOp(); - if (fc->isConstructor()) - op->setAdditionalFlag(PcodeOp::is_constructor); - else - op->clearAdditionalFlag(PcodeOp::is_constructor); - if (fc->isDestructor()) - op->setAdditionalFlag(PcodeOp::is_destructor); - else - op->clearAdditionalFlag(PcodeOp::is_destructor); - if (fc->hasThisPointer()) - op->setAdditionalFlag(PcodeOp::has_thisptr); - else - op->clearAdditionalFlag(PcodeOp::has_thisptr); -} - /// \brief Compare call specification objects by call site address /// /// \param a is the first call specification to compare diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh index 7bd1a02949..ed915d66c8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/op.hh @@ -100,15 +100,12 @@ public: indirect_store = 0x80000000 ///< CPUI_INDIRECT is caused by CPUI_STORE }; enum { - has_thisptr = 0x1, ///< First parameter ( getIn(1) ) is a this pointer - is_constructor = 0x2, ///< Op is call to a constructor - is_destructor = 0x4, ///< Op is call to a destructor - special_prop = 0x8, ///< Does some special form of datatype propagation - special_print = 0x10, ///< Op is marked for special printing - modified = 0x20, ///< This op has been modified by the current action - warning = 0x40, ///< Warning has been generated for this op - incidental_copy = 0x80, ///< Treat this as \e incidental for parameter recovery algorithms - is_cpool_transformed = 0x100 ///< Have we checked for cpool transforms + special_prop = 1, ///< Does some special form of datatype propagation + special_print = 2, ///< Op is marked for special printing + modified = 4, ///< This op has been modified by the current action + warning = 8, ///< Warning has been generated for this op + incidental_copy = 0x10, ///< Treat this as \e incidental for parameter recovery algorithms + is_cpool_transformed = 0x20 ///< Have we checked for cpool transforms }; private: TypeOp *opcode; ///< Pointer to class providing behavioral details of the operation @@ -199,9 +196,6 @@ public: bool isSplitting(void) const { return ((flags&PcodeOp::splittingbranch)!=0); } ///< Return \b true if this branch splits bool doesSpecialPropagation(void) const { return ((addlflags&PcodeOp::special_prop)!=0); } ///< Return \b true if this does datatype propagation bool doesSpecialPrinting(void) const { return ((addlflags&PcodeOp::special_print)!=0); } ///< Return \b true if this needs to special printing - bool hasThisPointer(void) const { return ((addlflags&PcodeOp::has_thisptr)!=0); } ///< Return \b true if this is a call taking 'this' parameter - bool isConstructor(void) const { return ((addlflags&PcodeOp::is_constructor)!=0); } ///< Return \b true if this is call to a constructor - bool isDestructor(void) const { return ((addlflags&PcodeOp::is_destructor)!=0); } ///< Return \b true if this is call to a destructor bool isIncidentalCopy(void) const { return ((addlflags&PcodeOp::incidental_copy)!=0); } ///< Return \b true if \b this COPY is \e incidental /// \brief Return \b true if output is 1-bit boolean bool isCalculatedBool(void) const { return ((flags&(PcodeOp::calculated_bool|PcodeOp::booloutput))!=0); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 7f4241e6db..2e7b1b8805 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -510,8 +510,9 @@ void PrintC::opCall(const PcodeOp *op) { pushOp(&function_call,op); const Varnode *callpoint = op->getIn(0); + FuncCallSpecs *fc; if (callpoint->getSpace()->getType()==IPTR_FSPEC) { - FuncCallSpecs *fc = FuncCallSpecs::getFspecFromConst(callpoint->getAddr()); + fc = FuncCallSpecs::getFspecFromConst(callpoint->getAddr()); if (fc->getName().size()==0) { string name = genericFunctionName(fc->getEntryAddress()); pushAtom(Atom(name,functoken,EmitXml::funcname_color,op,(const Funcdata *)0)); @@ -527,14 +528,22 @@ void PrintC::opCall(const PcodeOp *op) clear(); throw LowlevelError("Missing function callspec"); } - int4 startparam = isSet(hide_thisparam) && op->hasThisPointer() ? 2 : 1; - if (op->numInput() > startparam) { - for(int4 i=startparam;inumInput()-1;++i) + // TODO: Cannot hide "this" on a direct call until we print the whole + // thing with the proper C++ method invocation format. Otherwise the output + // gives no indication of what object has a method being called. + // int4 skip = getHiddenThisSlot(op, fc); + int4 skip = -1; + int4 count = op->numInput() - 1; // Number of parameter expressions printed + count -= (skip < 0) ? 0 : 1; // Subtract one if "this" is hidden + if (count > 0) { + for(int4 i=0;inumInput()-1;i>=startparam;--i) + // implied vn's pushed on in reverse order for efficiency + // see PrintLanguage::pushVnImplied + for(int4 i=op->numInput()-1;i>=1;--i) { + if (i == skip) continue; pushVnImplied(op->getIn(i),op,mods); + } } else // Push empty token for void pushAtom(Atom("",blanktoken,EmitXml::no_color)); @@ -545,18 +554,29 @@ void PrintC::opCallind(const PcodeOp *op) { pushOp(&function_call,op); pushOp(&dereference,op); - // implied vn's pushed on in reverse order for efficiency - // see PrintLanguage::pushVnImplied - int4 startparam = isSet(hide_thisparam) && op->hasThisPointer() ? 2 : 1; - if (op->numInput()>startparam + 1) { // Multiple parameters + const Funcdata *fd = op->getParent()->getFuncdata(); + FuncCallSpecs *fc = fd->getCallSpecs(op); + if (fc == (FuncCallSpecs *)0) + throw LowlevelError("Missing indirect function callspec"); + int4 skip = getHiddenThisSlot(op, fc); + int4 count = op->numInput() - 1; + count -= (skip < 0) ? 0 : 1; + if (count > 1) { // Multiple parameters pushVnImplied(op->getIn(0),op,mods); - for(int4 i=startparam;inumInput()-1;++i) + for(int4 i=0;inumInput()-1;i>=startparam;--i) + // implied vn's pushed on in reverse order for efficiency + // see PrintLanguage::pushVnImplied + for(int4 i=op->numInput()-1;i>=1;--i) { + if (i == skip) continue; pushVnImplied(op->getIn(i),op,mods); + } } - else if (op->numInput()==startparam + 1) { // One parameter - pushVnImplied(op->getIn(startparam),op,mods); + else if (count == 1) { // One parameter + if (skip == 1) + pushVnImplied(op->getIn(2),op,mods); + else + pushVnImplied(op->getIn(1),op,mods); pushVnImplied(op->getIn(0),op,mods); } else { // A void function @@ -1332,6 +1352,31 @@ bool PrintC::printCharacterConstant(ostream &s,const Address &addr,Datatype *cha return true; } +/// For the given CALL op, if a "this" pointer exists and needs to be hidden because +/// of the print configuration, return the Varnode slot corresponding to the "this". +/// Otherwise return -1. +/// \param op is the given CALL PcodeOp +/// \param fc is the function prototype corresponding to the CALL +/// \return the "this" Varnode slot or -1 +int4 PrintC::getHiddenThisSlot(const PcodeOp *op,FuncProto *fc) + +{ + int4 numInput = op->numInput(); + if (isSet(hide_thisparam) && fc->hasThisPointer()) { + for(int4 i=1;igetParam(i-1); + if (param != (ProtoParameter *)0 && param->isThisPointer()) + return i; + } + if (numInput >= 2) { + ProtoParameter *param = fc->getParam(numInput-2); + if (param != (ProtoParameter *)0 && param->isThisPointer()) + return numInput - 1; + } + } + return -1; +} + void PrintC::resetDefaultsPrintC(void) { @@ -1902,11 +1947,15 @@ void PrintC::emitPrototypeInputs(const FuncProto *proto) if (sz == 0) emit->print("void",EmitXml::keyword_color); else { + bool printComma = false; for(int4 i=0;iprint(","); ProtoParameter *param = proto->getParam(i); + if (isSet(hide_thisparam) && param->isThisPointer()) + continue; Symbol *sym = param->getSymbol(); + printComma = true; if (sym != (Symbol *)0) emitVarDecl(sym); else { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh index 431cbdedf7..54162016f5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.hh @@ -161,6 +161,7 @@ protected: void opTypeCast(const PcodeOp *op); ///< Push the given p-code op using type-cast syntax to the RPN stack void opHiddenFunc(const PcodeOp *op); ///< Push the given p-code op as a hidden token bool printCharacterConstant(ostream &s,const Address &addr,Datatype *charType) const; + int4 getHiddenThisSlot(const PcodeOp *op,FuncProto *fc); ///< Get position of "this" pointer needing to be hidden void resetDefaultsPrintC(void); ///< Set default values for options specific to PrintC virtual void pushConstant(uintb val,const Datatype *ct, const Varnode *vn,const PcodeOp *op); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc index 7b6c174963..1d140a3865 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc @@ -39,7 +39,6 @@ PrintJava::PrintJava(Architecture *glb,const string &nm) : PrintC(glb,nm) { resetDefaultsPrintJava(); nullToken = "null"; // Java standard lower-case 'null' - mods |= hide_thisparam; // turn on hiding of 'this' parameter if (castStrategy != (CastStrategy *)0) delete castStrategy; @@ -160,6 +159,7 @@ void PrintJava::resetDefaultsPrintJava(void) { option_NULL = true; // Automatically use 'null' token option_convention = false; // Automatically hide convention name + mods |= hide_thisparam; // turn on hiding of 'this' parameter } /// Assuming the given Varnode is a dereferenced pointer, determine whether @@ -258,18 +258,29 @@ void PrintJava::opCallind(const PcodeOp *op) { pushOp(&function_call,op); - // implied vn's pushed on in reverse order for efficiency - // see PrintLanguage::pushVnImplied - int4 startparam = isSet(hide_thisparam) && op->hasThisPointer() ? 2 : 1; - if (op->numInput()>startparam + 1) { // Multiple parameters + const Funcdata *fd = op->getParent()->getFuncdata(); + FuncCallSpecs *fc = fd->getCallSpecs(op); + if (fc == (FuncCallSpecs *)0) + throw LowlevelError("Missing indirect function callspec"); + int4 skip = getHiddenThisSlot(op, fc); + int4 count = op->numInput() - 1; + count -= (skip < 0) ? 0 : 1; + if (count > 1) { // Multiple parameters pushVnImplied(op->getIn(0),op,mods); - for(int4 i=startparam;inumInput()-1;++i) + for(int4 i=0;inumInput()-1;i>=startparam;--i) + // implied vn's pushed on in reverse order for efficiency + // see PrintLanguage::pushVnImplied + for(int4 i=op->numInput()-1;i>=1;--i) { + if (i == skip) continue; pushVnImplied(op->getIn(i),op,mods); + } } - else if (op->numInput()==startparam + 1) { // One parameter - pushVnImplied(op->getIn(startparam),op,mods); + else if (count == 1) { // One parameter + if (skip == 1) + pushVnImplied(op->getIn(2),op,mods); + else + pushVnImplied(op->getIn(1),op,mods); pushVnImplied(op->getIn(0),op,mods); } else { // A void function diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc index f3ce7f83ce..b7e92c93b9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc @@ -24,6 +24,7 @@ void AddrSpace::calcScaleMask(void) pointerLowerBound = (addressSize < 3) ? 0x100: 0x1000; highest = calc_mask(addressSize); // Maximum address highest = highest * wordsize + (wordsize-1); // Maximum byte address + pointerUpperBound = highest; } /// Initialize an address space with its basic attributes diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh index 30e9b0ec3f..175a9778ee 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh @@ -98,6 +98,7 @@ private: uint4 flags; ///< Attributes of the space uintb highest; ///< Highest (byte) offset into this space uintb pointerLowerBound; ///< Offset below which we don't search for pointers + uintb pointerUpperBound; ///< Offset above which we don't search for pointers char shortcut; ///< Shortcut character for printing protected: string name; ///< Name of this space @@ -127,6 +128,7 @@ public: uint4 getAddrSize(void) const; ///< Get the size of the space uintb getHighest(void) const; ///< Get the highest byte-scaled address uintb getPointerLowerBound(void) const; ///< Get lower bound for assuming an offset is a pointer + uintb getPointerUpperBound(void) const; ///< Get upper bound for assuming an offset is a pointer int4 getMinimumPtrSize(void) const; ///< Get the minimum pointer size for \b this space uintb wrapOffset(uintb off) const; ///< Wrap -off- to the offset that fits into this space char getShortcut(void) const; ///< Get the shortcut character @@ -340,13 +342,20 @@ inline uintb AddrSpace::getHighest(void) const { return highest; } -/// Constant offsets are tested against \b this bound as a quick filter before +/// Constant offsets are tested against \b this lower bound as a quick filter before /// attempting to lookup symbols. /// \return the minimum offset that will be inferred as a pointer inline uintb AddrSpace::getPointerLowerBound(void) const { return pointerLowerBound; } +/// Constant offsets are tested against \b this upper bound as a quick filter before +/// attempting to lookup symbols. +/// \return the maximum offset that will be inferred as a pointer +inline uintb AddrSpace::getPointerUpperBound(void) const { + return pointerUpperBound; +} + /// A value of 0 means the size must match exactly. If the space is truncated, or /// if there exists near pointers, this value may be non-zero. inline int4 AddrSpace::getMinimumPtrSize(void) const { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index a76ef2efb9..7091ed6a47 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -421,6 +421,16 @@ void AddrSpaceManager::insertResolver(AddrSpace *spc,AddressResolver *rsolv) resolvelist[ind] = rsolv; } +/// This method establishes for a single address space, what range of constants are checked +/// as possible symbol starts, when it is not known apriori that a constant is a pointer. +/// \param range is the range of values for a single address space +void AddrSpaceManager::setInferPtrBounds(const Range &range) + +{ + range.getSpace()->pointerLowerBound = range.getFirst(); + range.getSpace()->pointerUpperBound = range.getLast(); +} + /// Base destructor class, cleans up AddrSpace pointers which /// must be explicited created via \e new AddrSpaceManager::~AddrSpaceManager(void) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index 35b5f8367c..a278c219c7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -243,6 +243,7 @@ protected: void copySpaces(const AddrSpaceManager *op2); ///< Copy spaces from another manager void addSpacebasePointer(SpacebaseSpace *basespace,const VarnodeData &ptrdata,int4 truncSize,bool stackGrowth); ///< Set the base register of a spacebase space void insertResolver(AddrSpace *spc,AddressResolver *rsolv); ///< Override the base resolver for a space + void setInferPtrBounds(const Range &range); ///< Set the range of addresses that can be inferred as pointers JoinRecord *findJoinInternal(uintb offset) const; ///< Find JoinRecord for \e offset in the join space public: AddrSpaceManager(void); ///< Construct an empty address space manager diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index ff79355d3b..408cf0d2f9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -1153,13 +1153,11 @@ void TypeCode::printRaw(ostream &s) const } /// Assuming \b this has an underlying function prototype, set some of its boolean properties -/// \param hasThisPtr toggles whether prototype has takes a "this" pointer /// \param isConstructor toggles whether the function is a constructor /// \param isDestructor toggles whether the function is a destructor -void TypeCode::setProperties(bool hasThisPtr,bool isConstructor,bool isDestructor) +void TypeCode::setProperties(bool isConstructor,bool isDestructor) { - proto->setThisPointer(hasThisPtr); proto->setConstructor(isConstructor); proto->setDestructor(isDestructor); } @@ -2201,11 +2199,10 @@ Datatype *TypeFactory::restoreXmlType(const Element *el) /// /// Kludge to get flags into code pointer types, when they can't come through XML /// \param el is the XML element describing the Datatype -/// \param hasThisPtr toggles "this" pointer property on "function" datatypes /// \param isConstructor toggles "constructor" property on "function" datatypes /// \param isDestructor toggles "destructor" property on "function" datatypes /// \return the restored Datatype object -Datatype *TypeFactory::restoreXmlTypeWithCodeFlags(const Element *el,bool hasThisPtr,bool isConstructor,bool isDestructor) +Datatype *TypeFactory::restoreXmlTypeWithCodeFlags(const Element *el,bool isConstructor,bool isDestructor) { TypePointer tp; @@ -2226,8 +2223,8 @@ Datatype *TypeFactory::restoreXmlTypeWithCodeFlags(const Element *el,bool hasThi throw LowlevelError("Special type restoreXml does not see code"); TypeCode tc(""); tc.restoreXml(subel,*this); - tc.setProperties(hasThisPtr,isConstructor,isDestructor); // Add in flags - tp.ptrto = findAdd(tc); // THEN add to container + tc.setProperties(isConstructor,isDestructor); // Add in flags + tp.ptrto = findAdd(tc); // THEN add to container return findAdd(tp); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index 1fb0b1404e..059ec989f5 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -351,7 +351,7 @@ public: TypeCode(const string &nm); ///< Construct from a name int4 compareBasic(const TypeCode *op) const; ///< Compare surface characteristics of two TypeCodes const FuncProto *getPrototype(void) const { return proto; } ///< Get the function prototype - void setProperties(bool hasThisPtr,bool isConstructor,bool isDestructor); ///< Set additional function properties + void setProperties(bool isConstructor,bool isDestructor); ///< Set additional function properties virtual ~TypeCode(void); virtual void printRaw(ostream &s) const; virtual int4 compare(const Datatype &op,int4 level) const; @@ -432,7 +432,7 @@ public: const vector &assignlist, TypeEnum *te); ///< Set named values for an enumeration Datatype *restoreXmlType(const Element *el); ///< Restore Datatype from XML - Datatype *restoreXmlTypeWithCodeFlags(const Element *el,bool hasThisPtr,bool isConstructor,bool isDestructor); + Datatype *restoreXmlTypeWithCodeFlags(const Element *el,bool isConstructor,bool isDestructor); TypeVoid *getTypeVoid(void); ///< Get the "void" data-type Datatype *getBaseNoChar(int4 s,type_metatype m); ///< Get atomic type excluding "char" Datatype *getBase(int4 s,type_metatype m); ///< Get atomic type diff --git a/Ghidra/Framework/SoftwareModeling/certification.manifest b/Ghidra/Framework/SoftwareModeling/certification.manifest index 03c810cd8f..d467ee2798 100644 --- a/Ghidra/Framework/SoftwareModeling/certification.manifest +++ b/Ghidra/Framework/SoftwareModeling/certification.manifest @@ -29,7 +29,6 @@ src/main/java/ghidra/program/database/package.html||GHIDRA||||END| src/main/java/ghidra/program/model/address/package.html||GHIDRA||||END| src/main/java/ghidra/program/model/block/package.html||GHIDRA||||END| src/main/java/ghidra/program/model/data/package.html||GHIDRA||||END| -src/main/java/ghidra/program/model/graph/package.html||GHIDRA||||END| src/main/java/ghidra/program/model/lang/package.html||GHIDRA||||END| src/main/java/ghidra/program/model/listing/package.html||GHIDRA||||END| src/main/java/ghidra/program/model/mem/package.html||GHIDRA||||END| diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg index d22ad44b56..ca982d9e4b 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/compiler_spec.rxg @@ -228,6 +228,14 @@ + + + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg index faf6fd56da..34e699a252 100644 --- a/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg +++ b/Ghidra/Framework/SoftwareModeling/data/languages/processor_spec.rxg @@ -40,6 +40,14 @@ + + + + + + + + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java index 5c514cd70b..db82ef46f4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java @@ -653,7 +653,8 @@ public class SleighLanguage implements Language { XmlElement element = parser.start("processor_spec"); while (!parser.peek().isEnd()) { element = parser.start("properties", "segmented_address", "segmentop", "programcounter", - "data_space", "context_data", "volatile", "jumpassist", "incidentalcopy", + "data_space", "inferptrbounds", "context_data", "volatile", "jumpassist", + "incidentalcopy", "register_data", "default_symbols", "default_memory_blocks"); if (element.getName().equals("properties")) { while (!parser.peek().isEnd()) { @@ -822,6 +823,11 @@ public class SleighLanguage implements Language { parser.discardSubTree(); } } + else if (element.getName().equals("inferptrbounds")) { + while (parser.peek().isStart()) { + parser.discardSubTree(); + } + } else if (element.getName().equals("segmentop")) { InjectPayloadSleigh payload = parseSegmentOp(element, parser); addAdditionInject(payload); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ConstantPool.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ConstantPool.java index 100c29fde8..8b80114422 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ConstantPool.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ConstantPool.java @@ -41,7 +41,6 @@ public abstract class ConstantPool { public long value; // Primitive value of the object (if tag == PRIMITIVE) public byte[] byteData; public DataType type; - public boolean hasThisPtr = false; public boolean isConstructor = false; public StringBuilder build(long ref, PcodeDataTypeManager dtmanage) { @@ -72,9 +71,6 @@ public abstract class ConstantPool { else { SpecXmlUtils.encodeStringAttribute(buf, "tag", "primitive"); } - if (hasThisPtr) { - SpecXmlUtils.encodeBooleanAttribute(buf, "hasthis", true); - } if (isConstructor) { SpecXmlUtils.encodeBooleanAttribute(buf, "constructor", true); } diff --git a/Ghidra/Processors/Dalvik/data/languages/Dalvik.pspec b/Ghidra/Processors/Dalvik/data/languages/Dalvik.pspec index bb885c9f17..3585846d91 100644 --- a/Ghidra/Processors/Dalvik/data/languages/Dalvik.pspec +++ b/Ghidra/Processors/Dalvik/data/languages/Dalvik.pspec @@ -12,6 +12,10 @@ + + + + diff --git a/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java b/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java index f0e3211c86..f7480d2538 100644 --- a/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java +++ b/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java @@ -121,7 +121,8 @@ public class ConstantPoolDex extends ConstantPool { String defName = res.token + '_' + Integer.toHexString(methodID); FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(defName, dtManager); res.type = new PointerDataType(funcDef); - res.hasThisPtr = !isStatic; + funcDef.setGenericCallingConvention( + isStatic ? GenericCallingConvention.stdcall : GenericCallingConvention.thiscall); int prototypeIndex = methodIDItem.getProtoIndex() & 0xffff; PrototypesIDItem prototype = dexHeader.getPrototypes().get(prototypeIndex); diff --git a/Ghidra/Processors/JVM/data/languages/JVM.pspec b/Ghidra/Processors/JVM/data/languages/JVM.pspec index 22849d7c86..5b9340f531 100644 --- a/Ghidra/Processors/JVM/data/languages/JVM.pspec +++ b/Ghidra/Processors/JVM/data/languages/JVM.pspec @@ -11,6 +11,11 @@ + + + + + diff --git a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java index 3b03ee7672..9ec1451e6b 100644 --- a/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java +++ b/Ghidra/Processors/JVM/src/main/java/ghidra/app/util/pcodeInject/ConstantPoolJava.java @@ -103,7 +103,7 @@ public class ConstantPoolJava extends ConstantPool { new ParameterDefinitionImpl("", params.get(i), null); paramDefs[i] = currentParam; } - res.hasThisPtr = false; + funcDef.setGenericCallingConvention(GenericCallingConvention.stdcall); } //invokeinterface, invokespecial, and invokevirtual do have a this pointer else { @@ -116,7 +116,7 @@ public class ConstantPoolJava extends ConstantPool { new ParameterDefinitionImpl("", params.get(i - 1), null); paramDefs[i] = currentParam; } - res.hasThisPtr = true; + funcDef.setGenericCallingConvention(GenericCallingConvention.thiscall); } funcDef.setArguments(paramDefs); res.type = new PointerDataType(funcDef);