Merge remote-tracking branch 'origin/GP-4031_X86SystemVABI'

This commit is contained in:
Ryan Kurtz 2024-04-01 11:22:00 -04:00
commit f5d956d5e6
20 changed files with 1668 additions and 185 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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