Merge branch 'GP-260_ghidra1_DataTypeSizeUpdate' into Ghidra_9.2

This commit is contained in:
ghidra1 2020-11-10 13:44:53 -05:00
commit a1fb06ecc9
10 changed files with 304 additions and 55 deletions

View file

@ -0,0 +1,47 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Fixes up all composite datatypes within the current program to account for
// any changes to primitive datatype sizes or alignment rules as defined
// by the associated data organization.
//
// This script requires exclusive access to the program to avoid the possibilty
// of excessive change conflicts.
//
// This script can be run multiple times without harm
//@category Data Types
import ghidra.app.script.GhidraScript;
import ghidra.program.database.data.DataTypeManagerDB;
public class FixupCompositeDataTypesScript extends GhidraScript {
@Override
protected void run() throws Exception {
if (currentProgram == null) {
return;
}
if (!currentProgram.hasExclusiveAccess()) {
popup("This script requires an exclusive checkout of the program");
return;
}
DataTypeManagerDB dtm = (DataTypeManagerDB) currentProgram.getDataTypeManager();
dtm.fixupComposites(monitor);
}
}

View file

@ -89,6 +89,7 @@ public class AlignAllDataTypesAction extends DockingAction {
"Are you sure you want to align all of the data types in " + "Are you sure you want to align all of the data types in " +
dataTypeManager.getName() + dataTypeManager.getName() +
"?\nBoth structures and unions that are currently unaligned will become aligned.\n" + "?\nBoth structures and unions that are currently unaligned will become aligned.\n" +
"This could cause component offsets to change and datatype sizes to change.\n" +
"Do you want to continue?", "Continue", OptionDialog.WARNING_MESSAGE); "Do you want to continue?", "Continue", OptionDialog.WARNING_MESSAGE);
if (result == OptionDialog.CANCEL_OPTION) { if (result == OptionDialog.CANCEL_OPTION) {
return; return;
@ -125,7 +126,7 @@ public class AlignAllDataTypesAction extends DockingAction {
private void alignEachStructure(DataTypeManager dataTypeManager, private void alignEachStructure(DataTypeManager dataTypeManager,
DataOrganization dataOrganization) { DataOrganization dataOrganization) {
Iterator<Composite> allComposites = dataTypeManager.getAllComposites(); Iterator<? extends Composite> allComposites = dataTypeManager.getAllComposites();
while (allComposites.hasNext()) { while (allComposites.hasNext()) {
Composite composite = allComposites.next(); Composite composite = allComposites.next();
composite.setInternallyAligned(true); composite.setInternallyAligned(true);

View file

@ -24,7 +24,6 @@ import ghidra.app.util.bin.format.pdb.PdbParser.PdbXmlMember;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.graph.*; import ghidra.graph.*;
import ghidra.graph.algo.GraphNavigator; import ghidra.graph.algo.GraphNavigator;
import ghidra.graph.jung.JungDirectedGraph;
import ghidra.program.model.data.Composite; import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.symbol.SymbolUtilities; import ghidra.program.model.symbol.SymbolUtilities;
@ -67,8 +66,8 @@ public class ApplyDataTypes {
private List<CompositeDefinition> getCompositeDefinitionsInPostDependencyOrder( private List<CompositeDefinition> getCompositeDefinitionsInPostDependencyOrder(
TaskMonitor monitor) { TaskMonitor monitor) {
JungDirectedGraph<CompositeDefinition, GEdge<CompositeDefinition>> graph = GDirectedGraph<CompositeDefinition, GEdge<CompositeDefinition>> graph =
new JungDirectedGraph<>(); GraphFactory.createDirectedGraph();
for (CompositeDefinition compositeDefinition : compositeQueue.values()) { for (CompositeDefinition compositeDefinition : compositeQueue.values()) {
graph.addVertex(compositeDefinition); graph.addVertex(compositeDefinition);
for (PdbMember m : compositeDefinition.memberList) { for (PdbMember m : compositeDefinition.memberList) {

View file

@ -69,26 +69,20 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
protected abstract void initialize(); protected abstract void initialize();
/** /**
* Get the preferred length for a new component. For Unions and internally * Get the preferred length for a new component. Constraining length of fixed-length datatype
* aligned structures the preferred component length for a fixed-length dataType * may not be sustainable in response to datatype size changes over time.
* will be the length of that dataType. Otherwise the length returned will be no
* larger than the specified length.
*
* @param dataType new component datatype * @param dataType new component datatype
* @param length constrained length or -1 to force use of dataType size. * @param length specified length required for Dynamic types such as string
* Dynamic types such as string must have a positive length * which must have a positive length specified.
* specified.
* @return preferred component length * @return preferred component length
*/ */
protected int getPreferredComponentLength(DataType dataType, int length) { protected int getPreferredComponentLength(DataType dataType, int length) {
if ((isInternallyAligned() || (this instanceof Union)) && !(dataType instanceof Dynamic)) { if (length > 0 && (dataType instanceof Composite) &&
length = -1; // force use of datatype size ((Composite) dataType).isNotYetDefined()) {
return length;
} }
int dtLength = dataType.getLength(); int dtLength = dataType.getLength();
if (length <= 0) { if (dtLength > 0) {
length = dtLength;
}
else if (dtLength > 0 && dtLength < length) {
length = dtLength; length = dtLength;
} }
if (length <= 0) { if (length <= 0) {
@ -789,4 +783,13 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
} }
return " pack(" + packingValue + ")"; return " pack(" + packingValue + ")";
} }
/**
* Perform any neccessary component adjustments based on
* sizes and alignment of components differing from their
* specification which may be influenced by the data organization.
* If this composite changes parents will not be
* notified - handling this is the caller's responsibility.
*/
protected abstract void fixupComponents();
} }

View file

@ -28,6 +28,8 @@ import generic.jar.ResourceFile;
import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive;
import ghidra.framework.store.db.PackedDBHandle; import ghidra.framework.store.db.PackedDBHandle;
import ghidra.framework.store.db.PackedDatabase; import ghidra.framework.store.db.PackedDatabase;
import ghidra.graph.*;
import ghidra.graph.algo.GraphNavigator;
import ghidra.program.database.*; import ghidra.program.database.*;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -2711,7 +2713,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
class StructureIterator implements Iterator<Structure> { class StructureIterator implements Iterator<Structure> {
private RecordIterator it; private RecordIterator it;
private Structure nextStruct; private StructureDB nextStruct;
StructureIterator() throws IOException { StructureIterator() throws IOException {
it = compositeAdapter.getRecords(); it = compositeAdapter.getRecords();
@ -2731,9 +2733,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
} }
@Override @Override
public Structure next() { public StructureDB next() {
if (hasNext()) { if (hasNext()) {
Structure s = nextStruct; StructureDB s = nextStruct;
nextStruct = null; nextStruct = null;
return s; return s;
} }
@ -2746,7 +2748,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
Record rec = it.next(); Record rec = it.next();
DataType dt = getDataType(rec.getKey(), rec); DataType dt = getDataType(rec.getKey(), rec);
if (dt instanceof Structure) { if (dt instanceof Structure) {
nextStruct = (Structure) dt; nextStruct = (StructureDB) dt;
return; return;
} }
} }
@ -2759,7 +2761,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
class CompositeIterator implements Iterator<Composite> { class CompositeIterator implements Iterator<Composite> {
private RecordIterator it; private RecordIterator it;
private Composite nextComposite; private CompositeDB nextComposite;
CompositeIterator() throws IOException { CompositeIterator() throws IOException {
it = compositeAdapter.getRecords(); it = compositeAdapter.getRecords();
@ -2779,9 +2781,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
} }
@Override @Override
public Composite next() { public CompositeDB next() {
if (hasNext()) { if (hasNext()) {
Composite c = nextComposite; CompositeDB c = nextComposite;
nextComposite = null; nextComposite = null;
return c; return c;
} }
@ -2792,7 +2794,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
try { try {
if (it.hasNext()) { if (it.hasNext()) {
Record rec = it.next(); Record rec = it.next();
nextComposite = (Composite) getDataType(rec.getKey(), rec); nextComposite = (CompositeDB) getDataType(rec.getKey(), rec);
} }
} }
catch (IOException e) { catch (IOException e) {
@ -3787,6 +3789,99 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
} }
} }
/**
* Fixup all composites and thier components which may be affected by a data organization
* change include primitive type size changes and alignment changes. It is highly recommended
* that this program be open with exclusive access before invoking this method to avoid
* excessive merge conflicts with other users.
* @param monitor task monitor
* @throws CancelledException if operation is cancelled
*/
public void fixupComposites(TaskMonitor monitor) throws CancelledException {
lock.acquire();
try {
// NOTE: Any composite could be indirectly affected by a component size change
// based upon type relationships
// NOTE: Composites brought in from archive may have incorrect component size
// if not aligned and should not be used to guage a primitive size change
// Unfortunately parent table does not track use of primitives so a brute
// force search is required. Since all composites must be checked, this
// is combined with the composite graph generation to get ordered list
// of composites for subsequent size change operation.
List<CompositeDB> orderedComposites = getAllCompositesInPostDependencyOrder(monitor);
monitor.setProgress(0);
monitor.setMaximum(orderedComposites.size());
monitor.setMessage("Updating Datatype Sizes...");
int count = 0;
for (CompositeDB c : orderedComposites) {
monitor.checkCanceled();
c.fixupComponents();
monitor.setProgress(++count);
}
}
finally {
lock.release();
}
}
/**
* Get composite base type which corresponds to a specified datatype.
* Pointers to composites are ignored. This method is intended to be
* used by the {@link #getAllCompositesInPostDependencyOrder} method only.
* @param dt datatype
* @return base datatype if dt corresponds to a composite or array of composites,
* otherwise null is returned
*/
private CompositeDB getCompositeBaseType(DataType dt) {
while ((dt instanceof Array) || (dt instanceof TypeDef)) {
if (dt instanceof Array) {
dt = ((Array) dt).getDataType();
}
else {
dt = ((TypeDef) dt).getBaseDataType();
}
}
return (dt instanceof CompositeDB) ? (CompositeDB) dt : null;
}
/*
* Graph all composites return an ordered list with leaves returned first and detect
* primitve size changes based upon specified primitiveTypeIds. It is assumed TypeDef
* use of primitives have already be handled elsewhere.
* All pointers are ignored and not followed during graph generation.
* This method is intended to facilitate datatype size change propogation in an
* orderly fashion to reduce size change propogation.
* @param monitor task monitor
* @return order list of composites
* @throws CancelledException if task cancelled
*/
private List<CompositeDB> getAllCompositesInPostDependencyOrder(TaskMonitor monitor)
throws CancelledException {
GDirectedGraph<CompositeDB, GEdge<CompositeDB>> graph = GraphFactory.createDirectedGraph();
Iterator<Composite> allComposites = getAllComposites();
while (allComposites.hasNext()) {
monitor.checkCanceled();
CompositeDB c = (CompositeDB) allComposites.next();
graph.addVertex(c);
for (DataTypeComponent m : c.getDefinedComponents()) {
CompositeDB refC = getCompositeBaseType(m.getDataType());
if (refC != null) {
graph.addEdge(new DefaultGEdge<CompositeDB>(c, refC));
}
}
}
return GraphAlgorithms.getVerticesInPostOrder(graph, GraphNavigator.topDownNavigator());
}
/** /**
* Activate resolveCache and associated resolveQueue if not already active. If * Activate resolveCache and associated resolveQueue if not already active. If
* this method returns true caller is responsible for flushing resolveQueue and * this method returns true caller is responsible for flushing resolveQueue and

View file

@ -590,8 +590,7 @@ class StructureDB extends CompositeDB implements Structure {
if (equals(dataType)) { if (equals(dataType)) {
return true; return true;
} }
for (int i = 0; i < components.size(); i++) { for (DataTypeComponentDB dtc : components) {
DataTypeComponent dtc = components.get(i);
DataType subDt = dtc.getDataType(); DataType subDt = dtc.getDataType();
if (subDt instanceof Composite) { if (subDt instanceof Composite) {
if (((Composite) subDt).isPartOf(dataType)) { if (((Composite) subDt).isPartOf(dataType)) {
@ -1198,8 +1197,7 @@ class StructureDB extends CompositeDB implements Structure {
int oldLength = structLength; int oldLength = structLength;
int oldMinAlignment = getMinimumAlignment(); int oldMinAlignment = getMinimumAlignment();
for (int i = 0; i < components.size(); i++) { for (DataTypeComponentDB dtc : components) {
DataTypeComponentDB dtc = components.get(i);
dtc.getDataType().removeParent(this); dtc.getDataType().removeParent(this);
componentAdapter.removeRecord(dtc.getKey()); componentAdapter.removeRecord(dtc.getKey());
} }
@ -1364,37 +1362,44 @@ class StructureDB extends CompositeDB implements Structure {
try { try {
checkDeleted(); checkDeleted();
if (isInternallyAligned()) { if (isInternallyAligned()) {
adjustInternalAlignment(true); adjustComponents(true); // notifies parents
return; return;
} }
boolean didChange = false; boolean didChange = false;
boolean warn = false;
int n = components.size(); int n = components.size();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
DataTypeComponentDB dtc = components.get(i); DataTypeComponentDB dtc = components.get(i);
int nextIndex = i + 1;
if (dtc.getDataType() == dt) { if (dtc.getDataType() == dt) {
// assume no impact to bitfields since base types // assume no impact to bitfields since base types
// should not change size // should not change size
int dtLen = dt.getLength();
int dtcLen = dtc.getLength(); int dtcLen = dtc.getLength();
if (dtLen < dtcLen) { int length = getPreferredComponentLength(dt, dtcLen);
dtc.setLength(dtLen, true); if (length < dtcLen) {
shiftOffsets(nextIndex, dtcLen - dtLen, 0); dtc.setLength(length, true);
shiftOffsets(i + 1, dtcLen - length, 0);
didChange = true; didChange = true;
} }
else if (dtLen > dtcLen) { else if (length > dtcLen) {
int consumed = consumeBytesAfter(i, dtLen - dtcLen); int consumed = consumeBytesAfter(i, length - dtcLen);
if (consumed > 0) { if (consumed > 0) {
dtc.updateRecord(); dtc.updateRecord();
shiftOffsets(nextIndex, -consumed, 0); shiftOffsets(i + 1, -consumed, 0);
didChange = true; didChange = true;
} }
} }
if (dtc.getLength() != length) {
warn = true;
}
} }
} }
if (didChange) { if (didChange) {
adjustInternalAlignment(true); adjustInternalAlignment(false);
notifySizeChanged(); notifySizeChanged(); // notifies parents
}
if (warn) {
Msg.warn(this,
"Failed to resize one or more structure components: " + getPathName());
} }
} }
finally { finally {
@ -1402,6 +1407,56 @@ class StructureDB extends CompositeDB implements Structure {
} }
} }
@Override
protected void fixupComponents() {
if (isInternallyAligned()) {
// Do not notify parents
if (adjustComponents(false)) {
dataMgr.dataTypeChanged(this);
}
return;
}
boolean didChange = false;
boolean warn = false;
int n = components.size();
for (int i = 0; i < n; i++) {
DataTypeComponentDB dtc = components.get(i);
DataType dt = dtc.getDataType();
if (dt instanceof BitFieldDataType) {
// TODO: could get messy
continue;
}
int dtcLen = dtc.getLength();
int length = getPreferredComponentLength(dt, dtcLen);
if (dtcLen != length) {
if (length < dtcLen) {
dtc.setLength(length, true);
shiftOffsets(i + 1, dtcLen - length, 0);
didChange = true;
}
else if (length > dtcLen) {
int consumed = consumeBytesAfter(i, length - dtcLen);
if (consumed > 0) {
dtc.updateRecord();
shiftOffsets(i + 1, -consumed, 0);
didChange = true;
}
}
if (dtc.getLength() != length) {
warn = true;
}
}
}
if (didChange) {
// Do not notify parents
adjustInternalAlignment(false);
dataMgr.dataTypeChanged(this);
}
if (warn) {
Msg.warn(this, "Failed to resize one or more structure components: " + getPathName());
}
}
@Override @Override
public void dataTypeAlignmentChanged(DataType dt) { public void dataTypeAlignmentChanged(DataType dt) {
lock.acquire(); lock.acquire();
@ -1808,8 +1863,7 @@ class StructureDB extends CompositeDB implements Structure {
flexibleArrayComponent = null; flexibleArrayComponent = null;
} }
for (int i = 0; i < components.size(); i++) { for (DataTypeComponentDB dtc : components) {
DataTypeComponentDB dtc = components.get(i);
dtc.getDataType().removeParent(this); dtc.getDataType().removeParent(this);
try { try {
componentAdapter.removeRecord(dtc.getKey()); componentAdapter.removeRecord(dtc.getKey());
@ -1885,13 +1939,15 @@ class StructureDB extends CompositeDB implements Structure {
changed |= updateComposite(packResult.numComponents, packResult.structureLength, changed |= updateComposite(packResult.numComponents, packResult.structureLength,
packResult.alignment, false); packResult.alignment, false);
if (notify & changed) { if (changed) {
if (notify) {
if (oldLength != structLength) { if (oldLength != structLength) {
notifySizeChanged(); notifySizeChanged();
} }
else { else {
dataMgr.dataTypeChanged(this); dataMgr.dataTypeChanged(this);
} }
}
return true; return true;
} }
return false; return false;

View file

@ -277,8 +277,7 @@ class UnionDB extends CompositeDB implements Union {
int oldLength = unionLength; int oldLength = unionLength;
int oldMinAlignment = getMinimumAlignment(); int oldMinAlignment = getMinimumAlignment();
for (int i = 0; i < components.size(); i++) { for (DataTypeComponentDB dtc : components) {
DataTypeComponentDB dtc = components.get(i);
dtc.getDataType().removeParent(this); dtc.getDataType().removeParent(this);
removeComponent(dtc.getKey()); removeComponent(dtc.getKey());
} }
@ -432,7 +431,7 @@ class UnionDB extends CompositeDB implements Union {
} }
} }
if (changed) { if (changed) {
adjustLength(true, false); adjustLength(true, false); // notifies parents
} }
} }
finally { finally {
@ -440,6 +439,30 @@ class UnionDB extends CompositeDB implements Union {
} }
} }
@Override
protected void fixupComponents() {
boolean changed = false;
for (DataTypeComponentDB dtc : components) {
DataType dt = dtc.getDataType();
if (dt instanceof BitFieldDataType) {
dt = adjustBitField(dt); // in case base type changed
}
int dtcLen = dtc.getLength();
int length = getPreferredComponentLength(dt, dtcLen);
if (length != dtcLen) {
dtc.setLength(length, true);
changed = true;
}
}
if (changed || isInternallyAligned()) {
// NOTE: since we do not retain our external alignment we have no way of knowing if
// it has changed, so we must assume it has if we are an aligned union
// Do not notify parents
adjustLength(false, false);
dataMgr.dataTypeChanged(this);
}
}
@Override @Override
public void dataTypeAlignmentChanged(DataType dt) { public void dataTypeAlignmentChanged(DataType dt) {
adjustInternalAlignment(true); adjustInternalAlignment(true);
@ -614,8 +637,10 @@ class UnionDB extends CompositeDB implements Union {
catch (IOException e) { catch (IOException e) {
dataMgr.dbError(e); dataMgr.dbError(e);
} }
if (notify) {
notifySizeChanged(); notifySizeChanged();
} }
}
else if (notify) { else if (notify) {
dataMgr.dataTypeChanged(this); dataMgr.dataTypeChanged(this);
} }

View file

@ -82,7 +82,7 @@ public interface Program extends DataTypeManagerDomainObject {
* Returns the program's datatype manager. * Returns the program's datatype manager.
*/ */
@Override @Override
public DataTypeManager getDataTypeManager(); public ProgramBasedDataTypeManager getDataTypeManager();
/** /**
* Returns the programs function manager. * Returns the programs function manager.

View file

@ -85,6 +85,30 @@ public class StructureDBTest extends AbstractGTest {
return (Pointer) dataMgr.resolve(new Pointer32DataType(dataType), null); return (Pointer) dataMgr.resolve(new Pointer32DataType(dataType), null);
} }
@Test
public void testEmpty() throws Exception {
Structure s = new StructureDataType("foo", 0);
assertTrue(s.isNotYetDefined());
assertEquals(0, s.getNumComponents());
assertEquals(0, s.getNumDefinedComponents());
Structure s2 = (Structure) dataMgr.resolve(s, null);
assertTrue(s2.isNotYetDefined());
assertEquals(0, s2.getNumComponents());
assertEquals(0, s.getNumDefinedComponents());
}
@Test
public void testSizeOne() throws Exception {
Structure s = new StructureDataType("foo", 1);
assertFalse(s.isNotYetDefined());
assertEquals(1, s.getNumComponents());
assertEquals(0, s.getNumDefinedComponents());
Structure s2 = (Structure) dataMgr.resolve(s, null);
assertFalse(s2.isNotYetDefined());
assertEquals(1, s2.getNumComponents());
assertEquals(0, s2.getNumDefinedComponents());
}
@Test @Test
public void testAdd() throws Exception { public void testAdd() throws Exception {
assertEquals(8, struct.getLength()); assertEquals(8, struct.getLength());

View file

@ -9,5 +9,4 @@ LICENSE||GHIDRA||||END|
NOTICE||GHIDRA||||END| NOTICE||GHIDRA||||END|
README.md||GHIDRA||||END| README.md||GHIDRA||||END|
build.gradle||GHIDRA||||END| build.gradle||GHIDRA||||END|
ghidra.repos.config||GHIDRA||||END|
settings.gradle||GHIDRA||||END| settings.gradle||GHIDRA||||END|