mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch 'origin/GP-4031_X86SystemVABI'
This commit is contained in:
commit
f5d956d5e6
20 changed files with 1668 additions and 185 deletions
|
@ -409,6 +409,22 @@
|
|||
<attribute name="storage"/>
|
||||
</optional>
|
||||
</element>
|
||||
<element name="join_dual_class">
|
||||
<optional>
|
||||
<attribute name="reversejustify">
|
||||
<ref name="boolean_type"/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="storage"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="a"/>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="b"/>
|
||||
</optional>
|
||||
</element>
|
||||
</choice>
|
||||
<zeroOrMore>
|
||||
<element name="consume_extra">
|
||||
|
|
|
@ -127,6 +127,9 @@ public abstract class AssignAction {
|
|||
boolean consumeMostSig = res.getEntry(0).isBigEndian();
|
||||
action = new MultiMemberAssign(StorageClass.GENERAL, false, consumeMostSig, res);
|
||||
}
|
||||
else if (nm.equals(ELEM_JOIN_DUAL_CLASS.name())) {
|
||||
action = new MultiSlotDualAssign(res);
|
||||
}
|
||||
else {
|
||||
throw new XmlParseException("Unknown model rule action: " + nm);
|
||||
}
|
||||
|
|
|
@ -18,9 +18,8 @@ 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.data.DataType;
|
||||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
import ghidra.xml.*;
|
||||
|
@ -66,79 +65,6 @@ public interface DatatypeFilter {
|
|||
*/
|
||||
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 maximum number of extracted primitives is exceeded, or if the
|
||||
* primitives are not properly aligned, 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 res will hold the list of primitives
|
||||
* @return true if all primitives were extracted
|
||||
*/
|
||||
public static boolean extractPrimitives(DataType dt, int max, ArrayList<DataType> res) {
|
||||
if (dt instanceof TypeDef) {
|
||||
dt = ((TypeDef) dt).getBaseDataType();
|
||||
}
|
||||
int metaType = PcodeDataTypeManager.getMetatype(dt);
|
||||
switch (metaType) {
|
||||
case PcodeDataTypeManager.TYPE_UNKNOWN:
|
||||
return false; // Do not consider undefined data-types as primitive
|
||||
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, res)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PcodeDataTypeManager.TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Structure structPtr = (Structure) dt;
|
||||
boolean isPacked = structPtr.isPackingEnabled();
|
||||
int curOff = 0;
|
||||
DataTypeComponent[] components = structPtr.getDefinedComponents();
|
||||
for (DataTypeComponent component : components) {
|
||||
DataType compDT = component.getDataType();
|
||||
if (!isPacked) {
|
||||
int nextOff = component.getOffset();
|
||||
int align = dt.getAlignment();
|
||||
int rem = curOff % align;
|
||||
if (rem != 0) {
|
||||
curOff += (align - rem);
|
||||
}
|
||||
if (curOff != nextOff) {
|
||||
return false;
|
||||
}
|
||||
curOff = nextOff + compDT.getAlignedLength();
|
||||
}
|
||||
if (!extractPrimitives(compDT, max, res)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a filter from the given stream.
|
||||
* @param parser is the given stream decoder
|
||||
|
|
|
@ -19,7 +19,6 @@ 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;
|
||||
|
@ -67,17 +66,18 @@ public class HomogeneousAggregate extends SizeRestrictedFilter {
|
|||
if (meta != PcodeDataTypeManager.TYPE_ARRAY && meta != PcodeDataTypeManager.TYPE_STRUCT) {
|
||||
return false;
|
||||
}
|
||||
ArrayList<DataType> res = new ArrayList<>();
|
||||
if (!DatatypeFilter.extractPrimitives(dt, MAX_PRIMITIVES, res) || res.isEmpty()) {
|
||||
PrimitiveExtractor primitives = new PrimitiveExtractor(dt, true, 0, MAX_PRIMITIVES);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsUnknown() ||
|
||||
!primitives.isAligned() || primitives.containsHoles()) {
|
||||
return false;
|
||||
}
|
||||
DataType base = res.get(0);
|
||||
DataType base = primitives.get(0).dt;
|
||||
int baseMeta = PcodeDataTypeManager.getMetatype(base);
|
||||
if (baseMeta != metaType) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < res.size(); ++i) {
|
||||
if (res.get(i) != base) {
|
||||
for (int i = 1; i < primitives.size(); ++i) {
|
||||
if (primitives.get(i).dt != base) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,12 +77,13 @@ public class MultiMemberAssign extends AssignAction {
|
|||
int[] tmpStatus = status.clone();
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
ArrayList<DataType> primitives = new ArrayList<>();
|
||||
if (!DatatypeFilter.extractPrimitives(dt, 16, primitives) || primitives.isEmpty()) {
|
||||
PrimitiveExtractor primitives = new PrimitiveExtractor(dt, false, 0, 16);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsUnknown() ||
|
||||
!primitives.isAligned() || primitives.containsHoles()) {
|
||||
return FAIL;
|
||||
}
|
||||
for (int i = 0; i < primitives.size(); ++i) {
|
||||
DataType curType = primitives.get(i);
|
||||
DataType curType = primitives.get(i).dt;
|
||||
if (resource.assignAddressFallback(resourceType, curType, !consumeFromStack, tmpStatus,
|
||||
param) == FAIL) {
|
||||
return FAIL;
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
/* ###
|
||||
* 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.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.lang.protorules.PrimitiveExtractor.Primitive;
|
||||
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 from different storage classes to pass a data-type
|
||||
*
|
||||
* This action is for calling conventions that can use both floating-point and general purpose registers
|
||||
* when assigning storage for a single composite data-type, such as the X86-64 System V ABI
|
||||
*/
|
||||
public class MultiSlotDualAssign extends AssignAction {
|
||||
private StorageClass baseType; // Resource list from which to consume general tiles
|
||||
private StorageClass altType; // Resource list from which to consume alternate tiles
|
||||
private boolean consumeMostSig; // True if resources are consumed starting with most significant bytes
|
||||
private boolean justifyRight; // True if initial bytes are padding for odd data-type sizes
|
||||
private int tileSize; // Number of bytes in a tile
|
||||
private int baseIter; // Iterator to first element in the base resource list
|
||||
private int altIter; // Iterator to first element in alternate resource list
|
||||
|
||||
/**
|
||||
* Find the first ParamEntry matching the baseType, and the first matching altType.
|
||||
* @throws InvalidInputException if the required elements are not available in the resource list
|
||||
*/
|
||||
private void initializeEntries() throws InvalidInputException {
|
||||
baseIter = -1;
|
||||
altIter = -1;
|
||||
for (int i = 0; i < resource.getNumParamEntry(); ++i) {
|
||||
ParamEntry entry = resource.getEntry(i);
|
||||
if (baseIter == -1 && entry.isExclusion() && entry.getType() == baseType &&
|
||||
entry.getAllGroups().length == 1) {
|
||||
baseIter = i; // First matching base resource type
|
||||
}
|
||||
if (altIter == -1 && entry.isExclusion() && entry.getType() == altType &&
|
||||
entry.getAllGroups().length == 1) {
|
||||
altIter = i; // First matching alt resource type
|
||||
}
|
||||
}
|
||||
if (baseIter == -1 || altIter == -1) {
|
||||
throw new InvalidInputException(
|
||||
"Could not find matching resources for action: join_dual_class");
|
||||
}
|
||||
tileSize = resource.getEntry(baseIter).getSize();
|
||||
if (tileSize != resource.getEntry(altIter).getSize()) {
|
||||
throw new InvalidInputException(
|
||||
"Storage class register sizes do not match for action: join_dual_class");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first unused ParamEntry that matches the given storage class
|
||||
* @param iter points to the starting entry to search
|
||||
* @param storage is the given storage class to match
|
||||
* @param status is the usage information for the entries
|
||||
* @return the iterator to the unused ParamEntry
|
||||
*/
|
||||
private int getFirstUnused(int iter, StorageClass storage, int[] status) {
|
||||
int endIter = resource.getNumParamEntry();
|
||||
for (; iter != endIter; ++iter) {
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
if (!entry.isExclusion()) {
|
||||
break; // Reached end of resource list
|
||||
}
|
||||
if (entry.getType() != storage || entry.getAllGroups().length != 1) {
|
||||
continue; // Not a single register from desired resource
|
||||
}
|
||||
if (status[entry.getGroup()] != 0) {
|
||||
continue; // Already consumed
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
return endIter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage class to use for the specific section of the data-type
|
||||
*
|
||||
* For the section starting at -off- extending through -tileSize- bytes, if any primitive
|
||||
* overlaps the boundary of the section, return -1. Otherwise, if all the primitive data-types
|
||||
* in the section match the alternate storage class, return 1, or if one or more does not
|
||||
* match, return 0. The -index- of the first primitive after the start of the section is
|
||||
* provided and is then updated to be the first primitive after the end of the section.
|
||||
* @param primitives is the list of primitive data-types making up the data-type
|
||||
* @param off is the starting offset of the section
|
||||
* @param index is the index of the first primitive in the section
|
||||
* @return 0 for a base tile, 1 for an alternate tile, -1 for boundary overlaps
|
||||
*/
|
||||
private int getTileClass(PrimitiveExtractor primitives, int off, int[] index) {
|
||||
int res = 1;
|
||||
int count = 0;
|
||||
int endBoundary = off + tileSize;
|
||||
while (index[0] < primitives.size()) {
|
||||
Primitive element = primitives.get(index[0]);
|
||||
if (element.offset < off) {
|
||||
return -1;
|
||||
}
|
||||
if (element.offset >= endBoundary) {
|
||||
break;
|
||||
}
|
||||
if (element.offset + element.dt.getLength() > endBoundary) {
|
||||
return -1;
|
||||
}
|
||||
count += 1;
|
||||
index[0] += 1;
|
||||
StorageClass storage = ParamEntry.getBasicTypeClass(element.dt);
|
||||
if (storage != altType) {
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
return -1; // Must be at least one primitive in section
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for use with decode. Set default configuration.
|
||||
* @param res is the new resource set to associate with this action
|
||||
*/
|
||||
protected MultiSlotDualAssign(ParamListStandard res) {
|
||||
super(res);
|
||||
baseType = StorageClass.GENERAL; // Tile from general purpose registers
|
||||
altType = StorageClass.FLOAT; // Use specialized registers for floating-point components
|
||||
consumeMostSig = false;
|
||||
justifyRight = false;
|
||||
if (res.getEntry(0).isBigEndian()) {
|
||||
consumeMostSig = true;
|
||||
justifyRight = true;
|
||||
}
|
||||
tileSize = 0;
|
||||
}
|
||||
|
||||
public MultiSlotDualAssign(StorageClass baseStore, StorageClass altStore, boolean mostSig,
|
||||
boolean justRight, ParamListStandard res) throws InvalidInputException {
|
||||
super(res);
|
||||
baseType = baseStore;
|
||||
altType = altStore;
|
||||
consumeMostSig = mostSig;
|
||||
justifyRight = justRight;
|
||||
initializeEntries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssignAction clone(ParamListStandard newResource) throws InvalidInputException {
|
||||
return new MultiSlotDualAssign(baseType, altType, consumeMostSig, justifyRight,
|
||||
newResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEquivalent(AssignAction op) {
|
||||
if (this.getClass() != op.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MultiSlotDualAssign otherAction = (MultiSlotDualAssign) op;
|
||||
if (consumeMostSig != otherAction.consumeMostSig ||
|
||||
justifyRight != otherAction.justifyRight) {
|
||||
return false;
|
||||
}
|
||||
if (baseIter != otherAction.baseIter || altIter != otherAction.altIter) {
|
||||
return false;
|
||||
}
|
||||
if (baseType != otherAction.baseType || altType != otherAction.altType) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int assignAddress(DataType dt, PrototypePieces proto, int pos, DataTypeManager dtManager,
|
||||
int[] status, ParameterPieces res) {
|
||||
PrimitiveExtractor primitives = new PrimitiveExtractor(dt, false, 0, 1024);
|
||||
if (!primitives.isValid() || primitives.size() == 0 || primitives.containsHoles()) {
|
||||
return FAIL;
|
||||
}
|
||||
ParameterPieces param = new ParameterPieces();
|
||||
int[] primitiveIndex = new int[1];
|
||||
primitiveIndex[0] = 0;
|
||||
int[] tmpStatus = status.clone();
|
||||
ArrayList<Varnode> pieces = new ArrayList<>();
|
||||
int typeSize = dt.getLength();
|
||||
int sizeLeft = typeSize;
|
||||
int iterBase = baseIter;
|
||||
int iterAlt = altIter;
|
||||
int endIter = resource.getNumParamEntry();
|
||||
while (sizeLeft > 0) {
|
||||
int iter;
|
||||
int iterType = getTileClass(primitives, typeSize - sizeLeft, primitiveIndex);
|
||||
if (iterType < 0) {
|
||||
return FAIL;
|
||||
}
|
||||
if (iterType == 0) {
|
||||
iter = iterBase = getFirstUnused(iterBase, baseType, tmpStatus);
|
||||
}
|
||||
else {
|
||||
iter = iterAlt = getFirstUnused(iterAlt, altType, tmpStatus);
|
||||
}
|
||||
if (iter == endIter) {
|
||||
return FAIL; // Out of the particular resource
|
||||
}
|
||||
ParamEntry entry = resource.getEntry(iter);
|
||||
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;
|
||||
}
|
||||
if (sizeLeft < 0) { // Have odd data-type size
|
||||
if (justifyRight) {
|
||||
// Initial bytes of first entry 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) {
|
||||
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;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_JOIN_DUAL_CLASS);
|
||||
if (resource.getEntry(0).isBigEndian() != justifyRight) {
|
||||
encoder.writeBool(ATTRIB_REVERSEJUSTIFY, true);
|
||||
}
|
||||
if (baseType != StorageClass.GENERAL) {
|
||||
encoder.writeString(ATTRIB_STORAGE, baseType.toString());
|
||||
}
|
||||
if (altType != StorageClass.FLOAT) {
|
||||
encoder.writeString(ATTRIB_B, altType.toString());
|
||||
}
|
||||
encoder.closeElement(ELEM_JOIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) throws XmlParseException {
|
||||
XmlElement elem = parser.start(ELEM_JOIN_DUAL_CLASS.name());
|
||||
for (Entry<String, String> attrib : elem.getAttributes().entrySet()) {
|
||||
String name = attrib.getKey();
|
||||
if (name.equals(ATTRIB_REVERSEJUSTIFY.name())) {
|
||||
if (SpecXmlUtils.decodeBoolean(attrib.getValue())) {
|
||||
justifyRight = !justifyRight;
|
||||
}
|
||||
}
|
||||
else if (name.equals(ATTRIB_STORAGE.name()) || name.equals(ATTRIB_A.name())) {
|
||||
baseType = StorageClass.getClass(attrib.getValue());
|
||||
}
|
||||
else if (name.equals(ATTRIB_B.name())) {
|
||||
altType = StorageClass.getClass(attrib.getValue());
|
||||
}
|
||||
}
|
||||
parser.end(elem);
|
||||
try {
|
||||
initializeEntries();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new XmlParseException(e.getMessage());
|
||||
} // Need new firstIter
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
/* ###
|
||||
* 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.util.ArrayList;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.pcode.PcodeDataTypeManager;
|
||||
|
||||
public class PrimitiveExtractor {
|
||||
|
||||
public static class Primitive {
|
||||
public DataType dt; // The primitive data-type
|
||||
public int offset; // Offset within the container
|
||||
|
||||
public Primitive(DataType d, int off) {
|
||||
dt = d;
|
||||
offset = off;
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<Primitive> primitives; // List of extracted primitives
|
||||
private boolean valid; // Extraction was invalid
|
||||
private boolean aligned; // True if all primitives are properly aligned
|
||||
private boolean unknownElements; // True if at least one TYPE_UNKNOWN primitive
|
||||
private boolean extraSpace; // True if extra space not attributable to padding
|
||||
private boolean unionInvalid; // True if unions are treated as invalid primitive
|
||||
|
||||
/**
|
||||
* Check that a big Primitive properly overlaps smaller Primitives
|
||||
*
|
||||
* If the big Primitive does not properly overlap the smaller Primitives starting at the given
|
||||
* point, return -1. Otherwise, if the big Primitive is floating-point, add the overlapped
|
||||
* primitives to the common refinement list, or if not a floating-point, add the big Primitive
|
||||
* to the list. (Integer primitives are \e preferred over floating-point primitives in this way)
|
||||
* Return the index of the next primitive after the overlap.
|
||||
* @param res holds the common refinement list
|
||||
* @param small is the list of Primitives that are overlapped
|
||||
* @param point is the index of the first overlap
|
||||
* @param big is the big overlapping Primitive
|
||||
* @return the index of the next Primitive after the overlap or -1 if the overlap is invalid
|
||||
*/
|
||||
private int checkOverlap(ArrayList<Primitive> res, ArrayList<Primitive> small, int point,
|
||||
Primitive big) {
|
||||
int endOff = big.offset + big.dt.getAlignedLength();
|
||||
// If big data-type is a float, let smaller primitives override it, otherwise we keep the big primitive
|
||||
boolean useSmall =
|
||||
PcodeDataTypeManager.getMetatype(big.dt) == PcodeDataTypeManager.TYPE_FLOAT;
|
||||
while (point < small.size()) {
|
||||
int curOff = small.get(point).offset;
|
||||
if (curOff >= endOff) {
|
||||
break;
|
||||
}
|
||||
curOff += small.get(point).dt.getAlignedLength();
|
||||
if (curOff > endOff) {
|
||||
return -1; // Improper overlap of the end of big
|
||||
}
|
||||
if (useSmall) {
|
||||
res.add(small.get(point));
|
||||
}
|
||||
point += 1;
|
||||
}
|
||||
if (!useSmall) { // If big data-type was preferred
|
||||
res.add(big); // use big Primitive in the refinement
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite first list with common refinement of first and second
|
||||
*
|
||||
* Given two sets of overlapping Primitives, find a \e common \e refinement of the lists.
|
||||
* If there is any partial overlap of two Primitives, \b false is returned.
|
||||
* If the same primitive data-type occurs at the same offset, it is included in the refinement.
|
||||
* Otherwise an integer data-type is preferred over a floating-point data-type, or a bigger
|
||||
* primitive is preferred over smaller overlapping primitives.
|
||||
* The final refinement replaces the \b first list.
|
||||
* @param first is the first list of Primitives
|
||||
* @param second is the second list
|
||||
* @return true if a refinement was successfully constructed
|
||||
*/
|
||||
private boolean commonRefinement(ArrayList<Primitive> first, ArrayList<Primitive> second) {
|
||||
int firstPoint = 0;
|
||||
int secondPoint = 0;
|
||||
ArrayList<Primitive> common = new ArrayList<>();
|
||||
while (firstPoint < first.size() && secondPoint < second.size()) {
|
||||
Primitive firstElement = first.get(firstPoint);
|
||||
Primitive secondElement = second.get(secondPoint);
|
||||
if (firstElement.offset < secondElement.offset &&
|
||||
firstElement.offset + firstElement.dt.getAlignedLength() <= secondElement.offset) {
|
||||
common.add(firstElement);
|
||||
firstPoint += 1;
|
||||
continue;
|
||||
}
|
||||
if (secondElement.offset < firstElement.offset &&
|
||||
secondElement.offset + secondElement.dt.getAlignedLength() <= firstElement.offset) {
|
||||
common.add(secondElement);
|
||||
secondPoint += 1;
|
||||
continue;
|
||||
}
|
||||
if (firstElement.dt.getAlignedLength() >= secondElement.dt.getAlignedLength()) {
|
||||
secondPoint = checkOverlap(common, second, secondPoint, firstElement);
|
||||
if (secondPoint < 0) {
|
||||
return false;
|
||||
}
|
||||
firstPoint += 1;
|
||||
}
|
||||
else {
|
||||
firstPoint = checkOverlap(common, first, firstPoint, secondElement);
|
||||
if (firstPoint < 0) {
|
||||
return false;
|
||||
}
|
||||
secondPoint += 1;
|
||||
}
|
||||
}
|
||||
// Add any tail primitives from either list
|
||||
while (firstPoint < first.size()) {
|
||||
common.add(first.get(firstPoint));
|
||||
firstPoint += 1;
|
||||
}
|
||||
while (secondPoint < second.size()) {
|
||||
common.add(second.get(secondPoint));
|
||||
secondPoint += 1;
|
||||
}
|
||||
first.clear();
|
||||
first.addAll(common); // Replace first with the refinement
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form a primitive list for each field of the union. Then, if possible, form a common
|
||||
* refinement of all the primitive lists and add to the end of this extractor's list.
|
||||
* @param dt is the union data-type
|
||||
* @param max is the maximum number primitives allowed for \b this extraction
|
||||
* @param offset is the starting offset of the union within the parent
|
||||
* @return true if a common refinement was found and appended
|
||||
*/
|
||||
private boolean handleUnion(UnionDataType dt, int max, int offset) {
|
||||
if (unionInvalid) {
|
||||
return false;
|
||||
}
|
||||
int num = dt.getNumComponents();
|
||||
if (num == 0) {
|
||||
return false;
|
||||
}
|
||||
DataTypeComponent curField = dt.getComponent(0);
|
||||
|
||||
PrimitiveExtractor common = new PrimitiveExtractor(curField.getDataType(), false,
|
||||
offset + curField.getOffset(), max);
|
||||
if (!common.isValid()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < num; ++i) {
|
||||
curField = dt.getComponent(i);
|
||||
|
||||
PrimitiveExtractor next = new PrimitiveExtractor(curField.getDataType(), false,
|
||||
offset + curField.getOffset(), max);
|
||||
if (!next.isValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!commonRefinement(common.primitives, next.primitives)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (primitives.size() + common.primitives.size() > max) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < common.primitives.size(); ++i) {
|
||||
primitives.add(common.primitives.get(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of the primitive data-types, with their associated offsets, is constructed.
|
||||
* If the given data-type is already primitive it is put in the array by itself. Otherwise
|
||||
* if it is composite, its components are recursively added to the array.
|
||||
* Boolean properties about the primitives encountered are recorded:
|
||||
* - Are any of the primitives \b undefined
|
||||
* - Are all the primitives properly aligned.
|
||||
*
|
||||
* If a maximum number of extracted primitives is exceeded, or if an illegal
|
||||
* data-type is encountered (\b void or other internal data-type) 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 offset is the starting offset to associate with the first primitive
|
||||
* @return true if all primitives were extracted
|
||||
*/
|
||||
private boolean extract(DataType dt, int max, int offset) {
|
||||
if (dt instanceof TypeDef) {
|
||||
dt = ((TypeDef) dt).getBaseDataType();
|
||||
}
|
||||
int metaType = PcodeDataTypeManager.getMetatype(dt);
|
||||
switch (metaType) {
|
||||
case PcodeDataTypeManager.TYPE_UNKNOWN:
|
||||
unknownElements = true;
|
||||
// fall-thru
|
||||
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 (primitives.size() >= max) {
|
||||
return false;
|
||||
}
|
||||
primitives.add(new Primitive(dt, offset));
|
||||
return true;
|
||||
case PcodeDataTypeManager.TYPE_ARRAY: {
|
||||
int numEls = ((Array) dt).getNumElements();
|
||||
DataType base = ((Array) dt).getDataType();
|
||||
for (int i = 0; i < numEls; ++i) {
|
||||
if (!extract(base, max, offset)) {
|
||||
return false;
|
||||
}
|
||||
offset += base.getAlignedLength();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PcodeDataTypeManager.TYPE_UNION:
|
||||
return handleUnion((UnionDataType) dt, max, offset);
|
||||
case PcodeDataTypeManager.TYPE_STRUCT:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
Structure structPtr = (Structure) dt;
|
||||
boolean isPacked = structPtr.isPackingEnabled();
|
||||
DataTypeComponent[] components = structPtr.getDefinedComponents();
|
||||
int expectedOff = offset;
|
||||
for (DataTypeComponent component : components) {
|
||||
DataType compDT = component.getDataType();
|
||||
int curOff = component.getOffset() + offset;
|
||||
if (!isPacked) {
|
||||
int align = compDT.getAlignment();
|
||||
if (curOff % align != 0) {
|
||||
aligned = false;
|
||||
}
|
||||
int rem = expectedOff % align;
|
||||
if (rem != 0) {
|
||||
expectedOff += (align - rem);
|
||||
}
|
||||
if (expectedOff != curOff) {
|
||||
extraSpace = true;
|
||||
}
|
||||
}
|
||||
if (!extract(compDT, max, curOff)) {
|
||||
return false;
|
||||
}
|
||||
expectedOff = curOff + compDT.getAlignedLength();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dt is data-type extract from
|
||||
* @param unionIllegal is true if unions encountered during extraction are considered illegal
|
||||
* @param offset is the starting offset to associate with the data-type
|
||||
* @param max is the maximum number of primitives to extract before giving up
|
||||
*/
|
||||
public PrimitiveExtractor(DataType dt, boolean unionIllegal, int offset, int max) {
|
||||
primitives = new ArrayList<>();
|
||||
valid = true;
|
||||
aligned = true;
|
||||
unknownElements = false;
|
||||
extraSpace = false;
|
||||
unionInvalid = unionIllegal;
|
||||
if (!extract(dt, max, offset)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if all primitive elements were extracted
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if any extracted element was unknown/undefined
|
||||
*/
|
||||
public boolean containsUnknown() {
|
||||
return unknownElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if all extracted elements are aligned
|
||||
*/
|
||||
public boolean isAligned() {
|
||||
return aligned;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there is extra space in the data-type that is not alignment padding
|
||||
*/
|
||||
public boolean containsHoles() {
|
||||
return extraSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of primitives extracted
|
||||
*/
|
||||
public int size() {
|
||||
return primitives.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the i-th extracted primitive and its offset
|
||||
* @param i is the index
|
||||
* @return the primitive and offset
|
||||
*/
|
||||
public Primitive get(int i) {
|
||||
return primitives.get(i);
|
||||
}
|
||||
}
|
|
@ -454,6 +454,7 @@ public record ElementId(String name, int id) {
|
|||
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_JOIN_DUAL_CLASS = new ElementId("join_dual_class", 285);
|
||||
|
||||
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 285);
|
||||
public static final ElementId ELEM_UNKNOWN = new ElementId("XMLunknown", 286);
|
||||
}
|
||||
|
|
|
@ -541,14 +541,8 @@ public class PcodeDataTypeManager {
|
|||
private void encodeEnum(Encoder encoder, Enum type, int size) throws IOException {
|
||||
encoder.openElement(ELEM_TYPE);
|
||||
encodeNameIdAttributes(encoder, type);
|
||||
String metatype = type.isSigned() ? "int" : "uint";
|
||||
long[] keys = type.getValues();
|
||||
String metatype = "uint";
|
||||
for (long key : keys) {
|
||||
if (key < 0) {
|
||||
metatype = "int";
|
||||
break;
|
||||
}
|
||||
}
|
||||
encoder.writeString(ATTRIB_METATYPE, metatype);
|
||||
encoder.writeSignedInteger(ATTRIB_SIZE, type.getLength());
|
||||
encoder.writeBool(ATTRIB_ENUM, true);
|
||||
|
@ -1292,6 +1286,19 @@ public class PcodeDataTypeManager {
|
|||
if (tp instanceof Array) {
|
||||
return TYPE_ARRAY;
|
||||
}
|
||||
if (tp instanceof CharDataType) {
|
||||
return ((CharDataType) tp).isSigned() ? TYPE_INT : TYPE_UINT;
|
||||
}
|
||||
if (tp instanceof WideCharDataType || tp instanceof WideChar16DataType ||
|
||||
tp instanceof WideChar32DataType) {
|
||||
return TYPE_INT;
|
||||
}
|
||||
if (tp instanceof Enum) {
|
||||
return ((Enum) tp).isSigned() ? TYPE_INT : TYPE_UINT;
|
||||
}
|
||||
if (tp instanceof FunctionDefinition) {
|
||||
return TYPE_CODE;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue