Merge branch 'GT-3479_ghidra1_BitfieldMerge' into patch

This commit is contained in:
ghidra1
2020-02-03 13:38:14 -05:00
8 changed files with 1185 additions and 478 deletions
@@ -21,11 +21,11 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import ghidra.program.database.ProgramDB; import ghidra.program.database.*;
import ghidra.program.database.ProgramModifierListener;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitorAdapter;
/** /**
@@ -47,7 +47,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@@ -119,7 +119,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@@ -191,7 +191,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@@ -296,10 +296,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
setErrorsExpected(true); setErrorsExpected(true);
executeMerge(); executeMerge(true);
DataTypeManager dtm = resultProgram.getDataTypeManager();
waitForCompletion(); DataTypeManager dtm = resultProgram.getDataTypeManager();
checkConflictCount(0); checkConflictCount(0);
@@ -573,7 +572,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -646,7 +645,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
// causes Bar to be marked as changed // causes Bar to be marked as changed
commit = true; commit = true;
} }
@@ -720,7 +719,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
// causes Bar to be marked as changed // causes Bar to be marked as changed
commit = true; commit = true;
} }
@@ -796,7 +795,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Structure ms = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), Structure ms = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"MyStruct"); "MyStruct");
try { try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
Structure s1 = new StructureDataType( Structure s1 = new StructureDataType(
new CategoryPath("/Category1/Category2/Category5"), "s1", 0); new CategoryPath("/Category1/Category2/Category5"), "s1", 0);
s1.add(ms); s1.add(ms);
@@ -884,6 +883,196 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
assertEquals(3, dtcs.length); assertEquals(3, dtcs.length);
} }
@Test
public void testConflictUpdate5() throws Exception {
TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "BF",
IntegerDataType.dataType);
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
dtm.addDataType(td, null);
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyLatest(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
DataType dt = dtm.getDataType(new CategoryPath("/Category1/Category2"), "BF");
try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"Structure_1");
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
try {
s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1");
s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2");
foo.add(new FloatDataType());
}
catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
}
finally {
program.endTransaction(transactionID, true);
}
}
});
// bitfield silently transitions to int since typedef BF was removed
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Structure s1 =
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
assertNotNull(s1);
DataTypeComponent[] dtcs = s1.getComponents();
assertEquals(7, dtcs.length);
assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf1", dtcs[3].getFieldName());
assertEquals("my bf1", dtcs[3].getComment());
DataType dt = dtcs[3].getDataType();
assertTrue(dt instanceof BitFieldDataType);
BitFieldDataType bfDt = (BitFieldDataType) dt;
assertTrue(bfDt.getBaseDataType() instanceof IntegerDataType);
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(6, bfDt.getBitOffset());
assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf2", dtcs[4].getFieldName());
assertEquals("my bf2", dtcs[4].getComment());
dt = dtcs[4].getDataType();
assertTrue(dt instanceof BitFieldDataType);
bfDt = (BitFieldDataType) dt;
assertTrue(bfDt.getBaseDataType() instanceof IntegerDataType);
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(4, bfDt.getBitOffset());
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
// Structure_1 should contain MY Foo
assertEquals(foo, dtcs[5].getDataType());
dtcs = foo.getComponents();
assertEquals(5, dtcs.length);
assertTrue(dtcs[4].getDataType().isEquivalent(new FloatDataType()));
checkConflictCount(0);
}
@Test
public void testConflictUpdate6() throws Exception {
TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "BF",
IntegerDataType.dataType);
mtf.initialize("notepad2", new ProgramModifierListener() {
@Override
public void modifyLatest(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
// add new BF not compatible with BitFields
dtm.addDataType(
new StructureDataType(new CategoryPath("/Category1/Category2"), "BF", 0),
null);
}
finally {
program.endTransaction(transactionID, true);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
Structure s1 = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"Structure_1");
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
try {
s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1");
s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2");
foo.add(new FloatDataType());
}
catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
}
finally {
program.endTransaction(transactionID, true);
}
}
});
// bitfield silently transitions to BF.conflict since two different BF types were added
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
Structure s1 =
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
assertNotNull(s1);
DataTypeComponent[] dtcs = s1.getComponents();
assertEquals(7, dtcs.length);
assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf1", dtcs[3].getFieldName());
assertEquals("my bf1", dtcs[3].getComment());
DataType dt = dtcs[3].getDataType();
assertTrue(dt instanceof BitFieldDataType);
BitFieldDataType bfDt = (BitFieldDataType) dt;
DataType bdt = bfDt.getBaseDataType();
assertEquals("/Category1/Category2/BF.conflict", bdt.getPathName());
assertTrue(bdt.isEquivalent(td));
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(6, bfDt.getBitOffset());
assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined
assertEquals("bf2", dtcs[4].getFieldName());
assertEquals("my bf2", dtcs[4].getComment());
dt = dtcs[4].getDataType();
assertTrue(dt instanceof BitFieldDataType);
bfDt = (BitFieldDataType) dt;
bdt = bfDt.getBaseDataType();
assertEquals("/Category1/Category2/BF.conflict", bdt.getPathName());
assertTrue(bdt.isEquivalent(td));
assertEquals(2, bfDt.getDeclaredBitSize());
assertEquals(4, bfDt.getBitOffset());
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
// Structure_1 should contain MY Foo
assertEquals(foo, dtcs[5].getDataType());
dtcs = foo.getComponents();
assertEquals(5, dtcs.length);
assertTrue(dtcs[4].getDataType().isEquivalent(new FloatDataType()));
checkConflictCount(1);
}
@Test @Test
public void testEditUnions() throws Exception { public void testEditUnions() throws Exception {
@@ -896,7 +1085,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@@ -988,7 +1177,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
// 2 components should get removed from CoolUnion // 2 components should get removed from CoolUnion
commit = true; commit = true;
} }
@@ -1070,10 +1259,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -1157,10 +1346,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -1245,10 +1434,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -1332,10 +1521,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -1425,10 +1614,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -1526,10 +1715,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try { try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table"); Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt = DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion"); dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR); dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true; commit = true;
} }
finally { finally {
@@ -1624,4 +1813,109 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
checkConflictCount(0); checkConflictCount(0);
} }
@Test
public void testEditUnions9() throws Exception {
mtf.initialize("notepad", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "XYZ", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
dtm.addDataType(
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm),
null);
commit = true;
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyLatest(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
DataType enumm = dtm.getDataType(new CategoryPath("/Category1"), "XYZ");
dtm.remove(enumm, TaskMonitor.DUMMY);
Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"CoolUnion");
// NOTE: bit field component byte sizing is currently auto-sized and packed within unions
union.insertBitField(1, IntegerDataType.dataType, 4, "bf1", "latest bf1");
union.insertBitField(2, IntegerDataType.dataType, 2, "bf2", "latest bf2");
commit = true;
}
catch (InvalidDataTypeException e) {
e.printStackTrace();
Assert.fail();
}
finally {
program.endTransaction(transactionID, commit);
}
}
@Override
public void modifyPrivate(ProgramDB program) {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
try {
DataType enumm = dtm.getDataType(new CategoryPath("/Category1"), "XYZ");
assertTrue(enumm instanceof Enum);
Union union = (Union) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"CoolUnion");
// NOTE: bit field component byte sizing is currently auto-sized and packed within unions
union.insertBitField(1, enumm, 4, "BF1", "my bf1");
union.insertBitField(2, enumm, 2, "BF2", "my bf2");
commit = true;
}
catch (InvalidDataTypeException e) {
e.printStackTrace();
Assert.fail();
}
finally {
program.endTransaction(transactionID, commit);
}
}
});
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
chooseOption(DataTypeMergeManager.OPTION_MY);// MY bitfields w/ enum
waitForCompletion();
// primitive type of byte used in absence of enum
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
//@formatter:off
assertEquals("/Category1/Category2/CoolUnion\n" +
"Unaligned\n" +
"Union CoolUnion {\n" +
" 0 qword 8 null \"\"\n" +
" 0 byte:4(4) 1 BF1 \"my bf1\"\n" +
" 0 byte:2(6) 1 BF2 \"my bf2\"\n" +
" 0 word 2 null \"\"\n" +
" 0 undefined * * * * * 4 null \"\"\n" +
" 0 DLL_Table 96 null \"\"\n" +
" 0 DLL_Table * 4 null \"\"\n" +
"}\n" +
"Size = 96 Actual Alignment = 1\n", union.toString());
//@formatter:on
}
} }
@@ -15,8 +15,7 @@
*/ */
package ghidra.app.merge.datatypes; package ghidra.app.merge.datatypes;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import javax.swing.JLabel; import javax.swing.JLabel;
@@ -37,7 +36,7 @@ import ghidra.util.task.TaskMonitorAdapter;
public class DataTypeMerge8Test extends AbstractDataTypeMergeTest { public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
@Test @Test
public void testConflictFixUpForNonFittingStruct() throws Exception { public void testConflictFixUpForNonFittingStruct() throws Exception {
final CategoryPath miscPath = new CategoryPath("/MISC"); final CategoryPath miscPath = new CategoryPath("/MISC");
final CategoryPath rootPath = new CategoryPath("/"); final CategoryPath rootPath = new CategoryPath("/");
@@ -163,7 +162,7 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel); JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel);
String statusText = label.getText(); String statusText = label.getText();
String expectedText = String expectedText =
"Merging Data Types: Not enough undefined bytes to fit /XYZ in structure " + "Structure Merge: Not enough undefined bytes to fit /XYZ in structure " +
"/MISC/ABC at offset 0x4.\nIt needs 3 more byte(s) to be able to fit."; "/MISC/ABC at offset 0x4.\nIt needs 3 more byte(s) to be able to fit.";
assertTrue(statusText.contains(expectedText)); assertTrue(statusText.contains(expectedText));
} }
@@ -141,7 +141,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
} }
@Override @Override
public DataType getParent() { public Composite getParent() {
return parent; return parent;
} }
@@ -25,6 +25,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult; import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
/** /**
@@ -640,6 +641,13 @@ class StructureDB extends CompositeDB implements Structure {
} }
} }
/**
* Create copy of structure for target dtm (source archive information is discarded).
* WARNING! copying unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType copy(DataTypeManager dtm) { public DataType copy(DataTypeManager dtm) {
StructureDataType struct = StructureDataType struct =
@@ -649,6 +657,13 @@ class StructureDB extends CompositeDB implements Structure {
return struct; return struct;
} }
/**
* Create cloned structure for target dtm preserving source archive information.
* WARNING! cloning unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType clone(DataTypeManager dtm) { public DataType clone(DataTypeManager dtm) {
StructureDataType struct = StructureDataType struct =
@@ -891,12 +906,28 @@ class StructureDB extends CompositeDB implements Structure {
@Override @Override
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name, public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
String comment) { String comment) {
if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative.");
}
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bfDt = (BitFieldDataType) dataType;
if (length <= 0) {
length = dataType.getLength();
}
try {
return insertBitFieldAt(offset, length, bfDt.getBitOffset(), bfDt.getBaseDataType(),
bfDt.getDeclaredBitSize(), name, comment);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e);
}
}
lock.acquire(); lock.acquire();
try { try {
checkDeleted(); checkDeleted();
if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative.");
}
validateDataType(dataType); validateDataType(dataType);
dataType = resolve(dataType); dataType = resolve(dataType);
@@ -966,6 +997,10 @@ class StructureDB extends CompositeDB implements Structure {
if (ordinal < 0 || ordinal >= numComponents) { if (ordinal < 0 || ordinal >= numComponents) {
throw new ArrayIndexOutOfBoundsException(ordinal); throw new ArrayIndexOutOfBoundsException(ordinal);
} }
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException(
"Components may not be replaced with a bit-field");
}
validateDataType(dataType); validateDataType(dataType);
DataTypeComponent origDtc = getComponent(ordinal); DataTypeComponent origDtc = getComponent(ordinal);
@@ -1085,19 +1120,14 @@ class StructureDB extends CompositeDB implements Structure {
componentAdapter.removeRecord(dtc.getKey()); componentAdapter.removeRecord(dtc.getKey());
} }
components.clear(); components.clear();
numComponents = 0;
structLength = 0;
if (flexibleArrayComponent != null) { if (flexibleArrayComponent != null) {
flexibleArrayComponent.getDataType().removeParent(this); flexibleArrayComponent.getDataType().removeParent(this);
componentAdapter.removeRecord(flexibleArrayComponent.getKey()); componentAdapter.removeRecord(flexibleArrayComponent.getKey());
flexibleArrayComponent = null; flexibleArrayComponent = null;
} }
if (struct.isNotYetDefined()) {
numComponents = 0;
structLength = 0;
}
else {
structLength = struct.getLength();
numComponents = isInternallyAligned() ? 0 : structLength;
}
setAlignment(struct, false); setAlignment(struct, false);
@@ -1154,14 +1184,17 @@ class StructureDB extends CompositeDB implements Structure {
private void doReplaceWithUnaligned(Structure struct) throws IOException { private void doReplaceWithUnaligned(Structure struct) throws IOException {
// assumes components is clear and that alignment characteristics have been set. // assumes components is clear and that alignment characteristics have been set.
if (struct.isNotYetDefined()) {
return;
}
// NOTE: unaligned bitfields should remain unchanged when structLength = struct.getLength();
// transitioning endianess even though it makes little sense. numComponents = structLength;
// Unaligned structures are not intended to be portable!
DataTypeComponent[] otherComponents = struct.getDefinedComponents(); DataTypeComponent[] otherComponents = struct.getDefinedComponents();
for (int i = 0; i < otherComponents.length; i++) { for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i]; DataTypeComponent dtc = otherComponents[i];
DataType dt = resolve(dtc.getDataType()); DataType dt = resolve(dtc.getDataType());
checkAncestry(dt); checkAncestry(dt);
@@ -196,9 +196,8 @@ public interface Structure extends Composite {
public void deleteAtOffset(int offset); public void deleteAtOffset(int offset);
/** /**
* Remove all components from this structure, effectively setting the * Remove all components from this structure (including flex-array),
* length to zero. * effectively setting the length to zero.
*
*/ */
public void deleteAll(); public void deleteAll();
@@ -23,6 +23,7 @@ import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.UniversalID; import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
/** /**
@@ -292,9 +293,25 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
@Override @Override
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length, public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
String componentName, String comment) { String componentName, String comment) {
if (offset < 0) { if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative."); throw new IllegalArgumentException("Offset cannot be negative.");
} }
if (dataType instanceof BitFieldDataType) {
BitFieldDataType bfDt = (BitFieldDataType) dataType;
if (length <= 0) {
length = dataType.getLength();
}
try {
return insertBitFieldAt(offset, length, bfDt.getBitOffset(), bfDt.getBaseDataType(),
bfDt.getDeclaredBitSize(), componentName, comment);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e);
}
}
validateDataType(dataType); validateDataType(dataType);
dataType = dataType.clone(dataMgr); dataType = dataType.clone(dataMgr);
@@ -519,7 +536,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
} }
@Override @Override
public DataTypeComponent insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset, public DataTypeComponentImpl insertBitFieldAt(int byteOffset, int byteWidth, int bitOffset,
DataType baseDataType, int bitSize, String componentName, String comment) DataType baseDataType, int bitSize, String componentName, String comment)
throws InvalidDataTypeException { throws InvalidDataTypeException {
@@ -842,6 +859,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return available; return available;
} }
/**
* Create copy of structure for target dtm (source archive information is discarded).
* WARNING! copying unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType copy(DataTypeManager dtm) { public DataType copy(DataTypeManager dtm) {
StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm); StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm);
@@ -850,6 +874,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return struct; return struct;
} }
/**
* Create cloned structure for target dtm preserving source archive information.
* WARNING! cloning unaligned structures which contain bitfields can produce
* invalid results when switching endianess due to the differences in packing order.
* @param dtm target data type manager
* @return cloned structure
*/
@Override @Override
public DataType clone(DataTypeManager dtm) { public DataType clone(DataTypeManager dtm) {
if (dataMgr == dtm) { if (dataMgr == dtm) {
@@ -902,15 +933,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
int oldLength = structLength; int oldLength = structLength;
components.clear(); components.clear();
structLength = 0;
numComponents = 0;
flexibleArrayComponent = null; flexibleArrayComponent = null;
if (struct.isNotYetDefined()) {
structLength = 0;
numComponents = 0;
}
else {
structLength = struct.getLength();
numComponents = isInternallyAligned() ? 0 : structLength;
}
setAlignment(struct); setAlignment(struct);
@@ -945,14 +970,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
private void doReplaceWithUnaligned(Structure struct) { private void doReplaceWithUnaligned(Structure struct) {
// assumes components is clear and that alignment characteristics have been set. // assumes components is clear and that alignment characteristics have been set.
if (struct.isNotYetDefined()) {
return;
}
// NOTE: unaligned bitfields should remain unchanged when structLength = struct.getLength();
// transitioning endianess even though it makes little sense. numComponents = structLength;
// Unaligned structures are not intended to be portable!
DataTypeComponent[] otherComponents = struct.getDefinedComponents(); DataTypeComponent[] otherComponents = struct.getDefinedComponents();
for (int i = 0; i < otherComponents.length; i++) { for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i]; DataTypeComponent dtc = otherComponents[i];
DataType dt = dtc.getDataType().clone(dataMgr); DataType dt = dtc.getDataType().clone(dataMgr);
checkAncestry(dt); checkAncestry(dt);