From e98ddcc3b1df74c6dc468444ea7f4521af5d7265 Mon Sep 17 00:00:00 2001 From: caheckman <48068198+caheckman@users.noreply.github.com> Date: Thu, 18 Aug 2022 17:57:51 -0400 Subject: [PATCH] GP-2438 Turning on return value storage --- .../Decompiler/src/decompile/cpp/fspec.hh | 2 +- .../Decompiler/src/decompile/cpp/marshal.cc | 5 + .../Decompiler/src/decompile/cpp/marshal.hh | 52 +++---- .../Decompiler/src/decompile/cpp/space.cc | 2 +- .../Decompiler/src/decompile/cpp/translate.cc | 5 +- .../app/decompiler/DecompileProcess.java | 1 + .../program/model/pcode/AddressXML.java | 78 ++++++---- .../model/pcode/FunctionPrototype.java | 10 +- .../program/model/pcode/HighFunction.java | 2 +- .../model/pcode/HighFunctionDBUtil.java | 12 +- .../program/model/pcode/MappedEntry.java | 22 +-- .../program/model/pcode/PcodeFactory.java | 111 +++++++++++--- .../ghidra/program/model/pcode/PcodeOp.java | 8 +- .../program/model/pcode/PcodeSyntaxTree.java | 140 +++++------------- .../ghidra/program/model/pcode/Varnode.java | 119 +++++++++++++-- 15 files changed, 342 insertions(+), 227 deletions(-) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh index 86c6af060f..4e10040ac3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/fspec.hh @@ -123,7 +123,7 @@ public: int4 getSize(void) const { return size; } ///< Get the size of the memory range in bytes. int4 getMinSize(void) const { return minsize; } ///< Get the minimum size of a logical value contained in \b this int4 getAlign(void) const { return alignment; } ///< Get the alignment of \b this entry - JoinRecord *getJoinRecord(void) const { return joinrec; } + JoinRecord *getJoinRecord(void) const { return joinrec; } ///< Get record describing joined pieces (or null if only 1 piece) type_metatype getType(void) const { return type; } ///< Get the data-type class associated with \b this 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 diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc index 80090cdca5..20e51c1a98 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.cc @@ -857,6 +857,11 @@ AddrSpace *PackedDecode::readSpace(const AttributeId &attribId) return res; } +/// The value is either an unsigned integer, an address space index, or (the absolute value of) a signed integer. +/// A type header is passed in with the particular type code for the value already filled in. +/// This method then fills in the length code, outputs the full type header and the encoded bytes of the integer. +/// \param typeByte is the type header +/// \param val is the integer value void PackedEncode::writeInteger(uint1 typeByte,uint8 val) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh index 6c8edea813..c2eae63e0f 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/marshal.hh @@ -390,29 +390,29 @@ public: /// For strings, the integer encoded after the \e type byte, is the actual length of the string. The /// string data itself is stored immediately after the length integer using UTF8 format. namespace PackedFormat { - static const uint1 HEADER_MASK = 0xc0; - static const uint1 ELEMENT_START = 0x40; - static const uint1 ELEMENT_END = 0x80; - static const uint1 ATTRIBUTE = 0xc0; - static const uint1 HEADEREXTEND_MASK = 0x20; - static const uint1 ELEMENTID_MASK = 0x1f; - static const uint1 RAWDATA_MASK = 0x7f; - static const int4 RAWDATA_BITSPERBYTE = 7; - static const uint1 RAWDATA_MARKER = 0x80; - static const int4 TYPECODE_SHIFT = 4; - static const uint1 LENGTHCODE_MASK = 0xf; - static const uint1 TYPECODE_BOOLEAN = 1; - static const uint1 TYPECODE_SIGNEDINT_POSITIVE = 2; - static const uint1 TYPECODE_SIGNEDINT_NEGATIVE = 3; - static const uint1 TYPECODE_UNSIGNEDINT = 4; - static const uint1 TYPECODE_ADDRESSSPACE = 5; - static const uint1 TYPECODE_SPECIALSPACE = 6; - static const uint1 TYPECODE_STRING = 7; - static const uint4 SPECIALSPACE_STACK = 0; - static const uint4 SPECIALSPACE_JOIN = 1; - static const uint4 SPECIALSPACE_FSPEC = 2; - static const uint4 SPECIALSPACE_IOP = 3; - static const uint4 SPECIALSPACE_SPACEBASE = 4; + static const uint1 HEADER_MASK = 0xc0; ///< Bits encoding the record type + static const uint1 ELEMENT_START = 0x40; ///< Header for an element start record + static const uint1 ELEMENT_END = 0x80; ///< Header for an element end record + static const uint1 ATTRIBUTE = 0xc0; ///< Header for an attribute record + static const uint1 HEADEREXTEND_MASK = 0x20; ///< Bit indicating the id extends into the next byte + static const uint1 ELEMENTID_MASK = 0x1f; ///< Bits encoding (part of) the id in the record header + static const uint1 RAWDATA_MASK = 0x7f; ///< Bits of raw data in follow-on bytes + static const int4 RAWDATA_BITSPERBYTE = 7; ///< Number of bits used in a follow-on byte + static const uint1 RAWDATA_MARKER = 0x80; ///< The unused bit in follow-on bytes. (Always set to 1) + static const int4 TYPECODE_SHIFT = 4; ///< Bit position of the type code in the type byte + static const uint1 LENGTHCODE_MASK = 0xf; ///< Bits in the type byte forming the length code + static const uint1 TYPECODE_BOOLEAN = 1; ///< Type code for the \e boolean type + static const uint1 TYPECODE_SIGNEDINT_POSITIVE = 2; ///< Type code for the \e signed \e positive \e integer type + static const uint1 TYPECODE_SIGNEDINT_NEGATIVE = 3; ///< Type code for the \e signed \e negative \e integer type + static const uint1 TYPECODE_UNSIGNEDINT = 4; ///< Type code for the \e unsigned \e integer type + static const uint1 TYPECODE_ADDRESSSPACE = 5; ///< Type code for the \e address \e space type + static const uint1 TYPECODE_SPECIALSPACE = 6; ///< Type code for the \e special \e address \e space type + static const uint1 TYPECODE_STRING = 7; ///< Type code for the \e string type + static const uint4 SPECIALSPACE_STACK = 0; ///< Special code for the \e stack space + static const uint4 SPECIALSPACE_JOIN = 1; ///< Special code for the \e join space + static const uint4 SPECIALSPACE_FSPEC = 2; ///< Special code for the \e fspec space + static const uint4 SPECIALSPACE_IOP = 3; ///< Special code for the \e iop space + static const uint4 SPECIALSPACE_SPACEBASE = 4; ///< Special code for a \e spacebase space } /// \brief A byte-based decoder designed to marshal info to the decompiler efficiently @@ -482,8 +482,8 @@ public: /// See PackedDecode for details of the encoding format. class PackedEncode : public Encoder { ostream &outStream; ///< The stream receiving the encoded data - void writeHeader(uint1 header,uint4 id); - void writeInteger(uint1 typeByte,uint8 val); + void writeHeader(uint1 header,uint4 id); ///< Write a header, element or attribute, to stream + void writeInteger(uint1 typeByte,uint8 val); ///< Write an integer value to the stream public: PackedEncode(ostream &s) : outStream(s) {} ///< Construct from a stream virtual void openElement(const ElementId &elemId); @@ -547,6 +547,8 @@ inline void PackedDecode::advancePosition(Position &pos,int4 skip) pos.current += skip; } +/// \param header is the type of header +/// \param id is the id associated with the element or attribute inline void PackedEncode::writeHeader(uint1 header,uint4 id) { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc index 347137a0c6..40a3e3bfe0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc @@ -495,7 +495,7 @@ void JoinSpace::encodeAttributes(Encoder &encoder,uintb offset) const encoder.writeString(*attribId, t.str()); } if (num == 1) - encoder.writeSignedInteger(ATTRIB_LOGICALSIZE, rec->getUnified().size); + encoder.writeUnsignedInteger(ATTRIB_LOGICALSIZE, rec->getUnified().size); } /// Encode a \e join address to the stream. This method in the interface only diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc index d604790f5f..ec3ec9a010 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc @@ -906,9 +906,10 @@ const FloatFormat *Translate::getFloatFormat(int4 size) const return (const FloatFormat *)0; } -/// A convenience method for passing around pcode operations via stream. -/// A single pcode operation is parsed from an \ element and +/// A convenience method for passing around p-code operations via stream. +/// A single p-code operation is parsed from an \ element and /// returned to the application via the PcodeEmit::dump method. +/// \param addr is the address (of the instruction) to associate with the p-code op /// \param decoder is the stream decoder void PcodeEmit::decodeOp(const Address &addr,Decoder &decoder) diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java index 0c7aed7e19..9fcef70759 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileProcess.java @@ -258,6 +258,7 @@ public class DecompileProcess { } private void readResponse(ByteIngest mainResponse) throws IOException, DecompileException { + mainResponse.clear(); readToResponse(); int type = readToBurst(); int commandId; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java index 5f809fc89a..c7a281fa66 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/AddressXML.java @@ -24,6 +24,9 @@ import java.util.ArrayList; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.VariableStorage; +import ghidra.util.exception.InvalidInputException; import ghidra.util.xml.SpecXmlUtils; import ghidra.xml.XmlElement; import ghidra.xml.XmlParseException; @@ -388,6 +391,44 @@ public class AddressXML { return spc.getAddress(offset); } + /** + * Decode a VariableStorage object from the attributes in the current address element. + * The start of storage corresponds to the decoded address. The size is either passed + * in or is decoded from a size attribute. + * @param size is the desired size of storage or -1 to use the size attribute + * @param decoder is the stream decoder + * @param pcodeFactory is used to resolve address spaces, etc. + * @return the decoded VariableStorage + * @throws DecoderException for any errors in the encoding or problems creating the storage + */ + public static VariableStorage decodeStorageFromAttributes(int size, Decoder decoder, + PcodeFactory pcodeFactory) throws DecoderException { + VariableStorage storage; + try { + Address varAddr = decodeFromAttributes(decoder); + AddressSpace spc = varAddr.getAddressSpace(); + if (spc == null || varAddr == Address.NO_ADDRESS) { + storage = VariableStorage.VOID_STORAGE; + } + else if (spc.getType() != AddressSpace.TYPE_VARIABLE) { + if (size <= 0) { + size = (int) decoder.readSignedInteger(ATTRIB_SIZE); + } + Program program = pcodeFactory.getDataTypeManager().getProgram(); + storage = new VariableStorage(program, varAddr, size); + } + else { + decoder.rewindAttributes(); + Varnode[] pieces = Varnode.decodePieces(decoder); + storage = pcodeFactory.getJoinStorage(pieces); + } + } + catch (InvalidInputException e) { + throw new DecoderException("Invalid storage: " + e.getMessage()); + } + return storage; + } + /** * Create an address from a stream encoding. This recognizes elements * - \ @@ -545,23 +586,6 @@ public class AddressXML { encoder.closeElement(ELEM_ADDR); } - private static String encodeVarnodePiece(Varnode vn) { - StringBuilder buffer = new StringBuilder(); - Address addr = vn.getAddress(); - AddressSpace space = addr.getAddressSpace(); - if (space.isOverlaySpace()) { - space = space.getPhysicalSpace(); - addr = space.getAddress(addr.getOffset()); - } - buffer.append(space.getName()); - buffer.append(":0x"); - long off = addr.getUnsignedOffset(); - buffer.append(Long.toHexString(off)); - buffer.append(':'); - buffer.append(Integer.toString(vn.getSize())); - return buffer.toString(); - } - /** * Encode a sequence of Varnodes as a single \ element to the stream. * If there is more than one Varnode, or if the logical size is non-zero, @@ -586,33 +610,33 @@ public class AddressXML { } encoder.openElement(ELEM_ADDR); encoder.writeSpace(ATTRIB_SPACE, AddressSpace.VARIABLE_SPACE); - encoder.writeString(ATTRIB_PIECE1, encodeVarnodePiece(varnodes[0])); + encoder.writeString(ATTRIB_PIECE1, varnodes[0].encodePiece()); if (varnodes.length > 1) { - encoder.writeString(ATTRIB_PIECE2, encodeVarnodePiece(varnodes[1])); + encoder.writeString(ATTRIB_PIECE2, varnodes[1].encodePiece()); } if (varnodes.length > 2) { - encoder.writeString(ATTRIB_PIECE3, encodeVarnodePiece(varnodes[2])); + encoder.writeString(ATTRIB_PIECE3, varnodes[2].encodePiece()); } if (varnodes.length > 3) { - encoder.writeString(ATTRIB_PIECE4, encodeVarnodePiece(varnodes[3])); + encoder.writeString(ATTRIB_PIECE4, varnodes[3].encodePiece()); } if (varnodes.length > 4) { - encoder.writeString(ATTRIB_PIECE5, encodeVarnodePiece(varnodes[4])); + encoder.writeString(ATTRIB_PIECE5, varnodes[4].encodePiece()); } if (varnodes.length > 5) { - encoder.writeString(ATTRIB_PIECE6, encodeVarnodePiece(varnodes[5])); + encoder.writeString(ATTRIB_PIECE6, varnodes[5].encodePiece()); } if (varnodes.length > 6) { - encoder.writeString(ATTRIB_PIECE7, encodeVarnodePiece(varnodes[6])); + encoder.writeString(ATTRIB_PIECE7, varnodes[6].encodePiece()); } if (varnodes.length > 7) { - encoder.writeString(ATTRIB_PIECE8, encodeVarnodePiece(varnodes[7])); + encoder.writeString(ATTRIB_PIECE8, varnodes[7].encodePiece()); } if (varnodes.length > 8) { - encoder.writeString(ATTRIB_PIECE9, encodeVarnodePiece(varnodes[8])); + encoder.writeString(ATTRIB_PIECE9, varnodes[8].encodePiece()); } if (logicalsize != 0) { - encoder.writeSignedInteger(ATTRIB_LOGICALSIZE, logicalsize); + encoder.writeUnsignedInteger(ATTRIB_LOGICALSIZE, logicalsize); } encoder.closeElement(ELEM_ADDR); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java index 81f99cd413..36666d51ca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/FunctionPrototype.java @@ -416,11 +416,12 @@ public class FunctionPrototype { /** * Decode the function prototype from a {@code } element in the stream. * @param decoder is the stream decoder - * @param dtmanage is the DataTypeManager used to parse data-type tags + * @param pcodeFactory is used to resolve data-type and address space references * @throws DecoderException for invalid encodings */ - public void decodePrototype(Decoder decoder, PcodeDataTypeManager dtmanage) + public void decodePrototype(Decoder decoder, PcodeFactory pcodeFactory) throws DecoderException { + PcodeDataTypeManager dtmanage = pcodeFactory.getDataTypeManager(); int node = decoder.openElement(ELEM_PROTOTYPE); modelname = decoder.readString(ATTRIB_MODEL); PrototypeModel protoModel = @@ -485,8 +486,9 @@ public class FunctionPrototype { } } - decoder.skipElement(); - returnstorage = null; // For now don't use decompiler's return storage + int addrel = decoder.openElement(ELEM_ADDR); + returnstorage = AddressXML.decodeStorageFromAttributes(-1, decoder, pcodeFactory); + decoder.closeElement(addrel); returntype = dtmanage.decodeDataType(decoder); decoder.closeElement(retel); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java index 9b907f45d9..01761918e6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java @@ -273,7 +273,7 @@ public class HighFunction extends PcodeSyntaxTree { } } else if (subel == ELEM_PROTOTYPE.id()) { - proto.decodePrototype(decoder, getDataTypeManager()); + proto.decodePrototype(decoder, this); } else if (subel == ELEM_LOCALDB.id()) { localSymbols.decodeScope(decoder); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java index 3c04b04930..dc6e90d837 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java @@ -56,15 +56,13 @@ public class HighFunctionDBUtil { function.setCallingConvention(modelName); } - // TODO: no return storage currently returned from Decompiler - //highFunction.getFunction().setReturn(type, storage, source) - + VariableStorage storage = highFunction.getFunctionPrototype().getReturnStorage(); DataType dataType = highFunction.getFunctionPrototype().getReturnType(); if (dataType == null) { dataType = DefaultDataType.dataType; source = SourceType.DEFAULT; } - function.setReturnType(dataType, source); + function.setReturn(dataType, storage, source); } catch (InvalidInputException e) { Msg.error(HighFunctionDBUtil.class, e.getMessage()); @@ -98,7 +96,7 @@ public class HighFunctionDBUtil { Program program = function.getProgram(); DataTypeManager dtm = program.getDataTypeManager(); LocalSymbolMap symbolMap = highFunction.getLocalSymbolMap(); - List params = new ArrayList(); + List params = new ArrayList<>(); int paramCnt = symbolMap.getNumParams(); for (int i = 0; i < paramCnt; ++i) { HighSymbol param = symbolMap.getParamSymbol(i); @@ -301,7 +299,7 @@ public class HighFunctionDBUtil { * @return an array of all Variables intended to be merged. */ private static Variable[] gatherMergeSet(Function function, Variable seed) { - TreeMap nameMap = new TreeMap(); + TreeMap nameMap = new TreeMap<>(); for (Variable var : function.getAllVariables()) { nameMap.put(var.getName(), var); } @@ -314,7 +312,7 @@ public class HighFunctionDBUtil { Variable currentVar = nameMap.get(baseName); int index = 0; boolean sawSeed = false; - ArrayList mergeArray = new ArrayList(); + ArrayList mergeArray = new ArrayList<>(); for (;;) { if (currentVar == null) { break; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java index c13bff6c45..b701cbbe54 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/MappedEntry.java @@ -18,14 +18,12 @@ package ghidra.program.model.pcode; import java.io.IOException; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.AbstractFloatDataType; import ghidra.program.model.listing.Program; import ghidra.program.model.listing.VariableStorage; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.ReferenceIterator; -import ghidra.util.exception.InvalidInputException; /** * A normal mapping of a HighSymbol to a particular Address, consuming a set number of bytes @@ -55,29 +53,13 @@ public class MappedEntry extends SymbolEntry { @Override public void decode(Decoder decoder) throws DecoderException { - HighFunction function = symbol.function; - Program program = function.getFunction().getProgram(); - - int addrel = decoder.openElement(ElementId.ELEM_ADDR); int sz = symbol.type.getLength(); if (sz == 0) { throw new DecoderException( "Invalid symbol 0-sized data-type: " + symbol.type.getName()); } - try { - Address varAddr = AddressXML.decodeFromAttributes(decoder); - AddressSpace spc = varAddr.getAddressSpace(); - if ((spc == null) || (spc.getType() != AddressSpace.TYPE_VARIABLE)) { - storage = new VariableStorage(program, varAddr, sz); - } - else { - decoder.rewindAttributes(); - storage = function.decodeVarnodePieces(decoder, varAddr); - } - } - catch (InvalidInputException e) { - throw new DecoderException("Invalid storage: " + e.getMessage()); - } + int addrel = decoder.openElement(ElementId.ELEM_ADDR); + storage = AddressXML.decodeStorageFromAttributes(sz, decoder, symbol.function); decoder.closeElement(addrel); decodeRangeList(decoder); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java index 5c9d48fe4f..3a30767796 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeFactory.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressFactory; import ghidra.program.model.data.DataType; -import ghidra.program.model.lang.UnknownInstructionException; import ghidra.program.model.listing.VariableStorage; import ghidra.util.exception.InvalidInputException; @@ -42,53 +41,131 @@ public interface PcodeFactory { public PcodeDataTypeManager getDataTypeManager(); /** - * Create a new Varnode with the given size an location + * Create a new Varnode with the given size and location * - * @param sz size of varnode - * @param addr location of varnode + * @param sz size of the Varnode + * @param addr location of the Varnode * * @return a new varnode */ public Varnode newVarnode(int sz, Address addr); + /** + * Create a new Varnode with the given size and location. + * Associate the Varnode with a specific reference id so that it can be retrieved, + * using just the id, via getRef(); + * @param sz size of the Varnode + * @param addr location of the Varnode + * @param refId is the specific reference id + * @return the new Varnode + */ public Varnode newVarnode(int sz, Address addr, int refId); /** - * Decode a join address from "piece" attributes - * - * @param decoder is the stream decoder - * @param addr join address associated with pieces - * - * @return the decoded VariableStorage - * @throws DecoderException for an improperly encoded stream - * @throws InvalidInputException if the pieces are not valid storage locations + * Create a storage object representing a value split across multiple physical locations. + * The sequence of physical locations are passed in as an array of Varnodes and the storage + * object is returned. The storage is also assigned an Address in the join address space, + * which can be retrieved by calling the getJoinAddress() method. The join Address can + * be used to create a Varnode that represents the logical whole created by concatenating + * the Varnode pieces. + * @param pieces is the array of Varnode pieces to join + * @return the VariableStorage representing the whole + * @throws InvalidInputException if a valid storage object cannot be created */ - public VariableStorage decodeVarnodePieces(Decoder decoder, Address addr) - throws DecoderException, InvalidInputException; + public VariableStorage getJoinStorage(Varnode[] pieces) throws InvalidInputException; - public Varnode createFromStorage(Address addr, VariableStorage storage, int logicalSize); + /** + * Get the address (in the "join" space) corresponding to the given multi-piece storage. + * The storage must have been previously registered by a previous call to getJoinStorage(). + * If the storage is not multi-piece or was not registered, null is returned. + * @param storage is the multi-piece storage + * @return the corresponding "join" address + */ + public Address getJoinAddress(VariableStorage storage); + /** + * Build a storage object for a particular Varnode + * @param vn is the Varnode + * @return the storage object + * @throws InvalidInputException if valid storage cannot be created + */ public VariableStorage buildStorage(Varnode vn) throws InvalidInputException; + /** + * Return a Varnode given its reference id, or null if the id is not registered. + * The id must have previously been registered via newVarnode(). + * @param refid is the reference id + * @return the matching Varnode or null + */ public Varnode getRef(int refid); + /** + * Get a PcodeOp given a reference id. The reference id corresponds to the op's + * SequenceNumber.getTime() field. Return null if no op matching the id has been registered + * via newOp(). + * @param refid is the reference id + * @return the matching PcodeOp or null + */ public PcodeOp getOpRef(int refid); + /** + * Get the high symbol matching the given id that has been registered with this object + * @param symbolId is the given id + * @return the matching HighSymbol or null + */ public HighSymbol getSymbol(long symbolId); + /** + * Mark (or unmark) the given Varnode as an input (to its function) + * @param vn is the given Varnode + * @param val is true if the Varnode should be marked + * @return the altered Varnode, which may not be the same object passed in + */ public Varnode setInput(Varnode vn, boolean val); + /** + * Mark (or unmark) the given Varnode with the "address tied" property + * @param vn is the given Varnode + * @param val is true if the Varnode should be marked + */ public void setAddrTied(Varnode vn, boolean val); + /** + * Mark (or unmark) the given Varnode with the "persistent" property + * @param vn is the given Varnode + * @param val is true if the Varnode should be marked + */ public void setPersistent(Varnode vn, boolean val); + /** + * Mark (or unmark) the given Varnode with the "unaffected" property + * @param vn is the given Varnode + * @param val is true if the Varnode should be marked + */ public void setUnaffected(Varnode vn, boolean val); + /** + * Associate a specific merge group with the given Varnode + * @param vn is the given Varnode + * @param val is the merge group + */ public void setMergeGroup(Varnode vn, short val); + /** + * Attach a data-type to the given Varnode + * @param vn is the given Varnode + * @param type is the data-type + */ public void setDataType(Varnode vn, DataType type); - public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList inputs, Varnode output) - throws UnknownInstructionException; + /** + * Create a new PcodeOp given its opcode, sequence number, and input and output Varnodes + * @param sq is the sequence number + * @param opc is the opcode + * @param inputs is the array of input Varnodes, which may be empty + * @param output is the output Varnode, which may be null + * @return the new PcodeOp + */ + public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList inputs, Varnode output); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java index d2e697a9ce..aa889a4fed 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeOp.java @@ -476,13 +476,7 @@ public class PcodeOp { Varnode vn = Varnode.decode(decoder, pfact); inputlist.add(vn); } - PcodeOp res; - try { - res = pfact.newOp(seqnum, opc, inputlist, output); - } - catch (UnknownInstructionException e) { - throw new DecoderException("Bad opcode: " + e.getMessage(), e); - } + PcodeOp res = pfact.newOp(seqnum, opc, inputlist, output); decoder.closeElement(el); return res; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java index eb2d2710dd..0d58e2ee7a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeSyntaxTree.java @@ -22,7 +22,6 @@ import java.util.*; import ghidra.program.model.address.*; import ghidra.program.model.data.DataType; -import ghidra.program.model.lang.UnknownInstructionException; import ghidra.program.model.listing.VariableStorage; import ghidra.util.exception.InvalidInputException; @@ -35,9 +34,10 @@ public class PcodeSyntaxTree implements PcodeFactory { private AddressFactory addrFactory; private PcodeDataTypeManager datatypeManager; - private HashMap refmap; // Obtain varnode by id + private HashMap refmap; // Obtain varnode by id private HashMap oprefmap; // Obtain op by SequenceNumber unique id - private HashMap joinmap; // logical map of joined objects + private HashMap joinToStorage; // map "join" offsets to storage + private HashMap storageToJoin; // map storage to "join" offsets private int joinAllocate; // next offset to be allocated in join map private PcodeOpBank opbank; private VarnodeBank vbank; @@ -49,7 +49,8 @@ public class PcodeSyntaxTree implements PcodeFactory { datatypeManager = dtmanage; refmap = null; oprefmap = null; - joinmap = null; + joinToStorage = null; + storageToJoin = null; joinAllocate = 0; opbank = new PcodeOpBank(); vbank = new VarnodeBank(); @@ -60,7 +61,8 @@ public class PcodeSyntaxTree implements PcodeFactory { public void clear() { refmap = null; oprefmap = null; - joinmap = null; + joinToStorage = null; + storageToJoin = null; joinAllocate = 0; vbank.clear(); opbank.clear(); @@ -68,62 +70,21 @@ public class PcodeSyntaxTree implements PcodeFactory { uniqId = 0; } - private static Varnode getVarnodePiece(String pieceStr, AddressFactory addrFactory) - throws DecoderException { -// TODO: Can't handle register name since addrFactory can't handle this - String[] varnodeTokens = pieceStr.split(":"); - if (varnodeTokens.length != 3) { - throw new DecoderException("Invalid \"join\" address piece: " + pieceStr); + @Override + public Address getJoinAddress(VariableStorage storage) { + if (storageToJoin == null) { + return null; } - AddressSpace space = addrFactory.getAddressSpace(varnodeTokens[0]); - if (space == null) { - throw new DecoderException("Invalid space for \"join\" address piece: " + pieceStr); + Integer off = storageToJoin.get(storage); + if (off == null) { + return null; } - if (!varnodeTokens[1].startsWith("0x")) { - throw new DecoderException("Invalid offset for \"join\" address piece: " + pieceStr); - } - long offset; - try { - offset = Long.parseUnsignedLong(varnodeTokens[1].substring(2), 16); - } - catch (NumberFormatException e) { - throw new DecoderException("Invalid offset for \"join\" address piece: " + pieceStr); - } - int size; - try { - size = Integer.parseInt(varnodeTokens[2]); - } - catch (NumberFormatException e) { - throw new DecoderException("Invalid size for \"join\" address piece: " + pieceStr); - } - return new Varnode(space.getAddress(offset), size); + AddressSpace spc = AddressSpace.VARIABLE_SPACE; + return spc.getAddress(off.longValue()); } @Override - public VariableStorage decodeVarnodePieces(Decoder decoder, Address addr) - throws DecoderException, InvalidInputException { - ArrayList list = new ArrayList<>(); - for (;;) { - int attribId = decoder.getNextAttributeId(); - if (attribId == 0) { - break; - } - else if (attribId >= ATTRIB_PIECE1.id() && attribId <= ATTRIB_PIECE9.id()) { - int index = attribId - ATTRIB_PIECE1.id(); - if (index != list.size()) { - throw new DecoderException("\"piece\" attributes must be in order"); - } - list.add(getVarnodePiece(decoder.readString(), decoder.getAddressFactory())); - } - } - Varnode[] pieces = new Varnode[list.size()]; - list.toArray(pieces); - - return allocateJoinStorage(addr.getOffset(), pieces); - } - - private VariableStorage allocateJoinStorage(long offset, Varnode[] pieces) - throws InvalidInputException { + public VariableStorage getJoinStorage(Varnode[] pieces) throws InvalidInputException { VariableStorage storage; try { storage = new VariableStorage(datatypeManager.getProgram(), pieces); @@ -147,31 +108,30 @@ public class PcodeSyntaxTree implements PcodeFactory { Address uniqaddr = addrFactory.getUniqueSpace().getAddress(0x20000000); storage = new VariableStorage(datatypeManager.getProgram(), uniqaddr, sz); } - Integer offObject; + if (joinToStorage == null) { + joinToStorage = new HashMap<>(); + } + if (storageToJoin == null) { + storageToJoin = new HashMap<>(); + } + Integer offObject = storageToJoin.get(storage); + if (offObject != null) { // Same storage was previously registered + return joinToStorage.get(offObject); // Use the old version + } + int roundsize = (storage.size() + 15) & 0xfffffff0; - if (offset < 0) { - offObject = Integer.valueOf(joinAllocate); - joinAllocate += roundsize; - } - else { - offObject = Integer.valueOf((int) offset); - offset += roundsize; - if (offset > joinAllocate) { - joinAllocate = (int) offset; - } - } - if (joinmap == null) { - joinmap = new HashMap<>(); - } - joinmap.put(offObject, storage); + offObject = Integer.valueOf(joinAllocate); + joinAllocate += roundsize; + joinToStorage.put(offObject, storage); + storageToJoin.put(storage, offObject); return storage; } private VariableStorage findJoinStorage(long offset) { - if (joinmap == null) { + if (joinToStorage == null) { return null; } - return joinmap.get(Integer.valueOf((int) offset)); + return joinToStorage.get(Integer.valueOf((int) offset)); } @Override @@ -315,35 +275,6 @@ public class PcodeSyntaxTree implements PcodeFactory { return vn; } - @Override - public Varnode createFromStorage(Address addr, VariableStorage storage, int logicalSize) { - Varnode[] pieces = storage.getVarnodes(); - - // This is the most common case, 1 piece, and address is pulled from the piece - if ((pieces.length == 1) && (addr == null)) { - Varnode vn = newVarnode(pieces[0].getSize(), pieces[0].getAddress()); - return vn; - } - - // Anything past here allocates varnode from the JOIN (VARIABLE) space. - // addr should be non-null ONLY if it is in the JOIN space - try { - if (addr == null) { // addr can still be null for join space varnode - long joinoffset = joinAllocate; // Next available offset - storage = allocateJoinStorage(-1, pieces); // is allocated from JOIN space - addr = AddressSpace.VARIABLE_SPACE.getAddress(joinoffset); - } - else { - storage = allocateJoinStorage(addr.getOffset(), pieces); - } - } - catch (InvalidInputException e) { - return null; - } - Varnode vn = newVarnode(logicalSize, addr); - return vn; - } - @Override public Varnode setInput(Varnode vn, boolean val) { if ((!vn.isInput()) && val) { @@ -507,8 +438,7 @@ public class PcodeSyntaxTree implements PcodeFactory { } @Override - public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList inputs, Varnode output) - throws UnknownInstructionException { + public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList inputs, Varnode output) { PcodeOp op = opbank.create(opc, inputs.size(), sq); if (output != null) { setOutput(op, output); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java index 493ab946db..66875e29e7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Varnode.java @@ -19,11 +19,13 @@ import static ghidra.program.model.pcode.AttributeId.*; import static ghidra.program.model.pcode.ElementId.*; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; import ghidra.program.model.address.*; import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Register; +import ghidra.program.model.listing.VariableStorage; import ghidra.util.exception.InvalidInputException; /** @@ -324,6 +326,30 @@ public class Varnode { AddressXML.encode(encoder, address, size); } + /** + * Encode details of the Varnode as a formatted string with three colon separated fields. + * space:offset:size + * The name of the address space, the offset of the address as a hex number, and + * the size field as a decimal number. + * @return the formatted String + */ + public String encodePiece() { + StringBuilder buffer = new StringBuilder(); + Address addr = address; + AddressSpace space = addr.getAddressSpace(); + if (space.isOverlaySpace()) { + space = space.getPhysicalSpace(); + addr = space.getAddress(addr.getOffset()); + } + buffer.append(space.getName()); + buffer.append(":0x"); + long off = addr.getUnsignedOffset(); + buffer.append(Long.toHexString(off)); + buffer.append(':'); + buffer.append(Integer.toString(size)); + return buffer.toString(); + } + /** * Decode a Varnode from a stream * @@ -367,22 +393,25 @@ public class Varnode { decoder.rewindAttributes(); Varnode vn; Address addr = AddressXML.decodeFromAttributes(decoder); + AddressSpace spc = addr.getAddressSpace(); + if ((spc != null) && (spc.getType() == AddressSpace.TYPE_VARIABLE)) { // Check for a composite Address + decoder.rewindAttributes(); + try { + Varnode[] pieces = decodePieces(decoder); + VariableStorage storage = factory.getJoinStorage(pieces); + // Update "join" address to the one just registered with the pieces + addr = factory.getJoinAddress(storage); + } + catch (InvalidInputException e) { + throw new DecoderException("Invalid varnode pieces: " + e.getMessage()); + } + } if (ref != -1) { vn = factory.newVarnode(sz, addr, ref); } else { vn = factory.newVarnode(sz, addr); } - AddressSpace spc = addr.getAddressSpace(); - if ((spc != null) && (spc.getType() == AddressSpace.TYPE_VARIABLE)) { // Check for a composite Address - decoder.rewindAttributes(); - try { - factory.decodeVarnodePieces(decoder, addr); - } - catch (InvalidInputException e) { - throw new DecoderException("Invalid varnode pieces: " + e.getMessage()); - } - } decoder.rewindAttributes(); for (;;) { int attribId = decoder.getNextAttributeId(); @@ -418,6 +447,76 @@ public class Varnode { return vn; } + /** + * Decode a Varnode from a description in a string. + * The format should be three colon separated fields: space:offset:size + * The space field should be the name of an address space, the offset field should + * be a hexadecimal number, and the size field should be a decimal number. + * @param pieceStr is the formatted string + * @param addrFactory is the factory used to look up the address space + * @return a new Varnode as described by the string + * @throws DecoderException if the string is improperly formatted + */ + private static Varnode decodePiece(String pieceStr, AddressFactory addrFactory) + throws DecoderException { +// TODO: Can't handle register name since addrFactory can't handle this + String[] varnodeTokens = pieceStr.split(":"); + if (varnodeTokens.length != 3) { + throw new DecoderException("Invalid \"join\" address piece: " + pieceStr); + } + AddressSpace space = addrFactory.getAddressSpace(varnodeTokens[0]); + if (space == null) { + throw new DecoderException("Invalid space for \"join\" address piece: " + pieceStr); + } + if (!varnodeTokens[1].startsWith("0x")) { + throw new DecoderException("Invalid offset for \"join\" address piece: " + pieceStr); + } + long offset; + try { + offset = Long.parseUnsignedLong(varnodeTokens[1].substring(2), 16); + } + catch (NumberFormatException e) { + throw new DecoderException("Invalid offset for \"join\" address piece: " + pieceStr); + } + int size; + try { + size = Integer.parseInt(varnodeTokens[2]); + } + catch (NumberFormatException e) { + throw new DecoderException("Invalid size for \"join\" address piece: " + pieceStr); + } + return new Varnode(space.getAddress(offset), size); + } + + /** + * Decode a sequence of Varnodes from "piece" attributes for the current open element. + * The Varnodes are normally associated with an Address in the "join" space. In this virtual + * space, a contiguous sequence of bytes, at a specific Address, represent a logical value + * that may physically be split across multiple registers or other storage locations. + * @param decoder is the stream decoder + * @return an array of decoded Varnodes + * @throws DecoderException for any errors in the encoding + */ + public static Varnode[] decodePieces(Decoder decoder) throws DecoderException { + ArrayList list = new ArrayList<>(); + for (;;) { + int attribId = decoder.getNextAttributeId(); + if (attribId == 0) { + break; + } + else if (attribId >= ATTRIB_PIECE1.id() && attribId <= ATTRIB_PIECE9.id()) { + int index = attribId - ATTRIB_PIECE1.id(); + if (index != list.size()) { + throw new DecoderException("\"piece\" attributes must be in order"); + } + list.add(decodePiece(decoder.readString(), decoder.getAddressFactory())); + } + } + Varnode[] pieces = new Varnode[list.size()]; + list.toArray(pieces); + return pieces; + } + /** * Trim a varnode in a constant space to the correct starting offset. *