Merge branch 'GP-943_ghidra1_GetComponentContaining'

This commit is contained in:
ghidra1 2021-09-26 19:12:21 -04:00
commit a0af911366
11 changed files with 428 additions and 79 deletions

View File

@ -302,20 +302,16 @@ public class PreCommentFieldFactory extends FieldFactory {
if (dt instanceof Structure) {
Structure struct = (Structure) dt;
lastDtc = struct.getComponentContaining(struct.getLength());
int lastDtcOrdinal = struct.getNumComponents() - 1;
while (lastDtc != null && lastDtc.isBitFieldComponent() &&
lastDtc.getOrdinal() < lastDtcOrdinal) {
lastDtc = struct.getComponent(lastDtc.getOrdinal() + 1);
}
List<DataTypeComponent> components =
struct.getComponentsContaining(struct.getLength());
lastDtc = components.isEmpty() ? null : components.get(components.size() - 1);
}
else if (dt instanceof DynamicDataType) {
DynamicDataType ddt = (DynamicDataType) dt;
lastDtc = ddt.getComponentAt(data.getLength(), data);
int lastDtcOrdinal = ddt.getNumComponents(data);
while (lastDtc != null && lastDtc.isBitFieldComponent() &&
lastDtc.getOrdinal() < lastDtcOrdinal) {
lastDtc = ddt.getComponent(lastDtc.getOrdinal() + 1, data);
int lastDtcOrdinal = ddt.getNumComponents(data) - 1;
if (lastDtc != null && lastDtc.getOrdinal() < lastDtcOrdinal) {
lastDtc = ddt.getComponent(lastDtcOrdinal, data);
}
}

View File

@ -778,20 +778,20 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
DebugInfoEntry structDIE = newStruct("mystruct", 100).create(cu);
newMember(structDIE, "f1", intDIE, 0).create(cu);
newMember(structDIE, "flexarray", arrayDIE, 99).create(cu);
newMember(structDIE, "flexarray", arrayDIE, 100).create(cu);
importAllDataTypes();
Structure structdt = (Structure) dataMgr.getDataType(rootCP, "mystruct");
DataTypeComponent component = structdt.getComponentContaining(99);
assertNotNull(component);
List<DataTypeComponent> components = structdt.getComponentsContaining(100);
assertEquals(1, components.size());
DataTypeComponent component = components.get(0);
assertEquals(0, component.getLength());
DataType dt = component.getDataType();
assertTrue(dt.isEquivalent(new ArrayDataType(IntegerDataType.dataType, 0, -1)));
assertEquals(100, structdt.getLength());
}
@Test

View File

@ -1477,11 +1477,158 @@ public class StructureDataTypeTest extends AbstractGTest {
}
@Test
public void testgetComponentContaining() {
public void testGetComponentAt() {
/**
* /TestStruct
* pack(disabled)
* Structure TestStruct {
* 0 byte 1 field1 "Comment1"
* 1 word 2 null "Comment2"
* 3 dword 4 field3 ""
* 7 byte 1 field4 "Comment4"
* }
* Size = 8 Actual Alignment = 1
*/
DataTypeComponent dtc = struct.getComponentAt(3);
assertEquals(" 2 3 dword 4 field3 null", dtc.toString());
dtc = struct.getComponentAt(4); // offcut
assertNull(dtc);
assertEquals(8, struct.getLength());
dtc = struct.getComponentAt(8);
assertNull(dtc);
struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null);
struct.add(new LongDataType(), "field4", null);
struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null);
assertEquals(12, struct.getLength());
/**
* /TestStruct
* pack(disabled)
* Structure TestStruct {
* 0 byte 1 field1 "Comment1"
* 1 word 2 null "Comment2"
* 3 dword 4 field3 ""
* 7 byte 1 field4 "Comment4"
* 8 char[0] 0 zarray1 ""
* 8 long 4 field4 ""
* 12 long[0] 0 zarray2 ""
* }
* Size = 12 Actual Alignment = 1
*/
dtc = struct.getComponentAt(8);
assertEquals(" 5 8 long 4 field4 null", dtc.toString());
dtc = struct.getComponentAt(9); // offcut
assertNull(dtc);
dtc = struct.getComponentAt(12); // end-of-struct
assertNull(dtc);
// force components to align
struct.setPackingEnabled(true);
/**
* /Test
* pack(disabled)
* Structure Test {
* 0 byte 1 field1 "Comment1"
* 2 word 2 null "Comment2"
* 4 dword 4 field3 ""
* 8 byte 1 field4 "Comment4"
* 9 char[0] 0 zarray1 ""
* 12 long 4 field4 ""
* 16 long[0] 0 zarray2 ""
* }
* Size = 16 Actual Alignment = 1
*/
assertEquals(16, struct.getLength());
dtc = struct.getComponentAt(9); // offset of zero-length component
assertNull(dtc);
struct.setPackingEnabled(false);
dtc = struct.getComponentAt(9); // undefined at offset of zero-length component
assertEquals(" 5 9 undefined 1 null null", dtc.toString());
}
@Test
public void testGetComponentContaining() {
DataTypeComponent dtc = struct.getComponentContaining(4);
assertEquals(DWordDataType.class, dtc.getDataType().getClass());
assertEquals(2, dtc.getOrdinal());
assertEquals(3, dtc.getOffset());
assertEquals(" 2 3 dword 4 field3 null", dtc.toString());
assertEquals(8, struct.getLength());
dtc = struct.getComponentContaining(8);
assertNull(dtc);
struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null);
struct.add(new LongDataType(), "field4", null);
struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null);
assertEquals(12, struct.getLength());
/**
* /TestStruct
* pack(disabled)
* Structure TestStruct {
* 0 byte 1 field1 "Comment1"
* 1 word 2 null "Comment2"
* 3 dword 4 field3 ""
* 7 byte 1 field4 "Comment4"
* 8 char[0] 0 zarray1 ""
* 8 long 4 field4 ""
* 12 long[0] 0 zarray2 ""
* }
* Size = 12 Actual Alignment = 1
*/
dtc = struct.getComponentContaining(8);
assertEquals(" 5 8 long 4 field4 null", dtc.toString());
dtc = struct.getComponentContaining(9); // offcut
assertEquals(" 5 8 long 4 field4 null", dtc.toString());
dtc = struct.getComponentContaining(12); // end-of-struct
assertNull(dtc);
// force components to align
struct.setPackingEnabled(true);
/**
* /Test
* pack(disabled)
* Structure Test {
* 0 byte 1 field1 "Comment1"
* 2 word 2 null "Comment2"
* 4 dword 4 field3 ""
* 8 byte 1 field4 "Comment4"
* 9 char[0] 0 zarray1 ""
* 12 long 4 field4 ""
* 16 long[0] 0 zarray2 ""
* }
* Size = 16 Actual Alignment = 1
*/
assertEquals(16, struct.getLength());
dtc = struct.getComponentContaining(9); // offset of zero-length component
assertNull(dtc);
struct.setPackingEnabled(false);
dtc = struct.getComponentContaining(9); // undefined at offset of zero-length component
assertEquals(" 5 9 undefined 1 null null", dtc.toString());
}
@Test

View File

@ -345,8 +345,7 @@ public class EditStructureUtils {
while (index >= 0) {
monitor.checkCanceled();
DataTypeComponent component = structure.getComponentAt(index);
if (component.getDataType().getName().equals("undefined") &&
component.getLength() == 1) {
if (component != null && component.getDataType() == DataType.DEFAULT) {
index--;
numUndefineds++;
}

View File

@ -17,7 +17,6 @@ package ghidra.app.plugin.core.decompile.actions;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
import docking.action.KeyBindingData;
import docking.action.MenuData;
@ -67,21 +66,6 @@ public class RetypeFieldAction extends AbstractDecompilerAction {
return false;
}
private DataTypeComponent getComponentContaining(Structure struct, int offset) {
DataTypeComponent comp = null;
List<DataTypeComponent> components = struct.getComponentsContaining(offset);
if (components != null) {
for (DataTypeComponent c : components) {
// skip components not supported by decompiler
if (c.getLength() != 0) {
comp = c;
break;
}
}
}
return comp;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
Program program = context.getProgram();
@ -102,8 +86,14 @@ public class RetypeFieldAction extends AbstractDecompilerAction {
return;
}
// Get original component and datatype
DataTypeComponent comp = getComponentContaining(struct, offset);
// Get original component and datatype - structure may be packed so an offset which corresponds
// to padding byte may return null
DataTypeComponent comp = struct.getComponentContaining(offset);
if (comp != null && comp.getOffset() != offset) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",
"Retype offset does not correspond to start of component");
return;
}
DataType originalDataType = comp != null ? comp.getDataType() : DataType.DEFAULT;
if (originalDataType instanceof BitFieldDataType) {
Msg.showError(this, tool.getToolFrame(), "Retype Failed",

View File

@ -913,8 +913,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
/**
* Backup from specified ordinal to the first component which contains the specified offset.
* @param index defined component index
* Backup from specified defined-component index to the first component which contains the specified offset.
* @param index any defined component index which contains offset
* @param offset offset within structure
* @return index of first defined component containing specific offset.
*/
@ -933,8 +933,28 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
/**
* Advance from specified ordinal to the last component which contains the specified offset.
* @param index defined component index
* Identify defined-component index of the first non-zero-length component which contains the specified offset.
* If only zero-length components exist, the last zero-length component which contains the offset will be returned.
* @param index any defined component index which contains offset
* @param offset offset within structure
* @return index of first defined component containing specific offset.
*/
private int indexOfFirstNonZeroLenComponentContainingOffset(int index, int offset) {
index = backupToFirstComponentContainingOffset(index, offset);
DataTypeComponentDB next = components.get(index);
while (next.getLength() == 0 && index < (components.size() - 1)) {
next = components.get(index + 1);
if (!next.containsOffset(offset)) {
break;
}
++index;
}
return index;
}
/**
* Advance from specified defined-component index to the last component which contains the specified offset.
* @param index any defined component index which contains offset
* @param offset offset within structure
* @return index of last defined component containing specific offset.
*/
@ -1070,9 +1090,12 @@ class StructureDB extends CompositeDB implements StructureInternal {
if (index >= 0) {
// return first matching defined component containing offset
DataTypeComponent dtc = components.get(index);
index = backupToFirstComponentContainingOffset(index, offset);
index = indexOfFirstNonZeroLenComponentContainingOffset(index, offset);
dtc = components.get(index);
return dtc;
if (dtc.getLength() != 0) {
return dtc;
}
index = -index - 1;
}
if (offset != structLength && !isPackingEnabled()) {

View File

@ -116,10 +116,11 @@ public abstract class DynamicDataType extends BuiltIn implements Dynamic {
* to share the same offset.
* @param offset the offset into the dataType
* @param buf the memory buffer containing the bytes.
* @return the component containing the byte at the given offset or null if no
* component defined.
* @return the first component containing the byte at the given offset or null if no
* component defined. A zero-length component may be returned.
*/
public final DataTypeComponent getComponentAt(int offset, MemBuffer buf) {
// TODO: This interface should be consistent with Structure
DataTypeComponent[] comps = getComps(buf);
if (comps == null) {
return null;

View File

@ -44,48 +44,71 @@ public interface Structure extends Composite {
public DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException;
/**
* Gets the first immediate child component located at or after the given offset.
* Gets the first defined component located at or after the specified offset.
* Note: The returned component may be a zero-length component.
*
* @param offset the byte offset into this structure
* @return the immediate child component located at or after the given offset or null if not found.
* @return the first defined component located at or after the specified offset or null if not found.
*/
public DataTypeComponent getDefinedComponentAtOrAfterOffset(int offset);
/**
* Gets the first immediate child component that contains the byte at the given offset.
* Gets the first non-zero-length component that contains the byte at the specified offset.
* Note that one or more components may share the same offset when a bit-field or zero-length
* component is present since these may share an offset.
* component is present since these may share an offset. A null may be returned under one of
* the following conditions:
* <ul>
* <li>offset only corresponds to a zero-length component</li>
* <li>offset corresponds to a padding byte within a packed structure</li>
* <li>offset is &gt;= structure length.</li>
* </ul>
*
* @param offset the byte offset into this structure
* @return the first immediate child component containing offset or null if not found.
* @return the first non-zero-length component that contains the byte at the specified offset
* or null if not found.
*/
public DataTypeComponent getComponentContaining(int offset);
/**
* Gets the first immediate defined child component that contains the byte at the given offset.
* Gets the first non-zero-length child component that starts at the specified offset.
* Note that one or more components may share the same offset when a bit-field or zero-length
* component is present since these may share an offset.
* component is present since these may share an offset. A null may be returned under one of
* the following conditions:
* <ul>
* <li>offset only corresponds to a zero-length component</li>
* <li>offset corresponds to a padding byte within a packed structure</li>
* <li>offset corresponds to a component but is not the starting offset of that component</li>
* <li>offset is &gt;= structure length</li>
* </ul>
*
* @param offset the byte offset into this structure
* @return the first immediate child component containing offset or null if not found.
* @deprecated method name has been changed to better reflect behavior. The method
* {@link #getComponentContaining(int)} should be used instead. When switching over
* it may be a could time to verify that the caller can handle the possibility of multiple
* components containing the specified offset due to the possible presence of zero-length
* components, such as zero-element arrays, or bit-fields which can overlap at the
* byte-level.
* @return the first component that starts at specified offset or null if not found.
*/
public default DataTypeComponent getComponentAt(int offset) {
return getComponentContaining(offset);
DataTypeComponent dtc = getComponentContaining(offset);
if (dtc != null && dtc.getOffset() == offset) {
return dtc;
}
return null;
}
/**
* Get an ordered list of immediate child components that contain the byte at the given offset.
* Note that this will only return more than one component when a bit-field or zero-length
* component is present since these may share an offset.
* Get an ordered list of components that contain the byte at the specified offset.
* Unlike {@link #getComponentAt(int)} and {@link #getComponentContaining(int)} this method will
* include zero-length components if they exist at the specified offset. For this reason the
* specified offset may equal the structure length to obtain and trailing zero-length components.
* Note that this method will only return more than one component when a bit-fields and/or
* zero-length components are present since these may share an offset. An empty list may be
* returned under the following conditions:
* <ul>
* <li>offset corresponds to a padding byte within a packed structure</li>
* <li>offset corresponds to a component but is not the starting offset of that component</li>
* <li>offset is equal structure length and no trailing zero-length components exist</li>
* <li>offset is &gt; structure length</li>
* </ul>
*
* @param offset the byte offset into this structure
* @return a list of zero or more child components containing the specified offset
* @return a list of zero or more components containing the specified offset
*/
public List<DataTypeComponent> getComponentsContaining(int offset);
@ -256,7 +279,7 @@ public interface Structure extends Composite {
* components may be cleared. This method will preserve the structure length and placement
* of other components since freed space will appear as undefined components.
* <p>
* To avoid clearing zero-length components at a given offset within a non-packed structure,
* To avoid clearing zero-length components at a specified offset within a non-packed structure,
* the {@link #replaceAtOffset(int, DataType, int, String, String)} may be used with to clear
* only the sized component at the offset by specified {@link DataType#DEFAULT} as the replacement
* datatype.
@ -266,7 +289,7 @@ public interface Structure extends Composite {
public void clearAtOffset(int offset);
/**
* Clears the defined component at the given component ordinal. Clearing a component within
* Clears the defined component at the specified component ordinal. Clearing a component within
* a non-packed structure causes a defined component to be replaced with a number of undefined
* components. This may not the case when clearing a zero-length component or bit-field
* which may not result in such undefined components. In the case of a packed structure
@ -397,7 +420,7 @@ public interface Structure extends Composite {
String comment) throws IllegalArgumentException;
/**
* Increases the size of the structure by the given amount by adding undefined filler at the
* Increases the size of the structure by the specified amount by adding undefined filler at the
* end of the structure. NOTE: This method only has an affect on non-packed structures.
*
* @param amount the amount by which to grow the structure.

View File

@ -179,9 +179,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
if (index >= 0) {
// return first matching defined component containing offset
DataTypeComponent dtc = components.get(index);
index = backupToFirstComponentContainingOffset(index, offset);
index = indexOfFirstNonZeroLenComponentContainingOffset(index, offset);
dtc = components.get(index);
return dtc;
if (dtc.getLength() != 0) {
return dtc;
}
index = -index - 1;
}
if (offset != structLength && !isPackingEnabled()) {
@ -828,8 +831,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Backup from specified ordinal to the first component which contains the specified offset.
* @param index component ordinal
* Backup from specified defined-component index to the first component which contains the specified offset.
* @param index any defined component index which contains offset
* @param offset offset within structure
* @return index of first defined component containing specific offset.
*/
@ -848,8 +851,28 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
/**
* Advance from specified ordinal to the last component which contains the specified offset.
* @param index defined component index
* Identify defined-component index of the first non-zero-length component which contains the specified offset.
* If only zero-length components exist, the last zero-length component which contains the offset will be returned.
* @param index any defined component index which contains offset
* @param offset offset within structure
* @return index of first defined component containing specific offset.
*/
private int indexOfFirstNonZeroLenComponentContainingOffset(int index, int offset) {
index = backupToFirstComponentContainingOffset(index, offset);
DataTypeComponentImpl next = components.get(index);
while (next.getLength() == 0 && index < (components.size() - 1)) {
next = components.get(index + 1);
if (!next.containsOffset(offset)) {
break;
}
++index;
}
return index;
}
/**
* Advance from specified defined-component index to the last component which contains the specified offset.
* @param index any defined component index which contains offset
* @param offset offset within structure
* @return index of last defined component containing specific offset.
*/

View File

@ -1753,12 +1753,159 @@ public class StructureDBTest extends AbstractGTest {
assertEquals(0, s.getNumComponents());
}
@Test
public void testGetComponentAt() {
/**
* /TestStruct
* pack(disabled)
* Structure TestStruct {
* 0 byte 1 field1 "Comment1"
* 1 word 2 null "Comment2"
* 3 dword 4 field3 ""
* 7 byte 1 field4 "Comment4"
* }
* Size = 8 Actual Alignment = 1
*/
DataTypeComponent dtc = struct.getComponentAt(3);
assertEquals(" 2 3 dword 4 field3 null", dtc.toString());
dtc = struct.getComponentAt(4); // offcut
assertNull(dtc);
assertEquals(8, struct.getLength());
dtc = struct.getComponentAt(8);
assertNull(dtc);
struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null);
struct.add(new LongDataType(), "field4", null);
struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null);
assertEquals(12, struct.getLength());
/**
* /TestStruct
* pack(disabled)
* Structure TestStruct {
* 0 byte 1 field1 "Comment1"
* 1 word 2 null "Comment2"
* 3 dword 4 field3 ""
* 7 byte 1 field4 "Comment4"
* 8 char[0] 0 zarray1 ""
* 8 long 4 field4 ""
* 12 long[0] 0 zarray2 ""
* }
* Size = 12 Actual Alignment = 1
*/
dtc = struct.getComponentAt(8);
assertEquals(" 5 8 long 4 field4 null", dtc.toString());
dtc = struct.getComponentAt(9); // offcut
assertNull(dtc);
dtc = struct.getComponentAt(12); // end-of-struct
assertNull(dtc);
// force components to align
struct.setPackingEnabled(true);
/**
* /Test
* pack(disabled)
* Structure Test {
* 0 byte 1 field1 "Comment1"
* 2 word 2 null "Comment2"
* 4 dword 4 field3 ""
* 8 byte 1 field4 "Comment4"
* 9 char[0] 0 zarray1 ""
* 12 long 4 field4 ""
* 16 long[0] 0 zarray2 ""
* }
* Size = 16 Actual Alignment = 1
*/
assertEquals(16, struct.getLength());
dtc = struct.getComponentAt(9); // offset of zero-length component
assertNull(dtc);
struct.setPackingEnabled(false);
dtc = struct.getComponentAt(9); // undefined at offset of zero-length component
assertEquals(" 5 9 undefined 1 null null", dtc.toString());
}
@Test
public void testGetComponentContaining() {
DataTypeComponent dtc = struct.getComponentContaining(4);
assertEquals(DWordDataType.class, dtc.getDataType().getClass());
assertEquals(2, dtc.getOrdinal());
assertEquals(3, dtc.getOffset());
assertEquals(" 2 3 dword 4 field3 null", dtc.toString());
assertEquals(8, struct.getLength());
dtc = struct.getComponentContaining(8);
assertNull(dtc);
struct.add(new ArrayDataType(CharDataType.dataType, 0, -1), "zarray1", null);
struct.add(new LongDataType(), "field4", null);
struct.add(new ArrayDataType(LongDataType.dataType, 0, -1), "zarray2", null);
assertEquals(12, struct.getLength());
/**
* /TestStruct
* pack(disabled)
* Structure TestStruct {
* 0 byte 1 field1 "Comment1"
* 1 word 2 null "Comment2"
* 3 dword 4 field3 ""
* 7 byte 1 field4 "Comment4"
* 8 char[0] 0 zarray1 ""
* 8 long 4 field4 ""
* 12 long[0] 0 zarray2 ""
* }
* Size = 12 Actual Alignment = 1
*/
dtc = struct.getComponentContaining(8);
assertEquals(" 5 8 long 4 field4 null", dtc.toString());
dtc = struct.getComponentContaining(9); // offcut
assertEquals(" 5 8 long 4 field4 null", dtc.toString());
dtc = struct.getComponentContaining(12); // end-of-struct
assertNull(dtc);
// force components to align
struct.setPackingEnabled(true);
/**
* /Test
* pack(disabled)
* Structure Test {
* 0 byte 1 field1 "Comment1"
* 2 word 2 null "Comment2"
* 4 dword 4 field3 ""
* 8 byte 1 field4 "Comment4"
* 9 char[0] 0 zarray1 ""
* 12 long 4 field4 ""
* 16 long[0] 0 zarray2 ""
* }
* Size = 16 Actual Alignment = 1
*/
assertEquals(16, struct.getLength());
dtc = struct.getComponentContaining(9); // offset of zero-length component
assertNull(dtc);
struct.setPackingEnabled(false);
dtc = struct.getComponentContaining(9); // undefined at offset of zero-length component
assertEquals(" 5 9 undefined 1 null null", dtc.toString());
}
@Test

View File

@ -142,9 +142,9 @@ public class DataDBTest extends AbstractGenericTest {
assertNotNull(c);
assertEquals("c1", c.getComponentPathName());
Data c2 = c.getComponentContaining(4);
Data c2 = c.getComponentContaining(4); // zero-length component is ignored
assertNotNull(c2);
assertEquals("c1.", c2.getComponentPathName()); // zero-bitfield has no name
assertEquals("c1.bf1", c2.getComponentPathName());
Data c3 = c.getComponentContaining(5);
assertNotNull(c3);