GP-2438 Turning on return value storage

This commit is contained in:
caheckman 2022-08-18 17:57:51 -04:00
parent 62c0e444a5
commit e98ddcc3b1
15 changed files with 342 additions and 227 deletions

View file

@ -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

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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

View file

@ -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)

View file

@ -258,6 +258,7 @@ public class DecompileProcess {
}
private void readResponse(ByteIngest mainResponse) throws IOException, DecompileException {
mainResponse.clear();
readToResponse();
int type = readToBurst();
int commandId;

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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.
*