diff --git a/Ghidra/Features/Decompiler/certification.manifest b/Ghidra/Features/Decompiler/certification.manifest index 7df6e9e364..54230f327f 100644 --- a/Ghidra/Features/Decompiler/certification.manifest +++ b/Ghidra/Features/Decompiler/certification.manifest @@ -81,6 +81,7 @@ src/decompile/datatests/revisit.xml||GHIDRA||||END| src/decompile/datatests/sbyte.xml||GHIDRA||||END| src/decompile/datatests/skipnext2.xml||GHIDRA||||END| src/decompile/datatests/stackreturn.xml||GHIDRA||||END| +src/decompile/datatests/stackspill.xml||GHIDRA||||END| src/decompile/datatests/stackstring.xml||GHIDRA||||END| src/decompile/datatests/statuscmp.xml||GHIDRA||||END| src/decompile/datatests/switchhide.xml||GHIDRA||||END| diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index f9e147e628..f1664ac3a3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -1504,9 +1504,37 @@ void ActionFuncLink::funcLinkInput(FuncCallSpecs *fc,Funcdata &data) loadval->setSpacebasePlaceholder(); spacebase = (AddrSpace *)0; // With a locked stack parameter, we don't need a stackplaceholder } + continue; } - else - data.opInsertInput(op,data.newVarnode(param->getSize(),param->getAddress()),op->numInput()); + if (spc->getType() == IPTR_JOIN) { + JoinRecord *join = data.getArch()->findJoin(off); + int4 index = -1; + if (join->getPiece(0).space->getType() == IPTR_SPACEBASE) + index = 0; + else if (join->getPiece(join->numPieces()-1).space->getType() == IPTR_SPACEBASE) + index = join->numPieces()-1; + if (index >= 0) { + const VarnodeData &stack(join->getPiece(index)); + const VarnodeData &remain(data.getArch()->stripJoinPiece(join, index)); + Varnode *loadval = data.opStackLoad(stack.space,stack.offset,stack.size,op,(Varnode *)0,false); + Varnode *remainval = data.newVarnode(remain.size, remain.space, remain.offset); + PcodeOp *concatOp = data.newOp(2, op->getAddr()); + data.opSetOpcode(concatOp, CPUI_PIECE); + if (index == 0) { + data.opSetInput(concatOp,loadval,0); + data.opSetInput(concatOp,remainval,1); + } + else { + data.opSetInput(concatOp,remainval,0); + data.opSetInput(concatOp,loadval,1); + } + Varnode *outvn = data.newUniqueOut(sz, concatOp); + data.opInsertBefore(concatOp, op); + data.opInsertInput(op,outvn,op->numInput()); + continue; + } + } + data.opInsertInput(op,data.newVarnode(param->getSize(),param->getAddress()),op->numInput()); } } if (spacebase != (AddrSpace *)0) // If we need it, create the stackplaceholder @@ -1975,9 +2003,21 @@ int4 ActionRestrictLocal::apply(Funcdata &data) for(int4 j=0;jgetParam(j); Address addr = param->getAddress(); - if (addr.getSpace()->getType() != IPTR_SPACEBASE) continue; - uintb off = addr.getSpace()->wrapOffset(fc->getSpacebaseOffset() + addr.getOffset()); - data.getScopeLocal()->markNotMapped(addr.getSpace(),off,param->getSize(),true); + spacetype tp = addr.getSpace()->getType(); + if (tp == IPTR_SPACEBASE) { + uintb off = addr.getSpace()->wrapOffset(fc->getSpacebaseOffset() + addr.getOffset()); + data.getScopeLocal()->markNotMapped(addr.getSpace(),off,param->getSize(),true); + } + else if (tp == IPTR_JOIN) { + JoinRecord *joinRec = data.getArch()->findJoin(addr.getOffset()); + for(int4 k=0;knumPieces();++k) { + const VarnodeData &vdata(joinRec->getPiece(k)); + if (vdata.space->getType() == IPTR_SPACEBASE) { + uintb off = vdata.space->wrapOffset(fc->getSpacebaseOffset() + vdata.offset); + data.getScopeLocal()->markNotMapped(vdata.space,off,vdata.size,true); + } + } + } } } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index ec235a46d4..98060c2b96 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -915,6 +915,34 @@ void AddrSpaceManager::renormalizeJoinAddress(Address &addr,int4 size) addr = Address(newJoinRecord->unified.space,newJoinRecord->unified.offset); } +/// If only 1 piece remains, the VarnodeData of that piece is returned. +/// Otherwise a new JoinRecord is created and its unified VarnodeData is returned. +/// \param join is the JoinRecord to strip +/// \param index is the index of the piece to strip, which must be at the front or back +/// \return the VarnodeData corresponding to the remaining piece(s) +const VarnodeData &AddrSpaceManager::stripJoinPiece(JoinRecord *join,int4 index) + +{ + int4 start,end; + if (index == 0) { + start = 1; + end = join->numPieces()-1; + } + else if (index == join->numPieces() - 1) { + start = 0; + end = join->numPieces()-2; + } + else + throw LowlevelError("Stripping middle piece from JoinRecord"); + if (start == end) + return join->getPiece(start); + vector newPieces; + for(int4 i=start;i<=end;++i) + newPieces.push_back(join->getPiece(i)); + JoinRecord *newJoinRecord = findAddJoin(newPieces, 0); + return newJoinRecord->unified; +} + /// The string \e must contain a hexadecimal offset. The offset may be optionally prepended with "0x". /// The string may optionally start with the name of the address space to associate with the offset, followed /// by ':' to separate it from the offset. If the name is not present, the default data space is assumed. diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh index 09daa8005a..5abc6550f6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.hh @@ -281,6 +281,9 @@ public: /// \brief Make sure a possibly offset \e join address has a proper JoinRecord void renormalizeJoinAddress(Address &addr,int4 size); + /// \brief Create an Address by stripping a piece from a JoinRecord + const VarnodeData &stripJoinPiece(JoinRecord *join,int4 index); + /// \brief Parse a string with just an \e address \e space name and a hex offset Address parseAddressSimple(const string &val); }; diff --git a/Ghidra/Features/Decompiler/src/decompile/datatests/stackspill.xml b/Ghidra/Features/Decompiler/src/decompile/datatests/stackspill.xml new file mode 100644 index 0000000000..71a1beaf02 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/decompile/datatests/stackspill.xml @@ -0,0 +1,36 @@ + + + + +000000001000a28f0800e00321108200 +d0ffbd272c00bfaf640002241000a2af +253880001e000624140005240a000424 +b01d100c000000002c00bf8f00000000 +0800e0033000bd27 + + + + + + +return a \+ d\.field_b; +foo dvar; +dvar\.field_b = 100; +dvar\.field_a = val1; +receive\(10,0x14,0x1e,dvar\); +in_stack +CONCAT + diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java index ffcfe9cb68..bb32384b12 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/VariableStorage.java @@ -445,7 +445,7 @@ public class VariableStorage implements Comparable { * @return true if storage consists of a single stack varnode */ public boolean isStackStorage() { - if (varnodes == null || varnodes.length == 0) { + if (varnodes == null || varnodes.length != 1) { return false; } // check first varnode for stack use @@ -454,14 +454,20 @@ public class VariableStorage implements Comparable { } /** - * @return true if the last varnode for simple or compound storage is a stack varnode + * @return true if the first or last varnode for simple or compound storage is a stack varnode */ public boolean hasStackStorage() { if (varnodes == null || varnodes.length == 0) { return false; } - // check last varnode for stack use - Address storageAddr = getLastVarnode().getAddress(); + Address storageAddr = getFirstVarnode().getAddress(); + if (storageAddr.isStackAddress()) { + return true; + } + if (varnodes.length == 1) { + return false; + } + storageAddr = getLastVarnode().getAddress(); return storageAddr.isStackAddress(); } @@ -534,15 +540,21 @@ public class VariableStorage implements Comparable { /** * @return the stack offset associated with simple stack storage or compound - * storage where the last varnode is stack, see {@link #hasStackStorage()}. + * storage where the first or last varnode is stack, see {@link #hasStackStorage()}. * @throws UnsupportedOperationException if storage does not have a stack varnode */ public int getStackOffset() { if (varnodes != null && varnodes.length != 0) { - Address storageAddr = getLastVarnode().getAddress(); + Address storageAddr = getFirstVarnode().getAddress(); if (storageAddr.isStackAddress()) { return (int) storageAddr.getOffset(); } + if (varnodes.length > 1) { + storageAddr = getLastVarnode().getAddress(); + if (storageAddr.isStackAddress()) { + return (int) storageAddr.getOffset(); + } + } } throw new UnsupportedOperationException("Storage does not have a stack varnode"); }