diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java index 9416bde01a..ba70b5efb6 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/model/data/StructureDataTypeTest.java @@ -540,6 +540,40 @@ public class StructureDataTypeTest extends AbstractGenericTest { "Length: 13 Alignment: 1\n", struct.toString()); } + @Test + public void testInsertWithZeroArrayAtOffset3() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + + // Clear structure and set length to 8 with only zero-length components + struct.setLength(0); + struct.setLength(8); + + struct.insertAtOffset(2, zeroArray, -1, "z4", null); + struct.insertAtOffset(0, zeroArray, -1, "z1", null); + struct.insertAtOffset(0, zeroArray, -1, "z2", null); + + assertEquals("/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + + struct.insertAtOffset(0, zeroArray, -1, "z3", null); + + assertEquals("/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 0 float[0] 0 z3 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + } + @Test public void testInsertAtOffsetPastEnd() { struct.insertAtOffset(100, new FloatDataType(), 4); @@ -861,6 +895,44 @@ public class StructureDataTypeTest extends AbstractGenericTest { //@formatter:on } + @Test + public void testInsertZeroLengthBitfieldAtOffset() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + + // Clear structure and set length to 8 with only zero-length components + struct.setLength(0); + struct.setLength(8); + struct.insertAtOffset(0, zeroArray, -1, "z1", null); + struct.insertAtOffset(0, zeroArray, -1, "z2", null); + struct.insertAtOffset(2, zeroArray, -1, "z4", null); + + assertEquals("/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + + try { + struct.insertBitFieldAt(0, 1, 0, CharDataType.dataType, 0, null, null); + } + catch (InvalidDataTypeException e) { + failWithException("Unexpected", e); + } + + assertEquals("/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 0 char:0(0) 0 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + } + @Test public void testInsertBitFieldAtBigEndian() throws Exception { @@ -2053,6 +2125,39 @@ public class StructureDataTypeTest extends AbstractGenericTest { assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); } + @Test + public void testReplaceAtWithZeroLength1() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + + // Clear structure and set length to 8 with only zero-length components + struct.setLength(0); + struct.setLength(8); + struct.replaceAtOffset(0, zeroArray, -1, "z1", null); + struct.replaceAtOffset(0, zeroArray, -1, "z2", null); + struct.replaceAtOffset(2, zeroArray, -1, "z4", null); + + assertEquals("/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + + struct.replaceAtOffset(0, zeroArray, -1, "z3", null); + + assertEquals("/TestStruct\n" + + "pack(disabled)\n" + + "Structure TestStruct {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 0 float[0] 0 z3 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + } + @Test public void testSetName() throws Exception { Structure s1 = new StructureDataType("Test1", 0); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index cce8250878..5970c7e42f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -1337,6 +1337,8 @@ class StructureDB extends CompositeDB implements StructureInternal { dataType = resolve(dataType); DataTypeUtilities.checkAncestry(this, dataType); + length = getPreferredComponentLength(dataType, length); + if ((offset > structLength) && !isPackingEnabled()) { numComponents += offset - structLength; structLength = offset; @@ -1352,7 +1354,8 @@ class StructureDB extends CompositeDB implements StructureInternal { if (index >= 0) { index = backupToFirstComponentContainingOffset(index, offset); index = afterNonZeroComponentsAtOffset(index, offset); - if (index < components.size()) { + if (length != 0 && index < components.size()) { + // NOTE: zero-length component insert does not trigger offset shift DataTypeComponentDB dtc = components.get(index); additionalShift = offset - dtc.getOffset(); } @@ -1364,7 +1367,13 @@ class StructureDB extends CompositeDB implements StructureInternal { int ordinal = offset; if (index > 0) { DataTypeComponentDB dtc = components.get(index - 1); - ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); + ordinal = dtc.getOrdinal(); + if (dtc.getOffset() == offset) { + ordinal += 1; + } + else { + ordinal += offset - dtc.getEndOffset(); // account for undefined components + } } if (dataType == DataType.DEFAULT) { @@ -1375,8 +1384,6 @@ class StructureDB extends CompositeDB implements StructureInternal { return new DataTypeComponentDB(dataMgr, this, ordinal, offset); } - length = getPreferredComponentLength(dataType, length); - DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, length, ordinal, offset, name, comment); dataType.addParent(this); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java index 4e0f883b5b..53119e1ed1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureDataType.java @@ -520,6 +520,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur dataType = dataType.clone(dataMgr); DataTypeUtilities.checkAncestry(this, dataType); + length = getPreferredComponentLength(dataType, length); + if ((offset > structLength) && !isPackingEnabled()) { numComponents += offset - structLength; structLength = offset; @@ -535,7 +537,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur if (index >= 0) { index = backupToFirstComponentContainingOffset(index, offset); index = afterNonZeroComponentsAtOffset(index, offset); - if (index < components.size()) { + if (length != 0 && index < components.size()) { + // NOTE: zero-length component insert does not trigger offset shift DataTypeComponentImpl dtc = components.get(index); additionalShift = offset - dtc.getOffset(); } @@ -547,7 +550,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur int ordinal = offset; if (index > 0) { DataTypeComponent dtc = components.get(index - 1); - ordinal = dtc.getOrdinal() + offset - dtc.getEndOffset(); + ordinal = dtc.getOrdinal(); + if (dtc.getOffset() == offset) { + ordinal += 1; + } + else { + ordinal += offset - dtc.getEndOffset(); // account for undefined components + } } if (dataType == DataType.DEFAULT) { @@ -556,8 +565,6 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset); } - length = getPreferredComponentLength(dataType, length); - DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal, offset, componentName, comment); dataType.addParent(this); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java index 03adfc1d70..d91bc81946 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/data/StructureDBTest.java @@ -567,6 +567,42 @@ public class StructureDBTest extends AbstractGenericTest { "Length: 13 Alignment: 1\n", struct.toString()); } + @Test + public void testInsertWithZeroArrayAtOffset3() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + + // Clear structure and set length to 8 with only zero-length components + struct.setLength(0); + struct.setLength(8); + + struct.insertAtOffset(2, zeroArray, -1, "z4", null); + struct.insertAtOffset(0, zeroArray, -1, "z1", null); + struct.insertAtOffset(0, zeroArray, -1, "z2", null); + + assertEquals("/Test\n" + + "pack(disabled)\n" + + "Structure Test {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n" + + "", struct.toString()); + + struct.insertAtOffset(0, zeroArray, -1, "z3", null); + + assertEquals("/Test\n" + + "pack(disabled)\n" + + "Structure Test {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 0 float[0] 0 z3 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n" + + "", struct.toString()); + } + @Test public void testInsertAtOffsetPastEnd() { struct.insertAtOffset(100, new FloatDataType(), 4); @@ -1017,6 +1053,44 @@ public class StructureDBTest extends AbstractGenericTest { //@formatter:on } + @Test + public void testInsertZeroLengthBitfieldAtOffset() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + + // Clear structure and set length to 8 with only zero-length components + struct.setLength(0); + struct.setLength(8); + struct.insertAtOffset(0, zeroArray, -1, "z1", null); + struct.insertAtOffset(0, zeroArray, -1, "z2", null); + struct.insertAtOffset(2, zeroArray, -1, "z4", null); + + assertEquals("/Test\n" + + "pack(disabled)\n" + + "Structure Test {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + + try { + struct.insertBitFieldAt(0, 1, 0, CharDataType.dataType, 0, null, null); + } + catch (InvalidDataTypeException e) { + failWithException("Unexpected", e); + } + + assertEquals("/Test\n" + + "pack(disabled)\n" + + "Structure Test {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 0 char:0(0) 0 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n", struct.toString()); + } + @Test public void testInsertBitFieldAtBigEndian() throws Exception { @@ -2488,6 +2562,41 @@ public class StructureDBTest extends AbstractGenericTest { assertTrue(ByteDataType.dataType.isEquivalent(comps[4].getDataType())); } + @Test + public void testReplaceAtWithZeroLength1() { + Array zeroArray = new ArrayDataType(FloatDataType.dataType, 0, -1); + + // Clear structure and set length to 8 with only zero-length components + struct.setLength(0); + struct.setLength(8); + struct.replaceAtOffset(0, zeroArray, -1, "z1", null); + struct.replaceAtOffset(0, zeroArray, -1, "z2", null); + struct.replaceAtOffset(2, zeroArray, -1, "z4", null); + + assertEquals("/Test\n" + + "pack(disabled)\n" + + "Structure Test {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n" + + "", struct.toString()); + + struct.replaceAtOffset(0, zeroArray, -1, "z3", null); + + assertEquals("/Test\n" + + "pack(disabled)\n" + + "Structure Test {\n" + + " 0 float[0] 0 z1 \"\"\n" + + " 0 float[0] 0 z2 \"\"\n" + + " 0 float[0] 0 z3 \"\"\n" + + " 2 float[0] 0 z4 \"\"\n" + + "}\n" + + "Length: 8 Alignment: 1\n" + + "", struct.toString()); + } + @Test public void testSetName() throws Exception { Structure s1 = new StructureDataType("Test1", 0);