From 716dfb169066faff4ca567bf5a446af80c0ad898 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:28:16 -0400 Subject: [PATCH] GP-1871 Overlapping pentry tags --- .../Decompiler/src/decompile/cpp/fspec.cc | 190 ++++++++++-------- .../Decompiler/src/decompile/cpp/fspec.hh | 13 +- .../ghidra/program/model/lang/ParamEntry.java | 176 +++++++++++----- .../program/model/lang/ParamListStandard.java | 8 - .../Sparc/data/languages/SparcV9_32.cspec | 8 +- .../Sparc/data/languages/SparcV9_64.cspec | 32 +-- .../tricore/data/languages/tricore.cspec | 34 ++-- 7 files changed, 282 insertions(+), 179 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc index 5756f1e590..ec3fd7ec23 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.cc @@ -56,32 +56,72 @@ void ParamEntry::resolveJoin(list &curList) int4 max = entry->group + entry->groupsize; if (max > maxgrp) maxgrp = max; + // For output , if the most signifigant part overlaps with an earlier + // the least signifigant part is marked for extra checks, and vice versa. + flags |= (i==0) ? extracheck_low : extracheck_high; } } if (maxgrp < 0 || mingrp >= 1000) throw LowlevelError(" join must overlap at least one previous entry"); group = mingrp; groupsize = (maxgrp - mingrp); + flags |= overlapping; if (groupsize > joinrec->numPieces()) throw LowlevelError(" join must overlap sequential entries"); } -/// A ParamEntry with \e join storage must either overlap a single other ParamEntry or -/// all pieces must overlap. -/// \return \b true if \b this is a join whose pieces do not all overlap -bool ParamEntry::isNonOverlappingJoin(void) const +/// Search for overlaps of \b this with any previous entry. If an overlap is discovered, +/// verify the form is correct for the different ParamEntry to share \e group slots and +/// reassign \b this group. +/// \param curList is the list of previous entries +void ParamEntry::resolveOverlap(list &curList) { - if (joinrec == (JoinRecord *)0) - return false; - return (joinrec->numPieces() != groupsize); + if (joinrec != (JoinRecord *)0) + return; // Overlaps with join records dealt with in resolveJoin + int4 grpsize = 0; + int4 mingrp = 1000; + int4 maxgrp = -1; + list::const_iterator iter,enditer; + Address addr(spaceid,addressbase); + enditer = curList.end(); + --enditer; // The last entry is \b this ParamEntry + for(iter=curList.begin();iter!=enditer;++iter) { + const ParamEntry &entry(*iter); + if (!entry.intersects(addr, size)) continue; + if (contains(entry)) { // If this contains the intersecting entry + if (entry.isOverlap()) continue; // Don't count resources (already counted overlapped entry) + if (entry.group < mingrp) + mingrp = entry.group; + int4 max = entry.group + entry.groupsize; + if (max > maxgrp) + maxgrp = max; + grpsize += entry.groupsize; + // For output , if the most signifigant part overlaps with an earlier + // the least signifigant part is marked for extra checks, and vice versa. + if (addressbase == entry.addressbase) + flags |= spaceid->isBigEndian() ? extracheck_low : extracheck_high; + else + flags |= spaceid->isBigEndian() ? extracheck_high : extracheck_low; + } + else + throw LowlevelError("Illegal overlap of in compiler spec"); + } + + if (grpsize == 0) return; // No overlaps + if (grpsize != (maxgrp - mingrp)) + throw LowlevelError(" must overlap sequential entries"); + group = mingrp; + groupsize = grpsize; + flags |= overlapping; } /// This entry must properly contain the other memory range, and -/// the entry properties must be compatible. -/// \param op2 is the other entry to compare with \b this -/// \return \b true if the other entry is contained -bool ParamEntry::contains(const ParamEntry &op2) const +/// the entry properties must be compatible. A \e join ParamEntry can +/// subsume another \e join ParamEntry, but we expect the addressbase to be identical. +/// \param op2 is the given entry to compare with \b this +/// \return \b true if the given entry is subsumed +bool ParamEntry::subsumesDefinition(const ParamEntry &op2) const { if ((type!=TYPE_UNKNOWN)&&(op2.type != type)) return false; @@ -92,6 +132,7 @@ bool ParamEntry::contains(const ParamEntry &op2) const return true; } +/// We assume a \e join ParamEntry cannot be contained by a single contiguous memory range. /// \param addr is the starting address of the potential containing range /// \param sz is the number of bytes in the range /// \return \b true if the entire ParamEntry fits inside the range @@ -105,6 +146,38 @@ bool ParamEntry::containedBy(const Address &addr,int4 sz) const return (entryoff <= rangeoff); } +/// If \b this a a \e join, each piece is tested for intersection. +/// Otherwise, \b this, considered as a single memory, is tested for intersection. +/// \param addr is the starting address of the given memory range to test against +/// \param sz is the number of bytes in the given memory range +/// \return \b true if there is any kind of intersection +bool ParamEntry::intersects(const Address &addr,int4 sz) const + +{ + uintb rangeend; + if (joinrec != (JoinRecord *)0) { + rangeend = addr.getOffset() + sz - 1; + for(int4 i=0;inumPieces();++i) { + const VarnodeData &vdata( joinrec->getPiece(i) ); + if (addr.getSpace() != vdata.space) continue; + uintb vdataend = vdata.offset + vdata.size - 1; + if (addr.getOffset() < vdata.offset && rangeend < vdataend) + continue; + if (addr.getOffset() > vdata.offset && rangeend > vdataend) + continue; + return true; + } + } + if (spaceid != addr.getSpace()) return false; + rangeend = addr.getOffset() + sz - 1; + uintb thisend = addressbase + size - 1; + if (addr.getOffset() < addressbase && rangeend < thisend) + return false; + if (addr.getOffset() > addressbase && rangeend > thisend) + return false; + return true; +} + /// Check if the given memory range is contained in \b this. /// If it is contained, return the endian aware offset of the containment. /// I.e. if the least significant byte of the given range falls on the least significant @@ -194,6 +267,28 @@ bool ParamEntry::getContainer(const Address &addr,int4 sz,VarnodeData &res) cons return true; } +/// Test that \b this, as one or more memory ranges, contains the other ParamEntry's memory range. +/// A \e join ParamEntry cannot be contained by another entry, but it can contain an entry in one +/// of its pieces. +/// \param op2 is the given ParamEntry to test for containment +/// \return \b true if the given ParamEntry is contained +bool ParamEntry::contains(const ParamEntry &op2) const + +{ + if (op2.joinrec != (JoinRecord *)0) return false; // Assume a join entry cannot be contained + if (joinrec == (JoinRecord *)0) { + Address addr(spaceid,addressbase); + return op2.containedBy(addr, size); + } + for(int4 i=0;inumPieces();++i) { + const VarnodeData &vdata(joinrec->getPiece(i)); + Address addr = vdata.getAddr(); + if (op2.containedBy(addr,vdata.size)) + return true; + } + return false; +} + /// \brief Calculate the type of \e extension to expect for the given logical value /// /// Return: @@ -402,54 +497,7 @@ void ParamEntry::restoreXml(const Element *el,const AddrSpaceManager *manage,boo if (grouped) flags |= is_grouped; resolveJoin(curList); -} - -/// \brief Check if \b this entry represents a \e joined parameter and requires extra scrutiny -/// -/// Return value parameter lists allow overlapping entries if one of the overlapping entries -/// is a \e joined parameter. In this case the return value recovery logic needs to know -/// what portion(s) of the joined parameter are overlapped. This method sets flags on \b this -/// to indicate the overlap. -/// \param entry is the full parameter list to check for overlaps with \b this -void ParamEntry::extraChecks(list &entry) - -{ - if (joinrec == (JoinRecord *)0) return; // Nothing to do if not multiprecision - if (joinrec->numPieces() != 2) return; - const VarnodeData &highPiece(joinrec->getPiece(0)); - bool seenOnce = false; - list::const_iterator iter; - for(iter=entry.begin();iter!=entry.end();++iter) { // Search for high piece, used as whole/low in another entry - AddrSpace *spc = (*iter).getSpace(); - uintb off = (*iter).getBase(); - int4 sz = (*iter).getSize(); - if ((highPiece.offset == off)&&(highPiece.space == spc)&&(highPiece.size == sz)) { - if (seenOnce) throw LowlevelError("Extra check hits twice"); - seenOnce = true; - flags |= extracheck_low; // If found, we must do extra checks on the low - } - } - if (!seenOnce) - flags |= extracheck_high; // The default is to do extra checks on the high -} - -/// If the storage is in the \e join space, we count the number of join pieces that overlap something -/// in the given list of entries. -/// \param curList is the list of entries to check -/// \return the number of overlapping pieces -int4 ParamEntry::countJoinOverlap(const list &curList) const - -{ - if (joinrec == (JoinRecord *)0) - return 0; - - int count = 0; - for (int4 i=0;inumPieces();++i) { - const ParamEntry *match = findEntryByStorage(curList, joinrec->getPiece(i)); - if (match != (const ParamEntry *)0) - count += 1; - } - return count; + resolveOverlap(curList); } /// Entries within a group must be distinguishable by size or by type. @@ -1176,15 +1224,6 @@ void ParamListStandard::restoreXml(const Element *el,const AddrSpaceManager *man parseGroup(subel, manage, effectlist, numgroup, normalstack, autokilledbycall, splitFloat); } } - // Check that any pentry tags with join storage don't overlap following tags - for (list::const_iterator eiter=entry.begin();eiter!=entry.end();++eiter) { - const ParamEntry &curEntry( *eiter ); - if (curEntry.isNonOverlappingJoin()) { - if (curEntry.countJoinOverlap(entry) != 1) { - throw LowlevelError("pentry tag must be listed after all its overlaps"); - } - } - } calcDelay(); populateResolver(); } @@ -1302,17 +1341,6 @@ bool ParamListRegisterOut::possibleParam(const Address &loc,int4 size) const return false; } -void ParamListRegisterOut::restoreXml(const Element *el,const AddrSpaceManager *manage, - vector &effectlist,bool normalstack) -{ - ParamListStandard::restoreXml(el,manage,effectlist,normalstack); - list::iterator iter; - for(iter=entry.begin();iter!=entry.end();++iter) { - ParamEntry &curEntry(*iter); - curEntry.extraChecks(entry); - } -} - ParamList *ParamListRegisterOut::clone(void) const { @@ -1427,11 +1455,11 @@ void ParamListMerged::foldIn(const ParamListStandard &op2) int4 typeint = 0; list::iterator iter; for(iter=entry.begin();iter!=entry.end();++iter) { - if ((*iter).contains(opentry)) { + if ((*iter).subsumesDefinition(opentry)) { typeint = 2; break; } - if (opentry.contains( *iter )) { + if (opentry.subsumesDefinition( *iter )) { typeint = 1; break; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index f614c29085..4bf61496c1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -57,7 +57,8 @@ public: smallsize_floatext = 64, ///< Assume values smaller than max \b size are floating-point extended to full size extracheck_high = 128, ///< Perform extra checks during parameter recovery on most sig portion of the double extracheck_low = 256, ///< Perform extra checks during parameter recovery on least sig portion of the double - is_grouped = 512 ///< This entry is grouped with other entries + is_grouped = 512, ///< This entry is grouped with other entries + overlapping = 0x100 ///< Overlaps an earlier entry (and doesn't consume additional resource slots) }; private: uint4 flags; ///< Boolean properties of the parameter @@ -73,6 +74,7 @@ private: JoinRecord *joinrec; ///< Non-null if this is logical variable from joined pieces static const ParamEntry *findEntryByStorage(const list &entryList,const VarnodeData &vn); void resolveJoin(list &curList); ///< Make adjustments for a \e join ParamEntry + void resolveOverlap(list &curList); ///< Make adjustments for ParamEntry that overlaps others /// \brief Is the logical value left-justified within its container bool isLeftJustified(void) const { return (((flags&force_left_justify)!=0)||(!spaceid->isBigEndian())); } @@ -87,21 +89,21 @@ public: bool isExclusion(void) const { return (alignment==0); } ///< Return \b true if this holds a single parameter exclusively bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order bool isGrouped(void) const { return ((flags & is_grouped)!=0); } ///< Return \b true if \b this is grouped with other entries - bool isNonOverlappingJoin(void) const; ///< Return \b true if not all pieces overlap other ParamEntry tags - bool contains(const ParamEntry &op2) const; ///< Does \b this contain the indicated entry. + bool isOverlap(void) const { return ((flags & overlapping)!=0); } ///< Return \b true if \b this overlaps another entry + bool subsumesDefinition(const ParamEntry &op2) const; ///< Does \b this subsume the definition of the given ParamEntry bool containedBy(const Address &addr,int4 sz) const; ///< Is this entry contained by the given range + bool intersects(const Address &addr,int4 sz) const; ///< Does \b this intersect the given range in some way int4 justifiedContain(const Address &addr,int4 sz) const; ///< Calculate endian aware containment bool getContainer(const Address &addr,int4 sz,VarnodeData &res) const; + bool contains(const ParamEntry &op2) const; ///< Does \this contain the given entry (as a subpiece) OpCode assumedExtension(const Address &addr,int4 sz,VarnodeData &res) const; int4 getSlot(const Address &addr,int4 skip) const; AddrSpace *getSpace(void) const { return spaceid; } ///< Get the address space containing \b this entry uintb getBase(void) const { return addressbase; } ///< Get the starting offset of \b this entry Address getAddrBySlot(int4 &slot,int4 sz) const; void restoreXml(const Element *el,const AddrSpaceManager *manage,bool normalstack,bool grouped,list &curList); - void extraChecks(list &entry); bool isParamCheckHigh(void) const { return ((flags & extracheck_high)!=0); } ///< Return \b true if there is a high overlap bool isParamCheckLow(void) const { return ((flags & extracheck_low)!=0); } ///< Return \b true if there is a low overlap - int4 countJoinOverlap(const list &curList) const; ///< Count the number of other entries \b this overlaps static void orderWithinGroup(const ParamEntry &entry1,const ParamEntry &entry2); ///< Enforce ParamEntry group ordering rules }; @@ -561,7 +563,6 @@ public: virtual void assignMap(const vector &proto,TypeFactory &typefactory,vector &res) const; virtual void fillinMap(ParamActive *active) const; virtual bool possibleParam(const Address &loc,int4 size) const; - virtual void restoreXml(const Element *el,const AddrSpaceManager *manage,vector &effectlist,bool normalstack); virtual ParamList *clone(void) const; }; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java index eb9aebd14e..c874425573 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/ParamEntry.java @@ -39,6 +39,7 @@ public class ParamEntry { //private static final int EXTRACHECK_HIGH = 128; //private static final int EXTRACHECK_LOW = 256; private static final int IS_GROUPED = 512; // The entry is grouped with other entries + private static final int OVERLAPPING = 0x100; // This overlaps an earlier entry public static final int TYPE_UNKNOWN = 8; // Default type restriction public static final int TYPE_PTR = 2; // pointer types @@ -100,6 +101,10 @@ public class ParamEntry { return ((flags & IS_GROUPED) != 0); } + public boolean isOverlap() { + return ((flags & OVERLAPPING) != 0); + } + public boolean isBigEndian() { return ((flags & IS_BIG_ENDIAN) != 0); } @@ -112,16 +117,6 @@ public class ParamEntry { return (((flags & IS_BIG_ENDIAN) == 0) || ((flags & FORCE_LEFT_JUSTIFY) != 0)); } - /** - * @return true if at least one piece of a join doesn't overlap with another ParamEntry - */ - public boolean isNonOverlappingJoin() { - if (joinrec == null) { - return false; - } - return (joinrec.length != groupsize); - } - public AddressSpace getSpace() { return spaceid; } @@ -130,22 +125,61 @@ public class ParamEntry { return joinrec; } - public boolean contains(ParamEntry op2) { - if ((type != TYPE_UNKNOWN) && (op2.type != type)) { + /** + * Is this ParamEntry, as a memory range, contained by the given memory range. + * @param addr is the starting address of the given memory range + * @param sz is the number of bytes in the given memory range + * @return true if this is contained + */ + public boolean containedBy(Address addr, int sz) { + if (spaceid != addr.getAddressSpace()) { return false; } - if (spaceid != op2.spaceid) { + if (Long.compareUnsigned(addressbase, addr.getOffset()) < 0) { return false; } - if (unsignedCompare(op2.addressbase, addressbase)) { + long rangeEnd = addr.getOffset() + sz - 1; + long thisEnd = addressbase + size - 1; + return (Long.compareUnsigned(thisEnd, rangeEnd) <= 0); + } + + /** + * Does this ParamEntry intersect the given range in some way + * @param addr is the starting address of the given range + * @param sz is the number of bytes in the given range + * @return true if there is an intersection + */ + public boolean intersects(Address addr, int sz) { + long rangeend; + if (joinrec != null) { + rangeend = addr.getOffset() + sz - 1; + for (Varnode vn : joinrec) { + if (addr.getAddressSpace().getSpaceID() != vn.getSpace()) { + continue; + } + long vnend = vn.getOffset() + vn.getSize() - 1; + if (Long.compareUnsigned(addr.getOffset(), vn.getOffset()) < 0 && + Long.compareUnsigned(rangeend, vnend) < 0) { + continue; + } + if (Long.compareUnsigned(addr.getOffset(), vn.getOffset()) > 0 && + Long.compareUnsigned(rangeend, vnend) > 0) { + continue; + } + return true; + } + } + if (spaceid.getSpaceID() != addr.getAddressSpace().getSpaceID()) { return false; } - long op2end = op2.addressbase + op2.size - 1; - long end = addressbase + size - 1; - if (unsignedCompare(end, op2end)) { + rangeend = addr.getOffset() + sz - 1; + long thisend = addressbase + size - 1; + if (Long.compareUnsigned(addr.getOffset(), addressbase) < 0 && + Long.compareUnsigned(rangeend, thisend) < 0) { return false; } - if (alignment != op2.alignment) { + if (Long.compareUnsigned(addr.getOffset(), addressbase) > 0 && + Long.compareUnsigned(rangeend, thisend) > 0) { return false; } return true; @@ -177,14 +211,14 @@ public class ParamEntry { return -1; } long startaddr = addr.getOffset(); - if (unsignedCompare(startaddr, addressbase)) { + if (Long.compareUnsigned(startaddr, addressbase) < 0) { return -1; } long endaddr = startaddr + sz - 1; - if (unsignedCompare(endaddr, startaddr)) { + if (Long.compareUnsigned(endaddr, startaddr) < 0) { return -1; // Don't allow wrap around } - if (unsignedCompare(addressbase + size - 1, endaddr)) { + if (Long.compareUnsigned(addressbase + size - 1, endaddr) < 0) { return -1; } startaddr -= addressbase; @@ -199,6 +233,27 @@ public class ParamEntry { return (int) (startaddr % alignment); } + /** + * Does this ParamEntry contain another entry (as a subpiece) + * @param otherEntry is the other entry + * @return true if this contains the other entry + */ + public boolean contains(ParamEntry otherEntry) { + if (otherEntry.joinrec != null) { + return false; // Assume a join entry cannot be contained + } + if (joinrec == null) { + Address addr = spaceid.getAddress(addressbase); + return otherEntry.containedBy(addr, size); + } + for (Varnode vn : joinrec) { + if (otherEntry.containedBy(vn.getAddress(), vn.getSize())) { + return true; + } + } + return false; + } + /** * Assuming the address is contained in this entry and we -skip- to a certain byte * return the slot associated with that byte @@ -299,20 +354,6 @@ public class ParamEntry { return null; } - public int countJoinOverlap(List curList) { - if (joinrec == null) { - return 0; - } - int count = 0; - for (Varnode vn : joinrec) { - ParamEntry match = findEntryByStorage(curList, vn); - if (match != null) { - count += 1; - } - } - return count; - } - /** * Adjust the group and groupsize based on the ParamEntrys being overlapped * @param curList is the current list of ParamEntry @@ -341,11 +382,61 @@ public class ParamEntry { } group = mingrp; groupsize = (maxgrp - mingrp); + flags |= OVERLAPPING; if (groupsize > joinrec.length) { throw new XmlParseException(" join must overlap sequential entries"); } } + /** + * Search for overlap with any previous ParamEntry. Reassign group and groupsize to + * reflect this overlap. + * @param curList is the list of previous ParamEntry + * @throws XmlParseException if overlaps do not take the correct form + */ + private void resolveOverlap(List curList) throws XmlParseException { + if (joinrec != null) { + return; + } + int grpsize = 0; + int mingrp = 1000; + int maxgrp = -1; + Address addr = spaceid.getAddress(addressbase); + for (ParamEntry entry : curList) { + if (entry == this) { + continue; + } + if (!entry.intersects(addr, size)) { + continue; + } + if (contains(entry)) { + if (entry.isOverlap()) { + continue; // Don't count resources (already counted overlapped pentry) + } + if (entry.group < mingrp) { + mingrp = entry.group; + } + int max = entry.group + entry.groupsize; + if (max > maxgrp) { + maxgrp = max; + } + grpsize += entry.groupsize; + } + else { + throw new XmlParseException("Illegal overlap of in compiler spec"); + } + } + if (grpsize == 0) { + return; // No overlaps + } + if (grpsize != (maxgrp - mingrp)) { + throw new XmlParseException(" must overlap sequential entries"); + } + group = mingrp; + groupsize = grpsize; + flags |= OVERLAPPING; + } + public void saveXml(StringBuilder buffer) { buffer.append(" - - + + - - + + diff --git a/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec b/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec index 63170e2aca..67ccac7080 100644 --- a/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec +++ b/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec @@ -30,18 +30,6 @@ - - - - - - - - - - - - @@ -54,6 +42,18 @@ + + + + + + + + + + + + @@ -77,14 +77,14 @@ - - + + - - + + diff --git a/Ghidra/Processors/tricore/data/languages/tricore.cspec b/Ghidra/Processors/tricore/data/languages/tricore.cspec index 94d116cf6f..cc816d9be1 100644 --- a/Ghidra/Processors/tricore/data/languages/tricore.cspec +++ b/Ghidra/Processors/tricore/data/languages/tricore.cspec @@ -39,30 +39,30 @@ - - - - - - + + + + + + + + + - - - - - - + + + @@ -112,15 +112,12 @@ - - + + - - - @@ -133,6 +130,9 @@ + + +