GP-3938 PrototypeModel rules

This commit is contained in:
caheckman 2023-05-30 11:07:08 -04:00
parent dae07c1900
commit 191371675a
55 changed files with 6000 additions and 1016 deletions

View file

@ -22,7 +22,6 @@ import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import ghidra.app.plugin.processors.sleigh.VarnodeData;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
@ -44,12 +43,8 @@ public class ParamEntry {
private static final int IS_GROUPED = 512; // The entry is grouped with other entries
private static final int OVERLAPPING = 0x100; // This overlaps an earlier entry
public static final int TYPE_UNKNOWN = 8; // Default type restriction
public static final int TYPE_PTR = 2; // pointer types
public static final int TYPE_FLOAT = 3; // floating point types
private int flags;
private int type; // Restriction on DataType this entry must match
private StorageClass type; // Restriction on DataType this entry must match
private int[] groupSet; // Group(s) this entry belongs to
private AddressSpace spaceid; // Space of this range
private long addressbase; // Start of the range
@ -88,7 +83,7 @@ public class ParamEntry {
return addressbase;
}
public int getType() {
public StorageClass getType() {
return type;
}
@ -112,10 +107,6 @@ public class ParamEntry {
return ((flags & IS_BIG_ENDIAN) != 0);
}
public boolean isFloatExtended() {
return ((flags & SMALLSIZE_FLOAT) != 0);
}
private boolean isLeftJustified() {
return (((flags & IS_BIG_ENDIAN) == 0) || ((flags & FORCE_LEFT_JUSTIFY) != 0));
}
@ -131,7 +122,7 @@ public class ParamEntry {
* @param sz is the given size
* @return the collected array of Varnodes or null
*/
public Varnode[] getJoinPieces(int sz) {
private Varnode[] getJoinPieces(int sz) {
int num = 0;
int first, replace;
Varnode vn = null;
@ -337,18 +328,19 @@ public class ParamEntry {
}
/**
* Return the storage address assigned when allocating something of size -sz- assuming -slotnum- slots
* have already been assigned. Set res.space to null if the -sz- is too small or if
* Assign the storage address when allocating something of size -sz- assuming -slotnum- slots
* have already been assigned. Set the address to null if the -sz- is too small or if
* there are not enough slots left
* @param slotnum number of slots already assigned
* @param sz number of bytes to being assigned
* @param typeAlign required byte alignment for the parameter
* @param res the final storage address
* @param res will hold the final storage address
* @return slotnum plus the number of slots used
*/
public int getAddrBySlot(int slotnum, int sz, int typeAlign, VarnodeData res) {
res.space = null; // Start with an invalid result
public int getAddrBySlot(int slotnum, int sz, int typeAlign, ParameterPieces res) {
int spaceused;
long offset;
res.address = null; // Start with an invalid result
if (sz < minsize) {
return slotnum;
}
@ -359,10 +351,12 @@ public class ParamEntry {
if (sz > size) {
return slotnum; // Check on maximum size
}
res.space = spaceid;
res.offset = addressbase; // Get base address of the slot
offset = addressbase; // Get base address of the slot
spaceused = size;
if ((flags & SMALLSIZE_FLOAT) != 0) {
if ((flags & SMALLSIZE_FLOAT) != 0 && sz != size) {
res.address = spaceid.getAddress(offset);
res.joinPieces = new Varnode[1];
res.joinPieces[0] = new Varnode(res.address, size);
return slotnum;
}
}
@ -390,13 +384,17 @@ public class ParamEntry {
else {
index = slotnum;
}
res.space = spaceid;
res.offset = addressbase + index * alignment;
offset = addressbase + index * alignment;
slotnum += slotsused; // Inform caller of number of slots used
}
if (!isLeftJustified()) {
res.offset += (spaceused - sz);
offset += (spaceused - sz);
}
res.address = spaceid.getAddress(offset);
if (res.address.getAddressSpace().getType() == AddressSpace.TYPE_JOIN) {
res.joinPieces = getJoinPieces(sz);
}
return slotnum;
}
@ -496,9 +494,8 @@ public class ParamEntry {
if (alignment != 0) {
encoder.writeSignedInteger(ATTRIB_ALIGN, alignment);
}
if (type == TYPE_FLOAT || type == TYPE_PTR) {
String tok = (type == TYPE_FLOAT) ? "float" : "ptr";
encoder.writeString(ATTRIB_METATYPE, tok);
if (type != StorageClass.GENERAL) {
encoder.writeString(ATTRIB_STORAGE, type.toString());
}
String extString = null;
if ((flags & SMALLSIZE_SEXT) != 0) {
@ -531,41 +528,32 @@ public class ParamEntry {
public void restoreXml(XmlPullParser parser, CompilerSpec cspec, List<ParamEntry> curList,
boolean grouped) throws XmlParseException {
flags = 0;
type = TYPE_UNKNOWN;
type = StorageClass.GENERAL;
size = minsize = -1; // Must be filled in
alignment = 0; // default
numslots = 1;
XmlElement el = parser.start("pentry");
XmlElement el = parser.start(ELEM_PENTRY.name());
Iterator<Entry<String, String>> iter = el.getAttributes().entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> entry = iter.next();
String name = entry.getKey();
if (name.equals("minsize")) {
if (name.equals(ATTRIB_MINSIZE.name())) {
minsize = SpecXmlUtils.decodeInt(entry.getValue());
}
else if (name.equals("size")) { // old style
else if (name.equals(ATTRIB_SIZE.name())) { // old style
alignment = SpecXmlUtils.decodeInt(entry.getValue());
}
else if (name.equals("align")) {
else if (name.equals(ATTRIB_ALIGN.name())) {
alignment = SpecXmlUtils.decodeInt(entry.getValue());
}
else if (name.equals("maxsize")) {
else if (name.equals(ATTRIB_MAXSIZE.name())) {
size = SpecXmlUtils.decodeInt(entry.getValue());
}
else if (name.equals("metatype")) { // Not implemented at the moment
String meta = entry.getValue();
// TODO: Currently only supporting "float", "ptr", and "unknown" metatypes
if ((meta != null)) {
if (meta.equals("float")) {
type = TYPE_FLOAT;
}
else if (meta.equals("ptr")) {
type = TYPE_PTR;
}
}
else if (name.equals(ATTRIB_STORAGE.name()) || name.equals(ATTRIB_METATYPE.name())) {
type = StorageClass.getClass(entry.getValue());
}
else if (name.equals("extension")) {
else if (name.equals(ATTRIB_EXTENSION.name())) {
flags &= ~(SMALLSIZE_ZEXT | SMALLSIZE_SEXT | SMALLSIZE_INTTYPE | SMALLSIZE_FLOAT);
String value = entry.getValue();
if (value.equals("sign")) {
@ -702,18 +690,17 @@ public class ParamEntry {
return (int) (offset2 - offset1);
}
public static int getMetatype(DataType tp) {
// TODO: A complete metatype implementation
public static StorageClass getBasicTypeClass(DataType tp) {
if (tp instanceof TypeDef) {
tp = ((TypeDef) tp).getBaseDataType();
}
if (tp instanceof AbstractFloatDataType) {
return TYPE_FLOAT;
return StorageClass.FLOAT;
}
if (tp instanceof Pointer) {
return TYPE_PTR;
return StorageClass.PTR;
}
return TYPE_UNKNOWN;
return StorageClass.GENERAL;
}
/**
@ -728,7 +715,7 @@ public class ParamEntry {
return;
}
if (entry1.type != entry2.type) {
if (entry1.type == TYPE_UNKNOWN) {
if (entry1.type == StorageClass.GENERAL) {
throw new XmlParseException(
"<pentry> tags with a specific type must come before the general type");
}

View file

@ -19,7 +19,8 @@ import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.pcode.Encoder;
@ -37,14 +38,14 @@ public interface ParamList {
}
/**
* Given a list of datatypes, calculate the storage locations used for passing those datatypes
* @param prog is the active program
* Given a list of datatypes, calculate the storage locations used for passing those data-types
* @param proto is the list of datatypes
* @param res is the vector for holding the VariableStorage corresponding to datatypes
* @param dtManage is the data-type manager
* @param res is the vector for holding the storage locations and other parameter properties
* @param addAutoParams if true add/process auto-parameters
*/
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
boolean addAutoParams);
public void assignMap(PrototypePieces proto, DataTypeManager dtManage,
ArrayList<ParameterPieces> res, boolean addAutoParams);
public void encode(Encoder encoder, boolean isInput) throws IOException;
@ -80,6 +81,21 @@ public interface ParamList {
*/
public boolean possibleParamWithSlot(Address loc, int size, WithSlotRec res);
/**
* Get the address space associated with any stack based parameters in this list.
*
* @return the stack address space, if this models parameters passed on the stack, null otherwise
*/
public AddressSpace getSpacebase();
/**
* Return true if the this pointer occurs before an indirect return pointer
*
* The automatic parameters: this parameter and the hidden return value pointer both
* tend to be allocated from the initial general purpose registers reserved for parameter passing.
* This method returns true if the this parameter is allocated first.
* @return false if the hidden return value pointer is allocated first
*/
public boolean isThisBeforeRetPointer();
/**

View file

@ -17,9 +17,8 @@ package ghidra.program.model.lang;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.data.VoidDataType;
/**
* A list of resources describing possible storage locations for a function's return value,
@ -28,17 +27,22 @@ import ghidra.program.model.listing.VariableStorage;
* The assignment strategy for this class is to take the first storage location in the list
* that fits for the given function signature's return data-type.
*/
public class ParamListRegisterOut extends ParamListStandard {
public class ParamListRegisterOut extends ParamListStandardOut {
@Override
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
boolean addAutoParams) {
public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
ArrayList<ParameterPieces> res, boolean addAutoParams) {
int[] status = new int[numgroup];
for (int i = 0; i < numgroup; ++i) {
status[i] = 0;
}
VariableStorage store = assignAddress(prog, proto[0], status, false, false);
ParameterPieces store = new ParameterPieces();
res.add(store);
if (VoidDataType.isVoidDataType(proto.outtype)) {
store.type = proto.outtype;
return; // Don't assign storage for VOID
}
assignAddress(proto.outtype, proto, -1, dtManager, status, store);
}
}

View file

@ -21,13 +21,14 @@ import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.app.plugin.processors.sleigh.VarnodeData;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.protorules.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
@ -41,11 +42,11 @@ public class ParamListStandard implements ParamList {
protected int numgroup; // Number of "groups" in this parameter convention
// protected int maxdelay;
protected int pointermax; // If non-zero, maximum size of a datatype before converting to a pointer
protected boolean thisbeforeret; // Do hidden return pointers usurp the storage of the this pointer
protected boolean splitMetatype; // Are metatyped entries in separate resource sections
// protected int[] resourceStart; // The starting group for each resource section
protected ParamEntry[] entry;
protected ModelRule[] modelRules; // Rules to apply when assigning addresses
protected AddressSpace spacebase; // Space containing relative offset parameters
/**
@ -67,40 +68,37 @@ public class ParamListStandard implements ParamList {
}
/**
* Assign next available memory chunk to type
* @param program is the Program
* @param tp type being assigned storage
* @param status status from previous assignments
* @param ishiddenret is true if the parameter is a hidden return value
* @param isindirect is true if parameter is really a pointer to the real parameter value
* @return Address of assigned memory chunk
* Assign storage for given parameter class, using the fallback assignment algorithm
*
* Given a resource list, a data-type, and the status of previously allocated slots,
* select the storage location for the parameter. The status array is
* indexed by group: a positive value indicates how many slots have been allocated
* from that group, and a -1 indicates the group/resource is fully consumed.
* If an Address can be assigned to the parameter, it and other details are passed back in the
* ParameterPieces object and the SUCCESS code is returned. Otherwise, the FAIL code is returned.
* @param resource is the resource list to allocate from
* @param tp is the data-type of the parameter
* @param matchExact is false if TYPECLASS_GENERAL is considered a match for any storage class
* @param status is an array marking how many slots have already been consumed in a group
* @param param will hold the address and other details of the assigned parameter
* @return either SUCCESS or FAIL
*/
protected VariableStorage assignAddress(Program program, DataType tp, int[] status,
boolean ishiddenret, boolean isindirect) {
if (tp == null) {
tp = DataType.DEFAULT;
}
if (VoidDataType.isVoidDataType(tp)) {
return VariableStorage.VOID_STORAGE;
}
int sz = tp.getLength();
if (sz == 0) {
return VariableStorage.UNASSIGNED_STORAGE;
}
public int assignAddressFallback(StorageClass resource, DataType tp, boolean matchExact,
int[] status, ParameterPieces param) {
for (ParamEntry element : entry) {
int grp = element.getGroup();
if (status[grp] < 0) {
continue;
}
if ((element.getType() != ParamEntry.TYPE_UNKNOWN) &&
(ParamEntry.getMetatype(tp) != element.getType())) {
continue; // Wrong type
if (resource != element.getType()) {
if (matchExact || element.getType() != StorageClass.GENERAL) {
continue;
}
}
VarnodeData res = new VarnodeData();
status[grp] =
element.getAddrBySlot(status[grp], tp.getAlignedLength(), tp.getAlignment(), res);
if (res.space == null) {
element.getAddrBySlot(status[grp], tp.getAlignedLength(), tp.getAlignment(), param);
if (param.address == null) {
continue; // -tp- does not fit in this entry
}
if (element.isExclusion()) {
@ -108,72 +106,79 @@ public class ParamListStandard implements ParamList {
// For an exclusion entry
status[group] = -1; // some number of groups are taken up
}
if (element.isFloatExtended()) {
sz = element.getSize(); // Still use the entire container size, when assigning storage
}
}
VariableStorage store;
try {
if (res.space.getType() == AddressSpace.TYPE_JOIN) {
Varnode[] pieces = element.getJoinPieces(sz);
if (pieces != null) {
store = new DynamicVariableStorage(program, false, pieces);
}
else {
store = DynamicVariableStorage.getUnassignedDynamicStorage(false);
}
}
else {
Address addr = res.space.getAddress(res.offset);
if (ishiddenret) {
store = new DynamicVariableStorage(program,
AutoParameterType.RETURN_STORAGE_PTR, addr, sz);
}
else if (isindirect) {
store = new DynamicVariableStorage(program, true, addr, sz);
}
else {
store = new DynamicVariableStorage(program, false, addr, sz);
}
}
}
catch (InvalidInputException e) {
break;
}
return store;
param.type = tp;
return AssignAction.SUCCESS;
}
if (ishiddenret) {
return DynamicVariableStorage
.getUnassignedDynamicStorage(AutoParameterType.RETURN_STORAGE_PTR);
param.address = null;
return AssignAction.FAIL;
}
/**
* Fill in the Address and other details for the given parameter
*
* Attempt to apply a ModelRule first. If these do not succeed, use the fallback assignment algorithm.
* @param dt is the data-type assigned to the parameter
* @param proto is the description of the function prototype
* @param pos is the position of the parameter to assign (pos=-1 for output, pos >=0 for input)
* @param dtManager is the data-type manager for (possibly) transforming the parameter's data-type
* @param status is the consumed resource status array
* @param res is parameter description to be filled in
* @return the response code
*/
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res)
{
for (ModelRule modelRule : modelRules) {
int responseCode = modelRule.assignAddress(dt, proto, pos, dtManager, status, res);
if (responseCode != AssignAction.FAIL) {
return responseCode;
}
}
return DynamicVariableStorage.getUnassignedDynamicStorage(isindirect);
StorageClass store = ParamEntry.getBasicTypeClass(dt);
return assignAddressFallback(store, dt, false, status, res);
}
/**
* @return the number of ParamEntry objets in this list
*/
public int getNumParamEntry() {
return entry.length;
}
/**
* Within this list, get the ParamEntry at the given index
* @param index is the given index
* @return the selected ParamEntry
*/
public ParamEntry getEntry(int index) {
return entry[index];
}
@Override
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
boolean addAutoParams) {
public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
ArrayList<ParameterPieces> res, boolean addAutoParams) {
int[] status = new int[numgroup];
for (int i = 0; i < numgroup; ++i) {
status[i] = 0;
}
if (addAutoParams && res.size() == 2) { // Check for hidden parameters defined by the output list
DataTypeManager dtm = prog.getDataTypeManager();
Pointer pointer = dtm.getPointer(proto[0]);
VariableStorage store = assignAddress(prog, pointer, status, true, false);
res.set(1, store);
}
for (int i = 1; i < proto.length; ++i) {
VariableStorage store;
if ((pointermax != 0) && (proto[i] != null) && (proto[i].getLength() > pointermax)) { // DataType is too big
// Assume datatype is stored elsewhere and only the pointer is passed
DataTypeManager dtm = prog.getDataTypeManager();
Pointer pointer = dtm.getPointer(proto[i]);
store = assignAddress(prog, pointer, status, false, true);
ParameterPieces last = res.get(res.size() - 1);
StorageClass store;
if (last.hiddenReturnPtr) {
store = StorageClass.HIDDENRET;
}
else {
store = assignAddress(prog, proto[i], status, false, false);
store = ParamEntry.getBasicTypeClass(last.type);
}
assignAddressFallback(store, last.type, false, status, last);
last.hiddenReturnPtr = true;
}
for (int i = 0; i < proto.intypes.size(); ++i) {
ParameterPieces store = new ParameterPieces();
assignAddress(proto.intypes.get(i), proto, i, dtManager, status, store);
res.add(store);
}
}
@ -208,9 +213,6 @@ public class ParamListStandard implements ParamList {
@Override
public void encode(Encoder encoder, boolean isInput) throws IOException {
encoder.openElement(isInput ? ELEM_INPUT : ELEM_OUTPUT);
if (pointermax != 0) {
encoder.writeSignedInteger(ATTRIB_POINTERMAX, pointermax);
}
if (thisbeforeret) {
encoder.writeBool(ATTRIB_THISBEFORERETPOINTER, true);
}
@ -236,25 +238,28 @@ public class ParamListStandard implements ParamList {
if (curgroup >= 0) {
encoder.closeElement(ELEM_GROUP);
}
for (ModelRule modelRule : modelRules) {
modelRule.encode(encoder);
}
encoder.closeElement(isInput ? ELEM_INPUT : ELEM_OUTPUT);
}
private void parsePentry(XmlPullParser parser, CompilerSpec cspec, ArrayList<ParamEntry> pe,
int groupid, boolean splitFloat, boolean grouped) throws XmlParseException {
int lastMeta = -1; // Smaller than any real metatype
StorageClass lastClass = StorageClass.CLASS4;
if (!pe.isEmpty()) {
ParamEntry lastEntry = pe.get(pe.size() - 1);
lastMeta = lastEntry.isGrouped() ? ParamEntry.TYPE_UNKNOWN : lastEntry.getType();
lastClass = lastEntry.isGrouped() ? StorageClass.GENERAL : lastEntry.getType();
}
ParamEntry pentry = new ParamEntry(groupid);
pe.add(pentry);
pentry.restoreXml(parser, cspec, pe, grouped);
if (splitFloat) {
int currentMeta = grouped ? ParamEntry.TYPE_UNKNOWN : pentry.getType();
if (lastMeta != currentMeta) {
if (lastMeta > currentMeta) {
StorageClass currentClass = grouped ? StorageClass.GENERAL : pentry.getType();
if (lastClass != currentClass) {
if (lastClass.getValue() < currentClass.getValue()) {
throw new XmlParseException(
"parameter list entries must be ordered by metatype");
"parameter list entries must be ordered by storage class");
}
// int[] newResourceStart = new int[resourceStart.length + 1];
// System.arraycopy(resourceStart, 0, newResourceStart, 0, resourceStart.length);
@ -301,7 +306,7 @@ public class ParamListStandard implements ParamList {
ArrayList<ParamEntry> pe = new ArrayList<>();
numgroup = 0;
spacebase = null;
pointermax = 0;
int pointermax = 0;
thisbeforeret = false;
splitMetatype = true;
XmlElement mainel = parser.start();
@ -329,14 +334,47 @@ public class ParamListStandard implements ParamList {
else if (el.getName().equals("group")) {
parseGroup(parser, cspec, pe, numgroup, splitMetatype);
}
else if (el.getName().equals("rule")) {
break;
}
}
parser.end(mainel);
entry = new ParamEntry[pe.size()];
pe.toArray(entry);
ArrayList<ModelRule> rules = new ArrayList<>();
for (;;) {
XmlElement subId = parser.peek();
if (!subId.isStart()) {
break;
}
if (subId.getName().equals("rule")) {
ModelRule rule = new ModelRule();
rule.restoreXml(parser, this);
rules.add(rule);
}
else {
throw new XmlParseException(
"<pentry> and <group> elements must come before any <modelrule>");
}
}
parser.end(mainel);
// int[] newResourceStart = new int[resourceStart.length + 1];
// System.arraycopy(resourceStart, 0, newResourceStart, 0, resourceStart.length);
// newResourceStart[resourceStart.length] = numgroup;
// resourceStart = newResourceStart;
if (pointermax > 0) { // Add a ModelRule at the end that converts too big data-types to pointers
SizeRestrictedFilter typeFilter = new SizeRestrictedFilter(pointermax + 1, 0);
ConvertToPointer action = new ConvertToPointer(this);
try {
rules.add(new ModelRule(typeFilter, action, this));
}
catch (InvalidInputException e) {
throw new XmlParseException(e.getMessage());
}
}
modelRules = new ModelRule[rules.size()];
rules.toArray(modelRules);
}
@Override
@ -389,6 +427,11 @@ public class ParamListStandard implements ParamList {
return true;
}
@Override
public AddressSpace getSpacebase() {
return spacebase;
}
@Override
public boolean isEquivalent(ParamList obj) {
if (this.getClass() != obj.getClass()) {
@ -403,7 +446,15 @@ public class ParamListStandard implements ParamList {
return false;
}
}
if (numgroup != op2.numgroup || pointermax != op2.pointermax) {
if (modelRules.length != op2.modelRules.length) {
return false;
}
for (int i = 0; i < modelRules.length; ++i) {
if (!modelRules[i].isEquivalent(op2.modelRules[i])) {
return false;
}
}
if (numgroup != op2.numgroup) {
return false;
}
if (!SystemUtilities.isEqual(spacebase, op2.spacebase)) {

View file

@ -17,12 +17,8 @@ package ghidra.program.model.lang;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.XmlParseException;
import ghidra.xml.XmlPullParser;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.protorules.AssignAction;
/**
* A list of resources describing possible storage locations for a function's return value,
@ -36,50 +32,43 @@ import ghidra.xml.XmlPullParser;
*
* The resource list is checked to ensure entries are distinguishable.
*/
public class ParamListStandardOut extends ParamListRegisterOut {
public class ParamListStandardOut extends ParamListStandard {
@Override
public void assignMap(Program prog, DataType[] proto, ArrayList<VariableStorage> res,
boolean addAutoParams) {
public void assignMap(PrototypePieces proto, DataTypeManager dtManager,
ArrayList<ParameterPieces> res, boolean addAutoParams) {
int[] status = new int[numgroup];
for (int i = 0; i < numgroup; ++i) {
status[i] = 0;
}
VariableStorage store = assignAddress(prog, proto[0], status, false, false);
if (!store.isUnassignedStorage()) {
res.add(store);
return;
ParameterPieces store = new ParameterPieces();
res.add(store);
if (VoidDataType.isVoidDataType(proto.outtype)) {
store.type = proto.outtype;
return; // Don't assign storage for VOID
}
// If the storage is not assigned (because the datatype is too big) create a hidden input parameter
DataType pointer = prog.getDataTypeManager().getPointer(proto[0]);
store = assignAddress(prog, pointer, status, false, false);
try {
if (store.isValid()) {
store = new DynamicVariableStorage(prog, true, store.getVarnodes());
res.add(store);
// Signal to input assignment that there is a hidden return using additional unassigned storage param
int responseCode = assignAddress(proto.outtype, proto, -1, dtManager, status, store);
if (responseCode != AssignAction.SUCCESS) {
// If the storage is not assigned (because the datatype is too big) create a hidden input parameter
int sz = (spacebase == null) ? -1 : spacebase.getPointerSize();
DataType pointerType = dtManager.getPointer(proto.outtype, sz);
if (responseCode == AssignAction.HIDDENRET_SPECIALREG_VOID) {
store.type = VoidDataType.dataType;
}
else {
assignAddressFallback(StorageClass.PTR, pointerType, false, status, store);
store.type = pointerType;
store.isIndirect = true; // Signal that there is a hidden return
}
if (addAutoParams) {
res.add(VariableStorage.UNASSIGNED_STORAGE); // will get replaced during input storage assignments
}
}
catch (InvalidInputException e) {
store = VariableStorage.UNASSIGNED_STORAGE;
res.add(store);
}
}
@Override
public void restoreXml(XmlPullParser parser, CompilerSpec cspec) throws XmlParseException {
super.restoreXml(parser, cspec);
// ParamEntry tags in the output list are considered a group. Check that entries are distinguishable.
for (int i = 1; i < entry.length; ++i) {
for (int j = 0; j < i; ++j) {
ParamEntry.orderWithinGroup(entry[j], entry[i]);
ParameterPieces hiddenRet = new ParameterPieces();
hiddenRet.type = pointerType;
// Encode whether or not hidden return should be drawn from TYPECLASS_HIDDENRET
hiddenRet.hiddenReturnPtr = (responseCode == AssignAction.HIDDENRET_SPECIALREG) ||
(responseCode == AssignAction.HIDDENRET_SPECIALREG_VOID);
res.add(hiddenRet); // will get replaced during input storage assignments
}
}
}

View file

@ -0,0 +1,112 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.VoidDataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.exception.InvalidInputException;
/**
* Basic elements of a parameter: address, data-type, properties
*/
public class ParameterPieces {
public Address address; // Storage address of the parameter
public DataType type; // The data-type of the parameter
public Varnode[] joinPieces; // If non-null, multiple pieces stitched together for single logical value
public boolean isThisPointer = false; // True if the "this" pointer
public boolean hiddenReturnPtr = false; // True if input pointer to return storage
public boolean isIndirect = false; // True if parameter is indirect pointer to actual parameter
// public boolean nameLock;
// public boolean typeLock;
// public boolean sizeLock;
/**
* Swap data-type markup between this and another parameter
*
* Swap any data-type and flags, but leave the storage address intact.
* This assumes the two parameters are the same size.
* @param op is the other parameter to swap with this.
*/
public void swapMarkup(ParameterPieces op) {
boolean tmpHidden = hiddenReturnPtr;
boolean tmpIndirect = isIndirect;
boolean tmpThis = isThisPointer;
DataType tmpType = type;
Varnode[] tmpJoin = joinPieces;
hiddenReturnPtr = op.hiddenReturnPtr;
isIndirect = op.isIndirect;
isThisPointer = op.isThisPointer;
type = op.type;
joinPieces = op.joinPieces;
op.hiddenReturnPtr = tmpHidden;
op.isIndirect = tmpIndirect;
op.isThisPointer = tmpThis;
op.type = tmpType;
op.joinPieces = tmpJoin;
}
public VariableStorage getVariableStorage(Program program) {
if (type == null) {
type = DataType.DEFAULT;
}
if (VoidDataType.isVoidDataType(type)) {
return VariableStorage.VOID_STORAGE;
}
int sz = type.getLength();
if (sz == 0) {
return VariableStorage.UNASSIGNED_STORAGE;
}
if (isThisPointer) {
try {
if (address != null) {
return new DynamicVariableStorage(program, AutoParameterType.THIS, address, sz);
}
}
catch (InvalidInputException e) {
// Fall thru to getUnaassignedDynamicStorage
}
return DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
}
if ((address == null || address == Address.NO_ADDRESS) && joinPieces == null) {
return DynamicVariableStorage.getUnassignedDynamicStorage(isIndirect);
}
VariableStorage store;
try {
if (joinPieces != null) {
store = new DynamicVariableStorage(program, false, joinPieces);
}
else {
if (hiddenReturnPtr) {
store = new DynamicVariableStorage(program,
AutoParameterType.RETURN_STORAGE_PTR, address, sz);
}
else if (isIndirect) {
store = new DynamicVariableStorage(program, true, address, sz);
}
else {
store = new DynamicVariableStorage(program, false, address, sz);
}
}
}
catch (InvalidInputException e) {
store = DynamicVariableStorage.getUnassignedDynamicStorage(isIndirect);
}
return store;
}
}

View file

@ -27,7 +27,6 @@ import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
@ -222,24 +221,23 @@ public class PrototypeModel {
}
/**
* @deprecated
* Get the preferred return location given the specified dataType.
* In truth, there is no one location. The routines that use this method tend
* to want the default storage location for integer or pointer return values.
* If the return value is passed back through a hidden input pointer,
* i.e. {@link AutoParameterType#RETURN_STORAGE_PTR}, this routine will not pass back
* the storage location of the pointer, but will typically pass
* back the location of the normal return register which holds a copy of the pointer.
* @param dataType first parameter dataType or null for an undefined type.
* @param program is the Program
* @return return location or {@link VariableStorage#UNASSIGNED_STORAGE} if
* unable to determine suitable location
*/
@Deprecated
public VariableStorage getReturnLocation(DataType dataType, Program program) {
DataType clone = dataType.clone(program.getDataTypeManager());
DataType[] arr = new DataType[1];
arr[0] = clone;
ArrayList<VariableStorage> res = new ArrayList<>();
outputParams.assignMap(program, arr, res, false);
PrototypePieces proto = new PrototypePieces(this, clone);
ArrayList<ParameterPieces> res = new ArrayList<>();
outputParams.assignMap(proto, program.getDataTypeManager(), res, false);
if (res.size() > 0) {
return res.get(0);
return res.get(0).getVariableStorage(program);
}
return null;
}
@ -301,12 +299,51 @@ public class PrototypeModel {
}
/**
* Compute the variable storage for a given function and set of return/parameter datatypes
* defined by an array of data types.
* Calculate input and output storage locations given a function prototype
*
* The data-types of the function prototype are passed in. Based on this model, a
* location is selected for each (input and output) parameter and passed back to the
* caller. The passed back storage locations are ordered with the output storage
* as the first entry, followed by the input storage locations. The model has the option
* of inserting a hidden return value pointer in the input storage locations.
*
* If the model cannot assign storage, the ParameterPieces will have a null Address.
* @param proto is the function prototype parameter data-types
* @param dtManager is the manager used to create indirect data-types
* @param res will hold the storage addresses for each parameter
* @param addAutoParams is true if auto parameters (like the this pointer) should be processed
*/
public void assignParameterStorage(PrototypePieces proto, DataTypeManager dtManager,
ArrayList<ParameterPieces> res, boolean addAutoParams) {
outputParams.assignMap(proto, dtManager, res, addAutoParams);
inputParams.assignMap(proto, dtManager, res, addAutoParams);
if (hasThis && addAutoParams && res.size() > 1) {
int thisIndex = 1;
if (res.get(1).hiddenReturnPtr && res.size() > 2) {
if (inputParams.isThisBeforeRetPointer()) {
// pointer has been bumped by auto-return-storage
res.get(1).swapMarkup(res.get(2)); // must swap storage and position for slots 1 and 2
}
else {
thisIndex = 2;
}
}
res.get(thisIndex).isThisPointer = true;
}
}
/**
* Compute the variable storage for a given array of return/parameter datatypes. The first array element
* is the return datatype, which is followed by any input parameter datatypes in order.
* If addAutoParams is true, pointer datatypes will automatically be inserted for "this" or "hidden return"
* input parameters, if needed. In this case, the dataTypes array should not include explicit entries for
* these parameters. If addAutoParams is false, the dataTypes array is assumed to already contain explicit
* entries for any of these parameters.
* @param program is the Program
* @param dataTypes return/parameter datatypes (first element is always the return datatype,
* i.e., minimum array length is 1)
* @param addAutoParams TODO
* @param addAutoParams true if auto-parameter storage locations can be generated
* @return dynamic storage locations orders by ordinal where first element corresponds to
* return storage. The returned array may also include additional auto-parameter storage
* locations.
@ -314,61 +351,21 @@ public class PrototypeModel {
public VariableStorage[] getStorageLocations(Program program, DataType[] dataTypes,
boolean addAutoParams) {
boolean injectAutoThisParam = false;
DataType injectedThis = null;
if (addAutoParams && hasThis) {
// explicit support for auto 'this' parameter
// must inject pointer arg to obtain storage assignment
injectAutoThisParam = true;
DataType[] ammendedTypes = new DataType[dataTypes.length + 1];
ammendedTypes[0] = dataTypes[0];
ammendedTypes[1] = new PointerDataType(program.getDataTypeManager());
if (dataTypes.length > 1) {
System.arraycopy(dataTypes, 1, ammendedTypes, 2, dataTypes.length - 1);
}
dataTypes = ammendedTypes;
injectedThis = new PointerDataType(program.getDataTypeManager());
}
PrototypePieces proto = new PrototypePieces(this, dataTypes, injectedThis);
ArrayList<VariableStorage> res = new ArrayList<>();
outputParams.assignMap(program, dataTypes, res, addAutoParams);
inputParams.assignMap(program, dataTypes, res, addAutoParams);
ArrayList<ParameterPieces> res = new ArrayList<>();
assignParameterStorage(proto, program.getDataTypeManager(), res, addAutoParams);
VariableStorage[] finalres = new VariableStorage[res.size()];
res.toArray(finalres);
if (injectAutoThisParam) {
Varnode[] thisVarnodes = finalres[1].getVarnodes();
int thisIndex = 1;
try {
if (finalres[1].isAutoStorage()) {
if (inputParams.isThisBeforeRetPointer()) {
// pointer has been bumped by auto-return-storage
// must swap storage and position for slots 1 and 2
finalres[2] = new DynamicVariableStorage(program,
finalres[1].getAutoParameterType(), finalres[2].getVarnodes());
}
else {
thisIndex = 2;
thisVarnodes = finalres[2].getVarnodes();
}
}
if (thisVarnodes.length != 0) {
finalres[thisIndex] =
new DynamicVariableStorage(program, AutoParameterType.THIS, thisVarnodes);
}
else {
finalres[thisIndex] =
DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
}
}
catch (InvalidInputException e) {
finalres[thisIndex] =
DynamicVariableStorage.getUnassignedDynamicStorage(AutoParameterType.THIS);
}
for (int i = 0; i < finalres.length; ++i) {
finalres[i] = res.get(i).getVariableStorage(program);
}
return finalres;
}

View file

@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
/**
* Raw components of a function prototype (obtained from parsing source code)
*/
public class PrototypePieces {
public PrototypeModel model; // (Optional) model on which prototype is based
// public String name; // Identifier (function name) associated with prototype
public DataType outtype; // Return data-type
public ArrayList<DataType> intypes; // Input data-types
// public ArrayList<String> innames; // Identifiers for input types
public int firstVarArgSlot; // First position of a variable argument, or -1 if not vararg
/**
* Populate pieces from old-style array of DataTypes
* @param model is the prototype model
* @param oldList is the list of output and input data-types
* @param injectedThis if non-null is the data-type of the this pointer to be injected
*/
public PrototypePieces(PrototypeModel model, DataType[] oldList, DataType injectedThis) {
this.model = model;
outtype = oldList[0];
intypes = new ArrayList<>();
firstVarArgSlot = -1;
if (injectedThis != null) {
intypes.add(injectedThis);
}
for (int i = 1; i < oldList.length; ++i) {
intypes.add(oldList[i]);
}
}
/**
* Create prototype with output data-type and empty/unspecified input data-types
* @param model is the prototype model
* @param outType is the output data-type
*/
public PrototypePieces(PrototypeModel model, DataType outType) {
this.model = model;
outtype = outType;
intypes = new ArrayList<>();
firstVarArgSlot = -1;
}
}

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang;
import ghidra.xml.XmlParseException;
/**
* Data-type class for the purpose of assigning storage
*/
public enum StorageClass {
GENERAL(0, "general"), // General purpose
FLOAT(1, "float"), // Floating-point data-types
PTR(2, "ptr"), // Pointer data-types
HIDDENRET(3, "hiddenret"), // Class for hidden return values
VECTOR(4, "vector"), // Vector data-types
CLASS1(100, "class1"), // Architecture specific class 1
CLASS2(101, "class2"), // Architecture specific class 2
CLASS3(102, "class3"), // Architecture specific class 3
CLASS4(103, "class4"); // Architecture specific class 4
private int value; // Value for comparing storage classes
private String name; // Name for marshaling
private StorageClass(int val, String nm) {
value = val;
name = nm;
}
public int getValue() {
return value;
}
@Override
public String toString() {
return name;
}
public static StorageClass getClass(String val) throws XmlParseException {
switch (val) {
case "general":
return GENERAL;
case "float":
return FLOAT;
case "ptr":
return PTR;
case "hiddenret":
return HIDDENRET;
case "vector":
return VECTOR;
case "class1":
return CLASS1;
case "class2":
return CLASS2;
case "class3":
return CLASS3;
case "class4":
return CLASS4;
default:
break;
}
throw new XmlParseException("Unknown type class: " + val);
}
}

View file

@ -0,0 +1,95 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.lang.PrototypePieces;
import ghidra.program.model.pcode.Encoder;
import ghidra.xml.XmlParseException;
import ghidra.xml.XmlPullParser;
/**
* Logically AND multiple QualifierFilters together into a single filter.
* An instances contains some number of other arbitrary filters. In order for this filter to
* pass, all these contained filters must pass.
*/
public class AndFilter implements QualifierFilter {
private QualifierFilter[] subQualifiers; // Filters being logically ANDed together
/**
* The AndFilter assumes ownership of all the filters in the ArrayList
* @param qualifierList is the list of filters pulled into this filter
*/
public AndFilter(ArrayList<QualifierFilter> qualifierList) {
subQualifiers = new QualifierFilter[qualifierList.size()];
qualifierList.toArray(subQualifiers);
}
public AndFilter(AndFilter op) {
subQualifiers = new QualifierFilter[op.subQualifiers.length];
for (int i = 0; i < subQualifiers.length; ++i) {
subQualifiers[i] = op.subQualifiers[i].clone();
}
}
@Override
public QualifierFilter clone() {
return new AndFilter(this);
}
@Override
public boolean isEquivalent(QualifierFilter op) {
if (op.getClass() != this.getClass()) {
return false;
}
AndFilter otherFilter = (AndFilter) op;
if (subQualifiers.length != otherFilter.subQualifiers.length) {
return false;
}
// Preserve strict order
for (int i = 0; i < subQualifiers.length; ++i) {
if (!subQualifiers[i].isEquivalent(otherFilter.subQualifiers[i])) {
return false;
}
}
return true;
}
@Override
public boolean filter(PrototypePieces proto, int pos) {
for (int i = 0; i < subQualifiers.length; ++i) {
if (!subQualifiers[i].filter(proto, pos)) {
return false;
}
}
return true;
}
@Override
public void encode(Encoder encoder) throws IOException {
for (int i = 0; i < subQualifiers.length; ++i) {
subQualifiers[i].encode(encoder);
}
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
// This method is not called
}
}

View file

@ -0,0 +1,160 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*;
/**
* An action that assigns an Address to a function prototype parameter
*
* A request for the address of either return storage or an input parameter is made
* through the assignAddress() method, which is given full information about the function prototype.
* Details about how the action performs is configured through the restoreXml() method.
*/
public abstract class AssignAction {
public static final int SUCCESS = 0; // Data-type is fully assigned
public static final int FAIL = 1; // Action could not be applied (not enough resources)
public static final int HIDDENRET_PTRPARAM = 2; // Hidden return pointer as first input parameter
public static final int HIDDENRET_SPECIALREG = 3; // Hidden return pointer in special register
public static final int HIDDENRET_SPECIALREG_VOID = 4; // Hidden return pointer, but no normal return
protected ParamListStandard resource; // Resources to which this action applies
public AssignAction(ParamListStandard res) {
resource = res;
}
/**
* Make a copy of this action
* @param newResource is the new resource object that will own the clone
* @return the newly allocated copy
* @throws InvalidInputException if required configuration is not present in new resource object
*/
public abstract AssignAction clone(ParamListStandard newResource) throws InvalidInputException;
/**
* Test if the given action is configured and performs identically to this
* @param op is the given action
* @return true if the two actions are equivalent
*/
public abstract boolean isEquivalent(AssignAction op);
/**
* Assign an address and other meta-data for a specific parameter or for return storage in context
* The Address is assigned based on the data-type of the parameter, available register
* resources, and other details of the function prototype. Consumed resources are marked.
* This method returns a response code:
* - SUCCESS - indicating the Address was successfully assigned
* - FAIL - if the Address could not be assigned
* - HIDDENRET_PTRPARAM - if an additional hidden return parameter is required
* @param dt is the data-type of the parameter or return value
* @param proto is the high-level description of the function prototype
* @param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
* @param dtManager is a data-type manager for (possibly) transforming the data-type
* @param status is the resource consumption array
* @param res will hold the resulting description of the parameter
* @return the response code
*/
public abstract int assignAddress(DataType dt, PrototypePieces proto, int pos,
DataTypeManager dtManager, int[] status, ParameterPieces res);
/**
* Save this action and its configuration to a stream
* @param encoder is the stream encoder
* @throws IOException for problems writing to the stream
*/
public abstract void encode(Encoder encoder) throws IOException;
/**
* Configure any details of how this action should behave from the stream
* @param parser is the given stream decoder
* @throws XmlParseException is there are problems decoding the stream
*/
public abstract void restoreXml(XmlPullParser parser) throws XmlParseException;
/**
* Read the next action element from the stream and return the new configured
* AssignAction object. If the next element is not an action, throw an exception.
* @param parser is the stream parser
* @param res is the resource set for the new action
* @return the new action
* @throws XmlParseException for problems parsing the stream
*/
static public AssignAction restoreActionXml(XmlPullParser parser, ParamListStandard res)
throws XmlParseException {
AssignAction action;
XmlElement elemId = parser.peek();
String nm = elemId.getName();
if (nm.equals(ELEM_GOTO_STACK.name())) {
action = new GotoStack(res, 0);
}
else if (nm.equals(ELEM_JOIN.name())) {
action = new MultiSlotAssign(res);
}
else if (nm.equals(ELEM_CONSUME.name())) {
action = new ConsumeAs(StorageClass.GENERAL, res);
}
else if (nm.equals(ELEM_CONVERT_TO_PTR.name())) {
action = new ConvertToPointer(res);
}
else if (nm.equals(ELEM_HIDDEN_RETURN.name())) {
action = new HiddenReturnAssign(res, false);
}
else if (nm.equals(ELEM_JOIN_PER_PRIMITIVE.name())) {
boolean consumeMostSig = res.getEntry(0).isBigEndian();
action = new MultiMemberAssign(StorageClass.GENERAL, false, consumeMostSig, res);
}
else {
throw new XmlParseException("Unknown model rule action: " + nm);
}
action.restoreXml(parser);
return action;
}
/**
* Read the next sideeffect element from the stream and return the new configured
* AssignAction object. If the next element is not a sideeffect, throw an exception.
* @param parser is the stream parser
* @param res is the resource set for the new sideeffect
* @return the new sideeffect
* @throws XmlParseException for problems parsing the stream
*/
static public AssignAction restoreSideeffectXml(XmlPullParser parser, ParamListStandard res)
throws XmlParseException {
AssignAction action;
XmlElement elemId = parser.peek();
String nm = elemId.getName();
if (nm.equals(ELEM_CONSUME_EXTRA.name())) {
action = new ConsumeExtra(res);
}
else {
throw new XmlParseException("Unknown model rule sideeffect: " + nm);
}
action.restoreXml(parser);
return action;
}
}

View file

@ -0,0 +1,80 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.xml.*;
/**
* Consume a parameter from a specific resource list
*
* Normally the resource list is determined by the parameter data-type, but this
* action specifies an overriding resource list.
*/
public class ConsumeAs extends AssignAction {
private StorageClass resourceType; // The resource list the parameter is consumed from
public ConsumeAs(StorageClass store, ParamListStandard res) {
super(res);
resourceType = store;
}
@Override
public AssignAction clone(ParamListStandard newResource) {
return new ConsumeAs(resourceType, newResource);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
ConsumeAs otherAction = (ConsumeAs) op;
if (resourceType != otherAction.resourceType) {
return false;
}
return true;
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
return resource.assignAddressFallback(resourceType, dt, true, status, res);
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_CONSUME);
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
encoder.closeElement(ELEM_CONSUME);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_CONSUME.name());
resourceType = StorageClass.getClass(elem.getAttribute(ATTRIB_STORAGE.name()));
parser.end(elem);
}
}

View file

@ -0,0 +1,145 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*;
/**
* Consume additional registers from an alternate resource list
*
* This action is a side-effect and doesn't assign an address for the current parameter.
* The resource list, resourceType, is specified. If the side-effect is triggered,
* register resources from this list are consumed. If matchSize is true (the default),
* registers are consumed, until the number of bytes in the data-type is reached. Otherwise,
* only a single register is consumed. If all registers are already consumed, no action is taken.
*/
public class ConsumeExtra extends AssignAction {
private StorageClass resourceType; // The other resource list to consume from
private int firstIter; // Iterator to first element in the resource list
private boolean matchSize; // false, if side-effect only consumes a single register
/**
* Cache specific ParamEntry needed by the action.
* Find the first ParamEntry matching the resourceType.
* @throws InvalidInputException if it cannot find the configured ParamEntry objects
*/
private void initializeEntries() throws InvalidInputException {
firstIter = -1;
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
ParamEntry entry = resource.getEntry(i);
if (entry.isExclusion() && entry.getType() == resourceType &&
entry.getAllGroups().length == 1) {
firstIter = i; // First matching resource size
break;
}
}
if (firstIter == -1) {
throw new InvalidInputException(
"Could not find matching resources for action: consumeextra");
}
}
protected ConsumeExtra(ParamListStandard res) {
super(res);
resourceType = StorageClass.GENERAL;
matchSize = true;
}
public ConsumeExtra(StorageClass store, boolean match, ParamListStandard res)
throws InvalidInputException {
super(res);
resourceType = store;
matchSize = match;
initializeEntries();
}
@Override
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
return new ConsumeExtra(resourceType, matchSize, newResource);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
ConsumeExtra otherAction = (ConsumeExtra) op;
if (firstIter != otherAction.firstIter || matchSize != otherAction.matchSize ||
resourceType != otherAction.resourceType) {
return false;
}
return true;
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
int iter = firstIter;
int endIter = resource.getNumParamEntry();
int sizeLeft = dt.getLength();
while (sizeLeft > 0 && iter != endIter) {
ParamEntry entry = resource.getEntry(iter);
++iter;
if (!entry.isExclusion()) {
break; // Reached end of resource list
}
if (entry.getType() != resourceType || entry.getAllGroups().length != 1) {
continue; // Not a single register in desired list
}
if (status[entry.getGroup()] != 0) {
continue; // Already consumed
}
status[entry.getGroup()] = -1; // Consume the slot/register
sizeLeft -= entry.getSize();
if (!matchSize) {
break; // Only consume a single register
}
}
return SUCCESS;
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_CONSUME_EXTRA);
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
encoder.closeElement(ELEM_CONSUME_EXTRA);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_CONSUME_EXTRA.name());
resourceType = StorageClass.getClass(elem.getAttribute(ATTRIB_STORAGE.name()));
parser.end(elem);
try {
initializeEntries();
}
catch (InvalidInputException e) {
throw new XmlParseException(e.getMessage());
}
}
}

View file

@ -0,0 +1,91 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*;
/**
* Action converting the parameter's data-type to a pointer, and assigning storage for the pointer.
* This assumes the data-type is stored elsewhere and only the pointer is passed as a parameter.
*/
public class ConvertToPointer extends AssignAction {
private AddressSpace space; // Address space used for pointer size
public ConvertToPointer(ParamListStandard res) {
super(res);
space = res.getSpacebase();
}
@Override
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
return new ConvertToPointer(newResource);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
ConvertToPointer otherAction = (ConvertToPointer) op;
if (space == null && otherAction.space == null) {
return true;
}
if (space == null || otherAction.space == null) {
return false;
}
if (!space.equals(otherAction.space)) {
return false;
}
return true;
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
int pointersize = (space != null) ? space.getPointerSize() : -1;
// Convert the data-type to a pointer
DataType pointertp = dtManager.getPointer(dt, pointersize);
// (Recursively) assign storage
int responseCode = resource.assignAddress(pointertp, proto, pos, dtManager, status, res);
res.isIndirect = true;
return responseCode;
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_CONVERT_TO_PTR);
encoder.closeElement(ELEM_CONVERT_TO_PTR);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_CONVERT_TO_PTR.name());
parser.end(elem);
}
}

View file

@ -0,0 +1,165 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.data.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.PcodeDataTypeManager;
import ghidra.xml.*;
/**
* A filter selecting a specific class of data-type.
* A test of whether data-type belongs to its class can be performed by calling
* the filter() method.
*/
public interface DatatypeFilter {
/**
* Make a copy of this filter
* @return the new copy
*/
public DatatypeFilter clone();
/**
* Test if the given filter is configured and performs identically to this
* @param op is the given filter
* @return true if the two filters are equivalent
*/
public boolean isEquivalent(DatatypeFilter op);
/**
* Test whether the given data-type belongs to this filter's data-type class
* @param dt is the given data-type to test
* @return true if the data-type is in the class, false otherwise
*/
public boolean filter(DataType dt);
/**
* Encode this filter and its configuration to a stream
* @param encoder is the stream encoder
* @throws IOException for problems writing to the stream
*/
public void encode(Encoder encoder) throws IOException;
/**
* Configure details of the data-type class being filtered from the given stream
* @param parser is the given stream decoder
* @throws XmlParseException if there are problems with the stream
*/
public void restoreXml(XmlPullParser parser) throws XmlParseException;
/**
* Extract an ordered list of primitive data-types making up the given data-type
*
* The primitive data-types are passed back in an ArrayList. If the given data-type is already
* primitive, it is passed back as is. Otherwise if it is composite, its components are recursively
* listed. If a filler data-type is provided, it is used to fill holes in structures. If
* a maximum number of extracted primitives is exceeded, or if no filler is provided and a hole
* is encountered, or if a non-primitive non-composite data-type is encountered, false is returned.
* @param dt is the given data-type to extract primitives from
* @param max is the maximum number of primitives to extract before giving up
* @param filler is the data-type to use as filler (or null)
* @param res will hold the list of primitives
* @return true if all primitives were extracted
*/
public static boolean extractPrimitives(DataType dt, int max, DataType filler,
ArrayList<DataType> res) {
int metaType = PcodeDataTypeManager.getMetatype(dt);
switch (metaType) {
case PcodeDataTypeManager.TYPE_UNKNOWN:
case PcodeDataTypeManager.TYPE_INT:
case PcodeDataTypeManager.TYPE_UINT:
case PcodeDataTypeManager.TYPE_BOOL:
case PcodeDataTypeManager.TYPE_CODE:
case PcodeDataTypeManager.TYPE_FLOAT:
case PcodeDataTypeManager.TYPE_PTR:
case PcodeDataTypeManager.TYPE_PTRREL:
if (res.size() >= max) {
return false;
}
res.add(dt);
return true;
case PcodeDataTypeManager.TYPE_ARRAY: {
int numEls = ((Array) dt).getNumElements();
DataType base = ((Array) dt).getDataType();
for (int i = 0; i < numEls; ++i) {
if (!extractPrimitives(base, max, filler, res)) {
return false;
}
}
return true;
}
case PcodeDataTypeManager.TYPE_STRUCT:
break;
default:
return false;
}
Structure structPtr = (Structure) dt;
int curOff = 0;
DataTypeComponent[] components = structPtr.getDefinedComponents();
for (DataTypeComponent component : components) {
int nextOff = component.getOffset();
if (nextOff > curOff) {
if (filler == null) {
return false;
}
while (curOff < nextOff) {
if (res.size() >= max) {
return false;
}
res.add(filler);
curOff += filler.getLength();
}
}
if (!extractPrimitives(component.getDataType(), max, filler, res)) {
return false;
}
curOff += component.getDataType().getLength();
}
return true;
}
/**
* Instantiate a filter from the given stream.
* @param parser is the given stream decoder
* @return the new data-type filter instance
* @throws XmlParseException for problems reading the stream
*/
public static DatatypeFilter restoreFilterXml(XmlPullParser parser) throws XmlParseException {
DatatypeFilter filter;
XmlElement elemId = parser.peek();
String nm = elemId.getAttribute(ATTRIB_NAME.name());
if (nm.equals(SizeRestrictedFilter.NAME)) {
filter = new SizeRestrictedFilter();
}
else if (nm.equals(HomogeneousAggregate.NAME_FLOAT4)) {
filter = new HomogeneousAggregate(HomogeneousAggregate.NAME_FLOAT4,
PcodeDataTypeManager.TYPE_FLOAT, 4, 0, 0);
}
else {
// If no other name matches, assume this is a decompiler metatype
int meta = PcodeDataTypeManager.getMetatype(nm);
filter = new MetaTypeFilter(meta);
}
filter.restoreXml(parser);
return filter;
}
}

View file

@ -0,0 +1,95 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.PrototypePieces;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/**
* Check if the function signature has a specific data-type in a specific position.
* This filter does not match against the data-type in the current position
* being assigned, but against a parameter at a fixed position.
*/
public class DatatypeMatchFilter implements QualifierFilter {
private int position; // The position of the data-type to check
private DatatypeFilter typeFilter; // The data-type that must be at position
public DatatypeMatchFilter() {
position = -1;
typeFilter = null;
}
@Override
public QualifierFilter clone() {
DatatypeMatchFilter res = new DatatypeMatchFilter();
res.position = position;
res.typeFilter = typeFilter.clone();
return res;
}
@Override
public boolean isEquivalent(QualifierFilter op) {
if (this.getClass() != op.getClass()) {
return false;
}
DatatypeMatchFilter otherFilter = (DatatypeMatchFilter) op;
if (position != otherFilter.position) {
return false;
}
return typeFilter.isEquivalent(otherFilter.typeFilter);
}
@Override
public boolean filter(PrototypePieces proto, int pos) {
// The position of the current parameter being assigned, pos, is NOT used.
DataType dt;
if (position < 0) {
dt = proto.outtype;
}
else {
if (position >= proto.intypes.size()) {
return false;
}
dt = proto.intypes.get(position);
}
return typeFilter.filter(dt);
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_DATATYPE_AT);
encoder.writeSignedInteger(ATTRIB_INDEX, position);
typeFilter.encode(encoder);
encoder.closeElement(ELEM_DATATYPE_AT);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_DATATYPE_AT.name());
position = SpecXmlUtils.decodeInt(elem.getAttribute(ATTRIB_INDEX.name()));
typeFilter = DatatypeFilter.restoreFilterXml(parser);
parser.end(elem);
}
}

View file

@ -0,0 +1,106 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*;
/**
* Action assigning a parameter Address from the next available stack location
*/
public class GotoStack extends AssignAction {
private ParamEntry stackEntry; // Parameter Entry corresponding to the stack
private void initializeEntry() throws InvalidInputException {
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
ParamEntry entry = resource.getEntry(i);
if (!entry.isExclusion() && entry.getSpace().isStackSpace()) {
stackEntry = entry;
break;
}
}
if (stackEntry == null) {
throw new InvalidInputException("Cannot find matching <pentry> for action: gotostack");
}
}
/**
* Constructor for use with restoreXml
* @param res is the new resource list to associate with the action
* @param val is a dummy argument
*/
protected GotoStack(ParamListStandard res, int val) {
super(res);
stackEntry = null;
}
public GotoStack(ParamListStandard res) throws InvalidInputException {
super(res);
stackEntry = null;
initializeEntry();
}
@Override
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
return new GotoStack(newResource);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
GotoStack otherAction = (GotoStack) op;
return stackEntry.isEquivalent(otherAction.stackEntry);
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
int grp = stackEntry.getGroup();
res.type = dt;
status[grp] = stackEntry.getAddrBySlot(status[grp], dt.getLength(), dt.getAlignment(), res);
return SUCCESS;
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_GOTO_STACK);
encoder.closeElement(ELEM_GOTO_STACK);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_GOTO_STACK.name());
parser.end(elem);
try {
initializeEntry();
}
catch (InvalidInputException e) {
throw new XmlParseException(e.getMessage());
}
}
}

View file

@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/**
* Allocate the return value as special input register
*
* The assignAddress() method signals with hiddenret_specialreg, indicating that the
* input register assignMap() method should use storage class TYPECLASS_HIDDENRET to assign
* an additional input register to hold a pointer to the return value. This is different than
* the default hiddenret action that assigns a location based TYPECLASS_PTR and generally
* consumes a general purpose input register.
*/
public class HiddenReturnAssign extends AssignAction {
private int retCode; // The specific signal to pass back
public HiddenReturnAssign(ParamListStandard res, boolean voidLock) {
super(res);
retCode = voidLock ? HIDDENRET_SPECIALREG_VOID : HIDDENRET_SPECIALREG;
}
@Override
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
return new HiddenReturnAssign(newResource, retCode == HIDDENRET_SPECIALREG_VOID);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
HiddenReturnAssign otherOp = (HiddenReturnAssign) op;
return (retCode == otherOp.retCode);
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
return retCode; // Signal to assignMap to use TYPECLASS_HIDDENRET
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_HIDDEN_RETURN);
if (retCode == HIDDENRET_SPECIALREG_VOID) {
encoder.writeBool(ATTRIB_VOIDLOCK, true);
}
encoder.closeElement(ELEM_HIDDEN_RETURN);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
retCode = HIDDENRET_SPECIALREG;
XmlElement elem = parser.start(ELEM_HIDDEN_RETURN.name());
String voidLockString = elem.getAttribute(ATTRIB_VOIDLOCK.name());
if (SpecXmlUtils.decodeBoolean(voidLockString)) {
retCode = HIDDENRET_SPECIALREG_VOID;
}
parser.end(elem);
}
}

View file

@ -0,0 +1,102 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.PcodeDataTypeManager;
import ghidra.xml.*;
/**
* Filter on a homogeneous aggregate data-type
* All primitive data-types must be the same.
*/
public class HomogeneousAggregate extends SizeRestrictedFilter {
public static final String NAME_FLOAT4 = "homogeneous-float-aggregate";
public static final int MAX_PRIMITIVES = 4; // Maximum number of primitives in aggregate data-type
private String name;
private int metaType; // The expected meta-type
private int maxPrimitives; // Maximum number of primitives in the aggregate
/**
* Constructor for use with decode()
* @param nm is the name attribute associated with the tag
* @param meta is the expected element meta-type
*/
public HomogeneousAggregate(String nm, int meta) {
name = nm;
metaType = meta;
maxPrimitives = 2;
}
public HomogeneousAggregate(String nm, int meta, int maxPrim, int min, int max) {
super(min, max);
name = nm;
metaType = meta;
maxPrimitives = maxPrim;
}
@Override
public DatatypeFilter clone() {
return new HomogeneousAggregate(name, metaType, maxPrimitives, minSize, maxSize);
}
@Override
public boolean filter(DataType dt) {
int meta = PcodeDataTypeManager.getMetatype(dt);
if (meta != PcodeDataTypeManager.TYPE_ARRAY && meta != PcodeDataTypeManager.TYPE_STRUCT) {
return false;
}
ArrayList<DataType> res = new ArrayList<>();
if (!DatatypeFilter.extractPrimitives(dt, MAX_PRIMITIVES, null, res)) {
return false;
}
DataType base = res.get(0);
int baseMeta = PcodeDataTypeManager.getMetatype(base);
if (baseMeta != metaType) {
return false;
}
for (int i = 1; i < res.size(); ++i) {
if (res.get(i) != base) {
return false;
}
}
return true;
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_DATATYPE);
encoder.writeString(ATTRIB_NAME, name);
encodeAttributes(encoder);
encoder.closeElement(ELEM_DATATYPE);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_DATATYPE.name());
restoreAttributesXml(elem);
parser.end(elem);
}
}

View file

@ -0,0 +1,100 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.data.DataType;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.PcodeDataTypeManager;
import ghidra.xml.*;
/**
* Filter on a single meta data-type. Filters on TYPE_STRUCT or TYPE_FLOAT etc.
* Additional filtering on size of the data-type can be configured.
*/
public class MetaTypeFilter extends SizeRestrictedFilter {
protected int metaType; // The meta-type this filter lets through
/**
* Constructor for use with decode().
* @param meta is the data-type metatype to filter on
*/
public MetaTypeFilter(int meta) {
metaType = meta;
}
/**
* Constructor
* @param meta is the data-type metatype to filter on
* @param min is the minimum size in bytes
* @param max is the maximum size in bytes
*/
public MetaTypeFilter(int meta, int min, int max) {
super(min, max);
metaType = meta;
}
@Override
public boolean isEquivalent(DatatypeFilter op) {
if (!super.isEquivalent(op)) {
return false;
}
if (this.getClass() != op.getClass()) {
return false;
}
MetaTypeFilter otherFilter = (MetaTypeFilter) op;
if (metaType != otherFilter.metaType) {
return false;
}
return true;
}
@Override
public DatatypeFilter clone() {
return new MetaTypeFilter(metaType, minSize, maxSize);
}
@Override
public boolean filter(DataType dt) {
if (PcodeDataTypeManager.getMetatype(dt) != metaType) {
return false;
}
return filterOnSize(dt);
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_DATATYPE);
String meta = PcodeDataTypeManager.getMetatypeString(metaType);
encoder.writeString(ATTRIB_NAME, meta);
encodeAttributes(encoder);
encoder.closeElement(ELEM_DATATYPE);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_DATATYPE.name());
metaType = PcodeDataTypeManager.getMetatype(elem.getAttribute(ATTRIB_NAME.name()));
restoreAttributesXml(elem);
parser.end(elem);
}
}

View file

@ -0,0 +1,239 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*;
/**
* A rule controlling how parameters are assigned addresses
*
* Rules are applied to a parameter in the context of a full function prototype.
* A rule applies only for a specific class of data-type associated with the parameter, as
* determined by its DatatypeFilter, and may have other criteria limiting when it applies
* (via QualifierFilter).
*/
public class ModelRule {
private DatatypeFilter filter; // Which data-types this rule applies to
private QualifierFilter qualifier; // Additional qualifiers for when the rule should apply (if non-null)
private AssignAction assign; // How the Address should be assigned
private AssignAction[] sideeffects; // Extra actions that happen on success
public ModelRule() {
filter = null;
qualifier = null;
assign = null;
}
/**
* Copy constructor
* @param op2 is the ModelRule to copy from
* @param res is the new resource set to associate with the copy
* @throws InvalidInputException if necessary resources are not present in the resource set
*/
public ModelRule(ModelRule op2, ParamListStandard res) throws InvalidInputException {
if (op2.filter != null) {
filter = op2.filter.clone();
}
else {
filter = null;
}
if (op2.qualifier != null) {
qualifier = op2.qualifier.clone();
}
else {
qualifier = null;
}
if (op2.assign != null) {
assign = op2.assign.clone(res);
}
else {
assign = null;
}
sideeffects = new AssignAction[op2.sideeffects.length];
for (int i = 0; i < op2.sideeffects.length; ++i) {
sideeffects[i] = op2.sideeffects[i].clone(res);
}
}
/**
* Construct from components
*
* The provided components are cloned into the new object.
* @param typeFilter is the data-type filter the rule applies before performing the action
* @param action is the action that will be applied
* @param res is the resource list to which this rule will be applied
* @throws InvalidInputException if necessary resources are missing from the list
*/
public ModelRule(DatatypeFilter typeFilter, AssignAction action, ParamListStandard res)
throws InvalidInputException
{
filter = typeFilter.clone();
qualifier = null;
assign = action.clone(res);
sideeffects = new AssignAction[0];
}
public boolean isEquivalent(ModelRule op) {
if (assign == null && op.assign == null) {
// Nothing to compare
}
else if (assign != null && op.assign != null) {
if (!assign.isEquivalent(op.assign)) {
return false;
}
}
else {
return false;
}
if (filter == null && op.filter == null) {
// Nothing to compare
}
else if (filter != null && op.filter != null) {
if (!filter.isEquivalent(op.filter)) {
return false;
}
}
else {
return false;
}
if (qualifier == null && op.qualifier == null) {
// Nothing to compare
}
else if (qualifier != null && op.qualifier != null) {
if (!qualifier.isEquivalent(op.qualifier)) {
return false;
}
}
else {
return false;
}
if (sideeffects.length != op.sideeffects.length) {
return false;
}
for (int i = 0; i < sideeffects.length; ++i) {
if (!sideeffects[i].isEquivalent(op.sideeffects[i])) {
return false;
}
}
return true;
}
/**
* Assign an address and other details for a specific parameter or for return storage in context
*
* The Address is only assigned if the data-type filter and the optional qualifier filter
* pass, otherwise a FAIL response is returned.
* If the filters pass, the Address is assigned based on the AssignAction specific to
* this rule, and the action's response code is returned.
* @param dt is the data-type of the parameter or return value
* @param proto is the high-level description of the function prototype
* @param pos is the position of the parameter (pos>=0) or return storage (pos=-1)
* @param dtManager is a data-type manager for (possibly) transforming the data-type
* @param status is the resource consumption array
* @param res will hold the resulting description of the parameter
* @return the response code
*/
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
if (!filter.filter(dt)) {
return AssignAction.FAIL;
}
if (qualifier != null && !qualifier.filter(proto, pos)) {
return AssignAction.FAIL;
}
int response = assign.assignAddress(dt, proto, pos, dtManager, status, res);
if (response != AssignAction.FAIL) {
for (int i = 0; i < sideeffects.length; ++i) {
sideeffects[i].assignAddress(dt, proto, pos, dtManager, status, res);
}
}
return response;
}
/**
* Encode this rule to a stream
* @param encoder is the stream encode
* @throws IOException for problems with the stream
*/
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_RULE);
filter.encode(encoder);
if (qualifier != null) {
qualifier.encode(encoder);
}
assign.encode(encoder);
for (int i = 0; i < sideeffects.length; ++i) {
sideeffects[i].encode(encoder);
}
encoder.closeElement(ELEM_RULE);
}
/**
* Decode this rule from stream
*
* @param parser is the stream decoder
* @param res is the parameter resource list owning this rule
* @throws XmlParseException if there are problems decoding are missing resources
*/
public void restoreXml(XmlPullParser parser, ParamListStandard res) throws XmlParseException
{
XmlElement elemId = parser.start(ELEM_RULE.name());
filter = DatatypeFilter.restoreFilterXml(parser);
ArrayList<QualifierFilter> qualifierList = new ArrayList<>();
for (;;) {
QualifierFilter tmpFilter = QualifierFilter.restoreFilterXml(parser);
if (tmpFilter == null) {
break;
}
qualifierList.add(tmpFilter);
}
if (qualifierList.size() == 0) {
qualifier = null;
}
else if (qualifierList.size() == 1) {
qualifier = qualifierList.get(0);
qualifierList.clear();
}
else {
qualifier = new AndFilter(qualifierList);
}
assign = AssignAction.restoreActionXml(parser, res);
ArrayList<AssignAction> sideList = new ArrayList<>();
for (;;) {
XmlElement subEl = parser.peek();
if (!subEl.isStart()) {
break;
}
sideList.add(AssignAction.restoreSideeffectXml(parser, res));
}
sideeffects = new AssignAction[sideList.size()];
sideList.toArray(sideeffects);
parser.end(elemId);
}
}

View file

@ -0,0 +1,134 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import java.util.ArrayList;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.exception.InvalidInputException;
import ghidra.xml.*;
/**
* Consume a register per primitive member of an aggregate data-type
*
* The data-type is split up into its underlying primitive elements, and each one
* is assigned a register from the specific resource list. There must be no padding between
* elements. No packing of elements into a single register occurs.
*/
public class MultiMemberAssign extends AssignAction {
private StorageClass resourceType; // Resource list from which to consume
private boolean consumeFromStack; // True if resources should be consumed from the stack
private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes
public MultiMemberAssign(StorageClass store, boolean stack, boolean mostSig,
ParamListStandard res) {
super(res);
resourceType = store;
consumeFromStack = stack;
consumeMostSig = mostSig;
}
@Override
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
return new MultiMemberAssign(resourceType, consumeFromStack, consumeMostSig, newResource);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
MultiMemberAssign otherOp = (MultiMemberAssign) op;
if (resourceType != otherOp.resourceType) {
return false;
}
if (consumeFromStack != otherOp.consumeFromStack) {
return false;
}
return consumeMostSig == otherOp.consumeMostSig;
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
int[] tmpStatus = status.clone();
ArrayList<Varnode> pieces = new ArrayList<>();
ParameterPieces param = new ParameterPieces();
ArrayList<DataType> primitives = new ArrayList<>();
if (!DatatypeFilter.extractPrimitives(dt, 16, null, primitives)) {
return FAIL;
}
for (int i = 0; i < primitives.size(); ++i) {
DataType curType = primitives.get(i);
if (resource.assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,
param) == FAIL) {
return FAIL;
}
Varnode vn = new Varnode(param.address, curType.getLength());
pieces.add(vn);
}
System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces
res.type = dt;
if (pieces.size() == 1) {
res.address = pieces.get(0).getAddress();
return SUCCESS;
}
res.joinPieces = new Varnode[pieces.size()];
if (!consumeMostSig) {
for (int i = 0; i < res.joinPieces.length; ++i) {
res.joinPieces[i] = pieces.get(pieces.size() - 1 - i);
}
}
else {
for (int i = 0; i < pieces.size(); ++i) {
res.joinPieces[i] = pieces.get(i);
}
}
res.address = Address.NO_ADDRESS; // Placeholder for join space address
return SUCCESS;
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_JOIN_PER_PRIMITIVE);
if (resourceType != StorageClass.GENERAL) {
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
}
encoder.closeElement(ELEM_JOIN_PER_PRIMITIVE);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_JOIN_PER_PRIMITIVE.name());
String attribString = elem.getAttribute(ATTRIB_STORAGE.name());
if (attribString != null) {
resourceType = StorageClass.getClass(attribString);
}
parser.end(elem);
}
}

View file

@ -0,0 +1,289 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
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 java.util.Map.Entry;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeManager;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.Encoder;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/**
* Consume multiple registers to pass a data-type
*
* Available registers are consumed until the data-type is covered, and an appropriate
* join space address is assigned. Registers can be consumed from a specific resource list.
* Consumption can spill over onto the stack if desired.
*/
public class MultiSlotAssign extends AssignAction {
private StorageClass resourceType; // Resource list from which to consume
private boolean consumeFromStack; // True if resources should be consumed from the stack
private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes
private boolean enforceAlignment; // True if register resources are discarded to match alignment
private boolean justifyRight; // True if initial bytes are padding for odd data-type sizes
private ParamEntry stackEntry; // The stack resource
private int firstIter; // Iterator to first element in the resource list
/**
* Cache specific ParamEntry needed by the action
*
* Find the first ParamEntry matching the resourceType, and the ParamEntry
* corresponding to the stack if consumeFromStack is set.
* @throws InvalidInputException if the required elements are not available in the resource list
*/
private void initializeEntries() throws InvalidInputException {
firstIter = -1;
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
ParamEntry entry = resource.getEntry(i);
if (firstIter == -1 && entry.isExclusion() && entry.getType() == resourceType &&
entry.getAllGroups().length == 1) {
firstIter = i; // First matching resource size
}
if (!entry.isExclusion() && entry.getSpace().isStackSpace()) {
stackEntry = entry;
break;
}
}
if (firstIter == -1) {
throw new InvalidInputException("Could not find matching resources for action: join");
}
if (consumeFromStack && stackEntry == null) {
throw new InvalidInputException("Cannot find matching <pentry> for action: join");
}
}
/**
* Constructor for use with restoreXml
* @param res is the new resource set to associate with this action
*/
protected MultiSlotAssign(ParamListStandard res) {
super(res);
resourceType = StorageClass.GENERAL; // Join general purpose registers
consumeFromStack = !(res instanceof ParamListStandardOut); // Spill into stack by default
consumeMostSig = false;
enforceAlignment = false;
justifyRight = false;
if (res.getEntry(0).isBigEndian()) {
consumeMostSig = true;
justifyRight = true;
}
stackEntry = null;
}
public MultiSlotAssign(StorageClass store, boolean stack, boolean mostSig, boolean align,
boolean justRight, ParamListStandard res) throws InvalidInputException {
super(res);
resourceType = store;
consumeFromStack = stack;
consumeMostSig = mostSig;
enforceAlignment = align;
justifyRight = justRight;
stackEntry = null;
initializeEntries();
}
@Override
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
return new MultiSlotAssign(resourceType, consumeFromStack, consumeMostSig, enforceAlignment,
justifyRight, newResource);
}
@Override
public boolean isEquivalent(AssignAction op) {
if (this.getClass() != op.getClass()) {
return false;
}
MultiSlotAssign otherAction = (MultiSlotAssign) op;
if (consumeFromStack != otherAction.consumeFromStack ||
consumeMostSig != otherAction.consumeMostSig ||
enforceAlignment != otherAction.enforceAlignment) {
return false;
}
if (firstIter != otherAction.firstIter || justifyRight != otherAction.justifyRight) {
return false;
}
if (resourceType != otherAction.resourceType) {
return false;
}
if (stackEntry == null && otherAction.stackEntry == null) {
// Nothing to compare
}
else if (stackEntry != null && otherAction.stackEntry != null) {
if (!stackEntry.isEquivalent(otherAction.stackEntry)) {
return false;
}
}
else {
return false;
}
return true;
}
@Override
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
int[] status, ParameterPieces res) {
int[] tmpStatus = status.clone();
ArrayList<Varnode> pieces = new ArrayList<>();
ParameterPieces param = new ParameterPieces();
int sizeLeft = dt.getLength();
int iter = firstIter;
int endIter = resource.getNumParamEntry();
if (enforceAlignment) {
int resourcesConsumed = 0;
while (iter != endIter) {
ParamEntry entry = resource.getEntry(iter);
if (!entry.isExclusion()) {
break;
} // Reached end of resource list
if (entry.getType() == resourceType && entry.getAllGroups().length == 1) { // Single register
if (tmpStatus[entry.getGroup()] == 0) { // Not consumed
int align = dt.getAlignment();
int regSize = entry.getSize();
if (align <= regSize || (resourcesConsumed % align) == 0) {
break;
}
tmpStatus[entry.getGroup()] = -1; // Consume unaligned register
}
resourcesConsumed += entry.getSize();
}
++iter;
}
}
while (sizeLeft > 0 && iter != endIter) {
ParamEntry entry = resource.getEntry(iter);
++iter;
if (!entry.isExclusion()) {
break;
} // Reached end of resource list
if (entry.getType() != resourceType || entry.getAllGroups().length != 1) {
continue;
} // Not a single register from desired resource list
if (tmpStatus[entry.getGroup()] != 0) {
continue;
} // Already consumed
int trialSize = entry.getSize();
entry.getAddrBySlot(tmpStatus[entry.getGroup()], trialSize, 1, param);
tmpStatus[entry.getGroup()] = -1; // Consume the register
Varnode vn = new Varnode(param.address, trialSize);
pieces.add(vn);
sizeLeft -= trialSize;
}
boolean onePieceJoin = false;
if (sizeLeft > 0) { // Have to use stack to get enough bytes
if (!consumeFromStack) {
return FAIL;
}
int grp = stackEntry.getGroup();
tmpStatus[grp] = stackEntry.getAddrBySlot(tmpStatus[grp], sizeLeft, 1, param); // Consume all the space we need
Varnode vn = new Varnode(param.address, sizeLeft);
pieces.add(vn);
}
else if (sizeLeft < 0) { // Have odd data-type size
if (resourceType == StorageClass.FLOAT && pieces.size() == 1) {
// Floating-point register holding extended lower precision value
onePieceJoin = true; // Treat as "join" of full size register
}
else if (justifyRight) {
// Initial bytes are padding
Varnode vn = pieces.get(0);
Address addr = vn.getAddress().add(-sizeLeft);
int sz = vn.getSize() + sizeLeft;
vn = new Varnode(addr, sz);
pieces.set(0, vn);
}
else {
int end = pieces.size() - 1;
Varnode vn = pieces.get(end);
int sz = vn.getSize() + sizeLeft;
vn = new Varnode(vn.getAddress(), sz);
pieces.set(end, vn);
}
}
System.arraycopy(tmpStatus, 0, status, 0, tmpStatus.length); // Commit resource usage for all the pieces
res.type = dt;
if (pieces.size() == 1 && !onePieceJoin) {
res.address = pieces.get(0).getAddress();
return SUCCESS;
}
res.joinPieces = new Varnode[pieces.size()];
if (!consumeMostSig) {
for (int i = 0; i < res.joinPieces.length; ++i) {
res.joinPieces[i] = pieces.get(pieces.size() - 1 - i);
}
}
else {
for (int i = 0; i < pieces.size(); ++i) {
res.joinPieces[i] = pieces.get(i);
}
}
res.address = Address.NO_ADDRESS; // Placeholder for join space address
return SUCCESS;
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_JOIN);
if (resource.getEntry(0).isBigEndian() != justifyRight) {
encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true);
}
if (resourceType != StorageClass.GENERAL) {
encoder.writeString(ATTRIB_STORAGE, resourceType.toString());
}
encoder.writeBool(ATTRIB_ALIGN, enforceAlignment);
encoder.closeElement(ELEM_JOIN);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_JOIN.name());
Iterator<Entry<String, String>> iter = elem.getAttributes().entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> attrib = iter.next();
String name = attrib.getKey();
if (name.equals(ATTRIB_REVERSEJUSTIFY.name())) {
if (SpecXmlUtils.decodeBoolean(attrib.getValue())) {
justifyRight = !justifyRight;
}
}
else if (name.equals(ATTRIB_STORAGE.name())) {
resourceType = StorageClass.getClass(attrib.getValue());
}
else if (name.equals(ATTRIB_ALIGN.name())) {
enforceAlignment = SpecXmlUtils.decodeBoolean(attrib.getValue());
}
}
parser.end(elem);
try {
initializeEntries();
}
catch (InvalidInputException e) {
throw new XmlParseException(e.getMessage());
} // Need new firstIter
}
}

View file

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.lang.PrototypePieces;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/**
* Filter that selects for a particular parameter position.
* This matches if the position of the current parameter being assigned, within the data-type
* list, matches the position attribute of this filter.
*/
public class PositionMatchFilter implements QualifierFilter {
private int position; // Parameter position being filtered for
public PositionMatchFilter(int pos) {
position = pos;
}
@Override
public QualifierFilter clone() {
return new PositionMatchFilter(position);
}
@Override
public boolean isEquivalent(QualifierFilter op) {
if (this.getClass() != op.getClass()) {
return false;
}
PositionMatchFilter otherFilter = (PositionMatchFilter) op;
if (position != otherFilter.position) {
return false;
}
return true;
}
@Override
public boolean filter(PrototypePieces proto, int pos) {
return (pos == position);
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_POSITION);
encoder.writeSignedInteger(ATTRIB_INDEX, position);
encoder.closeElement(ELEM_POSITION);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_POSITION.name());
position = SpecXmlUtils.decodeInt(elem.getAttribute(ATTRIB_INDEX.name()));
parser.end(elem);
}
}

View file

@ -0,0 +1,94 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.lang.PrototypePieces;
import ghidra.program.model.pcode.Encoder;
import ghidra.xml.*;
/**
* A filter on some aspect of a specific function prototype.
* An instance is configured via the restoreXml() method, then a test of whether
* a function prototype meets its criteria can be performed by calling its filter() method.
*/
public interface QualifierFilter {
/**
* Make a copy of this qualifier
* @return the copy
*/
public QualifierFilter clone();
/**
* Test if the given filter is configured and performs identically to this
* @param op is the given filter
* @return true if the two filters are equivalent
*/
public boolean isEquivalent(QualifierFilter op);
/**
* Test whether the given function prototype meets this filter's criteria
* @param proto is the high-level description of the function prototype to test
* @param pos is the position of a specific output (pos=-1) or input (pos >=0) in context
* @return true if the prototype meets the criteria, false otherwise
*/
public boolean filter(PrototypePieces proto, int pos);
/**
* Save this filter and its configuration to a stream
* @param encoder is the stream encoder
* @throws IOException for problems writing to the stream
*/
public void encode(Encoder encoder) throws IOException;
/**
* Configure details of the criteria being filtered from the given stream
* @param parser is the given stream decoder
* @throws XmlParseException if there are problems with the stream
*/
public void restoreXml(XmlPullParser parser) throws XmlParseException;
/**
* Instantiate a qualifier from the stream. If the next element is not a qualifier,
* return null.
* @param parser is the given stream decoder
* @return the new qualifier instance or null
* @throws XmlParseException for problems decoding the stream
*/
public static QualifierFilter restoreFilterXml(XmlPullParser parser) throws XmlParseException {
QualifierFilter filter;
XmlElement elemId = parser.peek();
String nm = elemId.getName();
if (nm.equals(ELEM_VARARGS.name())) {
filter = new VarargsFilter();
}
else if (nm.equals(ELEM_POSITION.name())) {
filter = new PositionMatchFilter(-1);
}
else if (nm.equals(ELEM_DATATYPE_AT.name())) {
filter = new DatatypeMatchFilter();
}
else {
return null;
}
filter.restoreXml(parser);
return filter;
}
}

View file

@ -0,0 +1,130 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map.Entry;
import ghidra.program.model.data.DataType;
import ghidra.program.model.pcode.Encoder;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.*;
/**
* A common base class for data-type filters that tests for a size range.
* Any filter that inherits from this, can use ATTRIB_MINSIZE and ATTRIB_MAXSIZE
* to place bounds on the possible sizes of data-types. The bounds are enforced
* by calling filterOnSize() within the inheriting classes filter() method.
*/
public class SizeRestrictedFilter implements DatatypeFilter {
public static final String NAME = "any";
protected int minSize; // Minimum size of the data-type in bytes
protected int maxSize; // Maximum size of the data-type in bytes
public SizeRestrictedFilter() {
minSize = 0;
maxSize = 0;
}
public SizeRestrictedFilter(int min, int max) {
minSize = min;
maxSize = max;
if (maxSize == 0 && minSize >= 0) {
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
maxSize = 0x7fffffff;
}
}
/**
* Enforce any size bounds on a given data-type.
* If \b maxSize is not zero, the data-type is checked to see if its size in bytes
* falls between \b minSize and \b maxSize inclusive.
* @param dt is the data-type to test
* @return true if the data-type meets the size restrictions
*/
public boolean filterOnSize(DataType dt) {
if (maxSize == 0) {
return true; // maxSize of 0 means no size filtering is performed
}
return (dt.getLength() >= minSize && dt.getLength() <= maxSize);
}
@Override
public DatatypeFilter clone() {
return new SizeRestrictedFilter(minSize, maxSize);
}
@Override
public boolean isEquivalent(DatatypeFilter op) {
if (this.getClass() != op.getClass()) {
return false;
}
SizeRestrictedFilter otherFilter = (SizeRestrictedFilter) op;
if (maxSize != otherFilter.maxSize || minSize != otherFilter.minSize) {
return false;
}
return true;
}
@Override
public boolean filter(DataType dt) {
return filterOnSize(dt);
}
protected void encodeAttributes(Encoder encoder) throws IOException {
encoder.writeUnsignedInteger(ATTRIB_MINSIZE, minSize);
encoder.writeUnsignedInteger(ATTRIB_MAXSIZE, maxSize);
}
protected void restoreAttributesXml(XmlElement el) {
Iterator<Entry<String, String>> iter = el.getAttributes().entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> attrib = iter.next();
String nm = attrib.getKey();
if (nm.equals(ATTRIB_MINSIZE.name())) {
minSize = SpecXmlUtils.decodeInt(attrib.getValue());
}
else if (nm.equals(ATTRIB_MAXSIZE.name())) {
maxSize = SpecXmlUtils.decodeInt(attrib.getValue());
}
}
if (maxSize == 0 && minSize >= 0) {
// If no ATTRIB_MAXSIZE is given, assume there is no upper bound on size
maxSize = 0x7fffffff;
}
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_DATATYPE);
encoder.writeString(ATTRIB_NAME, NAME);
encodeAttributes(encoder);
encoder.closeElement(ELEM_DATATYPE);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_DATATYPE.name());
restoreAttributesXml(elem);
parser.end(elem);
}
}

View file

@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.model.lang.protorules;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.IOException;
import ghidra.program.model.lang.PrototypePieces;
import ghidra.program.model.pcode.Encoder;
import ghidra.xml.*;
/**
* A filter that selects function parameters that are considered optional.
* If the underlying function prototype is considered to take variable arguments, the first
* n parameters (as determined by PrototypePieces.firstVarArgSlot) are considered non-optional.
* If additional data-types are provided beyond the initial n, these are considered optional.
* This filter returns true for these optional parameters
*/
public class VarargsFilter implements QualifierFilter {
@Override
public QualifierFilter clone() {
return new VarargsFilter();
}
@Override
public boolean isEquivalent(QualifierFilter op) {
return (this.getClass() == op.getClass());
}
@Override
public boolean filter(PrototypePieces proto, int pos) {
if (proto.firstVarArgSlot < 0) {
return false;
}
return (pos >= proto.firstVarArgSlot);
}
@Override
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_VARARGS);
encoder.closeElement(ELEM_VARARGS);
}
@Override
public void restoreXml(XmlPullParser parser) throws XmlParseException {
XmlElement elem = parser.start(ELEM_VARARGS.name());
parser.end(elem);
}
}

View file

@ -236,5 +236,7 @@ public record AttributeId(String name, int id) {
// public static final AttributeId ATTRIB_VERSION = new AttributeId("version", 144);
// public static final AttributeId ATTRIB_ADDRESS = new AttributeId("address", 148);
public static final AttributeId ATTRIB_UNKNOWN = new AttributeId("XMLunknown", 149);
public static final AttributeId ATTRIB_STORAGE = new AttributeId("storage", 149);
public static final AttributeId ATTRIB_UNKNOWN = new AttributeId("XMLunknown", 150);
}

View file

@ -424,5 +424,20 @@ public record ElementId(String name, int id) {
public static final ElementId ELEM_SPLITDATATYPE = new ElementId("splitdatatype", 270);
public static final ElementId ELEM_JUMPTABLEMAX = new ElementId("jumptablemax", 271);
public static final ElementId ELEM_NANIGNORE = new ElementId("nanignore", 272);
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 273);
// modelrules
public static final ElementId ELEM_DATATYPE = new ElementId("datatype", 273);
public static final ElementId ELEM_CONSUME = new ElementId("consume", 274);
public static final ElementId ELEM_CONSUME_EXTRA = new ElementId("consume_extra", 275);
public static final ElementId ELEM_CONVERT_TO_PTR = new ElementId("convert_to_ptr", 276);
public static final ElementId ELEM_GOTO_STACK = new ElementId("goto_stack", 277);
public static final ElementId ELEM_JOIN = new ElementId("join", 278);
public static final ElementId ELEM_DATATYPE_AT = new ElementId("datatype_at", 279);
public static final ElementId ELEM_POSITION = new ElementId("position", 280);
public static final ElementId ELEM_VARARGS = new ElementId("varargs", 281);
public static final ElementId ELEM_HIDDEN_RETURN = new ElementId("hidden_return", 282);
public static final ElementId ELEM_JOIN_PER_PRIMITIVE =
new ElementId("join_per_primitive", 283);
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 284);
}

View file

@ -33,6 +33,7 @@ import ghidra.program.model.lang.DecompilerLanguage;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.NameTransformer;
import ghidra.util.UniversalID;
import ghidra.xml.XmlParseException;
/**
*
@ -51,6 +52,20 @@ public class PcodeDataTypeManager {
private static final long DEFAULT_DECOMPILER_ID = 0xC000000000000000L; // ID for "undefined" (decompiler side)
private static final long CODE_DECOMPILER_ID = 0xE000000000000001L; // ID for internal "code" data-type
public static final int TYPE_VOID = 14; // Standard "void" type, absence of type
public static final int TYPE_UNKNOWN = 12; // An unknown low-level type. Treated as an unsigned integer.
public static final int TYPE_INT = 11; // Signed integer. Signed is considered less specific than unsigned in C
public static final int TYPE_UINT = 10; // Unsigned integer
public static final int TYPE_BOOL = 9; // Boolean
public static final int TYPE_CODE = 8; // Data is actual executable code
public static final int TYPE_FLOAT = 7; // Floating-point
public static final int TYPE_PTR = 6; // Pointer data-type
public static final int TYPE_PTRREL = 5; // Pointer relative to another data-type (specialization of TYPE_PTR)
public static final int TYPE_ARRAY = 4; // Array data-type, made up of a sequence of "element" datatype
public static final int TYPE_STRUCT = 3; // Structure data-type, made up of component datatypes
public static final int TYPE_UNION = 2; // An overlapping union of multiple datatypes
/**
* A mapping between a DataType and its (name,id) on the decompiler side
*/
@ -1238,4 +1253,147 @@ public class PcodeDataTypeManager {
}
encoder.closeElement(ELEM_CORETYPES);
}
/**
* Get the decompiler meta-type associated with a data-type.
* @param tp is the data-type
* @return the meta-type
*/
public static int getMetatype(DataType tp) {
if (tp instanceof TypeDef) {
tp = ((TypeDef) tp).getBaseDataType();
}
if (tp instanceof Undefined) {
return TYPE_UNKNOWN;
}
if (tp instanceof AbstractFloatDataType) {
return TYPE_FLOAT;
}
if (tp instanceof Pointer) {
return TYPE_PTR;
}
if (tp instanceof BooleanDataType) {
return TYPE_BOOL;
}
if (tp instanceof AbstractSignedIntegerDataType) {
return TYPE_INT;
}
if (tp instanceof AbstractUnsignedIntegerDataType) {
return TYPE_UINT;
}
if (tp instanceof Structure) {
return TYPE_STRUCT;
}
if (tp instanceof Union) {
return TYPE_UNION;
}
if (tp instanceof Array) {
return TYPE_ARRAY;
}
return TYPE_UNKNOWN;
}
/**
* Convert an XML marshaling string to a metatype code
* @param metaString is the string
* @return the metatype code
* @throws XmlParseException if the string does not represent a valid metatype
*/
public static int getMetatype(String metaString) throws XmlParseException {
switch (metaString.charAt(0)) {
case 'p':
if (metaString.equals("ptr")) {
return TYPE_PTR;
}
else if (metaString.equals("ptrrel")) {
return TYPE_PTRREL;
}
break;
case 'a':
if (metaString.equals("array")) {
return TYPE_ARRAY;
}
break;
case 's':
if (metaString.equals("struct")) {
return TYPE_STRUCT;
}
break;
case 'u':
if (metaString.equals("unknown")) {
return TYPE_UNKNOWN;
}
else if (metaString.equals("uint")) {
return TYPE_UINT;
}
else if (metaString.equals("union")) {
return TYPE_UNION;
}
break;
case 'i':
if (metaString.equals("int")) {
return TYPE_INT;
}
break;
case 'f':
if (metaString.equals("float")) {
return TYPE_FLOAT;
}
break;
case 'b':
if (metaString.equals("bool")) {
return TYPE_BOOL;
}
break;
case 'c':
if (metaString.equals("code")) {
return TYPE_CODE;
}
break;
case 'v':
if (metaString.equals("void")) {
return TYPE_VOID;
}
break;
default:
break;
}
throw new XmlParseException("Unknown metatype: " + metaString);
}
/**
* Convert a decompiler metatype code to a string for XML marshaling
* @param meta is the metatype
* @return the marshaling string
* @throws IOException is the metatype is invalid
*/
public static String getMetatypeString(int meta) throws IOException {
switch (meta) {
case TYPE_VOID:
return "void";
case TYPE_UNKNOWN:
return "unknown";
case TYPE_INT:
return "int";
case TYPE_UINT:
return "uint";
case TYPE_BOOL:
return "bool";
case TYPE_CODE:
return "code";
case TYPE_FLOAT:
return "float";
case TYPE_PTR:
return "ptr";
case TYPE_PTRREL:
return "ptrrel";
case TYPE_ARRAY:
return "array";
case TYPE_STRUCT:
return "struct";
case TYPE_UNION:
return "union";
}
throw new IOException("Unknown metatype");
}
}