Merge remote-tracking branch 'origin/Ghidra_12.2'

This commit is contained in:
Ryan Kurtz
2026-09-23 09:25:19 -04:00
9 changed files with 315 additions and 65 deletions
@@ -1098,6 +1098,12 @@ class StackFrameDataType implements Structure {
return dtc != null ? new StackComponentWrapper(dtc) : null;
}
@Override
public DataTypeComponent getDefinedComponent(int index) throws IndexOutOfBoundsException {
DataTypeComponent dtc = wrappedStruct.getDefinedComponent(index);
return new StackComponentWrapper(dtc);
}
@Override
public StackComponentWrapper getComponent(int ordinal) throws IndexOutOfBoundsException {
DataTypeComponent dtc = wrappedStruct.getComponent(ordinal);
@@ -1169,6 +1169,72 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest {
checkConflictCount(0);
}
@Test
public void testMultiEdits14() throws Exception {
mtf.initialize("notepad2", new ProgramModifierListener() {
@Override
public void modifyLatest(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
// Create a conflict while retaining Bar's original six-byte length.
assertEquals(6, bar.getLength());
bar.getComponent(0).setComment("Latest Bar component");
int junk = 0;
}
@Override
public void modifyPrivate(ProgramDB program) {
DataTypeManager dtm = program.getDataTypeManager();
CategoryPath path = new CategoryPath("/MISC");
Structure bar = (Structure) dtm.getDataType(path, "Bar");
bar.delete(1); // Remove the pointer, shrinking Bar from six bytes to two.
assertEquals(2, bar.getLength());
// Allocate the container before its target so that the merge records a fixup
// for the as-yet-unresolved target when processing the added container.
Structure container = (Structure) dtm.addDataType(
new StructureDataType(path, "FixupContainer", 0),
DataTypeConflictHandler.DEFAULT_HANDLER);
Structure target = new StructureDataType(path, "FixupTarget", 0);
target.add(ByteDataType.dataType);
target = (Structure) dtm.addDataType(target, DataTypeConflictHandler.DEFAULT_HANDLER);
container.add(bar, "bar", null);
container.growStructure(4);
container.add(target, "target", null);
assertFalse(container.isPackingEnabled());
assertEquals(6, container.getComponent(5).getOffset());
int junk = 0;
}
});
executeMerge();
DataTypeManager dtm = resultProgram.getDataTypeManager();
// The added container initially uses Latest's six-byte Bar, so the placeholder
// at offset 6 has ordinal 1. Choosing MY Bar shrinks that component to two bytes,
// introducing four undefined components before the placeholder. Its ordinal is
// now 5, but FixUpInfo still records 1. The merge must not use that stale ordinal.
chooseOption(DataTypeMergeManager.OPTION_MY);
waitForCompletion();
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
Structure container =
(Structure) dtm.getDataType(new CategoryPath("/MISC"), "FixupContainer");
DataType target = dtm.getDataType(new CategoryPath("/MISC"), "FixupTarget");
assertEquals(2, bar.getLength());
assertEquals(7, container.getLength());
assertEquals(2, container.getNumDefinedComponents());
assertEquals(bar, container.getComponent(0).getDataType());
assertEquals(6, container.getComponent(5).getOffset());
assertEquals(target, container.getComponent(5).getDataType());
checkConflictCount(0);
}
@Test
public void testDeletedBaseTypeDef() throws Exception {
@@ -28,6 +28,107 @@ import ghidra.program.model.data.*;
*/
public class DataTypeMergeFixupTest extends AbstractDataTypeMergeTest {
@Test
public void testDefinedComponentFixupAfterResize() throws Exception {
checkDefinedComponentFixupAfterLayoutChange(false, false);
}
@Test
public void testDefinedComponentFixupAfterEnablingPacking() throws Exception {
checkDefinedComponentFixupAfterLayoutChange(false, true);
}
@Test
public void testDefinedComponentFixupAfterDisablingPacking() throws Exception {
checkDefinedComponentFixupAfterLayoutChange(true, false);
}
private void checkDefinedComponentFixupAfterLayoutChange(boolean initiallyPacked,
boolean finallyPacked) throws Exception {
CategoryPath path = new CategoryPath("/MISC");
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
@Override
public void modifyOriginal(ProgramDB program) throws Exception {
// Ensure the zero-length type is already resolved when the container is added.
program.getDataTypeManager().addDataType(new ArrayDataType(ByteDataType.dataType, 0),
DataTypeConflictHandler.DEFAULT_HANDLER);
}
@Override
public void modifyLatest(ProgramDB program) throws Exception {
Structure bar = (Structure) program.getDataTypeManager().getDataType(path, "Bar");
bar.getComponent(0).setComment("Latest Bar component");
}
@Override
public void modifyPrivate(ProgramDB program) throws Exception {
DataTypeManager dtm = program.getDataTypeManager();
Structure bar = (Structure) dtm.getDataType(path, "Bar");
bar.delete(1);
assertEquals(2, bar.getLength());
// Allocate the container first so its target components require deferred fixups.
Structure container = (Structure) dtm.addDataType(
new StructureDataType(path, "LayoutContainer", 0),
DataTypeConflictHandler.DEFAULT_HANDLER);
Structure target = new StructureDataType(path, "LayoutTarget", 0);
target.add(ByteDataType.dataType);
target = (Structure) dtm.addDataType(target, DataTypeConflictHandler.DEFAULT_HANDLER);
container.add(bar, "bar", null);
container.growStructure(4);
container.insertBitFieldAt(6, 1, 0, ByteDataType.dataType, 3, "bits1", null);
container.insertBitFieldAt(6, 1, 3, ByteDataType.dataType, 2, "bits2", null);
container.add(new ArrayDataType(ByteDataType.dataType, 0), "zero1", null);
container.add(new ArrayDataType(ByteDataType.dataType, 0), "zero2", null);
container.add(target, "target1", null);
container.add(target, "target2", null);
container.setPackingEnabled(initiallyPacked);
}
});
executeMerge();
waitForPrompting(); // Wait until additions are complete and the Bar conflict is displayed.
DataTypeManager dtm = resultProgram.getDataTypeManager();
Structure container = (Structure) dtm.getDataType(path, "LayoutContainer");
assertNotNull(container);
DataTypeComponent[] components = container.getDefinedComponents();
assertEquals(7, components.length);
assertEquals(BadDataType.dataType, components[5].getDataType());
assertEquals(BadDataType.dataType, components[6].getDataType());
// The merge is paused at the Bar conflict, after the container fixups were recorded.
// Exercise a packing transition without replacing its defined-component sequence.
int tx = resultProgram.startTransaction("Change packing with pending fixups");
try {
container.setPackingEnabled(finallyPacked);
}
finally {
resultProgram.endTransaction(tx, true);
}
chooseOption(DataTypeMergeManager.OPTION_MY);
waitForCompletion();
container = (Structure) dtm.getDataType(path, "LayoutContainer");
components = container.getDefinedComponents();
assertEquals(finallyPacked, container.isPackingEnabled());
assertEquals(7, components.length);
assertEquals(2, components[0].getLength());
assertTrue(components[1].isBitFieldComponent());
assertTrue(components[2].isBitFieldComponent());
assertEquals(components[1].getOffset(), components[2].getOffset());
assertEquals(0, components[3].getLength());
assertEquals(0, components[4].getLength());
assertEquals(components[3].getOffset(), components[4].getOffset());
DataType target = dtm.getDataType(path, "LayoutTarget");
assertNotNull(target);
assertEquals("target1", components[5].getFieldName());
assertEquals(target, components[5].getDataType());
assertEquals("target2", components[6].getFieldName());
assertEquals(target, components[6].getDataType());
checkConflictCount(0);
}
private void setupRemoveInnerVsAddOuterContainingChangedInner() throws Exception {
final CategoryPath rootPath = new CategoryPath("/");
@@ -746,10 +746,22 @@ class StructureDB extends CompositeDB implements StructureInternal {
@Override
public int getNumDefinedComponents() {
try (Closeable c = lock.read()) {
refreshIfNeeded();
return components.size();
}
}
@Override
public DataTypeComponentDB getDefinedComponent(int index) throws IndexOutOfBoundsException {
try (Closeable c = lock.read()) {
refreshIfNeeded();
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(index);
}
}
@Override
public DataTypeComponentDB getComponent(int ordinal) {
try (Closeable c = lock.read()) {
@@ -389,16 +389,21 @@ class UnionDB extends CompositeDB implements UnionInternal {
}
@Override
public DataTypeComponentDB getComponent(int ordinal) {
public DataTypeComponentDB getDefinedComponent(int index) throws IndexOutOfBoundsException {
try (Closeable c = lock.read()) {
refreshIfNeeded();
if (ordinal < 0 || ordinal >= components.size()) {
return null;
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(ordinal);
return components.get(index);
}
}
@Override
public DataTypeComponentDB getComponent(int ordinal) {
return getDefinedComponent(ordinal);
}
@Override
public DataTypeComponentDB[] getComponents() {
try (Closeable c = lock.read()) {
@@ -47,6 +47,17 @@ public interface Composite extends DataType {
*/
public abstract int getNumDefinedComponents();
/**
* Returns the defined component of this data type at the specified index
* within the ordered sequence of {@link #getDefinedComponents() defined components}.
* See {@link #getNumDefinedComponents()}.
* @param index the index of the defined component (numbering starts at 0).
* @return the data type component.
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public abstract DataTypeComponent getDefinedComponent(int index)
throws IndexOutOfBoundsException;
/**
* Returns the component of this data type with the indicated ordinal.
* @param ordinal the component's ordinal (numbering starts at 0).
@@ -448,6 +448,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
dtc.setOrdinal(dtc.getOrdinal() + deltaOrdinal);
}
@Override
public DataTypeComponentImpl getDefinedComponent(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(index);
}
@Override
public DataTypeComponentImpl getComponent(int ordinal) {
if (ordinal < 0 || ordinal >= numComponents) {
@@ -104,8 +104,16 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
}
@Override
public DataTypeComponent getComponent(int ordinal) {
return components.get(ordinal);
public DataTypeComponentImpl getDefinedComponent(int index) throws IndexOutOfBoundsException {
if (index < 0 || index >= components.size()) {
throw new IndexOutOfBoundsException(index);
}
return components.get(index);
}
@Override
public DataTypeComponentImpl getComponent(int ordinal) throws IndexOutOfBoundsException {
return getDefinedComponent(ordinal);
}
@Override