mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-958 corrected various issues related to use of enum bitfields and
flex-array with DynamicDataType
This commit is contained in:
parent
e21149816c
commit
0a5edb5251
11 changed files with 266 additions and 93 deletions
|
@ -274,7 +274,7 @@ public class PreCommentFieldFactory extends FieldFactory {
|
||||||
else {
|
else {
|
||||||
Program p = data.getProgram();
|
Program p = data.getProgram();
|
||||||
data = p.getListing().getDefinedDataContaining(addr);
|
data = p.getListing().getDefinedDataContaining(addr);
|
||||||
if (data == null || !data.isStructure()) {
|
if (data == null || !(data.isStructure() || data.isDynamic())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Symbol s = p.getSymbolTable().getPrimarySymbol(data.getAddress());
|
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) {
|
private String[] buildFlexArrayComment(Data data, int levelsToIgnore, String label) {
|
||||||
|
|
||||||
DataType dt = data.getBaseDataType();
|
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) {
|
if (flexComponent == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,19 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.database.data;
|
package ghidra.program.database.data;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -50,19 +52,14 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT
|
||||||
program.getMemory().createInitializedBlock("m", addr(0x1000), 0x100, (byte) 0,
|
program.getMemory().createInitializedBlock("m", addr(0x1000), 0x100, (byte) 0,
|
||||||
TaskMonitor.DUMMY, false);
|
TaskMonitor.DUMMY, false);
|
||||||
|
|
||||||
struct = createStructure("Test", 0);
|
program.getMemory()
|
||||||
struct.setPackingEnabled(true);
|
.setBytes(addr(0x1010),
|
||||||
struct.addBitField(IntegerDataType.dataType, 3, "bf1", "Nuts");
|
new byte[] { 0x12, 0x34, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd,
|
||||||
struct.addBitField(IntegerDataType.dataType, 24, "bf2", null);
|
(byte) 0xef, 0x12, 0x34, 0x45, 0x67, (byte) 0x89, (byte) 0xab, (byte) 0xcd,
|
||||||
struct.addBitField(IntegerDataType.dataType, 4, "bf3", null);
|
(byte) 0xef });
|
||||||
struct.addBitField(IntegerDataType.dataType, 12, "bf4", null);
|
|
||||||
struct.addBitField(IntegerDataType.dataType, 3, "bf4a", null);
|
struct =
|
||||||
struct.addBitField(IntegerDataType.dataType, 3, "bf5", null);
|
(Structure) getDataTypeManager().resolve(createStructure(getDataTypeManager()), 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.getListing().createData(addr(0x1010), struct);
|
program.getListing().createData(addr(0x1010), struct);
|
||||||
env = new TestEnv();
|
env = new TestEnv();
|
||||||
|
@ -70,6 +67,30 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT
|
||||||
plugin = getPlugin(tool, CodeBrowserPlugin.class);
|
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) {
|
private Address addr(long value) {
|
||||||
return space.getAddress(value);
|
return space.getAddress(value);
|
||||||
}
|
}
|
||||||
|
@ -80,10 +101,6 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT
|
||||||
env.dispose();
|
env.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Structure createStructure(String name, int length) {
|
|
||||||
return (Structure) getDataTypeManager().resolve(new StructureDataType(name, length), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DataTypeManager getDataTypeManager() {
|
protected DataTypeManager getDataTypeManager() {
|
||||||
return program.getDataTypeManager();
|
return program.getDataTypeManager();
|
||||||
}
|
}
|
||||||
|
@ -97,20 +114,44 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBitField() throws Exception {
|
public void testStructureBitFields() throws Exception {
|
||||||
openStructure(addr(0x1010));
|
openStructure(addr(0x1010));
|
||||||
assertMnemonic("Test", addr(0x1010), 0);
|
assertMnemonic("Test", addr(0x1010), 0);
|
||||||
assertMnemonic("int:3", addr(0x1010), 1);
|
assertComponents();
|
||||||
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);
|
|
||||||
|
|
||||||
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) {
|
private void assertMnemonic(String expectedValue, Address addr, int occurrence) {
|
||||||
|
@ -118,6 +159,11 @@ public class BitFieldListingDisplayTest extends AbstractGhidraHeadedIntegrationT
|
||||||
assertEquals(expectedValue, plugin.getCurrentFieldText());
|
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) {
|
private void openStructure(Address address) {
|
||||||
// open the structure
|
// open the structure
|
||||||
plugin.goToField(address, "+", 0, 0);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,7 +330,9 @@ public class PseudoData extends PseudoCodeUnit implements Data {
|
||||||
Structure struct = (Structure) baseDataType;
|
Structure struct = (Structure) baseDataType;
|
||||||
DataTypeComponent dtc = struct.getComponentAt(offset);
|
DataTypeComponent dtc = struct.getComponentAt(offset);
|
||||||
// Logic handles overlapping bit-fields
|
// 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();
|
int ordinal = dtc.getOrdinal();
|
||||||
list.add(getComponent(ordinal++));
|
list.add(getComponent(ordinal++));
|
||||||
dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null;
|
dtc = ordinal < struct.getNumComponents() ? struct.getComponent(ordinal) : null;
|
||||||
|
@ -339,8 +341,13 @@ public class PseudoData extends PseudoCodeUnit implements Data {
|
||||||
else if (baseDataType instanceof DynamicDataType) {
|
else if (baseDataType instanceof DynamicDataType) {
|
||||||
DynamicDataType ddt = (DynamicDataType) baseDataType;
|
DynamicDataType ddt = (DynamicDataType) baseDataType;
|
||||||
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
|
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
|
||||||
if (dtc != null) {
|
// Logic handles overlapping bit-fields
|
||||||
list.add(getComponent(dtc.getOrdinal()));
|
// 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) {
|
else if (baseDataType instanceof Union) {
|
||||||
|
|
|
@ -614,7 +614,7 @@ class DataDB extends CodeUnitDB implements Data {
|
||||||
Structure struct = (Structure) baseDataType;
|
Structure struct = (Structure) baseDataType;
|
||||||
DataTypeComponent dtc = struct.getComponentAt(offset);
|
DataTypeComponent dtc = struct.getComponentAt(offset);
|
||||||
// Logic handles overlapping bit-fields
|
// 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()) &&
|
while (dtc != null && (offset >= dtc.getOffset()) &&
|
||||||
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
|
(offset <= (dtc.getOffset() + dtc.getLength() - 1))) {
|
||||||
int ordinal = dtc.getOrdinal();
|
int ordinal = dtc.getOrdinal();
|
||||||
|
@ -625,8 +625,14 @@ class DataDB extends CodeUnitDB implements Data {
|
||||||
else if (baseDataType instanceof DynamicDataType) {
|
else if (baseDataType instanceof DynamicDataType) {
|
||||||
DynamicDataType ddt = (DynamicDataType) baseDataType;
|
DynamicDataType ddt = (DynamicDataType) baseDataType;
|
||||||
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
|
DataTypeComponent dtc = ddt.getComponentAt(offset, this);
|
||||||
if (dtc != null) {
|
// Logic handles overlapping bit-fields
|
||||||
list.add(getComponent(dtc.getOrdinal()));
|
// 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) {
|
else if (baseDataType instanceof Union) {
|
||||||
|
|
|
@ -499,7 +499,7 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||||
|
|
||||||
private List<BitGroup> getBitGroups() {
|
private List<BitGroup> getBitGroups() {
|
||||||
if (bitGroups == null) {
|
if (bitGroups == null) {
|
||||||
bitGroups = EnumValuePartitioner.partition(getValues());
|
bitGroups = EnumValuePartitioner.partition(getValues(), getLength());
|
||||||
}
|
}
|
||||||
return bitGroups;
|
return bitGroups;
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,18 +355,19 @@ public class BitFieldDataType extends AbstractDataType {
|
||||||
if (effectiveBitSize == 0) {
|
if (effectiveBitSize == 0) {
|
||||||
return new Scalar(0, 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) {
|
if (big == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (effectiveBitSize <= 64) {
|
if (effectiveBitSize <= 64) {
|
||||||
return new Scalar(effectiveBitSize, big.longValue(),
|
return new Scalar(effectiveBitSize, big.longValue(), isSigned);
|
||||||
getPrimitiveBaseDataType().isSigned());
|
|
||||||
}
|
}
|
||||||
return big;
|
return big;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigInteger getBigIntegerValue(MemBuffer buf, Settings settings) {
|
private BigInteger getBigIntegerValue(MemBuffer buf, boolean isSigned, Settings settings) {
|
||||||
if (effectiveBitSize == 0) {
|
if (effectiveBitSize == 0) {
|
||||||
return BigInteger.ZERO;
|
return BigInteger.ZERO;
|
||||||
}
|
}
|
||||||
|
@ -385,7 +386,7 @@ public class BitFieldDataType extends AbstractDataType {
|
||||||
BigInteger pow = BigInteger.valueOf(2).pow(effectiveBitSize);
|
BigInteger pow = BigInteger.valueOf(2).pow(effectiveBitSize);
|
||||||
BigInteger mask = pow.subtract(BigInteger.ONE);
|
BigInteger mask = pow.subtract(BigInteger.ONE);
|
||||||
big = big.shiftRight(bitOffset).and(mask);
|
big = big.shiftRight(bitOffset).and(mask);
|
||||||
if (big.testBit(effectiveBitSize - 1)) {
|
if (isSigned && big.testBit(effectiveBitSize - 1)) {
|
||||||
big = big.subtract(pow);
|
big = big.subtract(pow);
|
||||||
}
|
}
|
||||||
return big;
|
return big;
|
||||||
|
@ -406,7 +407,9 @@ public class BitFieldDataType extends AbstractDataType {
|
||||||
if (bitSize == 0) {
|
if (bitSize == 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
BigInteger big = getBigIntegerValue(buf, settings);
|
AbstractIntegerDataType primitiveBaseDataType = getPrimitiveBaseDataType();
|
||||||
|
boolean isSigned = primitiveBaseDataType.isSigned();
|
||||||
|
BigInteger big = getBigIntegerValue(buf, isSigned, settings);
|
||||||
if (big == null) {
|
if (big == null) {
|
||||||
return "??";
|
return "??";
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.program.model.data;
|
||||||
import ghidra.docking.settings.SettingsImpl;
|
import ghidra.docking.settings.SettingsImpl;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.datastruct.SoftCacheMap;
|
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.
|
* Gets the number of component data types in this data type.
|
||||||
* @param buf a membuffer to be used by dataTypes that change depending on
|
* @param buf a memory buffer to be used by dataTypes that change depending on
|
||||||
* their data context. A null value is acceptable to indicate that a memory
|
* their data context.
|
||||||
* context is not available. DataTypes that need a context will return -1
|
|
||||||
* if the context is null.
|
|
||||||
* @return the number of components that make up this data prototype
|
* @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 is an Array, return the number of elements in the array.
|
||||||
* - if this datatype is a subcomponent of another datatype and it
|
* - if this datatype is a subcomponent of another datatype and it
|
||||||
|
@ -63,11 +62,13 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
*/
|
*/
|
||||||
public final int getNumComponents(MemBuffer buf) {
|
public final int getNumComponents(MemBuffer buf) {
|
||||||
DataTypeComponent[] comps = getComps(buf);
|
DataTypeComponent[] comps = getComps(buf);
|
||||||
if (comps != null) {
|
if (comps == null || comps.length == 0) {
|
||||||
return comps.length;
|
|
||||||
}
|
|
||||||
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) {
|
protected DataTypeComponent[] getComps(MemBuffer buf) {
|
||||||
Address addr = buf.getAddress();
|
Address addr = buf.getAddress();
|
||||||
|
@ -78,6 +79,15 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
// data-type not valid at buf location
|
// data-type not valid at buf location
|
||||||
return null;
|
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);
|
map.put(addr, comps);
|
||||||
}
|
}
|
||||||
return 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.
|
* Returns the immediate n'th component of this data type.
|
||||||
* @param index the components index (zero based).
|
* @param ordinal the components ordinal (zero based).
|
||||||
* @param buf a membuffer to be used by dataTypes that change depending on
|
* @param buf a memory buffer to be used by dataTypes that change depending on
|
||||||
* their data context. A null value is acceptable to indicate that a memory
|
* their data context.
|
||||||
* context is not available. DataTypes that need a context will return -1
|
|
||||||
* if the context is null.
|
|
||||||
* @return the component data type or null if there is no component at the
|
* @return the component data type or null if there is no component at the
|
||||||
* indicated index.
|
* 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);
|
DataTypeComponent[] comps = getComps(buf);
|
||||||
if (comps != null) {
|
if (comps != null) {
|
||||||
return comps[index];
|
DataTypeComponent dtc = comps[ordinal];
|
||||||
|
if (dtc != null && !dtc.isFlexibleArrayComponent()) {
|
||||||
|
return dtc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of DataTypes that make up this data type.
|
* Returns an array of components that make up this data type.
|
||||||
* Could return null if there are no subcomponents.
|
* Could return null if there are no subcomponents. The last component
|
||||||
* If this is an Array, then only one element will be returned
|
* in the array may be a flexible array component.
|
||||||
* which is the Data Prototype for the elements in the array.
|
* @param buf a memory buffer to be used by dataTypes that change depending on
|
||||||
* Will return null if this is a subcomponent that doesn't fit in it's
|
* their data context.
|
||||||
* alloted space.
|
* @return datatype component array or null. Last component may be a flexible array component.
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
public final DataTypeComponent[] getComponents(MemBuffer buf) {
|
public final DataTypeComponent[] getComponents(MemBuffer buf) {
|
||||||
return getComps(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 offset the offset into the dataType
|
||||||
* @param buf the memoryBuffer containing the bytes.
|
* @param buf the memory buffer containing the bytes.
|
||||||
* @return the component containing the byte at the given offset
|
* @return the component containing the byte at the given offset or null if no
|
||||||
|
* component defined.
|
||||||
*/
|
*/
|
||||||
public final DataTypeComponent getComponentAt(int offset, MemBuffer buf) {
|
public final DataTypeComponent getComponentAt(int offset, MemBuffer buf) {
|
||||||
DataTypeComponent[] comps = getComps(buf);
|
DataTypeComponent[] comps = getComps(buf);
|
||||||
if (comps == null) {
|
if (comps == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < comps.length; i++) {
|
// TODO: could use binary search (watchout for bitfields at same offset)
|
||||||
if (comps[i].getOffset() > offset) {
|
for (DataTypeComponent comp : comps) {
|
||||||
return comps[i - 1];
|
if (comp == null) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
if (comp.isFlexibleArrayComponent()) {
|
||||||
int index = comps.length - 1;
|
|
||||||
if (index < 0) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return comps[index];
|
if (offset >= comp.getOffset() &&
|
||||||
|
(offset <= (comp.getOffset() + comp.getLength() - 1))) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all dynamic components associated with the specified MemBuffer
|
* Get all dynamic components associated with the specified MemBuffer
|
||||||
* @param buf memory buffer positioned at start of data type instance
|
* @param buf memory buffer positioned at start of data type instance
|
||||||
* @return all components or null if memory data is not valid for this
|
* @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);
|
protected abstract DataTypeComponent[] getAllComponents(MemBuffer buf);
|
||||||
|
|
||||||
|
@ -156,8 +168,8 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
DataTypeComponent last = comps[comps.length - 1];
|
DataTypeComponent last = comps[comps.length - 1];
|
||||||
if (last == null) {
|
if (last != null && last.isFlexibleArrayComponent()) {
|
||||||
return -1;
|
return last.getOffset();
|
||||||
}
|
}
|
||||||
return last.getOffset() + last.getLength();
|
return last.getOffset() + last.getLength();
|
||||||
}
|
}
|
||||||
|
@ -175,4 +187,20 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
|
||||||
public DataType getReplacementBaseType() {
|
public DataType getReplacementBaseType() {
|
||||||
return ByteDataType.dataType;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,7 +318,7 @@ public class EnumDataType extends GenericDataType implements Enum {
|
||||||
|
|
||||||
private List<BitGroup> getBitGroups() {
|
private List<BitGroup> getBitGroups() {
|
||||||
if (bitGroups == null) {
|
if (bitGroups == null) {
|
||||||
bitGroups = EnumValuePartitioner.partition(getValues());
|
bitGroups = EnumValuePartitioner.partition(getValues(), getLength());
|
||||||
}
|
}
|
||||||
return bitGroups;
|
return bitGroups;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,10 @@ public class EnumValuePartitioner {
|
||||||
/**
|
/**
|
||||||
* Partition the given values into a list of non-intersecting BitGroups.
|
* Partition the given values into a list of non-intersecting BitGroups.
|
||||||
* @param values the values to be partitioned.
|
* @param values the values to be partitioned.
|
||||||
|
* @param size size of enum value in bytes
|
||||||
* @return a list of BitGroups with non-intersecting bits.
|
* @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<>();
|
List<BitGroup> list = new LinkedList<>();
|
||||||
long totalMask = 0;
|
long totalMask = 0;
|
||||||
for (long value : values) {
|
for (long value : values) {
|
||||||
|
@ -49,7 +50,8 @@ public class EnumValuePartitioner {
|
||||||
merge(list, bitGroup);
|
merge(list, bitGroup);
|
||||||
}
|
}
|
||||||
// now create a BitGroup for all bits not accounted for
|
// 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;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,16 @@ public class BitFieldDataTypeTest extends AbstractGTest {
|
||||||
BitFieldDataType bf = new BitFieldDataType(typeDef, 1);
|
BitFieldDataType bf = new BitFieldDataType(typeDef, 1);
|
||||||
assertEquals(typeDef, bf.getBaseDataType());
|
assertEquals(typeDef, bf.getBaseDataType());
|
||||||
assertEquals(typeDef, bf.clone(null).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
|
@Test
|
||||||
|
@ -157,6 +167,21 @@ public class BitFieldDataTypeTest extends AbstractGTest {
|
||||||
assertEquals("5", getDecimalRepresentation(unsignedBitField(4, 0), 0x55));
|
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)
|
private String getRepresentation(BitFieldDataType bitField, int... unsignedBytes)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
MemBuffer membuf = membuf(unsignedBytes);
|
MemBuffer membuf = membuf(unsignedBytes);
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -29,13 +28,13 @@ public class EnumValuePartitionerTest extends AbstractGTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDisjointValues() {
|
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());
|
assertEquals(5, list.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllOverlappingValues() {
|
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());
|
assertEquals(2, list.size());
|
||||||
BitGroup group = list.get(0);
|
BitGroup group = list.get(0);
|
||||||
assertEquals(15, group.getMask());
|
assertEquals(15, group.getMask());
|
||||||
|
@ -50,7 +49,7 @@ public class EnumValuePartitionerTest extends AbstractGTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSomeOverlappingValues() {
|
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(4, list.size());
|
||||||
assertEquals(1, list.get(0).getMask());
|
assertEquals(1, list.get(0).getMask());
|
||||||
assertEquals(8, list.get(1).getMask());
|
assertEquals(8, list.get(1).getMask());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue