mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-01-22 09:20:11 +00:00
GP-2438 Turning on return value storage
This commit is contained in:
parent
62c0e444a5
commit
e98ddcc3b1
@ -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
|
||||
|
@ -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)
|
||||
|
||||
{
|
||||
|
@ -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)
|
||||
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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 \<op> element and
|
||||
/// A convenience method for passing around p-code operations via stream.
|
||||
/// A single p-code operation is parsed from an \<op> 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)
|
||||
|
||||
|
@ -258,6 +258,7 @@ public class DecompileProcess {
|
||||
}
|
||||
|
||||
private void readResponse(ByteIngest mainResponse) throws IOException, DecompileException {
|
||||
mainResponse.clear();
|
||||
readToResponse();
|
||||
int type = readToBurst();
|
||||
int commandId;
|
||||
|
@ -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
|
||||
* - \<addr>
|
||||
@ -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 \<addr> 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);
|
||||
}
|
||||
|
@ -416,11 +416,12 @@ public class FunctionPrototype {
|
||||
/**
|
||||
* Decode the function prototype from a {@code <prototype>} 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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<Parameter> params = new ArrayList<Parameter>();
|
||||
List<Parameter> 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<String, Variable> nameMap = new TreeMap<String, Variable>();
|
||||
TreeMap<String, Variable> 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<Variable> mergeArray = new ArrayList<Variable>();
|
||||
ArrayList<Variable> mergeArray = new ArrayList<>();
|
||||
for (;;) {
|
||||
if (currentVar == null) {
|
||||
break;
|
||||
|
@ -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);
|
||||
|
@ -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<Varnode> 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<Varnode> inputs, Varnode output);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Integer, Varnode> refmap; // Obtain varnode by id
|
||||
private HashMap<Integer, Varnode> refmap; // Obtain varnode by id
|
||||
private HashMap<Integer, PcodeOp> oprefmap; // Obtain op by SequenceNumber unique id
|
||||
private HashMap<Integer, VariableStorage> joinmap; // logical map of joined objects
|
||||
private HashMap<Integer, VariableStorage> joinToStorage; // map "join" offsets to storage
|
||||
private HashMap<VariableStorage, Integer> 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<Varnode> 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<Varnode> inputs, Varnode output)
|
||||
throws UnknownInstructionException {
|
||||
public PcodeOp newOp(SequenceNumber sq, int opc, ArrayList<Varnode> inputs, Varnode output) {
|
||||
PcodeOp op = opbank.create(opc, inputs.size(), sq);
|
||||
if (output != null) {
|
||||
setOutput(op, output);
|
||||
|
@ -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<Varnode> 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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user