diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index a2abeb6b44..1ccf67e3f9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -94,7 +94,6 @@ Architecture::Architecture(void) aggressive_ext_trim = false; readonlypropagate = false; infer_pointers = true; - pointer_lowerbound = 0x1000; funcptr_align = 0; flowoptions = 0; defaultfp = (ProtoModel *)0; @@ -306,21 +305,19 @@ void Architecture::collectBehaviors(vector &behave) const } } -/// A \b near pointer is some form of truncated pointer that needs -/// \e segment or other information to fully form an address. /// This method searches for a user-defined segment op registered -/// for the space +/// for the given space. /// \param spc is the address space to check -/// \return true if the space supports a segment operation -bool Architecture::hasNearPointers(AddrSpace *spc) const +/// \return the SegmentOp object or null +SegmentOp *Architecture::getSegmentOp(AddrSpace *spc) const { - if (spc->getIndex() >= userops.numSegmentOps()) return false; + if (spc->getIndex() >= userops.numSegmentOps()) return (SegmentOp *)0; SegmentOp *segdef = userops.getSegmentOp(spc->getIndex()); - if (segdef == (SegmentOp *)0) return false; + if (segdef == (SegmentOp *)0) return (SegmentOp *)0; if (segdef->getResolve().space != (AddrSpace *)0) - return true; - return false; + return segdef; + return (SegmentOp *)0; } /// Establish details of the prototype for a given function symbol @@ -601,6 +598,12 @@ void Architecture::buildInstructions(DocumentStorage &store) TypeOp::registerInstructions(inst,types,translate); } +void Architecture::postSpecFile(void) + +{ + cacheAddrSpaceProperties(); +} + /// Once the processor is known, the Translate object can be built and /// fully initialized. Processor and compiler specific configuration is performed /// \param store will hold parsed configuration information @@ -615,8 +618,6 @@ void Architecture::restoreFromSpec(DocumentStorage &store) insertSpace( new FspecSpace(this,translate,"fspec",numSpaces())); insertSpace( new IopSpace(this,translate,"iop",numSpaces())); insertSpace( new JoinSpace(this,translate,"join",numSpaces())); - if (translate->getDefaultSize() < 3) // For small architectures - pointer_lowerbound = 0x100; // assume constants are pointers starting at a much lower bound userops.initialize(this); if (translate->getAlignment() <= 8) min_funcsymbol_size = translate->getAlignment(); @@ -642,6 +643,48 @@ void Architecture::initializeSegments(void) } } +/// Determine the minimum pointer size for the space and whether or not there are near pointers. +/// Set up an ordered list of inferable spaces (where constant pointers can be infered). +/// Inferable spaces include the default space and anything explicitly listed +/// in the cspec \ tag that is not a register space. An initial list of potential spaces is +/// passed in that needs to be ordered, filtered, and deduplicated. +void Architecture::cacheAddrSpaceProperties(void) + +{ + vector copyList = inferPtrSpaces; + copyList.push_back(getDefaultSpace()); // Make sure the default space is their + inferPtrSpaces.clear(); + sort(copyList.begin(),copyList.end(),AddrSpace::compareByIndex); + AddrSpace *lastSpace = (AddrSpace *)0; + for(int4 i=0;igetDelay() == 0) continue; // Don't put in a register space + if (spc->getType() == IPTR_SPACEBASE) continue; + if (spc->isOtherSpace()) continue; + if (spc->isOverlay()) continue; + inferPtrSpaces.push_back(spc); + } + + int4 defPos = -1; + for(int4 i=0;igetInnerSize(); + markNearPointers(spc, val); + } + } + if (defPos > 0) { // Make sure the default space comes first + AddrSpace *tmp = inferPtrSpaces[0]; + inferPtrSpaces[0] = inferPtrSpaces[defPos]; + inferPtrSpaces[defPos] = tmp; + } +} + /// Recover information out of a \ tag and build the new Rule object. /// \param el is the XML element void Architecture::parseDynamicRule(const Element *el) @@ -749,7 +792,9 @@ void Architecture::parseGlobal(const Element *el) for(iter=list.begin();iter!=list.end();++iter) { Range range; range.restoreXml(*iter,this); - symboltab->addRange(scope,range.getSpace(),range.getFirst(),range.getLast()); + AddrSpace *spc = range.getSpace(); + inferPtrSpaces.push_back(spc); + symboltab->addRange(scope,spc,range.getFirst(),range.getLast()); if (range.getSpace()->isOverlayBase()) { // If the address space is overlayed // We need to duplicate the range being marked as global into the overlay space(s) int4 num = numSpaces(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index 7676d3278c..e8a84eda04 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -127,7 +127,7 @@ public: bool aggressive_ext_trim; ///< Aggressively trim inputs that look like they are sign extended bool readonlypropagate; ///< true if readonly values should be treated as constants bool infer_pointers; ///< True if we should infer pointers from constants that are likely addresses - uintb pointer_lowerbound; ///< Zero or lowest value that can be inferred as an address + vector inferPtrSpaces; ///< Set of address spaces in which a pointer constant is inferable int4 funcptr_align; ///< How many bits of alignment a function ptr has uint4 flowoptions; ///< options passed to flow following engine vector extra_pool_rules; ///< Extra rules that go in the main pool (cpu specific, experimental) @@ -173,7 +173,7 @@ public: void clearAnalysis(Funcdata *fd); ///< Clear analysis specific to a function void readLoaderSymbols(void); ///< Read any symbols from loader into database void collectBehaviors(vector &behave) const; ///< Provide a list of OpBehavior objects - bool hasNearPointers(AddrSpace *spc) const; ///< Does the given address space support \e near pointers + SegmentOp *getSegmentOp(AddrSpace *spc) const; ///< Retrieve the \e segment op for the given space if any void setPrototype(const PrototypePieces &pieces); ///< Set the prototype for a particular function void setPrintLanguage(const string &nm); ///< Establish a particular output language void globalify(void); ///< Mark \e all spaces as global @@ -241,14 +241,14 @@ protected: /// \param trans is the processor disassembly object virtual void modifySpaces(Translate *trans)=0; - virtual void postSpecFile(void) {} ///< Let components initialize after Translate is built - + virtual void postSpecFile(void); ///< Let components initialize after Translate is built virtual void resolveArchitecture(void)=0; ///< Figure out the processor and compiler of the target executable void restoreFromSpec(DocumentStorage &store); ///< Fully initialize the Translate object void fillinReadOnlyFromLoader(void); ///< Load info about read-only sections void initializeSegments(); ///< Set up segment resolvers + void cacheAddrSpaceProperties(void); ///< Calculate some frequently used space properties and cache them void parseProcessorConfig(DocumentStorage &store); ///< Apply processor specific configuration void parseCompilerConfig(DocumentStorage &store); ///< Apply compiler specific configuration diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/bfd_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/bfd_arch.cc index 3e1e503e0e..0adafa6949 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/bfd_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/bfd_arch.cc @@ -111,6 +111,7 @@ void BfdArchitecture::resolveArchitecture(void) void BfdArchitecture::postSpecFile(void) { // Attach default space to loader + Architecture::postSpecFile(); ((LoadImageBfd *)loader)->attachToSpace(getDefaultSpace()); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index 00d1e0c165..e7fb4573e0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -934,6 +934,66 @@ int4 ActionShadowVar::apply(Funcdata &data) return 0; } +/// \brief Make a limited search for a LOAD or STORE so we can see the AddrSpace being accessed +/// +/// We traverse forward through the given PcodeOp, through INT_ADD, INDIRECT, COPY, and MULTIEQUAL +/// until we hit a LOAD or STORE. +/// \param op is the given PcodeOp to start the search from +/// \return the discovered AddrSpace or null +AddrSpace *ActionConstantPtr::searchForLoadStore(PcodeOp *op) + +{ + for(int4 i=0;i<3;++i) { + switch(op->code()) { + case CPUI_INT_ADD: + case CPUI_COPY: + case CPUI_INDIRECT: + case CPUI_MULTIEQUAL: + op = op->getOut()->loneDescend(); + break; + case CPUI_LOAD: + case CPUI_STORE: + return Address::getSpaceFromConst(op->getIn(0)->getAddr()); + default: + return (AddrSpace *)0; + } + if (op == (PcodeOp *)0) break; + } + return (AddrSpace *)0; +} + +/// \brief Select the AddrSpace in which we infer with the given constant is a pointer +/// +/// The constant must match the AddrSpace address size. If there is more than one possible match, +/// search for more information in the syntax tree. +/// \param vn is the given constant Varnode +/// \param op is the PcodeOp which uses the constant +/// \param spaceList is the list of address spaces to select from +/// \return the selected address space or null +AddrSpace *ActionConstantPtr::selectInferSpace(Varnode *vn,PcodeOp *op,const vector &spaceList) + +{ + AddrSpace *resSpace = (AddrSpace *)0; + for(int4 i=0;igetMinimumPtrSize(); + if (minSize == 0) { + if (vn->getSize() != spc->getAddrSize()) + continue; + } + else if (vn->getSize() < minSize) + continue; + if (resSpace != (AddrSpace *)0) { + AddrSpace *searchSpc = searchForLoadStore(op); + if (searchSpc != (AddrSpace *)0) + resSpace = searchSpc; + break; + } + resSpace = spc; + } + return resSpace; +} + /// \brief Determine if given Varnode might be a pointer constant. /// /// If it is a pointer, return the symbol it points to, or NULL otherwise. If it is determined @@ -1001,7 +1061,7 @@ SymbolEntry *ActionConstantPtr::isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op return (SymbolEntry *)0; } // Make sure the constant is in the expected range for a pointer - if ((glb->pointer_lowerbound != 0)&&(glb->pointer_lowerbound > vn->getOffset())) + if (spc->getPointerLowerBound() > vn->getOffset()) return (SymbolEntry *)0; // Check if the constant looks like a single bit or mask if (bit_transitions(vn->getOffset(),vn->getSize()) < 3) @@ -1032,13 +1092,8 @@ int4 ActionConstantPtr::apply(Funcdata &data) VarnodeLocSet::const_iterator begiter,enditer; Architecture *glb = data.getArch(); AddrSpace *cspc = glb->getConstantSpace(); - AddrSpace *rspc = glb->getDefaultSpace(); SymbolEntry *entry; Varnode *vn; - bool arenearpointers = glb->hasNearPointers(rspc); - int4 minsize = rspc->isTruncated() ? rspc->getAddrSize() : 0; - if (minsize > 0) - arenearpointers = true; begiter = data.beginLoc(cspc); enditer = data.endLoc(cspc); @@ -1054,12 +1109,8 @@ int4 ActionConstantPtr::apply(Funcdata &data) PcodeOp *op = vn->loneDescend(); if (op == (PcodeOp *)0) continue; - if (arenearpointers) { - if (vn->getSize() < minsize) continue; - } - else { - if (vn->getSize() != rspc->getAddrSize()) continue; - } + AddrSpace *rspc = selectInferSpace(vn, op, glb->inferPtrSpaces); + if (rspc == (AddrSpace *)0) continue; int4 slot = op->getSlot(vn); OpCode opc = op->code(); if (opc == CPUI_INT_ADD) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index deeabd785d..abc7402441 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -180,6 +180,8 @@ public: /// \brief Check for constants, with pointer type, that correspond to global symbols class ActionConstantPtr : public Action { int4 localcount; ///< Number of passes made for this function + static AddrSpace *searchForLoadStore(PcodeOp *op); + static AddrSpace *selectInferSpace(Varnode *vn,PcodeOp *op,const vector &spaceList); static SymbolEntry *isPointer(AddrSpace *spc,Varnode *vn,PcodeOp *op,int4 slot, Address &rampoint,uintb &fullEncoding,Funcdata &data); public: diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc index 9a35bf8990..1db1e4ee18 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc @@ -273,6 +273,7 @@ void ArchitectureGhidra::buildSpecFile(DocumentStorage &store) void ArchitectureGhidra::postSpecFile(void) { + Architecture::postSpecFile(); ScopeGhidra *scopeGhidra = (ScopeGhidra *)symboltab->getGlobalScope(); scopeGhidra->lockDefaultProperties(); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/raw_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/raw_arch.cc index 6c1ac629ed..a580ecd3dd 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/raw_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/raw_arch.cc @@ -71,6 +71,7 @@ void RawBinaryArchitecture::resolveArchitecture(void) void RawBinaryArchitecture::postSpecFile(void) { + Architecture::postSpecFile(); ((RawLoadImage *)loader)->attachToSpace(getDefaultSpace()); // Attach default space to loader } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc index 7877c18ee9..f3ce7f83ce 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc @@ -17,9 +17,11 @@ #include "translate.hh" /// Calculate \e highest based on \e addressSize, and \e wordsize. +/// This also calculates the default pointerLowerBound void AddrSpace::calcScaleMask(void) { + pointerLowerBound = (addressSize < 3) ? 0x100: 0x1000; highest = calc_mask(addressSize); // Maximum address highest = highest * wordsize + (wordsize-1); // Maximum byte address } @@ -47,6 +49,7 @@ AddrSpace::AddrSpace(AddrSpaceManager *m,const Translate *t,spacetype tp,const s index = ind; delay = dl; deadcodedelay = dl; // Deadcode delay initially starts the same as heritage delay + minimumPointerSize = 0; // (initially) assume pointers must match the space size exactly shortcut = ' '; // Placeholder meaning shortcut is unassigned // These are the flags we allow to be set from constructor @@ -72,6 +75,7 @@ AddrSpace::AddrSpace(AddrSpaceManager *m,const Translate *t,spacetype tp) type = tp; flags = (heritaged | does_deadcode); // Always on unless explicitly turned off in derived constructor wordsize = 1; + minimumPointerSize = 0; shortcut = ' '; // We let big_endian get set by attribute } @@ -102,6 +106,7 @@ void AddrSpace::truncateSpace(uint4 newsize) { setFlags(truncated); addressSize = newsize; + minimumPointerSize = newsize; calcScaleMask(); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh index 4118d16313..30e9b0ec3f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh @@ -83,7 +83,8 @@ public: overlaybase = 64, ///< This is the base space for overlay space(s) truncated = 128, ///< Space is truncated from its original size, expect pointers larger than this size hasphysical = 256, ///< Has physical memory associated with it - is_otherspace = 512 ///< Quick check for the OtherSpace derived class + is_otherspace = 512, ///< Quick check for the OtherSpace derived class + has_nearpointers = 0x400 ///< Does there exist near pointers into this space }; enum { constant_space_index = 0, ///< Reserved index for the constant space @@ -96,11 +97,13 @@ private: int4 refcount; ///< Number of managers using this space 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 char shortcut; ///< Shortcut character for printing protected: string name; ///< Name of this space uint4 addressSize; ///< Size of an address into this space in bytes uint4 wordsize; ///< Size of unit being addressed (1=byte) + int4 minimumPointerSize; ///< Smallest size of a pointer into \b this space (in bytes) int4 index; ///< An integer identifier for the space int4 delay; ///< Delay in heritaging this space int4 deadcodedelay; ///< Delay before deadcode removal is allowed on this space @@ -123,6 +126,8 @@ public: uint4 getWordSize(void) const; ///< Get the addressable unit size 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 + 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 bool isHeritaged(void) const; ///< Return \b true if dataflow has been traced @@ -134,6 +139,7 @@ public: bool isOverlayBase(void) const; ///< Return \b true if other spaces overlay this space bool isOtherSpace(void) const; ///< Return \b true if \b this is the \e other address space bool isTruncated(void) const; ///< Return \b true if this space is truncated from its original size + bool hasNearPointers(void) const; ///< Return \b true if \e near (truncated) pointers into \b this space are possible void printOffset(ostream &s,uintb offset) const; ///< Write an address offset to a stream virtual int4 numSpacebase(void) const; ///< Number of base registers associated with this space @@ -153,6 +159,7 @@ public: static uintb byteToAddress(uintb val,uint4 ws); ///< Scale from byte units to addressable units static int4 addressToByteInt(int4 val,uint4 ws); ///< Scale int4 from addressable units to byte units static int4 byteToAddressInt(int4 val,uint4 ws); ///< Scale int4 from byte units to addressable units + static bool compareByIndex(const AddrSpace *a,const AddrSpace *b); ///< Compare two spaces by their index }; /// \brief Special AddrSpace for representing constants during analysis. @@ -333,6 +340,19 @@ inline uintb AddrSpace::getHighest(void) const { return highest; } +/// Constant offsets are tested against \b this 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; +} + +/// 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 { + return minimumPointerSize; +} + /// Calculate \e off modulo the size of this address space in /// order to construct the offset "equivalent" to \e off that /// fits properly into this space @@ -416,6 +436,10 @@ inline bool AddrSpace::isTruncated(void) const { return ((flags&truncated)!=0); } +inline bool AddrSpace::hasNearPointers(void) const { + return ((flags&has_nearpointers)!=0); +} + /// Some spaces are "virtual", like the stack spaces, where addresses are really relative to a /// base pointer stored in a register, like the stackpointer. This routine will return non-zero /// if \b this space is virtual and there is 1 (or more) associated pointer registers @@ -490,4 +514,13 @@ inline int4 AddrSpace::addressToByteInt(int4 val,uint4 ws) { inline int4 AddrSpace::byteToAddressInt(int4 val,uint4 ws) { return val/ws; } + +/// For sorting a sequence of address spaces. +/// \param a is the first space +/// \param b is the second space +/// \return \b true if the first space should come before the second +inline bool AddrSpace::compareByIndex(const AddrSpace *a,const AddrSpace *b) { + return (a->index < b->index); +} + #endif diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index 9586df45d7..08b0a95d0b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -456,6 +456,16 @@ void AddrSpaceManager::assignShortcut(AddrSpace *spc) spc->shortcut = (char)shortcut; } +/// \param spc is the AddrSpace to mark +/// \param size is the (minimum) size of a near pointer in bytes +void AddrSpaceManager::markNearPointers(AddrSpace *spc,int4 size) + +{ + spc->setFlags(AddrSpace::has_nearpointers); + if (spc->minimumPointerSize == 0 && spc->addressSize != size) + spc->minimumPointerSize = size; +} + /// All address spaces have a unique name associated with them. /// This routine retrieves the AddrSpace object based on the /// desired name. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index 26b048305b..74d998b2a2 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -235,6 +235,7 @@ protected: void setDefaultSpace(int4 index); ///< Set the default address space void setReverseJustified(AddrSpace *spc); ///< Set reverse justified property on this space void assignShortcut(AddrSpace *spc); ///< Select a shortcut character for a new space + void markNearPointers(AddrSpace *spc,int4 size); ///< Mark that given space can be accessed with near pointers void insertSpace(AddrSpace *spc); ///< Add a new address space to the model 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/xml_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/xml_arch.cc index 77ae75bf5a..864a8888a7 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/xml_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/xml_arch.cc @@ -72,6 +72,7 @@ void XmlArchitecture::buildLoader(DocumentStorage &store) void XmlArchitecture::postSpecFile(void) { + Architecture::postSpecFile(); ((LoadImageXml *)loader)->open(translate); if (adjustvma != 0) loader->adjustVma(adjustvma);