GP-958 corrected various issues related to use of enum bitfields and

flex-array with DynamicDataType
This commit is contained in:
ghidra1 2021-05-18 11:02:22 -04:00
parent e21149816c
commit 0a5edb5251
11 changed files with 266 additions and 93 deletions

View file

@ -330,7 +330,9 @@ public class PseudoData extends PseudoCodeUnit implements Data {
Structure struct = (Structure) baseDataType;
DataTypeComponent dtc = struct.getComponentAt(offset);
// Logic handles overlapping bit-fields
while (dtc != null && offset <= (dtc.getOffset() + dtc.getLength() - 1)) {
// Include if offset is contained within bounds of component
while (dtc != null && (offset >= dtc.getOffset()) &&
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
int ordinal = dtc.getOrdinal();
list.add(getComponent(ordinal++));
dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null;
@ -339,8 +341,13 @@ public class PseudoData extends PseudoCodeUnit implements Data {
else if (baseDataType instanceof DynamicDataType) {
DynamicDataType ddt = (DynamicDataType) baseDataType;
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
if (dtc != null) {
list.add(getComponent(dtc.getOrdinal()));
// Logic handles overlapping bit-fields
// Include if offset is contained within bounds of component
while (dtc != null && (offset >= dtc.getOffset()) &&
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
int ordinal = dtc.getOrdinal();
list.add(getComponent(ordinal++));
dtc = ordinal < ddt.getNumComponents(this) ? ddt.getComponent(ordinal, this) : null;
}
}
else if (baseDataType instanceof Union) {

View file

@ -614,7 +614,7 @@ class DataDB extends CodeUnitDB implements Data {
Structure struct = (Structure) baseDataType;
DataTypeComponent dtc = struct.getComponentAt(offset);
// Logic handles overlapping bit-fields
// Include if offset is contains within bounds of component
// Include if offset is contained within bounds of component
while (dtc != null && (offset >= dtc.getOffset()) &&
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
int ordinal = dtc.getOrdinal();
@ -625,8 +625,14 @@ class DataDB extends CodeUnitDB implements Data {
else if (baseDataType instanceof DynamicDataType) {
DynamicDataType ddt = (DynamicDataType) baseDataType;
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
if (dtc != null) {
list.add(getComponent(dtc.getOrdinal()));
// Logic handles overlapping bit-fields
// Include if offset is contained within bounds of component
while (dtc != null && (offset >= dtc.getOffset()) &&
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
int ordinal = dtc.getOrdinal();
list.add(getComponent(ordinal++));
dtc = ordinal < ddt.getNumComponents(this) ? ddt.getComponent(ordinal, this)
: null;
}
}
else if (baseDataType instanceof Union) {

View file

@ -499,7 +499,7 @@ class EnumDB extends DataTypeDB implements Enum {
private List<BitGroup> getBitGroups() {
if (bitGroups == null) {
bitGroups = EnumValuePartitioner.partition(getValues());
bitGroups = EnumValuePartitioner.partition(getValues(), getLength());
}
return bitGroups;
}

View file

@ -355,18 +355,19 @@ public class BitFieldDataType extends AbstractDataType {
if (effectiveBitSize == 0) {
return new Scalar(0, 0);
}
BigInteger big = getBigIntegerValue(buf, settings);
AbstractIntegerDataType primitiveBaseDataType = getPrimitiveBaseDataType();
boolean isSigned = primitiveBaseDataType.isSigned();
BigInteger big = getBigIntegerValue(buf, isSigned, settings);
if (big == null) {
return null;
}
if (effectiveBitSize <= 64) {
return new Scalar(effectiveBitSize, big.longValue(),
getPrimitiveBaseDataType().isSigned());
return new Scalar(effectiveBitSize, big.longValue(), isSigned);
}
return big;
}
private BigInteger getBigIntegerValue(MemBuffer buf, Settings settings) {
private BigInteger getBigIntegerValue(MemBuffer buf, boolean isSigned, Settings settings) {
if (effectiveBitSize == 0) {
return BigInteger.ZERO;
}
@ -385,7 +386,7 @@ public class BitFieldDataType extends AbstractDataType {
BigInteger pow = BigInteger.valueOf(2).pow(effectiveBitSize);
BigInteger mask = pow.subtract(BigInteger.ONE);
big = big.shiftRight(bitOffset).and(mask);
if (big.testBit(effectiveBitSize - 1)) {
if (isSigned && big.testBit(effectiveBitSize - 1)) {
big = big.subtract(pow);
}
return big;
@ -406,7 +407,9 @@ public class BitFieldDataType extends AbstractDataType {
if (bitSize == 0) {
return "";
}
BigInteger big = getBigIntegerValue(buf, settings);
AbstractIntegerDataType primitiveBaseDataType = getPrimitiveBaseDataType();
boolean isSigned = primitiveBaseDataType.isSigned();
BigInteger big = getBigIntegerValue(buf, isSigned, settings);
if (big == null) {
return "??";
}

View file

@ -18,6 +18,7 @@ package ghidra.program.model.data;
import ghidra.docking.settings.SettingsImpl;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg;
import ghidra.util.datastruct.SoftCacheMap;
/**
@ -52,10 +53,8 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
/**
* Gets the number of component data types in this data type.
* @param buf a membuffer to be used by dataTypes that change depending on
* their data context. A null value is acceptable to indicate that a memory
* context is not available. DataTypes that need a context will return -1
* if the context is null.
* @param buf a memory buffer to be used by dataTypes that change depending on
* their data context.
* @return the number of components that make up this data prototype
* - if this is an Array, return the number of elements in the array.
* - if this datatype is a subcomponent of another datatype and it
@ -63,10 +62,12 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
*/
public final int getNumComponents(MemBuffer buf) {
DataTypeComponent[] comps = getComps(buf);
if (comps != null) {
return comps.length;
if (comps == null || comps.length == 0) {
return -1;
}
return -1;
DataTypeComponent last = comps[comps.length - 1];
return (last != null && last.isFlexibleArrayComponent()) ? (comps.length - 1)
: comps.length;
}
protected DataTypeComponent[] getComps(MemBuffer buf) {
@ -78,6 +79,15 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
// data-type not valid at buf location
return null;
}
for (int i = 0; i < comps.length - 1; i++) {
// TODO: should we verify ordinals
if (comps[i] != null && comps[i].isFlexibleArrayComponent()) {
// Only the last component may be a flexible array
Msg.error(this,
getClass().getName() + " produced invalid flexible array component");
return null;
}
}
map.put(addr, comps);
}
return comps;
@ -85,67 +95,69 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
/**
* Returns the immediate n'th component of this data type.
* @param index the components index (zero based).
* @param buf a membuffer to be used by dataTypes that change depending on
* their data context. A null value is acceptable to indicate that a memory
* context is not available. DataTypes that need a context will return -1
* if the context is null.
* @param ordinal the components ordinal (zero based).
* @param buf a memory buffer to be used by dataTypes that change depending on
* their data context.
* @return the component data type or null if there is no component at the
* indicated index.
* @throws ArrayIndexOutOfBoundsException if index is out of bounds
*/
public final DataTypeComponent getComponent(int index, MemBuffer buf) {
public final DataTypeComponent getComponent(int ordinal, MemBuffer buf) {
DataTypeComponent[] comps = getComps(buf);
if (comps != null) {
return comps[index];
DataTypeComponent dtc = comps[ordinal];
if (dtc != null && !dtc.isFlexibleArrayComponent()) {
return dtc;
}
}
return null;
}
/**
* Returns an array of DataTypes that make up this data type.
* Could return null if there are no subcomponents.
* If this is an Array, then only one element will be returned
* which is the Data Prototype for the elements in the array.
* Will return null if this is a subcomponent that doesn't fit in it's
* alloted space.
* @param buf a membuffer to be used by dataTypes that change depending on
* their data context. A null value is acceptable to indicate that a memory
* context is not available. DataTypes that need a context will return -1
* if the context is null.
* Returns an array of components that make up this data type.
* Could return null if there are no subcomponents. The last component
* in the array may be a flexible array component.
* @param buf a memory buffer to be used by dataTypes that change depending on
* their data context.
* @return datatype component array or null. Last component may be a flexible array component.
*/
public final DataTypeComponent[] getComponents(MemBuffer buf) {
return getComps(buf);
}
/**
* Returns the component containing the byte at the given offset
* Returns the first component containing the byte at the given offset
* @param offset the offset into the dataType
* @param buf the memoryBuffer containing the bytes.
* @return the component containing the byte at the given offset
* @param buf the memory buffer containing the bytes.
* @return the component containing the byte at the given offset or null if no
* component defined.
*/
public final DataTypeComponent getComponentAt(int offset, MemBuffer buf) {
DataTypeComponent[] comps = getComps(buf);
if (comps == null) {
return null;
}
for (int i = 0; i < comps.length; i++) {
if (comps[i].getOffset() > offset) {
return comps[i - 1];
// TODO: could use binary search (watchout for bitfields at same offset)
for (DataTypeComponent comp : comps) {
if (comp == null) {
continue;
}
if (comp.isFlexibleArrayComponent()) {
return null;
}
if (offset >= comp.getOffset() &&
(offset <= (comp.getOffset() + comp.getLength() - 1))) {
return comp;
}
}
int index = comps.length - 1;
if (index < 0) {
return null;
}
return comps[index];
return null;
}
/**
* Get all dynamic components associated with the specified MemBuffer
* @param buf memory buffer positioned at start of data type instance
* @return all components or null if memory data is not valid for this
* data type.
* data type. Last component may be a flexible array component.
*/
protected abstract DataTypeComponent[] getAllComponents(MemBuffer buf);
@ -156,8 +168,8 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
return -1;
}
DataTypeComponent last = comps[comps.length - 1];
if (last == null) {
return -1;
if (last != null && last.isFlexibleArrayComponent()) {
return last.getOffset();
}
return last.getOffset() + last.getLength();
}
@ -175,4 +187,20 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
public DataType getReplacementBaseType() {
return ByteDataType.dataType;
}
/**
* Get trailing flexible array component.
* @return flexible array component or null if not applicable or valie.
*/
public final DataTypeComponent getFlexibleArrayComponent(MemBuffer buf) {
DataTypeComponent[] comps = getComps(buf);
if (comps == null || comps.length == 0) {
return null;
}
DataTypeComponent last = comps[comps.length - 1];
if (last != null && last.isFlexibleArrayComponent()) {
return last;
}
return null;
}
}

View file

@ -318,7 +318,7 @@ public class EnumDataType extends GenericDataType implements Enum {
private List<BitGroup> getBitGroups() {
if (bitGroups == null) {
bitGroups = EnumValuePartitioner.partition(getValues());
bitGroups = EnumValuePartitioner.partition(getValues(), getLength());
}
return bitGroups;
}

View file

@ -38,9 +38,10 @@ public class EnumValuePartitioner {
/**
* Partition the given values into a list of non-intersecting BitGroups.
* @param values the values to be partitioned.
* @param size size of enum value in bytes
* @return a list of BitGroups with non-intersecting bits.
*/
public static List<BitGroup> partition(long[] values) {
public static List<BitGroup> partition(long[] values, int size) {
List<BitGroup> list = new LinkedList<>();
long totalMask = 0;
for (long value : values) {
@ -49,7 +50,8 @@ public class EnumValuePartitioner {
merge(list, bitGroup);
}
// now create a BitGroup for all bits not accounted for
list.add(new BitGroup(~totalMask));
long enumMask = ~(-1 << (size * 8));
list.add(new BitGroup(~totalMask & enumMask));
return list;
}

View file

@ -67,6 +67,16 @@ public class BitFieldDataTypeTest extends AbstractGTest {
BitFieldDataType bf = new BitFieldDataType(typeDef, 1);
assertEquals(typeDef, bf.getBaseDataType());
assertEquals(typeDef, bf.clone(null).getBaseDataType());
EnumDataType enumDt = new EnumDataType("MyEnum", 1);
enumDt.add("A", 0);
enumDt.add("B", 1);
enumDt.add("C", 2);
enumDt.add("D", 4);
bf = new BitFieldDataType(enumDt, 4);
assertEquals(enumDt, bf.getBaseDataType());
assertEquals(enumDt, bf.clone(null).getBaseDataType());
}
@Test
@ -157,6 +167,21 @@ public class BitFieldDataTypeTest extends AbstractGTest {
assertEquals("5", getDecimalRepresentation(unsignedBitField(4, 0), 0x55));
}
@Test
public void testEnumRepresentation() throws Exception {
EnumDataType enumDt = new EnumDataType("MyEnum", 1);
enumDt.add("A", 1);
enumDt.add("B", 2);
enumDt.add("C", 4);
enumDt.add("D", 8);
BitFieldDataType bf = new BitFieldDataType(enumDt, 4);
assertEquals(enumDt, bf.getBaseDataType());
assertEquals(enumDt, bf.clone(null).getBaseDataType());
assertEquals("A | B | C | D", getRepresentation(bf, 0x0f));
}
private String getRepresentation(BitFieldDataType bitField, int... unsignedBytes)
throws Exception {
MemBuffer membuf = membuf(unsignedBytes);

View file

@ -15,8 +15,7 @@
*/
package ghidra.program.model.data;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
@ -29,13 +28,13 @@ public class EnumValuePartitionerTest extends AbstractGTest {
@Test
public void testDisjointValues() {
List<BitGroup> list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8 });
List<BitGroup> list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8 }, 1);
assertEquals(5, list.size());
}
@Test
public void testAllOverlappingValues() {
List<BitGroup> list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8, 15 });
List<BitGroup> list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8, 15 }, 1);
assertEquals(2, list.size());
BitGroup group = list.get(0);
assertEquals(15, group.getMask());
@ -50,7 +49,7 @@ public class EnumValuePartitionerTest extends AbstractGTest {
@Test
public void testSomeOverlappingValues() {
List<BitGroup> list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8, 6 });
List<BitGroup> list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8, 6 }, 1);
assertEquals(4, list.size());
assertEquals(1, list.get(0).getMask());
assertEquals(8, list.get(1).getMask());