diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java index 7f333da0b7..7fba5ce4e6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PreCommentFieldFactory.java @@ -274,7 +274,7 @@ public class PreCommentFieldFactory extends FieldFactory { else { Program p = data.getProgram(); data = p.getListing().getDefinedDataContaining(addr); - if (data == null || !data.isStructure()) { + if (data == null || !(data.isStructure() || data.isDynamic())) { return null; } Symbol s = p.getSymbolTable().getPrimarySymbol(data.getAddress()); @@ -297,11 +297,15 @@ public class PreCommentFieldFactory extends FieldFactory { private String[] buildFlexArrayComment(Data data, int levelsToIgnore, String label) { DataType dt = data.getBaseDataType(); - if (!(dt instanceof Structure)) { - return null; + + DataTypeComponent flexComponent = null; + if (dt instanceof Structure) { + flexComponent = ((Structure) dt).getFlexibleArrayComponent(); + } + else if (dt instanceof DynamicDataType) { + flexComponent = ((DynamicDataType) dt).getFlexibleArrayComponent(data); } - DataTypeComponent flexComponent = ((Structure) dt).getFlexibleArrayComponent(); if (flexComponent == null) { return null; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java index 3637f7be2f..2934ce2138 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/BitFieldListingDisplayTest.java @@ -15,17 +15,19 @@ */ package ghidra.program.database.data; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import org.junit.*; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; +import ghidra.docking.settings.Settings; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.*; +import ghidra.program.model.mem.MemBuffer; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.TestEnv; import ghidra.util.task.TaskMonitor; @@ -50,19 +52,14 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT program.getMemory().createInitializedBlock("m", addr(0x1000), 0x100, (byte) 0, TaskMonitor.DUMMY, false); - struct = createStructure("Test", 0); - struct.setPackingEnabled(true); - struct.addBitField(IntegerDataType.dataType, 3, "bf1", "Nuts"); - struct.addBitField(IntegerDataType.dataType, 24, "bf2", null); - struct.addBitField(IntegerDataType.dataType, 4, "bf3", null); - struct.addBitField(IntegerDataType.dataType, 12, "bf4", null); - struct.addBitField(IntegerDataType.dataType, 3, "bf4a", null); - struct.addBitField(IntegerDataType.dataType, 3, "bf5", null); - struct.addBitField(IntegerDataType.dataType, 3, "b6", null); - struct.add(new ByteDataType(), "field0", "Comment1"); - struct.add(new WordDataType(), null, "Comment2"); - struct.add(new DWordDataType(), "field3", null); - struct.add(new ByteDataType(), "field4", "Comment4"); + program.getMemory() + .setBytes(addr(0x1010), + new byte[] { 0x12, 0x34, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd, + (byte) 0xef, 0x12, 0x34, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd, + (byte) 0xef }); + + struct = + (Structure) getDataTypeManager().resolve(createStructure(getDataTypeManager()), null); program.getListing().createData(addr(0x1010), struct); env = new TestEnv(); @@ -70,6 +67,30 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT plugin = getPlugin(tool, CodeBrowserPlugin.class); } + private static Structure createStructure(DataTypeManager dtm) throws InvalidDataTypeException { + + EnumDataType e = new EnumDataType("MyEnum", 1); + e.add("A", 1); + e.add("B", 2); + e.add("C", 4); + + Structure struct = new StructureDataType("Test", 0, dtm); + struct.setPackingEnabled(true); + struct.addBitField(IntegerDataType.dataType, 3, "bf1", "Nuts"); + struct.addBitField(IntegerDataType.dataType, 24, "bf2", null); + struct.addBitField(IntegerDataType.dataType, 4, "bf3", null); + struct.addBitField(IntegerDataType.dataType, 12, "bf4", null); + struct.addBitField(e, 3, "bf4a", null); + struct.addBitField(e, 3, "bf5", null); + struct.addBitField(e, 3, "b6", null); + struct.add(new ByteDataType(), "field0", "Comment1"); + struct.add(new WordDataType(), null, "Comment2"); + struct.add(new DWordDataType(), "field3", null); + struct.add(new ByteDataType(), "field4", "Comment4"); +// struct.setFlexibleArrayComponent(CharDataType.dataType, "flex", "Flex Comment"); + return struct; + } + private Address addr(long value) { return space.getAddress(value); } @@ -80,10 +101,6 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT env.dispose(); } - protected Structure createStructure(String name, int length) { - return (Structure) getDataTypeManager().resolve(new StructureDataType(name, length), null); - } - protected DataTypeManager getDataTypeManager() { return program.getDataTypeManager(); } @@ -97,20 +114,44 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT } @Test - public void testBitField() throws Exception { + public void testStructureBitFields() throws Exception { openStructure(addr(0x1010)); assertMnemonic("Test", addr(0x1010), 0); - assertMnemonic("int:3", addr(0x1010), 1); - assertMnemonic("int:24", addr(0x1010), 2); - assertMnemonic("int:4", addr(0x1013), 0); - assertMnemonic("int:12", addr(0x1014), 0); - assertMnemonic("int:3", addr(0x1015), 0); - assertMnemonic("int:3", addr(0x1015), 1); - assertMnemonic("int:3", addr(0x1016), 0); - assertMnemonic("db", addr(0x1017), 0); - assertMnemonic("dw", addr(0x1018), 0); + assertComponents(); + } - System.out.println("wait"); + @Test + public void testDynamicBitFields() throws Exception { + + program.getListing().clearCodeUnits(addr(0x1010), addr(0x1010), false); + program.getListing().createData(addr(0x1010), new BitfieldDType(getDataTypeManager())); + + waitForSwing(); + + openStructure(addr(0x1010)); + assertMnemonic("BitfieldDType", addr(0x1010), 0); + assertComponents(); + } + + private void assertComponents() { + assertMnemonic("int:3", addr(0x1010), 1); + assertOperand("0h", addr(0x1010), 1); + assertMnemonic("int:24", addr(0x1010), 2); + assertOperand("91A22Bh", addr(0x1010), 2); + assertMnemonic("int:4", addr(0x1013), 0); + assertOperand("3h", addr(0x1013), 0); + assertMnemonic("int:12", addr(0x1014), 0); + assertOperand("89Ah", addr(0x1014), 0); + assertMnemonic("MyEnum:3", addr(0x1015), 0); + assertOperand("A | C", addr(0x1015), 0); + assertMnemonic("MyEnum:3", addr(0x1016), 0); + assertOperand("B | C", addr(0x1016), 0); + assertMnemonic("MyEnum:3", addr(0x1016), 1); + assertOperand("A | B", addr(0x1016), 1); + assertMnemonic("db", addr(0x1017), 0); + assertOperand("EFh", addr(0x1017), 0); + assertMnemonic("dw", addr(0x1018), 0); + assertOperand("1234h", addr(0x1018), 0); } private void assertMnemonic(String expectedValue, Address addr, int occurrence) { @@ -118,6 +159,11 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT assertEquals(expectedValue, plugin.getCurrentFieldText()); } + private void assertOperand(String expectedValue, Address addr, int occurrence) { + plugin.goToField(addr, "Operands", occurrence, 0, 0); + assertEquals(expectedValue, plugin.getCurrentFieldText()); + } + private void openStructure(Address address) { // open the structure plugin.goToField(address, "+", 0, 0); @@ -126,4 +172,57 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT } + public static class BitfieldDType extends DynamicDataType { + + public BitfieldDType() { + this(null); + } + + public BitfieldDType(DataTypeManager dtm) { + super("BitfieldDType", dtm); + } + + @Override + public DataType clone(DataTypeManager dtm) { + if (dtm == getDataTypeManager()) { + return this; + } + return new BitfieldDType(dtm); + } + + @Override + public String getDescription() { + return "BitfieldDType "; + } + + @Override + public String getRepresentation(MemBuffer buf, Settings settings, int length) { + return ""; + } + + @Override + public Object getValue(MemBuffer buf, Settings settings, int length) { + return null; + } + + @Override + protected DataTypeComponent[] getAllComponents(MemBuffer buf) { + try { + Structure struct = createStructure(dataMgr); + DataTypeComponent[] components = struct.getComponents(); + if (struct.hasFlexibleArrayComponent()) { + DataTypeComponent[] newArray = new DataTypeComponent[components.length + 1]; + System.arraycopy(components, 0, newArray, 0, components.length); + newArray[components.length] = struct.getFlexibleArrayComponent(); + components = newArray; + } + return components; + } + catch (InvalidDataTypeException e) { + return null; // test should fail as a result + } + } + + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java index 45cc848b28..6b5fa1654f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java @@ -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) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java index e5a32d2ce2..da27e2690e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java @@ -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) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java index 8ea854b2fd..85895556a0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java @@ -499,7 +499,7 @@ class EnumDB extends DataTypeDB implements Enum { private List getBitGroups() { if (bitGroups == null) { - bitGroups = EnumValuePartitioner.partition(getValues()); + bitGroups = EnumValuePartitioner.partition(getValues(), getLength()); } return bitGroups; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java index c835bcb590..59e9091b7b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/BitFieldDataType.java @@ -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 "??"; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java index 5ebd62c191..a5ff438a43 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DynamicDataType.java @@ -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; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java index 6a41b813b2..afbf6578dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java @@ -318,7 +318,7 @@ public class EnumDataType extends GenericDataType implements Enum { private List getBitGroups() { if (bitGroups == null) { - bitGroups = EnumValuePartitioner.partition(getValues()); + bitGroups = EnumValuePartitioner.partition(getValues(), getLength()); } return bitGroups; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumValuePartitioner.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumValuePartitioner.java index 06069e6208..afb9e831a9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumValuePartitioner.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumValuePartitioner.java @@ -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 partition(long[] values) { + public static List partition(long[] values, int size) { List 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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java index 33cfb26aec..a1d4ad8102 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/BitFieldDataTypeTest.java @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java index 2ee7d8435b..3377f4abb5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/EnumValuePartitionerTest.java @@ -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 list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8 }); + List list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8 }, 1); assertEquals(5, list.size()); } @Test public void testAllOverlappingValues() { - List list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8, 15 }); + List 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 list = EnumValuePartitioner.partition(new long[] { 1, 2, 4, 8, 6 }); + List 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());