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 docking.widgets.OptionDialog;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.ProgramModifierListener;
import ghidra.program.database.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
/**
@@ -47,7 +47,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
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
commit = true;
}
@@ -119,7 +119,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
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
commit = true;
}
@@ -191,7 +191,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
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
commit = true;
}
@@ -296,10 +296,9 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
setErrorsExpected(true);
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
executeMerge(true);
waitForCompletion();
DataTypeManager dtm = resultProgram.getDataTypeManager();
checkConflictCount(0);
@@ -573,7 +572,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -646,7 +645,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
// causes Bar to be marked as changed
commit = true;
}
@@ -720,7 +719,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
// causes Bar to be marked as changed
commit = true;
}
@@ -796,7 +795,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Structure ms = (Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
"MyStruct");
try {
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
Structure s1 = new StructureDataType(
new CategoryPath("/Category1/Category2/Category5"), "s1", 0);
s1.add(ms);
@@ -884,6 +883,196 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
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
public void testEditUnions() throws Exception {
@@ -896,7 +1085,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
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
commit = true;
}
@@ -988,7 +1177,7 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
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
commit = true;
}
@@ -1070,10 +1259,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -1157,10 +1346,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -1245,10 +1434,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -1332,10 +1521,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -1425,10 +1614,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -1526,10 +1715,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
try {
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
dtm.remove(s, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(s, TaskMonitorAdapter.DUMMY);
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
dtm.remove(dt, TaskMonitorAdapter.DUMMY_MONITOR);
dtm.remove(dt, TaskMonitorAdapter.DUMMY);
commit = true;
}
finally {
@@ -1624,4 +1813,109 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
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;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import javax.swing.JLabel;
@@ -37,7 +36,7 @@ import ghidra.util.task.TaskMonitorAdapter;
public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
@Test
public void testConflictFixUpForNonFittingStruct() throws Exception {
public void testConflictFixUpForNonFittingStruct() throws Exception {
final CategoryPath miscPath = new CategoryPath("/MISC");
final CategoryPath rootPath = new CategoryPath("/");
@@ -163,7 +162,7 @@ public class DataTypeMerge8Test extends AbstractDataTypeMergeTest {
JLabel label = (JLabel) TestUtils.getInstanceField("label", logPanel);
String statusText = label.getText();
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.";
assertTrue(statusText.contains(expectedText));
}
@@ -141,7 +141,7 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
@Override
public DataType getParent() {
public Composite getParent() {
return parent;
}
@@ -25,6 +25,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
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
public DataType copy(DataTypeManager dtm) {
StructureDataType struct =
@@ -649,6 +657,13 @@ class StructureDB extends CompositeDB implements Structure {
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
public DataType clone(DataTypeManager dtm) {
StructureDataType struct =
@@ -891,12 +906,28 @@ class StructureDB extends CompositeDB implements Structure {
@Override
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
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();
try {
checkDeleted();
if (offset < 0) {
throw new IllegalArgumentException("Offset cannot be negative.");
}
validateDataType(dataType);
dataType = resolve(dataType);
@@ -966,6 +997,10 @@ class StructureDB extends CompositeDB implements Structure {
if (ordinal < 0 || ordinal >= numComponents) {
throw new ArrayIndexOutOfBoundsException(ordinal);
}
if (dataType instanceof BitFieldDataType) {
throw new IllegalArgumentException(
"Components may not be replaced with a bit-field");
}
validateDataType(dataType);
DataTypeComponent origDtc = getComponent(ordinal);
@@ -1085,19 +1120,14 @@ class StructureDB extends CompositeDB implements Structure {
componentAdapter.removeRecord(dtc.getKey());
}
components.clear();
numComponents = 0;
structLength = 0;
if (flexibleArrayComponent != null) {
flexibleArrayComponent.getDataType().removeParent(this);
componentAdapter.removeRecord(flexibleArrayComponent.getKey());
flexibleArrayComponent = null;
}
if (struct.isNotYetDefined()) {
numComponents = 0;
structLength = 0;
}
else {
structLength = struct.getLength();
numComponents = isInternallyAligned() ? 0 : structLength;
}
setAlignment(struct, false);
@@ -1154,14 +1184,17 @@ class StructureDB extends CompositeDB implements Structure {
private void doReplaceWithUnaligned(Structure struct) throws IOException {
// assumes components is clear and that alignment characteristics have been set.
if (struct.isNotYetDefined()) {
return;
}
// NOTE: unaligned bitfields should remain unchanged when
// transitioning endianess even though it makes little sense.
// Unaligned structures are not intended to be portable!
structLength = struct.getLength();
numComponents = structLength;
DataTypeComponent[] otherComponents = struct.getDefinedComponents();
for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i];
DataType dt = resolve(dtc.getDataType());
checkAncestry(dt);
@@ -196,9 +196,8 @@ public interface Structure extends Composite {
public void deleteAtOffset(int offset);
/**
* Remove all components from this structure, effectively setting the
* length to zero.
*
* Remove all components from this structure (including flex-array),
* effectively setting the length to zero.
*/
public void deleteAll();
@@ -23,6 +23,7 @@ import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
import ghidra.program.model.mem.MemBuffer;
import ghidra.util.Msg;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException;
/**
@@ -292,9 +293,25 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
@Override
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
String componentName, 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(), componentName, comment);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e);
}
}
validateDataType(dataType);
dataType = dataType.clone(dataMgr);
@@ -519,7 +536,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
@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)
throws InvalidDataTypeException {
@@ -842,6 +859,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
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
public DataType copy(DataTypeManager dtm) {
StructureDataType struct = new StructureDataType(categoryPath, getName(), getLength(), dtm);
@@ -850,6 +874,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
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
public DataType clone(DataTypeManager dtm) {
if (dataMgr == dtm) {
@@ -902,15 +933,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
int oldLength = structLength;
components.clear();
structLength = 0;
numComponents = 0;
flexibleArrayComponent = null;
if (struct.isNotYetDefined()) {
structLength = 0;
numComponents = 0;
}
else {
structLength = struct.getLength();
numComponents = isInternallyAligned() ? 0 : structLength;
}
setAlignment(struct);
@@ -945,14 +970,17 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
private void doReplaceWithUnaligned(Structure struct) {
// assumes components is clear and that alignment characteristics have been set.
if (struct.isNotYetDefined()) {
return;
}
// NOTE: unaligned bitfields should remain unchanged when
// transitioning endianess even though it makes little sense.
// Unaligned structures are not intended to be portable!
structLength = struct.getLength();
numComponents = structLength;
DataTypeComponent[] otherComponents = struct.getDefinedComponents();
for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i];
DataType dt = dtc.getDataType().clone(dataMgr);
checkAncestry(dt);