diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index 1d60464aae..ab4d90c4ba 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -95,6 +95,7 @@ src/decompile/datatests/twodim.xml||GHIDRA||||END| src/decompile/datatests/union_datatype.xml||GHIDRA||||END| src/decompile/datatests/varcross.xml||GHIDRA||||END| src/decompile/datatests/wayoffarray.xml||GHIDRA||||END| +src/decompile/datatests/wraprange.xml||GHIDRA||||END| src/decompile/zlib/README.txt||GHIDRA||||END| src/main/doc/commonprofile.xsl||GHIDRA||||END| src/main/doc/cspec.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh index 510432e5a2..6fdc9a5478 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/address.hh @@ -87,6 +87,7 @@ public: Address operator+(int8 off) const; ///< Increment address by a number of bytes Address operator-(int8 off) const; ///< Decrement address by a number of bytes friend ostream &operator<<(ostream &s,const Address &addr); ///< Write out an address to stream + bool isValidRange(uint8 size) const; ///< Is the range properly contained in its address space bool containedBy(int4 sz,const Address &op2,int4 sz2) const; ///< Determine if \e op2 range contains \b this range int4 justifiedContain(int4 sz,const Address &op2,int4 sz2,bool forceleft) const; ///< Determine if \e op2 is the least significant part of \e this. int4 overlap(int4 skip,const Address &op,int4 size) const; ///< Determine how \b this address falls in a given address range @@ -465,6 +466,14 @@ inline Address Address::operator-(int8 off) const { return Address(base,base->wrapOffset(offset-off)); } +/// If the range starting at \b this address and extending for \b size bytes, encompasses bytes beyond +/// the edge of the address space (or wraps), then return \b false. +/// \param size is the number of bytes in the range (must be non-zero) +/// \return \b true if the range is properly contained in the address space +inline bool Address::isValidRange(uint8 size) const { + return (size-1) <= (base->getHighest() - offset); +} + /// This method is equivalent to Address::overlap, but a range in the \e join space can be /// considered overlapped with its constituent pieces. /// If \e this + \e skip falls in the range, \e op to \e op + \e size, then a non-negative integer is diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc index 7be1bf1b61..ae98900d54 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_varnode.cc @@ -106,8 +106,15 @@ Varnode *Funcdata::newUnique(int4 s,Datatype *ct) Varnode *Funcdata::newVarnodeOut(int4 s,const Address &m,PcodeOp *op) { + Varnode *vn; Datatype *ct = glb->types->getBase(s,TYPE_UNKNOWN); - Varnode *vn = vbank.createDef(s,m,ct,op); + if (m.isValidRange(s)) { + vn = vbank.createDef(s,m,ct,op); + } + else { + Address addr = glb->constructWrappingAddress(m, s); + vn = vbank.createDef(s,addr,ct,op); + } op->setOutput(vn); assignHigh(vn); @@ -154,8 +161,13 @@ Varnode *Funcdata::newVarnode(int4 s,const Address &m,Datatype *ct) if (ct == (const Datatype *)0) ct = glb->types->getBase(s,TYPE_UNKNOWN); - - vn = vbank.create(s,m,ct); + if (m.isValidRange(s)) { + vn = vbank.create(s,m,ct); + } + else { + Address addr = glb->constructWrappingAddress(m, s); + vn = vbank.create(s,addr,ct); + } assignHigh(vn); if (s >= minLanedSize) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh index bc88fcd6c5..ff49ee951c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.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. @@ -94,7 +94,8 @@ public: truncated = 0x100, ///< Space is truncated from its original size, expect pointers larger than this size hasphysical = 0x200, ///< Has physical memory associated with it is_otherspace = 0x400, ///< Quick check for the OtherSpace derived class - has_nearpointers = 0x800 ///< Does there exist near pointers into this space + has_nearpointers = 0x800, ///< Does there exist near pointers into this space + allows_wrapped_range = 0x1000 ///< A memory range for \b this space can wrap from high addresses to low }; private: spacetype type; ///< Type of space (PROCESSOR, CONSTANT, INTERNAL, ...) @@ -150,6 +151,7 @@ public: 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 + bool allowsWrappedRange(void) const; ///< Return \b true if memory range can span high to low addresses in \b this space 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 @@ -467,6 +469,10 @@ inline bool AddrSpace::hasNearPointers(void) const { return ((flags&has_nearpointers)!=0); } +inline bool AddrSpace::allowsWrappedRange(void) const { + return ((flags & allows_wrapped_range)!=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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index 8cca59a574..d5b5219b14 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -61,6 +61,7 @@ SpacebaseSpace::SpacebaseSpace(AddrSpaceManager *m,const Translate *t,const stri contain = base; hasbaseregister = false; // No base register assigned yet isNegativeStack = true; // default stack growth + setFlags(allows_wrapped_range); if (isFormal) setFlags(formal_stackspace); } @@ -73,9 +74,10 @@ SpacebaseSpace::SpacebaseSpace(AddrSpaceManager *m,const Translate *t,const stri SpacebaseSpace::SpacebaseSpace(AddrSpaceManager *m,const Translate *t) : AddrSpace(m,t,IPTR_SPACEBASE) { + contain = (AddrSpace *)0; hasbaseregister = false; isNegativeStack = true; - setFlags(programspecific); + setFlags(programspecific | allows_wrapped_range); } /// This routine sets the base register associated with this \b virtual space @@ -847,6 +849,38 @@ Address AddrSpaceManager::constructJoinAddress(const Translate *translate, return join->getUnified().getAddr(); } +/// Check if the address space allows wrapped ranges. If so, construct a \e joined address +/// out of the high address piece of the range and the low address piece. +/// \param addr is the initial address in the range +/// \param size is the number of bytes in the range +/// \return the address representing the wrapped range +Address AddrSpaceManager::constructWrappingAddress(const Address &addr,int4 size) + +{ + AddrSpace *spc = addr.getSpace(); + if (!spc->isHeritaged()) + return addr; // Size is ignored + uintb dist = spc->getHighest() - addr.getOffset() + 1; + if (size <= dist) + return addr; + if (!spc->allowsWrappedRange()) + throw LowlevelError("Trying to construct memory range beyond end of address space: "+spc->getName()); + int4 sizehi = (int4)dist; + int4 sizelo = size - sizehi; + vector pieces; + pieces.emplace_back(); + pieces.emplace_back(); + int4 highIndex = spc->isBigEndian() ? 0 : 1; + pieces[highIndex].space = spc; + pieces[highIndex].offset = addr.getOffset(); + pieces[highIndex].size = sizehi; + pieces[1-highIndex].space = spc; + pieces[1-highIndex].offset = 0; + pieces[1-highIndex].size = sizelo; + JoinRecord *join = findAddJoin(pieces,0); + return join->getUnified().getAddr(); +} + /// If an Address in the \e join AddressSpace is shifted from its original offset, it may no /// longer have a valid JoinRecord. The shift or size change may even make the address of /// one of the pieces a more natural representation. Given a new Address and size, this method diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index 5abc6550f6..0048fb772f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -278,6 +278,9 @@ public: /// \brief Build a logical whole from register pairs Address constructJoinAddress(const Translate *translate,const Address &hiaddr,int4 hisz,const Address &loaddr,int4 losz); + /// \brief Build a logical whole representing a range that \e wraps from a high address to a low address + Address constructWrappingAddress(const Address &addr,int4 size); + /// \brief Make sure a possibly offset \e join address has a proper JoinRecord void renormalizeJoinAddress(Address &addr,int4 size); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 9014a307b1..f97146fa50 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -120,6 +120,26 @@ void print_data(ostream &s,uint1 *buffer,int4 size,const Address &baseaddr) } } +/// Construct a data-type providing just the size, alignment, and meta-type. +/// Sets up the default configuration, which may be overridden by the derived constructor. +/// \param s is the size in bytes +/// \param align is the byte alignment required for \b this +/// \param m is the meta-type +Datatype::Datatype(int4 s,int4 align,type_metatype m) + +{ + if (s < 0) + throw LowlevelError("Bad data-type size"); + size = s; + metatype = m; + submeta = base2sub[m]; + flags = 0; + id = 0; + typedefImm = (Datatype *)0; + alignment = align; + alignSize = s; +} + /// If \b this and the other given data-type are both variable length and come from the /// the same base data-type, return \b true. /// \param ct is the other given data-type to compare with \b this @@ -4113,7 +4133,7 @@ Datatype *TypeFactory::getBase(int4 s,type_metatype m) { Datatype *ct; - if (s<9) { + if ((uint4)s<9) { if (m >= TYPE_FLOAT) { ct = typecache[s][m-TYPE_FLOAT]; if (ct != (Datatype *)0) @@ -4122,13 +4142,9 @@ Datatype *TypeFactory::getBase(int4 s,type_metatype m) } else if (m==TYPE_FLOAT) { if (s==10) - ct = typecache10; - else if (s==16) - ct = typecache16; - else - ct = (Datatype *)0; - if (ct != (Datatype *)0) - return ct; + return typecache10; + if (s==16) + return typecache16; } if (s > glb->max_basetype_size) { // Create array of unknown bytes to match size @@ -4159,7 +4175,7 @@ Datatype *TypeFactory::getBase(int4 s,type_metatype m,const string &n) Datatype *TypeFactory::getTypeChar(int4 s) { - if (s < 5) { + if ((uint4)s < 5) { Datatype *res = charcache[s]; if (res != (Datatype *)0) return res; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index 0bafaef240..cc0951c206 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -210,12 +210,10 @@ protected: protected: static int4 calcAlignSize(int4 sz,int4 align); ///< Calculate aligned size, given size and alignment of data-type public: - /// Construct the base data-type copying low-level properties of another + /// \brief Construct the base data-type copying low-level properties of another Datatype(const Datatype &op) { size = op.size; name=op.name; displayName=op.displayName; metatype=op.metatype; submeta=op.submeta; flags=op.flags; id=op.id; typedefImm=op.typedefImm; alignment=op.alignment; alignSize=op.alignSize; } - /// Construct the base data-type providing size and meta-type - Datatype(int4 s,int4 align,type_metatype m) { - size=s; metatype=m; submeta=base2sub[m]; flags=0; id=0; typedefImm=(Datatype *)0; alignment=align; alignSize=s; } + Datatype(int4 s,int4 align,type_metatype m); ///< Constructor virtual ~Datatype(void) {} ///< Destructor 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' diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/wraprange.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/wraprange.xml new file mode 100644 index 0000000000..e9319228f0 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/wraprange.xml @@ -0,0 +1,19 @@ + + + + +4883ec04488b04244883c404c3 + + + + +j\{0x00000000,0xfffffffffffffffc\}.* = CONCAT44\(s0x00000000:4\(i\),s0xfffffffffffffffc:4\(i\)\) +RAX\(.*\) = j\{0x00000000,0xfffffffffffffffc\} +