diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataAdapter.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataAdapter.java index 34e2273989..71703dc0bd 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataAdapter.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceDataAdapter.java @@ -20,6 +20,7 @@ import java.util.Collection; import ghidra.docking.settings.Settings; import ghidra.docking.settings.SettingsDefinition; import ghidra.program.model.address.Address; +import ghidra.program.model.data.TypeDefSettingsDefinition; import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.SourceType; import ghidra.trace.database.data.DBTraceDataSettingsOperations; @@ -68,6 +69,15 @@ public interface DBTraceDataAdapter extends DBTraceCodeUnitAdapter, DataAdapterM DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent); + @Override + default boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + if (settingsDefinition instanceof TypeDefSettingsDefinition) { + return false; + } + // assume instance setting allowed if default setting allowed + return getDefaultSettings().isChangeAllowed(settingsDefinition); + } + @Override default void setLong(String name, long value) { try (LockHold hold = getTrace().lockWrite()) { diff --git a/Ghidra/Features/Base/build.gradle b/Ghidra/Features/Base/build.gradle index 5b67b3061f..e5340b1370 100644 --- a/Ghidra/Features/Base/build.gradle +++ b/Ghidra/Features/Base/build.gradle @@ -65,6 +65,7 @@ dependencies { testImplementation project(path: ':Generic', configuration: 'testArtifacts') testImplementation project(path: ':Project', configuration: 'testArtifacts') testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts') + testImplementation project(path: ':DB', configuration: 'testArtifacts') javacc 'net.java.dev.javacc:javacc:5.0' } diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm index 715387aaad..d0694af781 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeManagerPlugin/data_type_manager_description.htm @@ -800,7 +800,16 @@

A Typedef created with a Pointer base type will allow additional Settings to be made which can influence how such a pointer should be interpretted - (see Pointer-Typedef Settings).

+ (see Pointer-Typedef Settings). + If no name is assigned to a new Pointer-Typedef is will be treated as an "auto-typedef" + where a dynamic name will be assigned based upon the underlying pointer and assigned + typedef attribute settings. Examples: + +

Creating a Pointer

To create a pointer, you can click NewThe following Pointer-Typedef settings are currently supported:

All Typedef Settings must be established on diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java index 84b6f91a50..4f8e5c9aeb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypeMergeManager.java @@ -665,7 +665,6 @@ public class DataTypeMergeManager implements MergeResolver { * in RESULT; false if the data type did not have to be added */ private boolean dataTypeRenamedOrMoved(long id) { - DataType newDt = null; switch (conflictOption) { @@ -700,7 +699,7 @@ public class DataTypeMergeManager implements MergeResolver { DataType resultDt = dtms[RESULT].getDataType(id); DataType newDt = null; if (resultDt != null) { - setDataTypeName(resultDt, dt.getName()); + setDataTypeName(resultDt, dt); setCategoryPath(resultDt, dt.getCategoryPath()); } else { @@ -1645,8 +1644,12 @@ public class DataTypeMergeManager implements MergeResolver { return; } } - String name = category.getName(); - String newName = name; + String newName = category.getName(); + String baseName = newName; + int index = newName.indexOf(DataType.CONFLICT_SUFFIX); + if (index > 0) { + baseName = newName.substring(0, index); + } int oneUpNumber = 0; while (true) { try { @@ -1656,7 +1659,7 @@ public class DataTypeMergeManager implements MergeResolver { return; } ++oneUpNumber; - newName = name + DataType.CONFLICT_SUFFIX + oneUpNumber; + newName = baseName + DataType.CONFLICT_SUFFIX + oneUpNumber; } catch (DuplicateNameException e) { throw new AssertException("Got DuplicateNameException"); @@ -1673,6 +1676,11 @@ public class DataTypeMergeManager implements MergeResolver { return; } String name = newName; + String baseName = newName; + int index = newName.indexOf(DataType.CONFLICT_SUFFIX); + if (index > 0) { + baseName = newName.substring(0, index); + } int oneUpNumber = 0; while (true) { try { @@ -1681,7 +1689,7 @@ public class DataTypeMergeManager implements MergeResolver { } catch (DuplicateNameException e) { ++oneUpNumber; - name = newName + DataType.CONFLICT_SUFFIX + oneUpNumber; + name = baseName + DataType.CONFLICT_SUFFIX + oneUpNumber; } catch (InvalidNameException e) { throw new AssertException("Got InvalidNameException: " + e); @@ -1689,20 +1697,31 @@ public class DataTypeMergeManager implements MergeResolver { } } - private void setDataTypeName(DataType dt, String newName) { + private void setDataTypeName(DataType dt, DataType dtToCopy) { + if (isAutoNamedTypedef(dtToCopy)) { + if (dt instanceof TypeDef) { + ((TypeDef) dt).enableAutoNaming(); + return; + } + } + String newName = dtToCopy.getName(); if (dt.getName().equals(newName)) { return; } - String name = newName; + String baseName = newName; + int index = newName.indexOf(DataType.CONFLICT_SUFFIX); + if (index > 0) { + baseName = newName.substring(0, index); + } int oneUpNumber = 0; while (true) { try { - dt.setName(name); + dt.setName(newName); return; } catch (DuplicateNameException e) { ++oneUpNumber; - name = newName + DataType.CONFLICT_SUFFIX + oneUpNumber; + newName = baseName + DataType.CONFLICT_SUFFIX + oneUpNumber; } catch (InvalidNameException e) { throw new AssertException("Got InvalidNameException: " + e); @@ -1710,6 +1729,7 @@ public class DataTypeMergeManager implements MergeResolver { } } + private boolean categoryWasMoved(long id, DataTypeManager dtm1, DataTypeManager dtm2) { Category cat1 = dtm1.getCategory(id); Category cat2 = dtm2.getCategory(id); @@ -1765,10 +1785,24 @@ public class DataTypeMergeManager implements MergeResolver { return dataTypeWasRenamed(id, dtms[ORIGINAL], dtm); } + private boolean isAutoNamedTypedef(DataType dt) { + if (dt instanceof TypeDef) { + TypeDef td = (TypeDef) dt; + return td.isAutoNamed(); + } + return false; + } + private boolean dataTypeWasRenamed(long id, DataTypeManager dtm1, DataTypeManager dtm2) { DataType dt1 = dtm1.getDataType(id); DataType dt2 = dtm2.getDataType(id); if (dt1 != null && dt2 != null) { + if (isAutoNamedTypedef(dt1)) { + return isAutoNamedTypedef(dt2); + } + else if (isAutoNamedTypedef(dt2)) { + return false; + } String name1 = dt1.getName(); String name2 = dt2.getName(); return !name1.equals(name2); @@ -2695,7 +2729,7 @@ public class DataTypeMergeManager implements MergeResolver { DataType dt = dtms[RESULT].getDataType(id); if (dataTypeWasRenamed(id, dtms[MY])) { if (dt != null) { - setDataTypeName(dt, myDt.getName()); + setDataTypeName(dt, myDt); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java index 9c1550817d..c24c055bac 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/propertylist/PropertyListMergeManager.java @@ -57,7 +57,6 @@ public class PropertyListMergeManager implements MergeResolver { private int currentConflict; private int totalConflictCount; private ProgramMultiUserMergeManager mergeManager; - private int progressIndex; private int propertyListChoice = ASK_USER; /** @@ -157,7 +156,7 @@ public class PropertyListMergeManager implements MergeResolver { } } mergeManager.updateProgress(100); - currentMonitor.initialize(myNamesCount); + try { processConflicts(); commit = true; @@ -193,7 +192,6 @@ public class PropertyListMergeManager implements MergeResolver { return; } addProperty(list, resultList, optionName); - currentMonitor.setProgress(++progressIndex); } } @@ -251,7 +249,6 @@ public class PropertyListMergeManager implements MergeResolver { if (latestValue.equals(origValue)) { latestList.removeOption(propertyName); - currentMonitor.setProgress(++progressIndex); } else { String listName = latestList.getName(); @@ -275,25 +272,28 @@ public class PropertyListMergeManager implements MergeResolver { Object resultValue = getValue(resultList, propertyName); Object origValue = getValue(origList, propertyName); - if (!SystemUtilities.isEqual(resultValue, myValue)) { - if (propertyName.equals(Program.ANALYZED) && (myValue instanceof Boolean)) { - // If latest or my version sets "Analyzed" to true, then it should result in true. - setValue(resultList, propertyName, myList.getType(propertyName), Boolean.TRUE); - currentMonitor.setProgress(++progressIndex); - return; - } - if (SystemUtilities.isEqual(resultValue, origValue)) { - setValue(resultList, propertyName, myList.getType(propertyName), myValue); - currentMonitor.setProgress(++progressIndex); - } - else { - String listName = resultList.getName(); - ArrayList mapList = getConflictList(listName); - mapList.add(new ConflictInfo(listName, propertyName, - resultList.getType(propertyName), myList.getType(propertyName), - origList.getType(propertyName), resultValue, myValue, origValue)); - ++totalConflictCount; - } + if (SystemUtilities.isEqual(origValue, myValue) || + SystemUtilities.isEqual(resultValue, myValue)) { + // value was not modified in my program or it was changed the same as in latest + return; + } + if (propertyName.equals(Program.ANALYZED) && Boolean.TRUE.equals(myValue)) { + // If my version sets "Analyzed" to true, then it should result in true. + setValue(resultList, propertyName, myList.getType(propertyName), Boolean.TRUE); + return; + } + if (SystemUtilities.isEqual(resultValue, origValue)) { + // no change by latest - use my value + setValue(resultList, propertyName, myList.getType(propertyName), myValue); + } + else { + // my change conflicts with latest change + String listName = resultList.getName(); + ArrayList mapList = getConflictList(listName); + mapList.add(new ConflictInfo(listName, propertyName, + resultList.getType(propertyName), myList.getType(propertyName), + origList.getType(propertyName), resultValue, myValue, origValue)); + ++totalConflictCount; } } @@ -322,7 +322,6 @@ public class PropertyListMergeManager implements MergeResolver { if (!myValue.equals(origValue)) { setValue(resultList, propertyName, myList.getType(propertyName), myValue); - currentMonitor.setProgress(++progressIndex); } } @@ -412,7 +411,7 @@ public class PropertyListMergeManager implements MergeResolver { String currentListName) throws CancelledException { for (int i = 0; i < conflictList.size(); i++) { - currentMonitor.setProgress(++progressIndex); + currentMonitor.setProgress(i); ConflictInfo info = conflictList.get(i); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java index aadd6ecd3e..8af34f0823 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java @@ -67,7 +67,11 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider { Settings originalSettings) { super(title, true, false, true, false); this.settingsDefinitions = settingDefinitions; - settings = new SettingsImpl(originalSettings); + settings = new SettingsImpl(originalSettings) { + public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + return originalSettings.isChangeAllowed(settingsDefinition); + } + }; defaultSettings = settings.getDefaultSettings(); if (originalSettings != null && defaultSettings == null) { // ensure we have defaults to facilitate revert to default @@ -206,6 +210,17 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider { workPanel.add(scrollpane, BorderLayout.CENTER); + boolean hasImmutableSettings = false; + for (SettingsDefinition def : settingsDefinitions) { + if (!settings.isChangeAllowed(def)) { + hasImmutableSettings = true; + break; + } + } + if (hasImmutableSettings) { + workPanel.add(new JLabel("* Immutable setting"), BorderLayout.SOUTH); + } + return workPanel; } @@ -366,6 +381,10 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider { return definition.getName(); } + boolean isEditable() { + return settings.isChangeAllowed(definition); + } + Object getSettingsObject() { if (definition instanceof EnumSettingsDefinition) { StringChoices choices = getChoices((EnumSettingsDefinition) definition); @@ -464,7 +483,11 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider { @Override public boolean isCellEditable(int row, int col) { - return col != 0; + if (col == 0) { + return false; + } + SettingsRowObject rowObject = rows.get(row); + return rowObject.isEditable(); } @Override @@ -503,7 +526,11 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider { public Object getColumnValueForRow(SettingsRowObject t, int columnIndex) { switch (columnIndex) { case 0: - return t.getName(); + String name = t.getName(); + if (!t.isEditable()) { + name += "*"; // append immutable indicator + } + return name; case 1: return t.getSettingsObject(); case 2: diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java index 66a8429673..206f67ec26 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java @@ -63,6 +63,8 @@ public class DataTypeSettingsDialog extends AbstractSettingsDialog { if (dtm instanceof DataTypeManagerDB) { long id = dtm.getID(dt); if (id > 0) { + // FIXME: this does not handle re-mapped BuiltIn datatypes + // since multiple instances may be defined if (dt == dtm.getDataType(id)) { return; // valid original instance } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSyncInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSyncInfo.java index 3e4fb55ce8..9045246d16 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSyncInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSyncInfo.java @@ -144,7 +144,7 @@ public class DataTypeSyncInfo { } public String getRefDtPath() { - return refDt.getPathName(); + return refDt.getCategoryPath().getPath(); } public long getLastChangeTime(boolean useSource) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java index 30bf283847..3e6a3a15b1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java @@ -119,7 +119,7 @@ public class DataTypeSynchronizer { long lastChangeTime = refDT.getLastChangeTime(); DataType sourceDT = sourceDTM.resolve(refDT, DataTypeConflictHandler.REPLACE_HANDLER); if (!namesAreEquivalent(refDT, sourceDT)) { - renameDataType(sourceDTM, sourceDT, refDT.getName()); + renameDataType(sourceDTM, sourceDT, refDT); } if (!StringUtils.equals(refDT.getDescription(), sourceDT.getDescription())) { sourceDT.setDescription(refDT.getDescription()); @@ -132,7 +132,7 @@ public class DataTypeSynchronizer { long lastChangeTime = sourceDT.getLastChangeTime(); DataType refDT = refDTM.resolve(sourceDT, DataTypeConflictHandler.REPLACE_HANDLER); if (!namesAreEquivalent(refDT, sourceDT)) { - renameDataType(refDTM, refDT, sourceDT.getName()); + renameDataType(refDTM, refDT, sourceDT); } if (!StringUtils.equals(sourceDT.getDescription(), refDT.getDescription())) { refDT.setDescription(sourceDT.getDescription()); @@ -231,7 +231,15 @@ public class DataTypeSynchronizer { } } - private static void renameDataType(DataTypeManager sourceDTM, DataType sourceDT, String name) { + private static void renameDataType(DataTypeManager sourceDTM, DataType sourceDT, + DataType dtToCopy) { + if (isAutoNamedTypedef(dtToCopy)) { + if (sourceDT instanceof TypeDef) { + ((TypeDef) sourceDT).enableAutoNaming(); + return; + } + } + String name = dtToCopy.getName(); int index = name.indexOf(DataType.CONFLICT_SUFFIX); if (index > 0) { name = name.substring(0, index); @@ -254,7 +262,21 @@ public class DataTypeSynchronizer { } } + private static boolean isAutoNamedTypedef(DataType dt) { + if (dt instanceof TypeDef) { + TypeDef td = (TypeDef) dt; + return td.isAutoNamed(); + } + return false; + } + public static boolean namesAreEquivalent(DataType dt1, DataType dt2) { + if (isAutoNamedTypedef(dt1)) { + return isAutoNamedTypedef(dt2); + } + else if (isAutoNamedTypedef(dt2)) { + return false; + } String name1 = dt1.getName(); String name2 = dt2.getName(); if (name1.equals(name2)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractTypeDefAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractTypeDefAction.java index 92695f11fa..4c9d9582f4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractTypeDefAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/AbstractTypeDefAction.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +15,17 @@ */ package ghidra.app.plugin.core.datamgr.actions; -import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; -import ghidra.program.model.data.*; - import java.awt.Component; +import org.apache.commons.lang3.StringUtils; + import docking.ActionContext; import docking.ComponentProvider; import docking.action.DockingAction; import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; +import ghidra.program.model.data.*; abstract class AbstractTypeDefAction extends DockingAction { @@ -55,15 +55,27 @@ abstract class AbstractTypeDefAction extends DockingAction { return null; } - return createNewDataType(gTree, dataType, categoryPath, dataTypeManager, typeDefName); + if (StringUtils.isBlank(typeDefName)) { + // use auto-naming for pointer-typedef + if (dataType instanceof Pointer) { + // category ignored + TypeDef typedef = new PointerTypedef(null, (Pointer) dataType, dataTypeManager); + return createNewTypeDef(gTree, typedef, categoryPath, dataTypeManager); + } + // generate default typedef name + String baseName = getBaseName(dataType) + "Typedef"; + typeDefName = dataTypeManager.getUniqueName(dataType.getCategoryPath(), baseName); + } + + TypeDef typedef = new TypedefDataType(categoryPath, typeDefName, dataType); + return createNewTypeDef(gTree, typedef, categoryPath, dataTypeManager); } - private DataType createNewDataType(Component parentComponent, DataType dataType, - CategoryPath categoryPath, DataTypeManager dataTypeManager, String name) { + private DataType createNewTypeDef(Component parentComponent, TypeDef typedef, + CategoryPath categoryPath, DataTypeManager dataTypeManager) { DataType newdt = null; int transactionID = dataTypeManager.startTransaction("Create Typedef"); try { - DataType typedef = new TypedefDataType(categoryPath, name, dataType); newdt = dataTypeManager.addDataType(typedef, plugin.getConflictHandler()); } finally { @@ -72,4 +84,16 @@ abstract class AbstractTypeDefAction extends DockingAction { return newdt; } + + protected static String getBaseName(DataType dt) { + if (dt instanceof Pointer) { + DataType dataType = ((Pointer) dt).getDataType(); + if (dataType == null) { + // must be a generic pointer type + return dt.getName(); + } + return getBaseName(dataType) + "Ptr"; + } + return dt.getDisplayName(); + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java index 22e5fa35f1..2e6a1793a7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefAction.java @@ -113,16 +113,15 @@ public class CreateTypeDefAction extends AbstractTypeDefAction { DataTypeNode dataTypeNode = (DataTypeNode) selectionPaths[0].getLastPathComponent(); DataType dataType = dataTypeNode.getDataType(); - String baseName = getBaseName(dataType) + "Typedef"; + DerivativeDataTypeInfo info = new DerivativeDataTypeInfo(plugin, gTree, dataTypeNode, dataType); DataTypeManager dataTypeManager = info.getDataTypeManager(); - String name = dataTypeManager.getUniqueName(dataType.getCategoryPath(), baseName); CategoryPath categoryPath = info.getCategoryPath(); DataType newTypeDef = createTypeDef(dataTypeManager, dataType, categoryPath, context, - dataTypeNode.getParent(), name); + dataTypeNode.getParent(), null); if (newTypeDef == null) { return; } @@ -133,15 +132,5 @@ public class CreateTypeDefAction extends AbstractTypeDefAction { gTree.startEditing(finalParentNode, newNodeName); } - private static String getBaseName(DataType dt) { - if (dt instanceof Pointer) { - DataType dataType = ((Pointer) dt).getDataType(); - if (dataType == null) { - // must be a generic pointer type - return dt.getName(); - } - return getBaseName(dataType) + "Ptr"; - } - return dt.getDisplayName(); - } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java index 0da7e2667a..c69538acc8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/hover/AbstractReferenceHover.java @@ -176,6 +176,13 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover { return null; } + if (programLocation instanceof MnemonicFieldLocation) { + CodeUnit cu = program.getListing().getCodeUnitAt(programLocation.getAddress()); + if (!(cu instanceof Instruction)) { + return null; // defer to mnemonic hover for Data + } + } + Address refAddr = programLocation.getRefAddress(); if (refAddr != null && refAddr.isExternalAddress()) { return createExternalToolTipComponent(program, refAddr); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/DbViewerComponent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/DbViewerComponent.java index 436a21a894..b49678f762 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/DbViewerComponent.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/debug/DbViewerComponent.java @@ -118,7 +118,6 @@ class DbViewerComponent extends JPanel { if (dbh == null) { return; } - Msg.info(this, "Updating dbViewer..."); synchronized (dbh) { updateTableChoices((TableItem) combo.getSelectedItem()); updateTable(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/StructConverter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/StructConverter.java index 23259ebcfe..68b779cf61 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/StructConverter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/StructConverter.java @@ -70,11 +70,11 @@ public interface StructConverter { /** * Reusable 32-bit image base offset datatype. */ - public final static DataType IBO32 = new ImageBaseOffset32DataType(); + public final static DataType IBO32 = IBO32DataType.dataType; /** * Reusable 64-bit image base offset datatype. */ - public final static DataType IBO64 = new ImageBaseOffset64DataType(); + public final static DataType IBO64 = IBO64DataType.dataType; /** * Returns a structure datatype representing the diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java index 35048f503f..5f227d7296 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/ControlFlowGuard.java @@ -173,7 +173,7 @@ public class ControlFlowGuard { IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT; // Pre-define base data types used to define table entry data type - DataType ibo32 = new ImageBaseOffset32DataType(); + DataType ibo32 = new IBO32DataType(); DataType byteType = ByteDataType.dataType; CategoryPath categoryPath = new CategoryPath(CategoryPath.ROOT, "CFG"); @@ -255,7 +255,7 @@ public class ControlFlowGuard { program.getSymbolTable() .createLabel(tableAddr, GuardCFAddressTakenIatTableName, SourceType.IMPORTED); // Each table entry is an RVA (32-bit image base offset) - DataType ibo32 = new ImageBaseOffset32DataType(); + DataType ibo32 = new IBO32DataType(); for (long i = 0; i < functionCount; i++) { Data d = PeUtils.createData(program, tableAddr.add(i * ibo32.getLength()), ibo32, log); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DelayImportDescriptor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DelayImportDescriptor.java index ae65465baa..1c104676d4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DelayImportDescriptor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/DelayImportDescriptor.java @@ -299,7 +299,7 @@ public class DelayImportDescriptor implements StructConverter { @Override public DataType toDataType() throws DuplicateNameException, IOException { - DataType ibo32 = new ImageBaseOffset32DataType(); + DataType ibo32 = new IBO32DataType(); StructureDataType struct = new StructureDataType(NAME, 0); struct.add(DWORD, "grAttrs", null); struct.add(ibo32, "szName", null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDataDirectory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDataDirectory.java index c7ee77b6d8..6ad74e69e7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDataDirectory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/LoadConfigDataDirectory.java @@ -84,7 +84,8 @@ public class LoadConfigDataDirectory extends DataDirectory { if (monitor.isCancelled()) { return; } - DataType dt = ntHeader.getOptionalHeader().is64bit() ? new ImageBaseOffset64DataType() : new ImageBaseOffset32DataType(); + DataType dt = ntHeader.getOptionalHeader().is64bit() ? IBO64DataType.dataType + : IBO32DataType.dataType; PeUtils.createData(program, addr, dt, log); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java index 7d74a0bf4c..f6b71959d8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/pe/PEx64UnwindInfoDataType.java @@ -34,7 +34,7 @@ public class PEx64UnwindInfoDataType extends DynamicDataType { private final static int UNWIND_OP_INFO_FIELD_LENGTH = 0x04; private final static DataType BYTE = ByteDataType.dataType; - private final static DataType IBO32 = new ImageBaseOffset32DataType(); + private final static DataType IBO32 = new IBO32DataType(); public PEx64UnwindInfoDataType() { this(null); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java index 7b56c34661..d5b8ddbe43 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/MSDataTypeUtils.java @@ -273,7 +273,7 @@ public class MSDataTypeUtils { */ public static DataType getReferenceDataType(Program program, DataType referredToDataType) { DataTypeManager dtm = program.getDataTypeManager(); - return is64Bit(program) ? new ImageBaseOffset32DataType(dtm) + return is64Bit(program) ? new IBO32DataType(dtm) : new PointerDataType(referredToDataType); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java index a60b5fdfb9..38b7445ef1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/TypeDefDataTypeHTMLRepresentation.java @@ -86,9 +86,6 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio DataType basedataType = dataType; while (basedataType instanceof TypeDef) { basedataType = ((TypeDef) basedataType).getDataType(); - while (basedataType instanceof Pointer) { - basedataType = ((Pointer) basedataType).getDataType(); - } } return basedataType; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/ByteCountSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/ByteCountSettingsDefinition.java index 537bc1e877..f698a23633 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/ByteCountSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/ByteCountSettingsDefinition.java @@ -71,6 +71,11 @@ public class ByteCountSettingsDefinition implements EnumSettingsDefinition { return BYTE_COUNT; } + @Override + public String getStorageKey() { + return BYTE_COUNT; + } + @Override public String getDescription() { return "Selects the number of bytes to display"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitCountSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitCountSettingsDefinition.java index 53c2968ee3..10cb074c9a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitCountSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitCountSettingsDefinition.java @@ -90,6 +90,11 @@ public class CodeUnitCountSettingsDefinition implements EnumSettingsDefinition { return CODE_UNIT_COUNT; } + @Override + public String getStorageKey() { + return CODE_UNIT_COUNT; + } + @Override public String getDescription() { return "Selects the number of bytes to display"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitOffsetSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitOffsetSettingsDefinition.java index 93996d63d0..0921e229b5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitOffsetSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/CodeUnitOffsetSettingsDefinition.java @@ -92,6 +92,11 @@ public class CodeUnitOffsetSettingsDefinition implements EnumSettingsDefinition return MEMORY_OFFSET; } + @Override + public String getStorageKey() { + return MEMORY_OFFSET; + } + @Override public String getDescription() { return "Selects the relative byte offset from which to display"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionInlineSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionInlineSettingsDefinition.java index 9f9ae3f3ee..fee69246e8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionInlineSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionInlineSettingsDefinition.java @@ -77,6 +77,11 @@ public class FunctionInlineSettingsDefinition implements BooleanSettingsDefiniti return NAME; } + @Override + public String getStorageKey() { + return INLINE; + } + @Override public boolean hasValue(Settings settings) { return settings.getValue(INLINE) != null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionNoReturnSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionNoReturnSettingsDefinition.java index 1760178a24..38c2baa4e4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionNoReturnSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionNoReturnSettingsDefinition.java @@ -77,6 +77,11 @@ public class FunctionNoReturnSettingsDefinition implements BooleanSettingsDefini return NAME; } + @Override + public String getStorageKey() { + return NORETURN; + } + @Override public boolean hasValue(Settings settings) { return settings.getValue(NORETURN) != null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionThunkSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionThunkSettingsDefinition.java index 138473874d..aaad3631bd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionThunkSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/FunctionThunkSettingsDefinition.java @@ -76,6 +76,11 @@ public class FunctionThunkSettingsDefinition implements BooleanSettingsDefinitio return NAME; } + @Override + public String getStorageKey() { + return THUNK; + } + @Override public boolean hasValue(Settings settings) { return settings.getValue(THUNK) != null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryOffsetSettingsDefinition.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryOffsetSettingsDefinition.java index 046c589c45..f9d923c4bc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryOffsetSettingsDefinition.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryOffsetSettingsDefinition.java @@ -91,6 +91,11 @@ public class MemoryOffsetSettingsDefinition implements EnumSettingsDefinition { return MEMORY_OFFSET; } + @Override + public String getStorageKey() { + return MEMORY_OFFSET; + } + @Override public String getDescription() { return "Selects the relative byte offset from which to display"; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge5Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge5Test.java index a28f076ec9..dd943adc02 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge5Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge5Test.java @@ -24,6 +24,7 @@ import org.junit.Test; import ghidra.docking.settings.Settings; import ghidra.program.database.*; +import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; import ghidra.util.InvalidNameException; @@ -1433,7 +1434,7 @@ public class DataTypeMerge5Test extends AbstractDataTypeMergeTest { // NOTE: these are not viable settings but are intended to exercise all of them Settings settings = td.getDefaultSettings(); PointerTypeSettingsDefinition.DEF.setType(settings, - PointerType.IBO); + PointerType.IMAGE_BASE_RELATIVE); AddressSpaceSettingsDefinition.DEF.setValue(settings, "ROM"); ComponentOffsetSettingsDefinition.DEF.setValue(settings, 0x10); OffsetMaskSettingsDefinition.DEF.setValue(settings, 0x1234); @@ -1515,6 +1516,211 @@ public class DataTypeMerge5Test extends AbstractDataTypeMergeTest { checkConflictCount(0); } + private static String formatAttributes(String attrs) { + StringBuilder buf = new StringBuilder(DataType.TYPEDEF_ATTRIBUTE_PREFIX); + buf.append(attrs); + buf.append(DataType.TYPEDEF_ATTRIBUTE_SUFFIX); + return buf.toString(); + } + + @Test + public void testTypeDefs11() throws Exception { + + // Exercise pointer-typedef auto-naming with setting changes + + mtf.initialize("notepad2", new OriginalProgramModifierListener() { + + @Override + public void modifyOriginal(ProgramDB program) throws Exception { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + // must specify datatype manager when constructing to allow for settings to be made + Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + PointerTypedef td = + new PointerTypedef(null, foo, -1, dtm, PointerType.IMAGE_BASE_RELATIVE); + DataType dt = dtm.resolve(td, DataTypeConflictHandler.DEFAULT_HANDLER); + assertEquals("Foo * " + formatAttributes("image-base-relative"), dt.getName()); + 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 { + TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), + "Foo * " + formatAttributes("image-base-relative")); + assertNotNull(td); + td.setName("Bob_Ptr_Td"); + + Settings settings = td.getDefaultSettings(); + PointerTypeSettingsDefinition.DEF.setType(settings, + PointerType.RELATIVE); + + Structure st = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + st.setName("Bob"); + + commit = true; + } + catch (InvalidNameException | DuplicateNameException e) { + failWithException("unexpected", e); + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), + "Foo * " + formatAttributes("image-base-relative")); + assertNotNull(td); + + Settings settings = td.getDefaultSettings(); + ComponentOffsetSettingsDefinition.DEF.setValue(settings, 0x123); + + Structure st = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + st.setName("Bill"); + + commit = true; + } + catch (InvalidNameException | DuplicateNameException e) { + failWithException("unexpected", e); + } + finally { + program.endTransaction(transactionID, commit); + } + } + }); + executeMerge(); + + chooseOption(DataTypeMergeManager.OPTION_MY); // choose Bill rename of Foo + chooseOption(DataTypeMergeManager.OPTION_LATEST); // choose RELATIVE setting and Bob_Ptr_Td rename + + waitForCompletion(); + + DataTypeManager dtm = resultProgram.getDataTypeManager(); + TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), "Bob_Ptr_Td"); + assertNotNull(td); + + DataType dt = DataTypeUtilities.getBaseDataType(td.getDataType()); + assertTrue(dt instanceof Structure); + assertEquals("Bill", dt.getName()); + + Settings settings = td.getDefaultSettings(); + assertEquals( + "Expected pointer-typedef type: relative", + PointerType.RELATIVE, PointerTypeSettingsDefinition.DEF.getType(settings)); + assertFalse( + "Unexpected setting: " + + ComponentOffsetSettingsDefinition.DEF.getAttributeSpecification(settings), + ComponentOffsetSettingsDefinition.DEF.hasValue(settings)); + + checkConflictCount(0); + } + + @Test + public void testTypeDefs12() throws Exception { + + // Exercise pointer-typedef auto-naming with setting changes + + mtf.initialize("notepad2", new OriginalProgramModifierListener() { + + @Override + public void modifyOriginal(ProgramDB program) throws Exception { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + // must specify datatype manager when constructing to allow for settings to be made + Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + PointerTypedef td = + new PointerTypedef(null, foo, -1, dtm, PointerType.IMAGE_BASE_RELATIVE); + DataType dt = dtm.resolve(td, DataTypeConflictHandler.DEFAULT_HANDLER); + assertEquals("Foo * " + formatAttributes("image-base-relative"), dt.getName()); + 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 { + TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), + "Foo * " + formatAttributes("image-base-relative")); + assertNotNull(td); + td.setName("Bob_Ptr_Td"); + + Settings settings = td.getDefaultSettings(); + PointerTypeSettingsDefinition.DEF.setType(settings, + PointerType.RELATIVE); + + commit = true; + } + catch (InvalidNameException | DuplicateNameException e) { + failWithException("unexpected", e); + } + finally { + program.endTransaction(transactionID, commit); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + boolean commit = false; + DataTypeManager dtm = program.getDataTypeManager(); + int transactionID = program.startTransaction("test"); + try { + Structure st = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo"); + st.setName("Bill"); + commit = true; + } + catch (InvalidNameException | DuplicateNameException e) { + failWithException("unexpected", e); + } + finally { + program.endTransaction(transactionID, commit); + } + } + }); + executeMerge(true); + + DataTypeManager dtm = resultProgram.getDataTypeManager(); + TypeDef td = (TypeDef) dtm.getDataType(new CategoryPath("/MISC"), "Bob_Ptr_Td"); + assertNotNull(td); + + DataType dt = DataTypeUtilities.getBaseDataType(td.getDataType()); + assertTrue(dt instanceof Structure); + assertEquals("Bill", dt.getName()); + + Settings settings = td.getDefaultSettings(); + assertEquals( + "Expected pointer-typedef type: relative", + PointerType.RELATIVE, PointerTypeSettingsDefinition.DEF.getType(settings)); + assertFalse( + "Unexpected setting: " + + ComponentOffsetSettingsDefinition.DEF.getAttributeSpecification(settings), + ComponentOffsetSettingsDefinition.DEF.hasValue(settings)); + + checkConflictCount(0); + } + @Test public void testArrays() throws Exception { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/listing/ProgramContextMergeManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/listing/ProgramContextMergeManagerTest.java index 0666acc30c..0441079c0a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/listing/ProgramContextMergeManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/listing/ProgramContextMergeManagerTest.java @@ -15,8 +15,7 @@ */ package ghidra.app.merge.listing; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import java.awt.Window; import java.math.BigInteger; @@ -1165,7 +1164,7 @@ public class ProgramContextMergeManagerTest extends AbstractListingMergeManagerT */ @Override public Address addr(String address) { - return mtf.getResultProgram().getAddressFactory().getAddress(address); + return mtf.getOriginalProgram().getAddressFactory().getAddress(address); } private void setRegValue(ProgramContext pc, Address start, Address end, Register reg, diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/propertylist/PropertyListMergeManager2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/propertylist/PropertyListMergeManager2Test.java index 198f43d9e7..0be82b314a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/propertylist/PropertyListMergeManager2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/propertylist/PropertyListMergeManager2Test.java @@ -522,19 +522,7 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest { @Test public void testAnalyzedFalseInLatest() throws Exception { - mtf.initialize("notepad", new OriginalProgramModifierListener() { - - @Override - public void modifyOriginal(ProgramDB program) throws Exception { - int transactionID = program.startTransaction("test"); - try { - Options list = program.getOptions("Program Information"); - list.setBoolean("Analyzed", false); - } - finally { - program.endTransaction(transactionID, true); - } - } + mtf.initialize("notepad", new ProgramModifierListener() { @Override public void modifyLatest(ProgramDB program) { @@ -605,19 +593,7 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest { @Test public void testAnalyzedFalseInMy() throws Exception { - mtf.initialize("notepad", new OriginalProgramModifierListener() { - - @Override - public void modifyOriginal(ProgramDB program) throws Exception { - int transactionID = program.startTransaction("test"); - try { - Options list = program.getOptions("Program Information"); - list.setBoolean("Analyzed", false); - } - finally { - program.endTransaction(transactionID, true); - } - } + mtf.initialize("notepad", new ProgramModifierListener() { @Override public void modifyLatest(ProgramDB program) { @@ -734,10 +710,20 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest { public void testAnalyzedTrueInLatestFalseInMy() throws Exception { // test case: conflict because both values changed // Choose 'latest' - mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + mtf.initialize("notepad", new OriginalProgramModifierListener() { + + @Override + public void modifyOriginal(ProgramDB program) throws Exception { + int transactionID = program.startTransaction("test"); + try { + Options list = program.getOptions("Program Information"); + list.setBoolean("Analyzed", false); // revert to default state + } + finally { + program.endTransaction(transactionID, true); + } + } + @Override public void modifyLatest(ProgramDB program) { int transactionID = program.startTransaction("test"); @@ -777,10 +763,20 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest { public void testAnalyzedFalseInLatestTrueInMy() throws Exception { // test case: conflict because both values changed // Choose 'latest' - mtf.initialize("notepad", new ProgramModifierListener() { - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB) - */ + mtf.initialize("notepad", new OriginalProgramModifierListener() { + + @Override + public void modifyOriginal(ProgramDB program) throws Exception { + int transactionID = program.startTransaction("test"); + try { + Options list = program.getOptions("Program Information"); + list.setBoolean("Analyzed", false); // revert to default value + } + finally { + program.endTransaction(transactionID, true); + } + } + @Override public void modifyLatest(ProgramDB program) { int transactionID = program.startTransaction("test"); @@ -793,9 +789,6 @@ public class PropertyListMergeManager2Test extends AbstractMergeTest { } } - /* (non-Javadoc) - * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB) - */ @Override public void modifyPrivate(ProgramDB program) { int transactionID = program.startTransaction("test"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java index f18dcd8a4f..aeb266dde1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MoveBlockModelTest.java @@ -25,6 +25,8 @@ import ghidra.program.database.ProgramBuilder; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.ProgramBasedDataTypeManager; +import ghidra.program.model.data.StringDataType; +import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; import ghidra.test.AbstractGhidraHeadedIntegrationTest; @@ -44,6 +46,10 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest private volatile boolean success; private volatile String errMsg; + // Suitable settings allowed for StringDataType data + private static String LONG_SETTING_NAME = "mutability"; + private static String STRING_SETTING_NAME = "charset"; + private Program buildProgram1(String programName) throws Exception { ProgramBuilder builder = new ProgramBuilder(programName, ProgramBuilder._TOY); builder.createMemory(".text", Long.toHexString(0x1001000), 0x6600); @@ -80,11 +86,13 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest model.initialize(block); int transactionID = x8051.startTransaction("Set settings"); + ProgramBasedDataTypeManager dtm = x8051.getDataTypeManager(); for (int i = 0; i < 10; i++) { Address a = getAddr(x8051, "BITS", i); - dtm.setStringSettingsValue(a, "color", "red" + i); - dtm.setLongSettingsValue(a, "someLongValue", i); + Data d = x8051.getListing().createData(a, StringDataType.dataType, 1); + dtm.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dtm.setLongSettingsValue(d, LONG_SETTING_NAME, i); } x8051.endTransaction(transactionID, true); } @@ -198,11 +206,13 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest ProgramBasedDataTypeManager dtm = x8051.getDataTypeManager(); for (int i = 0; i < 10; i++) { Address a = getAddr(x8051, "CODE", 0x2000 + i); + Data d = x8051.getListing().getDataAt(a); + assertNotNull(d); - String s = dtm.getStringSettingsValue(a, "color"); + String s = dtm.getStringSettingsValue(d, STRING_SETTING_NAME); assertEquals("red" + i, s); - Long lvalue = dtm.getLongSettingsValue(a, "someLongValue"); + Long lvalue = dtm.getLongSettingsValue(d, LONG_SETTING_NAME); assertEquals(i, lvalue.longValue()); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java index d34ca2ef19..7668dadd3f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/ArrayTest.java @@ -17,6 +17,8 @@ package ghidra.program.database.data; import static org.junit.Assert.*; +import org.junit.*; + import ghidra.docking.settings.SettingsDefinition; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; @@ -29,8 +31,6 @@ import ghidra.program.model.mem.Memory; import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.util.task.TaskMonitorAdapter; -import org.junit.*; - /** * * To change the template for this generated type comment go to @@ -171,15 +171,15 @@ public class ArrayTest extends AbstractGhidraHeadedIntegrationTest { for (int i = 0; i < 10; i++) { Data comp = data.getComponent(i); - assertEquals(null, comp.getLong("MySetting")); + assertEquals(null, comp.getLong("format")); } Data component4 = data.getComponent(4); - component4.setLong("MySetting", 10L); + component4.setLong("format", 10L); for (int i = 0; i < 10; i++) { Data comp = data.getComponent(i); - assertEquals((Long) 10L, comp.getLong("MySetting")); + assertEquals((Long) 10L, comp.getLong("format")); } } @@ -199,15 +199,15 @@ public class ArrayTest extends AbstractGhidraHeadedIntegrationTest { for (int i = 0; i < 10; i++) { Data comp = subData.getComponent(i); - assertEquals(null, comp.getLong("MySetting")); + assertEquals(null, comp.getLong("format")); } Data component4 = subData.getComponent(4); - component4.setLong("MySetting", 10L); + component4.setLong("format", 10L); for (int i = 0; i < 10; i++) { Data comp = subData.getComponent(i); - assertEquals((Long) 10L, comp.getLong("MySetting")); + assertEquals((Long) 10L, comp.getLong("format")); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java index 87545151e9..f06e93686d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/data/SettingsTest.java @@ -27,6 +27,7 @@ import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.*; +import ghidra.program.model.data.DataUtilities.ClearDataMode; import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Listing; import ghidra.program.model.mem.Memory; @@ -47,6 +48,10 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { private AddressSpace space; private int transactionID; + // Suitable settings allowed for StringDataType data + private static String LONG_SETTING_NAME = "mutability"; + private static String STRING_SETTING_NAME = "charset"; + // NOTE: Datatypes must be resolved before settings may be changed // with the exception of TypeDefDataType which does permit // TypeDefSettingsDefinition settings defined by the base-datatype. @@ -63,6 +68,16 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { listing = program.getListing(); transactionID = program.startTransaction("Test"); addBlock(); + // pointer-typedef has the largest +// System.out.println("Defined string settings:"); +// for (SettingsDefinition def : StringDataType.dataType.getSettingsDefinitions()) { +// System.out.println(def.getStorageKey()); +// } + + for (int i = 0; i < 40; i++) { + DataUtilities.createData(program, addr(i), StringDataType.dataType, 1, false, + ClearDataMode.CLEAR_ALL_CONFLICT_DATA); + } } @After @@ -76,31 +91,33 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testSetDefaultSettings() throws Exception { - DataType dt = ByteDataType.dataType; + DataType dt = StringDataType.dataType; Settings defaultSettings = dt.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); - assertNull(defaultSettings.getString("color")); - assertNull(defaultSettings.getLong("someLongValue")); + // immutable warnings expected + defaultSettings.setString(STRING_SETTING_NAME, "red"); + defaultSettings.setLong(LONG_SETTING_NAME, 10); + + assertNull(defaultSettings.getString(STRING_SETTING_NAME)); + assertNull(defaultSettings.getLong(LONG_SETTING_NAME)); // May modify byte default settings after resolve dt = dataMgr.resolve(dt, null); defaultSettings = dt.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); + defaultSettings.setString(STRING_SETTING_NAME, "red"); + defaultSettings.setLong(LONG_SETTING_NAME, 10); - assertEquals("red", defaultSettings.getString("color")); - Long lv = defaultSettings.getLong("someLongValue"); + assertEquals("red", defaultSettings.getString(STRING_SETTING_NAME)); + Long lv = defaultSettings.getLong(LONG_SETTING_NAME); assertNotNull(lv); assertEquals(10, lv.longValue()); - defaultSettings.setValue("long", 10L); - Object obj = defaultSettings.getValue("long"); - assertNotNull(obj); - assertEquals(10, ((Long) obj).longValue()); + defaultSettings.setValue(LONG_SETTING_NAME, 20L); + Object obj = defaultSettings.getValue(LONG_SETTING_NAME); + assertTrue(obj instanceof Long); + assertEquals(20, ((Long) obj).longValue()); } @Test @@ -112,31 +129,32 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { assertEquals(0, ByteDataType.dataType.getTypeDefSettingsDefinitions().length); Settings defaultSettings = typeDef.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); - assertNull(defaultSettings.getString("color")); - assertNull(defaultSettings.getLong("someLongValue")); + // immutable warnings expected + FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR); + EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false); + PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true); + + assertNull(defaultSettings.getLong("format")); + assertNull(defaultSettings.getLong("endian")); + assertNull(defaultSettings.getLong("padding")); // May modify arbitrary typedef default settings after resolve typeDef = (TypeDef) dataMgr.resolve(typeDef, null); defaultSettings = typeDef.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); + FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR); + EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false); + PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true); - assertEquals("red", defaultSettings.getString("color")); - Long lv = defaultSettings.getLong("someLongValue"); - assertNotNull(lv); - assertEquals(10, lv.longValue()); - - defaultSettings.setValue("long", 10L); - Object obj = defaultSettings.getValue("long"); - assertNotNull(obj); - assertEquals(10, ((Long) obj).longValue()); + assertEquals(FormatSettingsDefinition.CHAR, defaultSettings.getLong("format").longValue()); + assertEquals(EndianSettingsDefinition.LITTLE, + defaultSettings.getLong("endian").longValue()); + assertEquals(PaddingSettingsDefinition.PADDED_VALUE, + defaultSettings.getLong("padded").longValue()); try { - defaultSettings.setValue("color", Color.RED); + defaultSettings.setValue("format", Color.RED); Assert.fail("Should not be able to set arbitrary objects"); } catch (IllegalArgumentException e) { @@ -147,10 +165,10 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testIsEmpty() throws Exception { - DataType dt = dataMgr.resolve(ByteDataType.dataType, null); - Settings defaultSettings = dt.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); + Data data = listing.getDataAt(addr(10)); + Settings defaultSettings = data.getDataType().getDefaultSettings(); + defaultSettings.setString(STRING_SETTING_NAME, "red"); + defaultSettings.setLong(LONG_SETTING_NAME, 10); assertTrue(!defaultSettings.isEmpty()); @@ -161,107 +179,99 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { @Test public void testGetNames() throws Exception { - DataType dt = dataMgr.resolve(ByteDataType.dataType, null); + DataType dt = dataMgr.resolve(StringDataType.dataType, null); Settings defaultSettings = dt.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); - defaultSettings.setString("endian", "big Endian"); + defaultSettings.setString(STRING_SETTING_NAME, "red"); + defaultSettings.setLong(LONG_SETTING_NAME, 10); String[] names = defaultSettings.getNames(); - assertEquals(3, names.length); + assertEquals(2, names.length); } @Test public void testClearSetting() throws Exception { - DataType dt = dataMgr.resolve(ByteDataType.dataType, null); + DataType dt = dataMgr.resolve(StringDataType.dataType, null); Settings defaultSettings = dt.getDefaultSettings(); - defaultSettings.setString("color", "red"); - defaultSettings.setLong("someLongValue", 10); + defaultSettings.setString(STRING_SETTING_NAME, "red"); + defaultSettings.setLong(LONG_SETTING_NAME, 10); - defaultSettings.clearSetting("color"); - assertNull(defaultSettings.getString("color")); + defaultSettings.clearSetting(STRING_SETTING_NAME); + assertNull(defaultSettings.getString(STRING_SETTING_NAME)); } @Test public void testInstanceSettings() throws Exception { - listing.createData(addr(10), new ByteDataType(), 1); - Data data = listing.getDataAt(addr(10)); - ByteDataType dt = (ByteDataType) data.getDataType(); + Data data = DataUtilities.createData(program, addr(10), ByteDataType.dataType, 1, false, + ClearDataMode.CLEAR_ALL_CONFLICT_DATA); + + DataType dt = data.getDataType(); Settings defaultSettings = dt.getDefaultSettings(); - defaultSettings.setLong("format", FormatSettingsDefinition.CHAR); - defaultSettings.setLong("signed", 0); - defaultSettings.setLong("padded", 1); + FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.CHAR); + EndianSettingsDefinition.DEF.setBigEndian(defaultSettings, false); + PaddingSettingsDefinition.DEF.setPadded(defaultSettings, true); - SettingsDefinition[] defs = dt.getSettingsDefinitions(); - for (int i = 0; i < defs.length; i++) { + assertEquals(FormatSettingsDefinition.CHAR, data.getLong("format").longValue()); + FormatSettingsDefinition.DEF.setChoice(data, FormatSettingsDefinition.DECIMAL); + assertEquals(FormatSettingsDefinition.DECIMAL, data.getLong("format").longValue()); - if (defs[i] instanceof EnumSettingsDefinition) { - EnumSettingsDefinition enumDef = (EnumSettingsDefinition) defs[i]; - int value = enumDef.getChoice(data); - enumDef.setChoice(data, value); - if (i == 0) { - assertEquals(FormatSettingsDefinition.CHAR, data.getLong("format").longValue()); - } - else if (i == 1) { - assertEquals(0, data.getLong("signed").longValue()); - } - else if (i == 2) { - assertEquals(1, data.getLong("padded").longValue()); - } + assertEquals(EndianSettingsDefinition.LITTLE, data.getLong("endian").longValue()); + EndianSettingsDefinition.DEF.setChoice(data, EndianSettingsDefinition.BIG); + assertEquals(EndianSettingsDefinition.BIG, data.getLong("endian").longValue()); - } - } + assertEquals(PaddingSettingsDefinition.PADDED_VALUE, data.getLong("padded").longValue()); + PaddingSettingsDefinition.DEF.setChoice(data, PaddingSettingsDefinition.UNPADDED_VALUE); + assertEquals(PaddingSettingsDefinition.UNPADDED_VALUE, data.getLong("padded").longValue()); + + FormatSettingsDefinition.DEF.setChoice(defaultSettings, FormatSettingsDefinition.HEX); + EndianSettingsDefinition.DEF.clear(defaultSettings); + PaddingSettingsDefinition.DEF.clear(defaultSettings); + + assertEquals(FormatSettingsDefinition.DECIMAL, data.getLong("format").longValue()); + assertEquals(EndianSettingsDefinition.BIG, data.getLong("endian").longValue()); + assertEquals(PaddingSettingsDefinition.UNPADDED_VALUE, data.getLong("padded").longValue()); } @Test public void testGetInstanceNames() throws Exception { - listing.createData(addr(10), new ByteDataType(), 1); Data data = listing.getDataAt(addr(10)); - data.setString("color", "red"); - data.setLong("someLongValue", 10); - data.setString("endian", "big Endian"); + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); String[] names = data.getNames(); - assertEquals(3, names.length); + assertEquals(2, names.length); } @Test public void testClearInstanceSettings() throws Exception { - listing.createData(addr(10), new ByteDataType(), 1); Data data = listing.getDataAt(addr(10)); - data.setString("color", "red"); - data.setLong("someLongValue", 10); + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); - data.clearSetting("color"); - assertNull(data.getString("color")); + data.clearSetting(STRING_SETTING_NAME); + assertNull(data.getString(STRING_SETTING_NAME)); } @Test public void testClearAllInstanceSettings() throws Exception { - listing.createData(addr(10), new ByteDataType(), 1); Data data = listing.getDataAt(addr(10)); - data.setString("color", "red"); - data.setLong("someLongValue", 10); - data.setString("endian", "big Endian"); + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); data.clearAllSettings(); - assertNull(data.getString("color")); - assertNull(data.getLong("someLongValue")); - assertNull(data.getString("endian")); + assertNull(data.getString(STRING_SETTING_NAME)); + assertNull(data.getLong(LONG_SETTING_NAME)); } @Test public void testIsEmptyInstanceSettings() throws Exception { - listing.createData(addr(10), new ByteDataType(), 1); Data data = listing.getDataAt(addr(10)); - data.setString("color", "red"); - data.setLong("someLongValue", 10); - data.setString("endian", "big Endian"); + data.setString(STRING_SETTING_NAME, "red"); + data.setLong(LONG_SETTING_NAME, 10); assertTrue(!data.isEmpty()); data.clearAllSettings(); @@ -269,23 +279,29 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { assertTrue(data.isEmpty()); } + private Data getDataAt(long offset) { + Data data = listing.getDataAt(addr(offset)); + assertNotNull("expected data at address 0x" + Long.toHexString(offset)); + return data; + } + @Test public void testMoveSettings() throws Exception { for (int i = 0; i < 10; i++) { - Address a = addr(i); - dataMgr.setStringSettingsValue(a, "color", "red" + i); - dataMgr.setLongSettingsValue(a, "someLongValue", i); + Data d = getDataAt(i); + dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i); } dataMgr.moveAddressRange(addr(0), addr(20), 10, TaskMonitor.DUMMY); int j = 0; for (int i = 20; i < 30; i++, j++) { - Address a = addr(i); + Data d = getDataAt(i); - String s = dataMgr.getStringSettingsValue(a, "color"); + String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME); assertEquals("red" + j, s); - Long lvalue = dataMgr.getLongSettingsValue(a, "someLongValue"); + Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME); assertEquals(j, lvalue.longValue()); } } @@ -294,9 +310,9 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { public void testMoveSettings2() { for (int i = 0; i < 10; i++) { - Address a = addr(i); - dataMgr.setStringSettingsValue(a, "color", "red" + i); - dataMgr.setLongSettingsValue(a, "someLongValue", i); + Data d = getDataAt(i); + dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i); } try { dataMgr.moveAddressRange(addr(0), addr(5), 10, TaskMonitor.DUMMY); @@ -307,12 +323,12 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { int j = 0; for (int i = 5; i < 15; i++, j++) { - Address a = addr(i); + Data d = getDataAt(i); - String s = dataMgr.getStringSettingsValue(a, "color"); + String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME); assertEquals("red" + j, s); - Long lvalue = dataMgr.getLongSettingsValue(a, "someLongValue"); + Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME); assertEquals(j, lvalue.longValue()); } } @@ -322,9 +338,9 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { int j = 20; for (int i = 20; i < 30; i++, j++) { - Address a = addr(i); - dataMgr.setStringSettingsValue(a, "color", "red" + i); - dataMgr.setLongSettingsValue(a, "someLongValue", i); + Data d = getDataAt(i); + dataMgr.setStringSettingsValue(d, STRING_SETTING_NAME, "red" + i); + dataMgr.setLongSettingsValue(d, LONG_SETTING_NAME, i); } j = 20; try { @@ -334,12 +350,12 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { Assert.fail("Unexpected cancelled exception"); } for (int i = 5; i < 15; i++, j++) { - Address a = addr(i); + Data d = getDataAt(i); - String s = dataMgr.getStringSettingsValue(a, "color"); + String s = dataMgr.getStringSettingsValue(d, STRING_SETTING_NAME); assertEquals("red" + j, s); - Long lvalue = dataMgr.getLongSettingsValue(a, "someLongValue"); + Long lvalue = dataMgr.getLongSettingsValue(d, LONG_SETTING_NAME); assertEquals(j, lvalue.longValue()); } @@ -378,8 +394,7 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { DataType byteDT = dataMgr.resolve(new ByteDataType(), null); SettingsDefinition[] settingsDefinitions = byteDT.getSettingsDefinitions(); Settings settings = byteDT.getDefaultSettings(); - settings.setLong("format", FormatSettingsDefinition.OCTAL); - settings.setString("color", "red"); + FormatSettingsDefinition.DEF.setChoice(settings, FormatSettingsDefinition.OCTAL); TypedefDataType tdt = new TypedefDataType("ByteTypedef", byteDT); TypeDef td = (TypeDef) dataMgr.addDataType(tdt, null); @@ -388,11 +403,18 @@ public class SettingsTest extends AbstractGhidraHeadedIntegrationTest { assertTrue(sdefs.length >= settingsDefinitions.length); // TypeDef may add some of its own Settings defSettings = td.getDefaultSettings(); - defSettings.setLong("someLongValue", 10); + assertEquals(FormatSettingsDefinition.OCTAL, + FormatSettingsDefinition.DEF.getChoice(defSettings)); + + FormatSettingsDefinition.DEF.setChoice(defSettings, FormatSettingsDefinition.DECIMAL); + assertEquals(FormatSettingsDefinition.DECIMAL, + FormatSettingsDefinition.DEF.getChoice(defSettings)); + + FormatSettingsDefinition.DEF.setChoice(settings, FormatSettingsDefinition.HEX); + + assertEquals(FormatSettingsDefinition.DECIMAL, + FormatSettingsDefinition.DEF.getChoice(defSettings)); // unchanged - assertEquals((long) FormatSettingsDefinition.OCTAL, defSettings.getValue("format")); // inherits from byteDt - assertEquals("red", defSettings.getValue("color")); // inherits from byteDt - assertEquals(10L, defSettings.getValue("someLongValue")); } @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/PointerTypedefDataTypeTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/PointerTypedefDataTypeTest.java index 31d18f0d6e..b24c469385 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/PointerTypedefDataTypeTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/model/data/PointerTypedefDataTypeTest.java @@ -19,6 +19,8 @@ import static org.junit.Assert.*; import org.junit.*; +import ghidra.docking.settings.Settings; +import ghidra.program.database.DatabaseObject; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.data.PointerTypedefInspector; import ghidra.program.model.address.AddressSpace; @@ -52,7 +54,7 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT program = buildProgram("notepad"); dtm = program.getDataTypeManager(); builtInDtm = BuiltInDataTypeManager.getDataTypeManager(); - + program.startTransaction("TEST"); } @@ -63,75 +65,241 @@ public class PointerTypedefDataTypeTest extends AbstractGhidraHeadedIntegrationT } @Test - public void testIBOBuiltIn() throws Exception { + public void testBuiltInIBODataTypes() throws Exception { DataType dt = builtInDtm.getDataType(CategoryPath.ROOT, IBO32DataType.NAME); - assertTrue(dt instanceof TypeDef); - assertFalse(dt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB + assertTrue(dt instanceof IBO32DataType); assertEquals(IBO32DataType.NAME, dt.getName()); assertFalse(dt.hasLanguageDependantLength()); - assertTrue(dt.isEquivalent(dtm.resolve(dt, null))); - - dt = new IBO32DataType(CharDataType.dataType, dtm); - assertTrue(dt instanceof TypeDef); - assertTrue(dt instanceof BuiltIn); - assertEquals("char *32 __attribute__((image-base-relative))", dt.getName()); DataType dbDt = dtm.resolve(dt, null); - assertTrue(dbDt instanceof TypeDef); - assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB + assertTrue(dbDt instanceof IBO32DataType); assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); dt = builtInDtm.getDataType(CategoryPath.ROOT, IBO64DataType.NAME); - assertTrue(dt instanceof TypeDef); - assertFalse(dt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB + assertTrue(dt instanceof IBO64DataType); assertEquals(IBO64DataType.NAME, dt.getName()); assertFalse(dt.hasLanguageDependantLength()); - assertTrue(dt.isEquivalent(dtm.resolve(dt, null))); - - dt = new IBO64DataType(CharDataType.dataType, dtm); - assertTrue(dt instanceof TypeDef); - assertTrue(dt instanceof BuiltIn); - assertEquals("char *64 __attribute__((image-base-relative))", dt.getName()); dbDt = dtm.resolve(dt, null); - assertTrue(dbDt instanceof TypeDef); - assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB + assertTrue(dbDt instanceof IBO64DataType); assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); } @Test public void testPointerTypedef() throws Exception { - DataType dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm, - program.getAddressFactory().getRegisterSpace()); + DataType dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm, 0x8); assertTrue(dt.hasLanguageDependantLength()); assertEquals(4, dt.getLength()); assertTrue(dt instanceof TypeDef); - assertTrue(dt instanceof BuiltIn); - assertEquals("char * __attribute__((space(register)))", dt.getName()); + assertEquals("char * " + formatAttributes("offset(0x8)"), dt.getName()); DataType dbDt = dtm.resolve(dt, null); assertTrue(dbDt instanceof TypeDef); - assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); AddressSpace space = PointerTypedefInspector.getPointerAddressSpace((TypeDef) dbDt, program.getAddressFactory()); - assertTrue(program.getAddressFactory().getRegisterSpace().equals(space)); + assertNull(space); dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm, PointerType.RELATIVE); assertTrue(dt.hasLanguageDependantLength()); assertEquals(4, dt.getLength()); assertTrue(dt instanceof TypeDef); - assertTrue(dt instanceof BuiltIn); - assertEquals("char * __attribute__((relative))", dt.getName()); + assertEquals("char * " + formatAttributes("relative"), dt.getName()); dbDt = dtm.resolve(dt, null); assertTrue(dbDt instanceof TypeDef); - assertFalse(dbDt instanceof BuiltIn); // transforms from BuiltIn to DataTypeDB + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); assertEquals(PointerType.RELATIVE, PointerTypedefInspector.getPointerType((TypeDef) dbDt)); } + @Test + public void testPointerTypedefWithAddrSpace() throws Exception { + DataType dt = new PointerTypedef(null, CharDataType.dataType, -1, dtm, + program.getAddressFactory().getRegisterSpace()); + assertFalse(dt.hasLanguageDependantLength()); + assertEquals(2, dt.getLength()); + assertTrue(dt instanceof TypeDef); + assertEquals("char *16 " + formatAttributes("space(register)"), dt.getName()); + DataType dbDt = dtm.resolve(dt, null); + assertTrue(dbDt instanceof TypeDef); + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB + assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); + + AddressSpace space = PointerTypedefInspector.getPointerAddressSpace((TypeDef) dbDt, + program.getAddressFactory()); + assertTrue(program.getAddressFactory().getRegisterSpace().equals(space)); + + dt = new PointerTypedef(null, CharDataType.dataType, 4, dtm, + program.getAddressFactory().getRegisterSpace()); + assertFalse(dt.hasLanguageDependantLength()); + assertEquals(4, dt.getLength()); + assertTrue(dt instanceof TypeDef); + assertEquals("char *32 " + formatAttributes("space(register)"), dt.getName()); + dbDt = dtm.resolve(dt, null); + assertTrue(dbDt instanceof TypeDef); + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB + assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); + + space = PointerTypedefInspector.getPointerAddressSpace((TypeDef) dbDt, + program.getAddressFactory()); + assertTrue(program.getAddressFactory().getRegisterSpace().equals(space)); + } + + @Test + public void testPointerTypedefAutoNaming() throws Exception { + + DataType st = dtm.resolve(new StructureDataType("foo", 10), null); + + DataType dt = new PointerTypedef(null, st, -1, dtm, + program.getAddressFactory().getRegisterSpace()); + assertFalse(dt.hasLanguageDependantLength()); + assertEquals(2, dt.getLength()); + assertTrue(dt instanceof TypeDef); + assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName()); + DataType dbDt = dtm.resolve(dt, null); + assertTrue(dbDt instanceof TypeDef); + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB + assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); + + st.setName("bob"); + + // auto-name should update + assertEquals("bob *16 " + formatAttributes("space(register)"), dbDt.getName()); + + Settings settings = dbDt.getDefaultSettings(); + + OffsetMaskSettingsDefinition.DEF.setValue(settings, 0x123456789abcdef0L); + assertEquals("bob *16 " + formatAttributes("space(register),mask(0x123456789abcdef0)"), + dbDt.getName()); + + ComponentOffsetSettingsDefinition.DEF.setValue(settings, 0x123); + assertEquals( + "bob *16 " + formatAttributes("space(register),mask(0x123456789abcdef0),offset(0x123)"), + dbDt.getName()); + + OffsetShiftSettingsDefinition.DEF.setValue(settings, 16); + assertEquals( + "bob *16 " + formatAttributes( + "space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"), + dbDt.getName()); + + PointerTypeSettingsDefinition.DEF.setType(settings, PointerType.IMAGE_BASE_RELATIVE); + assertEquals( + "bob *16 " + formatAttributes( + "image-base-relative,space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"), + dbDt.getName()); + + st.setName("bill"); + assertEquals( + "bill *16 " + formatAttributes( + "image-base-relative,space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"), + dbDt.getName()); + + PointerTypeSettingsDefinition.DEF.clear(settings); + assertEquals( + "bill *16 " + formatAttributes( + "space(register),mask(0x123456789abcdef0),shift(16),offset(0x123)"), + dbDt.getName()); + + ComponentOffsetSettingsDefinition.DEF.clear(settings); + assertEquals( + "bill *16 " + formatAttributes("space(register),mask(0x123456789abcdef0),shift(16)"), + dbDt.getName()); + + // NOTE: Changing address space setting will not alter pointer size + + AddressSpaceSettingsDefinition.DEF.clear(settings); + assertEquals( + "bill *16 " + formatAttributes("mask(0x123456789abcdef0),shift(16)"), + dbDt.getName()); + + OffsetShiftSettingsDefinition.DEF.clear(settings); + assertEquals( + "bill *16 " + formatAttributes("mask(0x123456789abcdef0)"), + dbDt.getName()); + + } + + @Test + public void testPointerTypedefEquivalence() throws Exception { + + DataType st = dtm.resolve(new StructureDataType("foo", 10), null); + + TypeDef dt = new PointerTypedef(null, st, -1, dtm, + program.getAddressFactory().getRegisterSpace()); + assertTrue(dt.isAutoNamed()); + assertFalse(dt.hasLanguageDependantLength()); + assertEquals(2, dt.getLength()); + assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName()); + TypeDef dbDt = (TypeDef) dtm.resolve(dt, null); + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB + assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); + + dt = (TypeDef) dt.copy(dtm); + assertTrue(dbDt == dtm.resolve(dt, null)); // should resolve to same instance + + dt = (TypeDef) dt.copy(dtm); + PointerTypeSettingsDefinition.DEF.setType(dt.getDefaultSettings(), PointerType.IMAGE_BASE_RELATIVE); + TypeDef dbDt2 = (TypeDef) dtm.resolve(dt, null); // should resolve to new instance + assertTrue(dbDt != dbDt2); + assertEquals("foo *16 " + formatAttributes("image-base-relative,space(register)"), + dbDt2.getName()); + + PointerTypeSettingsDefinition.DEF.clear(dbDt2.getDefaultSettings()); + assertEquals("foo *16 " + formatAttributes("space(register)") + DataType.CONFLICT_SUFFIX, + dbDt2.getName()); + + } + + @Test + public void testPointerTypedefEquivalence2() throws Exception { + + DataType st = dtm.resolve(new StructureDataType("foo", 10), null); + + TypeDef dt = new PointerTypedef(null, st, -1, dtm, + program.getAddressFactory().getRegisterSpace()); + assertTrue(dt.isAutoNamed()); + assertFalse(dt.hasLanguageDependantLength()); + assertEquals(2, dt.getLength()); + assertEquals("foo *16 " + formatAttributes("space(register)"), dt.getName()); + TypeDef dbDt = (TypeDef) dtm.resolve(dt, null); + assertTrue(dbDt instanceof DatabaseObject); // transforms to TypedefDB + assertTrue(dt.isEquivalent(dbDt)); + assertEquals(dt.getName(), dbDt.getName()); + + TypeDef dt2 = new PointerTypedef("john", st, -1, dtm, + program.getAddressFactory().getRegisterSpace()); + assertFalse(dt2.isAutoNamed()); + assertFalse(dt2.hasLanguageDependantLength()); + assertEquals(2, dt.getLength()); + assertEquals("john", dt2.getName()); + TypeDef dbDt2 = (TypeDef) dtm.resolve(dt2, null); + assertTrue(dbDt != dbDt2); + assertFalse(dbDt.isEquivalent(dbDt2)); + assertFalse(dbDt2.isEquivalent(dbDt)); + assertTrue(dbDt2 instanceof DatabaseObject); // transforms to TypedefDB + assertTrue(dt2.isEquivalent(dbDt2)); + assertEquals(dt2.getName(), dbDt2.getName()); + + } + + private static String formatAttributes(String attrs) { + StringBuilder buf = new StringBuilder(DataType.TYPEDEF_ATTRIBUTE_PREFIX); + buf.append(attrs); + buf.append(DataType.TYPEDEF_ATTRIBUTE_SUFFIX); + return buf.toString(); + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/AbstractMTFModel.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/AbstractMTFModel.java index d5fb7ca90c..52d07c1b86 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/AbstractMTFModel.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/AbstractMTFModel.java @@ -17,6 +17,7 @@ package ghidra.program.database; import java.io.IOException; +import db.*; import db.buffers.BufferFile; import generic.test.AbstractGenericTest; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; @@ -28,6 +29,7 @@ import ghidra.program.model.listing.ProgramChangeSet; import ghidra.test.TestEnv; import ghidra.util.InvalidNameException; import ghidra.util.exception.CancelledException; +import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; /** @@ -105,7 +107,7 @@ public abstract class AbstractMTFModel { return privateChangeSet; } - public ProgramChangeSet getResultChangeSet() { + public ProgramChangeSet getLatestChangeSet() { return latestChangeSet; } @@ -113,7 +115,7 @@ public abstract class AbstractMTFModel { return env; } - protected void disableAutoAnalysis(Program p) { + protected static void disableAutoAnalysis(Program p) { // Disable all analysis AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(p); AbstractGenericTest.setInstanceField("isEnabled", analysisMgr, Boolean.FALSE); @@ -169,4 +171,25 @@ public abstract class AbstractMTFModel { throws Exception; public abstract void initialize(String programName, ProgramModifierListener l) throws Exception; + + /** + * Clone a program to a new instance. The new instance will be assigned an empty change-set. + * @param prog program to be cloned + * @param consumer new program consumer + * @return new program instance + * @throws IOException if a file IO error occurs + */ + public static ProgramDB cloneProgram(ProgramDB prog, Object consumer) throws IOException { + try { + DBHandle newDbh = DBTestUtils.cloneDbHandle(prog.getDBHandle()); + ProgramDB newProg = + new ProgramDB(newDbh, DBConstants.UPDATE, TaskMonitor.DUMMY, consumer); + newProg.setChangeSet(new ProgramDBChangeSet(newProg.getAddressMap(), 20)); + disableAutoAnalysis(newProg); + return newProg; + } + catch (CancelledException | VersionException e) { + throw new RuntimeException(e); // unexpected + } + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java index 574d072473..66bcf0ef1a 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/MergeTestFacilitator.java @@ -90,7 +90,7 @@ public class MergeTestFacilitator { * Get the change set for Result program. */ public ProgramChangeSet getResultChangeSet() { - return model.getResultChangeSet(); + return model.getLatestChangeSet(); } public void dispose() { diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/RealProgramMTFModel.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/RealProgramMTFModel.java index 369b3b7496..814c28517e 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/RealProgramMTFModel.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/RealProgramMTFModel.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,11 +29,7 @@ import ghidra.util.exception.AssertException; // TODO rename--this is no longer using real programs public class RealProgramMTFModel extends AbstractMTFModel { - /** - * We install our test ID generator to ensure that all datatypes we create will share the - * same ID across all versions of the programs we create. If we do not do this, then they will - * be different, which breaks many tests. - */ + // Use simple ID generation private TestUniversalIdGenerator universalIdGenerator = new TestUniversalIdGenerator(); RealProgramMTFModel(TestEnv env) { @@ -46,12 +41,15 @@ public class RealProgramMTFModel extends AbstractMTFModel { cleanup(); MergeProgramGenerator programGenerator = createProgramGenerator(programName); - generatePrograms(programName, programGenerator); + originalProgram = programGenerator.generateProgram(programName); - disableAutoAnalysis(); + latestProgram = cloneProgram(originalProgram, this); + modifier.modifyLatest(latestProgram); - makeIncomingChanges(modifier); - makeLocalChanges(modifier); + resultProgram = cloneProgram(latestProgram, this); + + privateProgram = cloneProgram(originalProgram, this); + modifier.modifyPrivate(privateProgram); recordChanges(); clearChanges(); @@ -63,36 +61,21 @@ public class RealProgramMTFModel extends AbstractMTFModel { cleanup(); MergeProgramGenerator programGenerator = createProgramGenerator(programName); - generatePrograms(programName, programGenerator); + originalProgram = programGenerator.generateProgram(programName); + modifier.modifyOriginal(originalProgram); - disableAutoAnalysis(); + privateProgram = cloneProgram(originalProgram, this); + modifier.modifyPrivate(privateProgram); - createCustomStartingProgram(modifier); - clearChanges(); + latestProgram = cloneProgram(originalProgram, this); + modifier.modifyLatest(latestProgram); - makeIncomingChanges(modifier); - makeLocalChanges(modifier); + resultProgram = cloneProgram(latestProgram, this); recordChanges(); clearChanges(); } - /** - * Tests that use this method are creating a new 'original' program, rather than using - * a pre-fabricated one from a program builder. - */ - private void createCustomStartingProgram(OriginalProgramModifierListener modifier) - throws Exception { - universalIdGenerator.checkpoint(); - modifier.modifyOriginal(originalProgram); - universalIdGenerator.restore(); - modifier.modifyOriginal(privateProgram); - universalIdGenerator.restore(); - modifier.modifyOriginal(latestProgram); - universalIdGenerator.restore(); - modifier.modifyOriginal(resultProgram); - } - private MergeProgramGenerator createProgramGenerator(String programName) { if (programName.toLowerCase().contains("notepad")) { return new MergeProgramGenerator_Notepads(this); @@ -117,35 +100,6 @@ public class RealProgramMTFModel extends AbstractMTFModel { throw new UnsupportedOperationException(); } - private void disableAutoAnalysis() { - disableAutoAnalysis(privateProgram); - disableAutoAnalysis(resultProgram); - disableAutoAnalysis(latestProgram); - } - - private void makeLocalChanges(ProgramModifierListener modifier) throws Exception { - modifier.modifyPrivate(privateProgram); - } - - private void makeIncomingChanges(ProgramModifierListener modifier) throws Exception { - universalIdGenerator.checkpoint(); - modifier.modifyLatest(latestProgram); - universalIdGenerator.restore(); - modifier.modifyLatest(resultProgram); - } - - private void generatePrograms(String programName, MergeProgramGenerator programGenerator) - throws Exception { - universalIdGenerator.checkpoint(); - privateProgram = programGenerator.generateProgram(programName); - universalIdGenerator.restore(); - originalProgram = programGenerator.generateProgram(programName); - universalIdGenerator.restore(); - resultProgram = programGenerator.generateProgram(programName); - universalIdGenerator.restore(); - latestProgram = programGenerator.generateProgram(programName); - } - private void recordChanges() { // ...keep track of the changes we've made latestChangeSet = latestProgram.getChanges(); diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java index c4adf5df18..777e511f05 100644 --- a/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java +++ b/Ghidra/Features/Decompiler/ghidra_scripts/classrecovery/ExtendedFlatProgramAPI.java @@ -581,8 +581,8 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI { int addressSize = address.getSize(); if (addressSize == 64 && getIboIf64bit) { - ImageBaseOffset32DataType ibo32 = - new ImageBaseOffset32DataType(currentProgram.getDataTypeManager()); + IBO32DataType ibo32 = + new IBO32DataType(currentProgram.getDataTypeManager()); int length = ibo32.getLength(); DumbMemBufferImpl compMemBuffer = new DumbMemBufferImpl(currentProgram.getMemory(), address); diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHCatchHandlerModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHCatchHandlerModel.java index c24a6eb193..20e61a8770 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHCatchHandlerModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHCatchHandlerModel.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.exceptionhandling; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import ghidra.app.cmd.data.*; import ghidra.app.util.datatype.microsoft.DataValidationOptions; @@ -116,7 +116,7 @@ public class EHCatchHandlerModel extends AbstractCreateDataTypeModel { /* comps[1] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispType", null); } else { @@ -136,7 +136,7 @@ public class EHCatchHandlerModel extends AbstractCreateDataTypeModel { /* comps[3] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispOfHandler", null); } else { diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHESTypeListModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHESTypeListModel.java index c6163ef40f..469e160a43 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHESTypeListModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHESTypeListModel.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.exceptionhandling; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import ghidra.app.cmd.data.AbstractCreateDataTypeModel; import ghidra.app.cmd.data.EHDataTypeUtilities; @@ -101,7 +101,7 @@ public class EHESTypeListModel extends AbstractCreateDataTypeModel { /* comps[1] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispTypeArray", null); } else { diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHFunctionInfoModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHFunctionInfoModel.java index c662368fd4..f47e48a333 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHFunctionInfoModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHFunctionInfoModel.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.exceptionhandling; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import ghidra.app.cmd.data.AbstractCreateDataTypeModel; import ghidra.app.cmd.data.EHDataTypeUtilities; @@ -150,7 +150,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel { DataTypeManager dataTypeManager = getProgram().getDataTypeManager(); int intSize = new IntegerDataType(dataTypeManager).getLength(); int uintSize = new UnsignedIntegerDataType(dataTypeManager).getLength(); - int ibo32Size = new ImageBaseOffset32DataType(dataTypeManager).getLength(); + int ibo32Size = new IBO32DataType(dataTypeManager).getLength(); int defaultPointerSize = getDefaultPointerSize(); int size20; int additional21; @@ -316,7 +316,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel { /* comps[2] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispUnwindMap", null); } else { @@ -330,7 +330,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel { /* comps[4] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispTryBlockMap", null); } else { @@ -344,7 +344,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel { /* comps[6] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispIPToStateMap", null); } else { @@ -359,7 +359,7 @@ public class EHFunctionInfoModel extends AbstractCreateDataTypeModel { if (isV2 || isV3) { if (isRelative) { /* comps[8] */ - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispESTypeList", null); } else { /* comps[7] */ diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHIPToStateModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHIPToStateModel.java index 4214c92949..46cfcd9b63 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHIPToStateModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHIPToStateModel.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.exceptionhandling; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import ghidra.app.cmd.data.AbstractCreateDataTypeModel; import ghidra.app.cmd.data.EHDataTypeUtilities; @@ -102,7 +102,7 @@ public class EHIPToStateModel extends AbstractCreateDataTypeModel { /* comps[0] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); } else { DataType dwordDt = new TypedefDataType(new CategoryPath("/WinDef.h"), "DWORD", diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHTryBlockModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHTryBlockModel.java index a0c94043c7..a23deb0640 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHTryBlockModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHTryBlockModel.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.exceptionhandling; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import ghidra.app.cmd.data.AbstractCreateDataTypeModel; import ghidra.app.cmd.data.EHDataTypeUtilities; @@ -114,7 +114,7 @@ public class EHTryBlockModel extends AbstractCreateDataTypeModel { /* comps[4] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); struct.add(compDt, "dispHandlerArray", null); } else { diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHUnwindModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHUnwindModel.java index be386b881a..b09153e91c 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHUnwindModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/exceptionhandling/EHUnwindModel.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.exceptionhandling; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import ghidra.app.cmd.data.AbstractCreateDataTypeModel; import ghidra.app.cmd.data.EHDataTypeUtilities; @@ -106,7 +106,7 @@ public class EHUnwindModel extends AbstractCreateDataTypeModel { /* comps[1] */ if (isRelative) { - compDt = new ImageBaseOffset32DataType(dataTypeManager); + compDt = new IBO32DataType(dataTypeManager); } else { diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti1Model.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti1Model.java index 4dfea16da2..ee5261e991 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti1Model.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti1Model.java @@ -232,7 +232,7 @@ public class Rtti1Model extends AbstractCreateRttiDataModel { boolean is64Bit = MSDataTypeUtils.is64Bit(program); Structure rtti1Struct = (Structure) DataTypeUtils.getBaseDataType(rtti1Dt); DataType rtti3RefDt = - is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti3Dt); + is64Bit ? new IBO32DataType(dataTypeManager) : new PointerDataType(rtti3Dt); rtti1Struct.replace(CLASS_HIERARCHY_POINTER_ORDINAL, rtti3RefDt, rtti3RefDt.getLength(), "pClassHierarchyDescriptor", "ref to ClassHierarchyDescriptor (RTTI 3) for class"); } @@ -248,9 +248,9 @@ public class Rtti1Model extends AbstractCreateRttiDataModel { boolean is64Bit = MSDataTypeUtils.is64Bit(program); DataType rtti0Dt = TypeDescriptorModel.getDataType(program); DataType rtti0RefDt = - is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti0Dt); + is64Bit ? new IBO32DataType(dataTypeManager) : new PointerDataType(rtti0Dt); DataType rtti3RefDt = - is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(); + is64Bit ? new IBO32DataType(dataTypeManager) : new PointerDataType(); CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH); StructureDataType struct = diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti2Model.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti2Model.java index 88efe8b986..aa20822dea 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti2Model.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti2Model.java @@ -15,7 +15,7 @@ */ package ghidra.app.cmd.data.rtti; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; import java.util.ArrayList; import java.util.List; @@ -145,11 +145,8 @@ public class Rtti2Model extends AbstractCreateRttiDataModel { DataTypeManager dataTypeManager = program.getDataTypeManager(); - if (MSDataTypeUtils.is64Bit(program)) { - return new ImageBaseOffset32DataType(dataTypeManager); - } - - return new PointerDataType(rtti1Dt, dataTypeManager); + return MSDataTypeUtils.is64Bit(program) ? IBO32DataType.createIBO32PointerTypedef(rtti1Dt) + : new PointerDataType(rtti1Dt, dataTypeManager); } /** @@ -162,11 +159,8 @@ public class Rtti2Model extends AbstractCreateRttiDataModel { DataTypeManager dataTypeManager = program.getDataTypeManager(); - if (MSDataTypeUtils.is64Bit(program)) { - return new ImageBaseOffset32DataType(dataTypeManager); - } - - return new PointerDataType(dataTypeManager); + return MSDataTypeUtils.is64Bit(program) ? IBO32DataType.dataType + : new PointerDataType(dataTypeManager); } /** diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti3Model.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti3Model.java index 19b1372bab..be9b4f8415 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti3Model.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/Rtti3Model.java @@ -15,9 +15,6 @@ */ package ghidra.app.cmd.data.rtti; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress; - import java.util.List; import ghidra.app.cmd.data.EHDataTypeUtilities; @@ -147,8 +144,8 @@ public class Rtti3Model extends AbstractCreateRttiDataModel { boolean is64Bit = MSDataTypeUtils.is64Bit(program); Structure rtti3Struct = (Structure) DataTypeUtils.getBaseDataType(rtti3Dt); DataType individualRtti2EntryDt = Rtti2Model.getIndividualEntryDataType(program, rtti1Dt); - DataType rtti2RefDt = is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) - : new PointerDataType(individualRtti2EntryDt); + DataType rtti2RefDt = is64Bit ? IBO32DataType.createIBO32PointerTypedef(individualRtti2EntryDt) + : new PointerDataType(individualRtti2EntryDt, dataTypeManager); rtti3Struct.replace(BASE_ARRAY_PTR_ORDINAL, rtti2RefDt, rtti2RefDt.getLength(), "pBaseClassArray", "ref to BaseClassArray (RTTI 2)"); } @@ -165,7 +162,7 @@ public class Rtti3Model extends AbstractCreateRttiDataModel { CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH); StructureDataType struct = - getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME); + MSDataTypeUtils.getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME); // Add the components. DWordDataType dWordDataType = new DWordDataType(dataTypeManager); @@ -174,8 +171,10 @@ public class Rtti3Model extends AbstractCreateRttiDataModel { struct.add(dWordDataType, "numBaseClasses", "number of base classes (i.e. rtti1Count)"); DataType rtti2Dt = Rtti2Model.getSimpleIndividualEntryDataType(program); + // FIXME! I don't think we should be making a pointer-to-pointer DataType rtti2RefDt = - is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti2Dt); + is64Bit ? IBO32DataType.createIBO32PointerTypedef(rtti2Dt) + : new PointerDataType(rtti2Dt, dataTypeManager); struct.add(rtti2RefDt, "pBaseClassArray", "ref to BaseClassArray (RTTI 2)"); return new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager); @@ -264,7 +263,7 @@ public class Rtti3Model extends AbstractCreateRttiDataModel { Memory memory = program.getMemory(); Address rtti2CompAddress = rtti3Address.add(BASE_ARRAY_PTR_OFFSET); - Address pointedToAddress = getReferencedAddress(program, rtti2CompAddress); + Address pointedToAddress = MSDataTypeUtils.getReferencedAddress(program, rtti2CompAddress); if (pointedToAddress == null || !memory.contains(pointedToAddress)) { return null; } diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/rtti/AbstractRttiTest.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/rtti/AbstractRttiTest.java index 85fa51c2f5..9457988424 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/rtti/AbstractRttiTest.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/rtti/AbstractRttiTest.java @@ -15,9 +15,8 @@ */ package ghidra.app.cmd.data.rtti; -import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*; +import static org.junit.Assert.*; import ghidra.app.cmd.data.AbstractCreateDataTypeModelTest; import ghidra.app.cmd.data.TypeDescriptorModel; @@ -591,10 +590,10 @@ class AbstractRttiTest extends AbstractCreateDataTypeModelTest { } protected void checkRtti2Data(ProgramDB program, long address, int numEntries) { + DataType rtti1Dt = Rtti1Model.getDataType(program); DataType expectedDataType = - MSDataTypeUtils.is64Bit(program) ? new ImageBaseOffset32DataType() - : new PointerDataType(Rtti1Model.getDataType(program), - program.getDataTypeManager()); + MSDataTypeUtils.is64Bit(program) ? IBO32DataType.createIBO32PointerTypedef(rtti1Dt) + : new PointerDataType(rtti1Dt, program.getDataTypeManager()); checkArrayData(program, address, expectedDataType, numEntries); } diff --git a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java index 3647bce50a..319e9479b1 100644 --- a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java +++ b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java @@ -679,11 +679,16 @@ public class DBHandle { * @param outFile buffer file open for writing * @param newDatabaseId database ID to be forced for new database or null to generate * new database ID + * @param associateWithNewFile if true the outFile will be associated with this DBHandle as the + * current source file, if false no change will be made to this DBHandle's state and the outFile + * will be written and set as read-only. The caller is responsbile for disposing the outFile if + * this parameter is false. * @param monitor progress monitor * @throws IOException if IO error occurs * @throws CancelledException if monitor cancels operation */ - protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId, TaskMonitor monitor) + protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId, + boolean associateWithNewFile, TaskMonitor monitor) throws IOException, CancelledException { if (txStarted) { @@ -704,7 +709,7 @@ public class DBHandle { endTransaction(txId, true); // saved file may be corrupt on IOException } - bufferMgr.saveAs(outFile, true, monitor); + bufferMgr.saveAs(outFile, associateWithNewFile, monitor); } /** diff --git a/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java b/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java index eedec3b7db..28b828273e 100644 --- a/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java +++ b/Ghidra/Framework/DB/src/test/java/db/DBTestUtils.java @@ -23,8 +23,9 @@ import java.util.Random; import org.junit.Assert; -import db.buffers.BufferFileManager; -import db.buffers.DummyBufferFileMgr; +import db.buffers.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; /** * @@ -803,6 +804,30 @@ public class DBTestUtils { static BufferFileManager getBufferFileManager(File dir, String dbName) { return new DummyBufferFileMgr(dir, dbName, false, false); } + + /** + * Clone a DBHandle backed by a new temporary source buffer file. + * The specified dbh will remain associated with its original source buffer file. + * @param dbh DBHandle to clone + * @return new DBhandle + * @throws IOException if a file IO error occurs + */ + public static DBHandle cloneDbHandle(DBHandle dbh) throws IOException { + + try { + File tmpFile = File.createTempFile("tmp", ".db"); + tmpFile.delete(); + + LocalBufferFile bf = new LocalBufferFile(tmpFile, dbh.getBufferSize()); + dbh.saveAs(bf, dbh.getDatabaseId(), false, TaskMonitor.DUMMY); + tmpFile.deleteOnExit(); + + return new DBHandle(bf); + } + catch (CancelledException e) { + throw new IOException(e); // unexpected + } + } } class DuplicateKeyException extends Exception { diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FloatingPointPrecisionSettingsDefinition.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FloatingPointPrecisionSettingsDefinition.java index 00bd9597e9..7acb3ff028 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FloatingPointPrecisionSettingsDefinition.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FloatingPointPrecisionSettingsDefinition.java @@ -92,6 +92,11 @@ public class FloatingPointPrecisionSettingsDefinition implements EnumSettingsDef return PRECISION_DIGITS; } + @Override + public String getStorageKey() { + return PRECISION_DIGITS; + } + @Override public String getDescription() { return "Selects the number of digits of precision to display"; diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FormatSettingsDefinition.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FormatSettingsDefinition.java index c1b934d1e3..6709d5a589 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FormatSettingsDefinition.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/FormatSettingsDefinition.java @@ -114,6 +114,11 @@ public class FormatSettingsDefinition implements EnumSettingsDefinition { return "Format"; } + @Override + public String getStorageKey() { + return FORMAT; + } + @Override public String getDescription() { return "Selects the display format"; diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/IntegerSignednessFormattingModeSettingsDefinition.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/IntegerSignednessFormattingModeSettingsDefinition.java index ace9083fdb..26d2529fb2 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/IntegerSignednessFormattingModeSettingsDefinition.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/IntegerSignednessFormattingModeSettingsDefinition.java @@ -113,6 +113,11 @@ public class IntegerSignednessFormattingModeSettingsDefinition implements EnumSe return "Signedness Mode"; } + @Override + public String getStorageKey() { + return SIGN_FORMAT; + } + @Override public String getDescription() { return "Selects the display mode for signed values"; diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/JavaEnumSettingsDefinition.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/JavaEnumSettingsDefinition.java index d4347b0124..6dcf822ed1 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/JavaEnumSettingsDefinition.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/JavaEnumSettingsDefinition.java @@ -80,7 +80,7 @@ public class JavaEnumSettingsDefinition> implements EnumSettin * @return Enum<T> value, or the specified defaultValueOveride if not present. */ public T getEnumValue(Settings settings, T defaultValueOverride) { - Long lvalue = settings.getLong(getSettingName()); + Long lvalue = settings.getLong(settingName); if (lvalue == null) { return defaultValueOverride; } @@ -126,38 +126,34 @@ public class JavaEnumSettingsDefinition> implements EnumSettin return -1; } - /** - * The name of this setting as it is stored in a {@link Settings} object. - * - * @return String name. - */ - public String getSettingName() { - return settingName; - } - @Override public boolean hasValue(Settings setting) { - return setting.getValue(getSettingName()) != null; + return setting.getValue(settingName) != null; } @Override - public String getName() { + public final String getName() { return name; } @Override - public String getDescription() { + public final String getStorageKey() { + return settingName; + } + + @Override + public final String getDescription() { return description; } @Override public void clear(Settings settings) { - settings.clearSetting(getSettingName()); + settings.clearSetting(settingName); } @Override public void copySetting(Settings srcSettings, Settings destSettings) { - Long l = srcSettings.getLong(getSettingName()); + Long l = srcSettings.getLong(settingName); if (l == null) { clear(destSettings); } @@ -168,7 +164,7 @@ public class JavaEnumSettingsDefinition> implements EnumSettin @Override public int getChoice(Settings settings) { - Long lvalue = settings.getLong(getSettingName()); + Long lvalue = settings.getLong(settingName); int value = (lvalue != null) ? (int) (long) lvalue : defaultValue.ordinal(); return Math.min(Math.max(value, 0), values.length - 1); @@ -181,7 +177,7 @@ public class JavaEnumSettingsDefinition> implements EnumSettin @Override public void setChoice(Settings settings, int value) { - settings.setLong(getSettingName(), value); + settings.setLong(settingName, value); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/Settings.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/Settings.java index 03740c3783..fe4e2c53d6 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/Settings.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/Settings.java @@ -22,6 +22,14 @@ package ghidra.docking.settings; */ public interface Settings { + /** + * Determine if a settings change corresponding to the specified + * settingsDefinition is permitted. + * @param settingsDefinition settings definition + * @return true if change permitted else false + */ + boolean isChangeAllowed(SettingsDefinition settingsDefinition); + /** * Gets the Long value associated with the given name * @param name the key used to retrieve a value diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsDefinition.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsDefinition.java index 01c78d0723..0f2b45ec86 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsDefinition.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsDefinition.java @@ -16,6 +16,7 @@ package ghidra.docking.settings; import java.util.ArrayList; +import java.util.Arrays; import java.util.function.Predicate; /** @@ -26,8 +27,7 @@ public interface SettingsDefinition { /** * Create a new list of {@link SettingsDefinition}s by concat'ing a base list with - * a var-arg'ish additional list of setting defs. - * + * a var-arg'ish additional list of setting defs. Any additional duplicates are discarded. * @param settings List of settings defs. * @param additional More settings defs to add * @return new array with all the settings defs joined together. @@ -40,11 +40,14 @@ public interface SettingsDefinition { if (settings == null) { return additional; } - - SettingsDefinition[] result = new SettingsDefinition[settings.length + additional.length]; - System.arraycopy(settings, 0, result, 0, settings.length); - System.arraycopy(additional, 0, result, settings.length, additional.length); - return result; + ArrayList list = new ArrayList<>(); + list.addAll(Arrays.asList(settings)); + for (SettingsDefinition def : additional) { + if (!list.contains(def)) { + list.add(def); + } + } + return list.toArray(new SettingsDefinition[list.size()]); } /** @@ -81,11 +84,17 @@ public interface SettingsDefinition { public String getValueString(Settings settings); /** - * Returns the name of this SettingsDefinition + * Returns the display name of this SettingsDefinition * @return display name for setting */ public String getName(); + /** + * Get the {@link Settings} key which is used when storing a key/value entry. + * @return settings storage key + */ + String getStorageKey(); + /** * Returns a description of this settings definition * @return setting description diff --git a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsImpl.java b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsImpl.java index 228c26418a..98c077d538 100644 --- a/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsImpl.java +++ b/Ghidra/Framework/Docking/src/main/java/ghidra/docking/settings/SettingsImpl.java @@ -65,31 +65,19 @@ public class SettingsImpl implements Settings, Serializable { } /** - * Check for immutable settings and log error of modification not permitted - * @param type setting type or null - * @param name setting name or null - * @return true if change permitted + * Construct a new SettingsImpl object. If settings object is specified this + * settings will copy all name/value pairs and underlying defaults. + * @param settings the settings object to copy */ - private boolean checkSetting(String type, String name) { - if (immutable) { - String typeStr = ""; - if (type != null) { - typeStr = type + " "; + public SettingsImpl(Settings settings) { + this(); + if (settings != null) { + String[] names = settings.getNames(); + for (int i = 0; i < names.length; i++) { + map.put(names[i], settings.getValue(names[i])); } - String nameStr = ": " + name; - if (name == null) { - nameStr = "s"; - } - Msg.warn(SettingsImpl.class, - "Ignored invalid attempt to modify immutable " + typeStr + "component setting" + - nameStr); - return false; + defaultSettings = settings.getDefaultSettings(); } - if (allowedSettingPredicate != null && !allowedSettingPredicate.apply(name)) { - Msg.warn(this, "Ignored disallowed setting '" + name + "'"); - return false; - } - return true; } /** @@ -113,20 +101,45 @@ public class SettingsImpl implements Settings, Serializable { this.changeSourceObj = changeSourceObj; } - /** - * Construct a new SettingsImpl object. If settings object is specified this - * settings will copy all name/value pairs and underlying defaults. - * @param settings the settings object to copy - */ - public SettingsImpl(Settings settings) { - this(); - if (settings != null) { - String[] names = settings.getNames(); - for (int i = 0; i < names.length; i++) { - map.put(names[i], settings.getValue(names[i])); - } - defaultSettings = settings.getDefaultSettings(); + @Override + public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + if (immutable) { + return false; } + if (allowedSettingPredicate != null && + !allowedSettingPredicate.apply(settingsDefinition.getStorageKey())) { + return false; + } + return true; + } + + /** + * Check for immutable settings and log error of modification not permitted + * @param type setting type or null + * @param name setting name or null + * @return true if change permitted + */ + private boolean checkSetting(String type, String name) { + if (immutable) { + String typeStr = ""; + if (type != null) { + typeStr = type + " "; + } + String nameStr = ": " + name; + if (name == null) { + nameStr = "s"; + } + Msg.warn(SettingsImpl.class, + "Ignored invalid attempt to modify immutable " + typeStr + "component setting" + + nameStr); + return false; + } + if (name != null && allowedSettingPredicate != null && + !allowedSettingPredicate.apply(name)) { + Msg.warn(this, "Ignored disallowed setting '" + name + "'"); + return false; + } + return true; } @Override diff --git a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDBHandle.java b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDBHandle.java index 49ea1afae6..e5af28139a 100644 --- a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDBHandle.java +++ b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDBHandle.java @@ -82,9 +82,10 @@ public class PackedDBHandle extends DBHandle { } @Override - protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId, TaskMonitor monitor) + protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId, + boolean associateWithNewFile, TaskMonitor monitor) throws IOException, CancelledException { - super.saveAs(outFile, newDatabaseId, monitor); + super.saveAs(outFile, newDatabaseId, associateWithNewFile, monitor); if (pdb != null) { pdb.dispose(); pdb = null; diff --git a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDatabase.java b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDatabase.java index c36c663248..7c52469008 100644 --- a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDatabase.java +++ b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/store/db/PackedDatabase.java @@ -179,7 +179,7 @@ public class PackedDatabase extends Database { LocalManagedBufferFile bfile = new LocalManagedBufferFile(dbHandle.getBufferSize(), bfMgr, FolderItem.DEFAULT_CHECKOUT_ID); - dbHandle.saveAs(bfile, newDatabaseId, monitor); + dbHandle.saveAs(bfile, newDatabaseId, true, monitor); packDatabase(monitor); addInstance(this); success = true; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java index bd837d1457..6ee5a8174b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoData.java @@ -18,6 +18,7 @@ package ghidra.app.util; import java.util.*; import ghidra.docking.settings.Settings; +import ghidra.docking.settings.SettingsDefinition; import ghidra.program.database.ProgramDB; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.address.Address; @@ -228,7 +229,12 @@ public class PseudoData extends PseudoCodeUnit implements Data { @Override public Long getLong(String name) { - return null; + return getDefaultSettings().getLong(name); + } + + @Override + public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + return false; } @Override @@ -238,15 +244,12 @@ public class PseudoData extends PseudoCodeUnit implements Data { @Override public String getString(String name) { - return null; + return getDefaultSettings().getString(name); } @Override public Object getValue(String name) { - if (baseDataType != null) { - return baseDataType.getValue(this, this, length); - } - return null; + return getDefaultSettings().getValue(name); } @Override @@ -369,41 +372,6 @@ public class PseudoData extends PseudoCodeUnit implements Data { return null; } -// /** -// * @see ghidra.program.model.listing.Data#getComponents() -// */ -// public Data[] getComponents() { -// if (length < dataType.getLength()) { -// return null; -// } -// Data[] retData = EMPTY_COMPONENTS; -// if (baseDataType instanceof Composite) { -// Composite composite = (Composite)baseDataType; -// int n = composite.getNumComponents(); -// retData = new Data[n]; -// for(int i=0;i c; - try { c = Class.forName(classPath); } @@ -2322,18 +2324,41 @@ abstract public class DataTypeManagerDB implements DataTypeManager { BuiltInDataType bdt = (BuiltInDataType) c.getDeclaredConstructor().newInstance(); bdt.setName(name); bdt.setCategoryPath(catPath); - bdt = (BuiltInDataType) bdt.clone(this); - if (allowsDefaultBuiltInSettings() && bdt.getSettingsDefinitions().length != 0) { - bdt.setDefaultSettings(new DataTypeSettingsDB(this, bdt, dataTypeID)); + + final BuiltInDataType builtInDt = (BuiltInDataType) bdt.clone(this); + + // check for prior instantiation with different id + Long id = builtIn2IdMap.get(builtInDt); + if (id != null) { + DataType datatype = builtInMap.get(id); + if (datatype != null) { + builtInMap.put(dataTypeID, datatype); + return datatype; + } } - dt = bdt; + + if (allowsDefaultBuiltInSettings() && + builtInDt.getSettingsDefinitions().length != 0) { + DataTypeSettingsDB settings = + new DataTypeSettingsDB(this, builtInDt, dataTypeID); + if (builtInDt instanceof TypeDef) { + // Copy default immutable builtin typedef settings + Settings typedefSettings = builtInDt.getDefaultSettings(); + for (String n : typedefSettings.getNames()) { + settings.setValue(n, typedefSettings.getValue(n)); + } + } + settings.setAllowedSettingPredicate(n -> isBuiltInSettingAllowed(builtInDt, n)); + builtInDt.setDefaultSettings(settings); + } + dt = builtInDt; } catch (Exception e) { Msg.error(this, e); dt = new MissingBuiltInDataType(catPath, name, classPath, this); } - builtInMap.put(key, dt); - builtIn2IdMap.put(dt, key); + builtInMap.put(dataTypeID, dt); + builtIn2IdMap.put(dt, dataTypeID); return dt; } catch (IOException e) { @@ -2345,6 +2370,18 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return null; } + private boolean isBuiltInSettingAllowed(BuiltInDataType bdt, String settingName) { + SettingsDefinition def = null; + for (SettingsDefinition sd : bdt.getSettingsDefinitions()) { + if (sd.getStorageKey().equals(settingName)) { + def = sd; + break; + } + } + // restrict to non-TypeDefSettingsDefinitions which are defined for the datatype + return def != null && !(def instanceof TypeDefSettingsDefinition); + } + private Enum getEnumDataType(long dataTypeID, DBRecord record) { lock.acquire(); try { @@ -2526,6 +2563,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager { int len = ptr.hasLanguageDependantLength() ? -1 : ptr.getLength(); newDataType = createPointer(ptr.getDataType(), cat, (byte) len, handler); } + else if (dt instanceof BuiltInDataType) { + BuiltInDataType builtInDataType = (BuiltInDataType) dt; + newDataType = createBuiltIn(builtInDataType, cat); + } else if (dt instanceof StructureInternal) { StructureInternal structure = (StructureInternal) dt; newDataType = createStructure(structure, name, cat, sourceArchiveIdValue, @@ -2550,10 +2591,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager { newDataType = createFunctionDefinition(funDef, name, cat, sourceArchiveIdValue, id.getValue()); } - else if (dt instanceof BuiltInDataType) { - BuiltInDataType builtInDataType = (BuiltInDataType) dt; - newDataType = createBuiltIn(builtInDataType, cat); - } else if (dt instanceof MissingBuiltInDataType) { MissingBuiltInDataType missingBuiltInDataType = (MissingBuiltInDataType) dt; newDataType = createMissingBuiltIn(missingBuiltInDataType, cat); @@ -2621,7 +2658,13 @@ abstract public class DataTypeManagerDB implements DataTypeManager { throw new IllegalArgumentException("Data type must have a valid name"); } DataType dataType = resolve(typedef.getDataType(), getDependencyConflictHandler()); - DBRecord record = typedefAdapter.createRecord(getID(dataType), name, cat.getID(), + boolean isAutoNamed = typedef.isAutoNamed(); + short flags = 0; + if (isAutoNamed) { + flags = (short) TypedefDBAdapter.TYPEDEF_FLAG_AUTONAME; + cat = getCategory(dataType.getCategoryPath()); // force category + } + DBRecord record = typedefAdapter.createRecord(getID(dataType), name, flags, cat.getID(), sourceArchiveIdValue, universalIdValue, typedef.getLastChangeTime()); TypedefDB typedefDB = new TypedefDB(this, dtCache, typedefAdapter, record); @@ -2631,6 +2674,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager { TypedefDataType.copyTypeDefSettings(typedef, typedefDB, false); settings.setLock(wasLocked); + typedefDB.updateAutoName(false); + dataType.addParent(typedefDB); return typedefDB; } @@ -3047,8 +3092,9 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @param oldCatId the old category's record id */ void dataTypeCategoryPathChanged(DataTypeDB dt, CategoryPath oldPath, long oldCatId) { - if (!(dt instanceof Array) && !(dt instanceof Pointer)) { - try { + + try { + if (!(dt instanceof Array) && !(dt instanceof Pointer)) { for (Field arrayId : arrayAdapter.getRecordIdsInCategory(oldCatId)) { long id = arrayId.getLongValue(); DBRecord rec = arrayAdapter.getRecord(id); @@ -3062,10 +3108,18 @@ abstract public class DataTypeManagerDB implements DataTypeManager { ptr.updatePath(dt); } } - catch (IOException e) { - dbError(e); + + // only affects those with auto-naming which must follow category change + for (Field ptrId : typedefAdapter.getRecordIdsInCategory(oldCatId)) { + long id = ptrId.getLongValue(); + DBRecord rec = typedefAdapter.getRecord(id); + TypedefDB td = (TypedefDB) getDataType(id, rec); + td.updatePath(dt); } } + catch (IOException e) { + dbError(e); + } dataTypeMoved(dt, new DataTypePath(oldPath, dt.getName()), dt.getDataTypePath()); } @@ -3341,7 +3395,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { return creatingDataType != 0; } - @Override + /** + * Notification when data type is changed. + * @param dt data type that is changed + * @param isAutoChange true if change was an automatic change in response to + * another datatype's change (e.g., size, alignment). + */ public void dataTypeChanged(DataType dt, boolean isAutoChange) { if (dt instanceof Enum) { enumValueMap = null; @@ -3353,6 +3412,22 @@ abstract public class DataTypeManagerDB implements DataTypeManager { defaultListener.dataTypeChanged(this, dt.getDataTypePath()); } + /** + * Notification when data type settings have changed. + * @param dt data type that is changed + */ + public void dataTypeSettingsChanged(DataType dt) { + if (dt instanceof TypedefDB) { + TypedefDB td = (TypedefDB) dt; + td.updateAutoName(true); + if (creatingDataType == 0) { + td.setLastChangeTime(System.currentTimeMillis()); + setDirtyFlag(dt); + } + } + defaultListener.dataTypeChanged(this, dt.getDataTypePath()); + } + protected void dataTypeAdded(DataType newDt, DataType originalDataType) { CategoryDB category = (CategoryDB) getCategory(newDt.getCategoryPath()); category.dataTypeAdded(newDt); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeSettingsDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeSettingsDB.java index 4208d0118f..0e2b4c88e0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeSettingsDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeSettingsDB.java @@ -17,8 +17,7 @@ package ghidra.program.database.data; import com.google.common.base.Predicate; -import ghidra.docking.settings.Settings; -import ghidra.docking.settings.SettingsImpl; +import ghidra.docking.settings.*; import ghidra.program.model.data.*; import ghidra.util.Msg; @@ -78,6 +77,18 @@ class DataTypeSettingsDB implements Settings { return wasLocked; } + @Override + public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + if (locked) { + return false; + } + if (allowedSettingPredicate != null && + !allowedSettingPredicate.apply(settingsDefinition.getStorageKey())) { + return false; + } + return true; + } + /** * Set predicate for settings modification * @param allowedSettingPredicate callback for checking an allowed setting modification @@ -107,7 +118,8 @@ class DataTypeSettingsDB implements Settings { nameStr); return false; } - if (allowedSettingPredicate != null && !allowedSettingPredicate.apply(name)) { + if (name != null && allowedSettingPredicate != null && + !allowedSettingPredicate.apply(name)) { Msg.warn(this, "Ignored disallowed setting '" + name + "'"); return false; } @@ -118,7 +130,7 @@ class DataTypeSettingsDB implements Settings { // NOTE: Merge currently only supports TypeDefDB default settings changes which correspond // to TypeDefSettingsDefinition established by the base datatype // and does not consider DataTypeComponent default settings changes or other setting types. - dataMgr.dataTypeChanged(dataType, false); + dataMgr.dataTypeSettingsChanged(dataType); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerTypedefInspector.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerTypedefInspector.java index c38023c790..a605019334 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerTypedefInspector.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerTypedefInspector.java @@ -52,8 +52,8 @@ public class PointerTypedefInspector { * its default settings. * @param pointerTypeDef Pointer TypeDef * @param addrFactory target address factory - * @return referenced address space or null if not applicable - * or the space setting is not defined within the addrFactory. + * @return referenced address space or null if not specified or address space + * lookup fails. */ public static AddressSpace getPointerAddressSpace(TypeDef pointerTypeDef, AddressFactory addrFactory) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java index 8f74f0547e..20cde8ca57 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ProgramBasedDataTypeManagerDB.java @@ -20,11 +20,14 @@ import java.util.List; import db.*; import db.util.ErrorHandler; +import ghidra.docking.settings.SettingsDefinition; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.Address; import ghidra.program.model.address.KeyRange; -import ghidra.program.model.data.ProgramBasedDataTypeManager; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.Data; import ghidra.util.Lock; +import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.exception.VersionException; import ghidra.util.task.TaskMonitor; @@ -91,22 +94,36 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB abstract protected void dataSettingChanged(Address address); @Override - public boolean setLongSettingsValue(Address dataAddr, String name, long value) { - return updateInstanceSettings(dataAddr, name, null, value); + public boolean isChangeAllowed(Data data, + SettingsDefinition settingsDefinition) { + if (settingsDefinition instanceof TypeDefSettingsDefinition) { + return false; + } + for (SettingsDefinition def : data.getDataType().getSettingsDefinitions()) { + if (def.equals(settingsDefinition)) { + return true; + } + } + return false; } @Override - public boolean setStringSettingsValue(Address dataAddr, String name, String value) { - return updateInstanceSettings(dataAddr, name, value, -1); + public boolean setLongSettingsValue(Data data, String name, long value) { + return updateInstanceSettings(data, name, null, value); } @Override - public boolean setSettings(Address dataAddr, String name, Object value) { + public boolean setStringSettingsValue(Data data, String name, String value) { + return updateInstanceSettings(data, name, value, -1); + } + + @Override + public boolean setSettings(Data data, String name, Object value) { if (value instanceof String) { - return updateInstanceSettings(dataAddr, name, (String) value, -1); + return updateInstanceSettings(data, name, (String) value, -1); } else if (isAllowedNumberType(value)) { - return updateInstanceSettings(dataAddr, name, null, ((Number) value).longValue()); + return updateInstanceSettings(data, name, null, ((Number) value).longValue()); } throw new IllegalArgumentException( "Unsupportd Settings Value: " + (value == null ? "null" : value.getClass().getName())); @@ -129,8 +146,8 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB } @Override - public Long getLongSettingsValue(Address dataAddr, String name) { - SettingDB settings = getSettingDB(dataAddr, name); + public Long getLongSettingsValue(Data data, String name) { + SettingDB settings = getSettingDB(data, name); if (settings != null) { return settings.getLongValue(); } @@ -138,8 +155,8 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB } @Override - public String getStringSettingsValue(Address dataAddr, String name) { - SettingDB settings = getSettingDB(dataAddr, name); + public String getStringSettingsValue(Data data, String name) { + SettingDB settings = getSettingDB(data, name); if (settings != null) { return settings.getStringValue(); } @@ -147,21 +164,22 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB } @Override - public Object getSettings(Address dataAddr, String name) { - Object obj = getStringSettingsValue(dataAddr, name); + public Object getSettings(Data data, String name) { + Object obj = getStringSettingsValue(data, name); if (obj != null) { return obj; } - return getLongSettingsValue(dataAddr, name); + return getLongSettingsValue(data, name); } @Override - public boolean clearSetting(Address dataAddr, String name) { + public boolean clearSetting(Data data, String name) { if (instanceSettingsAdapter == null) { throw new UnsupportedOperationException(); } lock.acquire(); try { + Address dataAddr = getDataSettingsAddress(data); instanceSettingsCache.remove(dataAddr, name); long addr = addrMap.getKey(dataAddr, false); if (instanceSettingsAdapter.removeSettingsRecord(addr, name)) { @@ -180,7 +198,7 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB } @Override - public void clearAllSettings(Address dataAddr) { + public void clearAllSettings(Data data) { if (instanceSettingsAdapter == null) { throw new UnsupportedOperationException(); } @@ -188,6 +206,7 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB try { instanceSettingsCache.clear(); boolean changed = false; + Address dataAddr = getDataSettingsAddress(data); Field[] keys = instanceSettingsAdapter.getSettingsKeys(addrMap.getKey(dataAddr, false)); for (Field key : keys) { instanceSettingsAdapter.removeSettingsRecord(key.getLongValue()); @@ -267,12 +286,13 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB } @Override - public String[] getInstanceSettingsNames(Address dataAddr) { + public String[] getInstanceSettingsNames(Data data) { if (instanceSettingsAdapter == null) { throw new UnsupportedOperationException(); } lock.acquire(); try { + Address dataAddr = getDataSettingsAddress(data); return instanceSettingsAdapter.getSettingsNames(addrMap.getKey(dataAddr, false)); } catch (IOException e) { @@ -285,11 +305,12 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB } @Override - public boolean isEmptySetting(Address dataAddr) { + public boolean isEmptySetting(Data data) { if (instanceSettingsAdapter == null) { throw new UnsupportedOperationException(); } try { + Address dataAddr = getDataSettingsAddress(data); return instanceSettingsAdapter .getSettingsKeys(addrMap.getKey(dataAddr, false)).length == 0; } @@ -299,7 +320,7 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB return true; } - private boolean updateInstanceSettings(Address dataAddr, String name, String strValue, + private boolean updateInstanceSettings(Data data, String name, String strValue, long longValue) { boolean wasChanged = false; @@ -309,6 +330,10 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB if (instanceSettingsAdapter == null) { throw new UnsupportedOperationException(); } + if (!checkSetting(data, name)) { + return false; + } + Address dataAddr = getDataSettingsAddress(data); long addrKey = addrMap.getKey(dataAddr, true); DBRecord rec = instanceSettingsAdapter.updateSettingsRecord(addrKey, name, strValue, longValue); @@ -330,12 +355,31 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB return wasChanged; } - private SettingDB getSettingDB(Address dataAddr, String name) { + private boolean checkSetting(Data data, String name) { + SettingsDefinition settingsDefinition = null; + for (SettingsDefinition def : data.getDataType().getSettingsDefinitions()) { + if (def.getStorageKey().equals(name)) { + settingsDefinition = def; + break; + } + } + if (settingsDefinition == null) { + Msg.warn(this, "Ignored unrecognized setting '" + name + "'"); + return false; + } + if (settingsDefinition instanceof TypeDefSettingsDefinition) { + Msg.warn(this, "Ignored disallowed instance setting '" + name + "'"); + } + return true; + } + + private SettingDB getSettingDB(Data data, String name) { lock.acquire(); try { if (instanceSettingsAdapter == null) { throw new UnsupportedOperationException(); } + Address dataAddr = getDataSettingsAddress(data); SettingDB settings = instanceSettingsCache.get(dataAddr, name); if (settings != null) { return settings; @@ -380,4 +424,15 @@ public abstract class ProgramBasedDataTypeManagerDB extends DataTypeManagerDB lock.release(); } } + + private static Address getDataSettingsAddress(Data data) { + Data parent = data.getParent(); + if (parent != null) { + DataType dataType = parent.getDataType(); + if (dataType instanceof Array) { + return getDataSettingsAddress(parent); + } + } + return data.getAddress(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV1.java index 3f3326aa5d..6a9cfae3fc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/SettingsDBAdapterV1.java @@ -55,8 +55,8 @@ class SettingsDBAdapterV1 extends SettingsDBAdapter { private Table settingsTable; private Table settingsNameTable; - private HashMap nameIndexMap = new HashMap<>(); - private HashMap nameStringMap = new HashMap<>(); + private HashMap nameIndexMap; + private HashMap nameStringMap; SettingsDBAdapterV1(String tableName, DBHandle handle, boolean create) throws VersionException, IOException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java index 4f419875b8..c1ca9ab810 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java @@ -24,6 +24,7 @@ import ghidra.program.database.DBObjectCache; import ghidra.program.model.data.*; import ghidra.program.model.mem.MemBuffer; import ghidra.util.UniversalID; +import ghidra.util.exception.DuplicateNameException; /** * Database implementation for a Typedef data type. @@ -49,6 +50,73 @@ class TypedefDB extends DataTypeDB implements TypeDef { this.defaultSettings = null; // ensure lazy initialization } + private void setFlags(int flags) { + record.setShortValue(TypedefDBAdapter.TYPEDEF_FLAGS_COL, (short) flags); + } + + private int getFlags() { + return record.getShortValue(TypedefDBAdapter.TYPEDEF_FLAGS_COL); + } + + @Override + public void enableAutoNaming() { + if (isAutoNamed()) { + return; + } + lock.acquire(); + try { + checkDeleted(); + + String oldName = getName(); + + setFlags(getFlags() | TypedefDBAdapter.TYPEDEF_FLAG_AUTONAME); + adapter.updateRecord(record, true); + + // auto-named typedef follows category of associated datatype + CategoryPath oldPath = getCategoryPath(); + CategoryPath currentPath = getDataType().getCategoryPath(); + + String newName = generateTypedefName(currentPath); + record.setString(TypedefDBAdapter.TYPEDEF_NAME_COL, newName); + adapter.updateRecord(record, true); + refreshName(); + + if (!currentPath.equals(oldPath)) { + // update category for typedef + try { + super.setCategoryPath(currentPath); + } + catch (DuplicateNameException e) { + // should not happen + } + } + + notifyNameChanged(oldName); + } + catch (IOException e) { + dataMgr.dbError(e); + } + finally { + lock.release(); + } + } + + @Override + public boolean isAutoNamed() { + int flags = getFlags(); + if (isInvalid()) { + lock.acquire(); + try { + checkIsValid(); + flags = getFlags(); + } + finally { + lock.release(); + } + } + return (flags & TypedefDBAdapter.TYPEDEF_FLAG_AUTONAME) != 0; + } + @Override protected long doGetCategoryID() { return record.getLongValue(TypedefDBAdapter.TYPEDEF_CAT_COL); @@ -67,6 +135,7 @@ class TypedefDB extends DataTypeDB implements TypeDef { @Override protected void doSetNameRecord(String name) throws IOException { record.setString(TypedefDBAdapter.TYPEDEF_NAME_COL, name); + setFlags(getFlags() & ~TypedefDBAdapter.TYPEDEF_FLAG_AUTONAME); // clear auto-name flag if name is set adapter.updateRecord(record, true); } @@ -165,19 +234,13 @@ class TypedefDB extends DataTypeDB implements TypeDef { } @Override - public DataType clone(DataTypeManager dtm) { - TypedefDataType typeDef = - new TypedefDataType(getCategoryPath(), getName(), getDataType(), getUniversalID(), - getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm); - TypedefDataType.copyTypeDefSettings(this, typeDef, false); - return typeDef; + public TypeDef clone(DataTypeManager dtm) { + return TypedefDataType.clone(this, dtm); } @Override - public DataType copy(DataTypeManager dtm) { - TypedefDataType typeDef = new TypedefDataType(getCategoryPath(), getName(), getDataType(), dtm); - TypedefDataType.copyTypeDefSettings(this, typeDef, false); - return typeDef; + public TypedefDataType copy(DataTypeManager dtm) { + return TypedefDataType.copy(this, dtm); } @Override @@ -190,7 +253,12 @@ class TypedefDB extends DataTypeDB implements TypeDef { } TypeDef td = (TypeDef) obj; validate(lock); - if (!DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) { + + boolean autoNamed = isAutoNamed(); + if (autoNamed != td.isAutoNamed()) { + return false; + } + if (!autoNamed && !DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) { return false; } if (!hasSameTypeDefSettings(td)) { @@ -199,6 +267,13 @@ class TypedefDB extends DataTypeDB implements TypeDef { return DataTypeUtilities.isSameOrEquivalentDataType(getDataType(), td.getDataType()); } + public void setCategoryPath(CategoryPath path) throws DuplicateNameException { + if (isAutoNamed()) { + return; // ignore category change if auto-naming enabled + } + super.setCategoryPath(path); + } + @Override protected void doSetCategoryPathRecord(long categoryID) throws IOException { record.setLongValue(TypedefDBAdapter.TYPEDEF_CAT_COL, categoryID); @@ -251,7 +326,9 @@ class TypedefDB extends DataTypeDB implements TypeDef { @Override public void dataTypeNameChanged(DataType dt, String oldName) { - // ignore + if (getDataType() == dt) { + updateAutoName(true); + } } @Override @@ -330,6 +407,9 @@ class TypedefDB extends DataTypeDB implements TypeDef { @Override public String toString() { + if (isAutoNamed()) { + return getName(); + } return "typedef " + this.getName() + " " + getDataType().getName(); } @@ -440,10 +520,92 @@ class TypedefDB extends DataTypeDB implements TypeDef { TypeDef td = (TypeDef) dataType; dataTypeReplaced(getDataType(), td.getDataType()); TypedefDataType.copyTypeDefSettings(td, this, true); + // NOTE: as with the name, auto-name setting is left unchanged } finally { lock.release(); } } + @Override + protected void updatePath(DataTypeDB dt) { + if (isAutoNamed() && dt == getDataType()) { + // auto-named typedef follows category of associated datatype + CategoryPath oldPath = getCategoryPath(); + CategoryPath currentPath = dt.getCategoryPath(); + if (!currentPath.equals(oldPath)) { + try { + boolean nameChanged = false; + String oldName = getName(); + String newName = generateTypedefName(currentPath); + if (!newName.equals(oldName)) { + nameChanged = true; + record.setString(TypedefDBAdapter.TYPEDEF_NAME_COL, newName); + refreshName(); + } + super.setCategoryPath(currentPath); + if (nameChanged) { + notifyNameChanged(oldName); + } + } + catch (DuplicateNameException e) { + // should not happen + } + } + } + } + + private String generateTypedefName(CategoryPath path) { + String newName = TypedefDataType.generateTypedefName(this); + DataType dt = dataMgr.getDataType(path, newName); + if (dt == null || dt == this) { + return newName; + } + + String baseName = newName + DataType.CONFLICT_SUFFIX; + newName = baseName; + int count = 0; + while (true) { + dt = dataMgr.getDataType(path, newName); + if (dt == null || dt == this) { + break; + } + count++; + newName = baseName + count; + } + return newName; + } + + boolean updateAutoName(boolean notify) { + lock.acquire(); + try { + checkIsValid(); + + if (!isAutoNamed()) { + return false; + } + + String oldName = getName(); + String newName = generateTypedefName(getCategoryPath()); + if (oldName.equals(newName)) { + return false; + } + + record.setString(TypedefDBAdapter.TYPEDEF_NAME_COL, newName); + adapter.updateRecord(record, false); + refreshName(); + + if (notify) { + notifyNameChanged(oldName); + } + } + catch (IOException e) { + dataMgr.dbError(e); + } + finally { + lock.release(); + } + return true; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java index 9dd75a5a81..a2746aa88a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapter.java @@ -28,19 +28,23 @@ import ghidra.util.task.TaskMonitor; abstract class TypedefDBAdapter { static final String TYPEDEF_TABLE_NAME = "Typedefs"; - static final Schema SCHEMA = TypedefDBAdapterV1.V1_SCHEMA; + static final Schema SCHEMA = TypedefDBAdapterV2.V2_SCHEMA; - static final int TYPEDEF_DT_ID_COL = TypedefDBAdapterV1.V1_TYPEDEF_DT_ID_COL; - static final int TYPEDEF_NAME_COL = TypedefDBAdapterV1.V1_TYPEDEF_NAME_COL; - static final int TYPEDEF_CAT_COL = TypedefDBAdapterV1.V1_TYPEDEF_CAT_COL; + static final int TYPEDEF_DT_ID_COL = TypedefDBAdapterV2.V2_TYPEDEF_DT_ID_COL; + static final int TYPEDEF_FLAGS_COL = TypedefDBAdapterV2.V2_TYPEDEF_FLAGS_COL; + static final int TYPEDEF_NAME_COL = TypedefDBAdapterV2.V2_TYPEDEF_NAME_COL; + static final int TYPEDEF_CAT_COL = TypedefDBAdapterV2.V2_TYPEDEF_CAT_COL; static final int TYPEDEF_SOURCE_ARCHIVE_ID_COL = - TypedefDBAdapterV1.V1_TYPEDEF_SOURCE_ARCHIVE_ID_COL; + TypedefDBAdapterV2.V2_TYPEDEF_SOURCE_ARCHIVE_ID_COL; static final int TYPEDEF_UNIVERSAL_DT_ID_COL = - TypedefDBAdapterV1.V1_TYPEDEF_UNIVERSAL_DT_ID_COL; + TypedefDBAdapterV2.V2_TYPEDEF_UNIVERSAL_DT_ID_COL; static final int TYPEDEF_SOURCE_SYNC_TIME_COL = - TypedefDBAdapterV1.V1_TYPEDEF_SOURCE_SYNC_TIME_COL; + TypedefDBAdapterV2.V2_TYPEDEF_SOURCE_SYNC_TIME_COL; static final int TYPEDEF_LAST_CHANGE_TIME_COL = - TypedefDBAdapterV1.V1_TYPEDEF_LAST_CHANGE_TIME_COL; + TypedefDBAdapterV2.V2_TYPEDEF_LAST_CHANGE_TIME_COL; + + // Typedef flags bits + static final int TYPEDEF_FLAG_AUTONAME = 0x1; /** * Gets an adapter for working with the Typedef data type database table. The adapter is based @@ -55,7 +59,7 @@ abstract class TypedefDBAdapter { static TypedefDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor) throws VersionException, IOException { try { - return new TypedefDBAdapterV1(handle, openMode == DBConstants.CREATE); + return new TypedefDBAdapterV2(handle, openMode == DBConstants.CREATE); } catch (VersionException e) { if (!e.isUpgradable() || openMode == DBConstants.UPDATE) { @@ -76,6 +80,12 @@ abstract class TypedefDBAdapter { * @throws VersionException if a read only adapter can't be obtained for the database handle's version. */ static TypedefDBAdapter findReadOnlyAdapter(DBHandle handle) throws VersionException { + try { + return new TypedefDBAdapterV1(handle); + } + catch (VersionException e) { + // ignore + } return new TypedefDBAdapterV0(handle); } @@ -95,14 +105,14 @@ abstract class TypedefDBAdapter { long id = tmpHandle.startTransaction(); TypedefDBAdapter tmpAdapter = null; try { - tmpAdapter = new TypedefDBAdapterV1(tmpHandle, true); + tmpAdapter = new TypedefDBAdapterV2(tmpHandle, true); RecordIterator it = oldAdapter.getRecords(); while (it.hasNext()) { DBRecord rec = it.next(); tmpAdapter.updateRecord(rec, false); } oldAdapter.deleteTable(handle); - TypedefDBAdapter newAdapter = new TypedefDBAdapterV1(handle, true); + TypedefDBAdapter newAdapter = new TypedefDBAdapterV2(handle, true); it = tmpAdapter.getRecords(); while (it.hasNext()) { DBRecord rec = it.next(); @@ -120,6 +130,7 @@ abstract class TypedefDBAdapter { * Creates a database record for a type definition data type. * @param dataTypeID the ID of the data type that is referred to by this type definition. * @param name the unique name for this data type + * @param flags typedef flags (e.g., auto-name flag bit). * @param categoryID the ID for the category that contains this data type. * @param sourceArchiveID the ID for the source archive where this data type originated. * @param sourceDataTypeID the ID of the associated data type in the source archive. @@ -127,7 +138,7 @@ abstract class TypedefDBAdapter { * @return the database record for this data type. * @throws IOException if the database can't be accessed. */ - abstract DBRecord createRecord(long dataTypeID, String name, long categoryID, + abstract DBRecord createRecord(long dataTypeID, String name, short flags, long categoryID, long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException; /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java index 2c1dbffb0e..6eaa2a2d3c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV0.java @@ -68,10 +68,9 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { } @Override - public DBRecord createRecord(long dataTypeID, String name, long categoryID, long sourceArchiveID, - long sourceDataTypeID, long lastChangeTime) throws IOException { - throw new UnsupportedOperationException("Not allowed to update prior version #" + VERSION + - " of " + TYPEDEF_TABLE_NAME + " table."); + public DBRecord createRecord(long dataTypeID, String name, short flags, long categoryID, + long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { + throw new UnsupportedOperationException(); } @Override @@ -91,7 +90,7 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { @Override public boolean removeRecord(long dataID) throws IOException { - return table.deleteRecord(dataID); + throw new UnsupportedOperationException(); } @Override @@ -111,6 +110,7 @@ class TypedefDBAdapterV0 extends TypedefDBAdapter implements RecordTranslator { } DBRecord rec = TypedefDBAdapter.SCHEMA.createRecord(oldRec.getKey()); rec.setLongValue(TYPEDEF_DT_ID_COL, oldRec.getLongValue(V0_TYPEDEF_DT_ID_COL)); + // default TYPEDEF_FLAGS_COL to 0 rec.setString(TYPEDEF_NAME_COL, oldRec.getString(V0_TYPEDEF_NAME_COL)); rec.setLongValue(TYPEDEF_CAT_COL, oldRec.getLongValue(V0_TYPEDEF_CAT_COL)); rec.setLongValue(TYPEDEF_SOURCE_ARCHIVE_ID_COL, DataTypeManager.LOCAL_ARCHIVE_KEY); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java index eda97c81c4..d89c386594 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV1.java @@ -16,7 +16,6 @@ package ghidra.program.database.data; import java.io.IOException; -import java.util.Date; import db.*; import ghidra.util.UniversalID; @@ -25,7 +24,7 @@ import ghidra.util.exception.VersionException; /** * Version 1 implementation for accessing the Typedef database table. */ -class TypedefDBAdapterV1 extends TypedefDBAdapter { +class TypedefDBAdapterV1 extends TypedefDBAdapter implements RecordTranslator { static final int VERSION = 1; static final int V1_TYPEDEF_DT_ID_COL = 0; static final int V1_TYPEDEF_NAME_COL = 1; @@ -34,41 +33,35 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { static final int V1_TYPEDEF_UNIVERSAL_DT_ID_COL = 4; static final int V1_TYPEDEF_SOURCE_SYNC_TIME_COL = 5; static final int V1_TYPEDEF_LAST_CHANGE_TIME_COL = 6; - static final Schema V1_SCHEMA = new Schema(VERSION, "Typedef ID", - new Field[] { LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, - LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, - new String[] { "Data Type ID", "Name", "Category ID", "Source Archive ID", - "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); + +// DO NOT REMOVE WHAT'S BELOW - this documents the schema used in version 0. +// static final Schema V1_SCHEMA = new Schema(VERSION, "Typedef ID", +// new Field[] { LongField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, +// LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, +// new String[] { "Data Type ID", "Name", "Category ID", "Source Archive ID", +// "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); private Table table; /** * Gets a version 1 adapter for the Typedef database table. * @param handle handle to the database containing the table. - * @param create true if this constructor should create the table. * @throws VersionException if the the table's version does not match the expected version * for this adapter. */ - public TypedefDBAdapterV1(DBHandle handle, boolean create) - throws VersionException, IOException { + public TypedefDBAdapterV1(DBHandle handle) throws VersionException { - if (create) { - table = handle.createTable(TYPEDEF_TABLE_NAME, V1_SCHEMA, - new int[] { V1_TYPEDEF_CAT_COL, V1_TYPEDEF_UNIVERSAL_DT_ID_COL }); + table = handle.getTable(TYPEDEF_TABLE_NAME); + if (table == null) { + throw new VersionException("Missing Table: " + TYPEDEF_TABLE_NAME); } - else { - table = handle.getTable(TYPEDEF_TABLE_NAME); - if (table == null) { - throw new VersionException("Missing Table: " + TYPEDEF_TABLE_NAME); - } - int version = table.getSchema().getVersion(); - if (version != VERSION) { - String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + - " but got " + table.getSchema().getVersion(); - if (version < VERSION) { - throw new VersionException(msg, VersionException.OLDER_VERSION, true); - } - throw new VersionException(msg, VersionException.NEWER_VERSION, false); + int version = table.getSchema().getVersion(); + if (version != VERSION) { + String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + + " but got " + table.getSchema().getVersion(); + if (version < VERSION) { + throw new VersionException(msg, VersionException.OLDER_VERSION, true); } + throw new VersionException(msg, VersionException.NEWER_VERSION, false); } } @@ -78,49 +71,29 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { } @Override - public DBRecord createRecord(long dataTypeID, String name, long categoryID, long sourceArchiveID, - long sourceDataTypeID, long lastChangeTime) throws IOException { - - long tableKey = table.getKey(); -// if (tableKey <= DataManager.VOID_DATATYPE_ID) { -// tableKey = DataManager.VOID_DATATYPE_ID +1; -// } - long key = DataTypeManagerDB.createKey(DataTypeManagerDB.TYPEDEF, tableKey); - - DBRecord record = V1_SCHEMA.createRecord(key); - record.setLongValue(V1_TYPEDEF_DT_ID_COL, dataTypeID); - record.setString(V1_TYPEDEF_NAME_COL, name); - record.setLongValue(V1_TYPEDEF_CAT_COL, categoryID); - record.setLongValue(V1_TYPEDEF_SOURCE_ARCHIVE_ID_COL, sourceArchiveID); - record.setLongValue(V1_TYPEDEF_UNIVERSAL_DT_ID_COL, sourceDataTypeID); - record.setLongValue(V1_TYPEDEF_SOURCE_SYNC_TIME_COL, lastChangeTime); - record.setLongValue(V1_TYPEDEF_LAST_CHANGE_TIME_COL, lastChangeTime); - table.putRecord(record); - return record; + public DBRecord createRecord(long dataTypeID, String name, short flags, long categoryID, + long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { + throw new UnsupportedOperationException(); } @Override public DBRecord getRecord(long typedefID) throws IOException { - return table.getRecord(typedefID); + return translateRecord(table.getRecord(typedefID)); } @Override - public RecordIterator getRecords() throws IOException { - return table.iterator(); + RecordIterator getRecords() throws IOException { + return new TranslatedRecordIterator(table.iterator(), this); } @Override public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { - if (setLastChangeTime) { - record.setLongValue(TypedefDBAdapter.TYPEDEF_LAST_CHANGE_TIME_COL, - (new Date()).getTime()); - } - table.putRecord(record); + throw new UnsupportedOperationException(); } @Override public boolean removeRecord(long dataID) throws IOException { - return table.deleteRecord(dataID); + throw new UnsupportedOperationException(); } @Override @@ -137,14 +110,33 @@ class TypedefDBAdapterV1 extends TypedefDBAdapter { DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { Field[] keys = table.findRecords(new LongField(datatypeID.getValue()), V1_TYPEDEF_UNIVERSAL_DT_ID_COL); - for (int i = 0; i < keys.length; i++) { DBRecord record = table.getRecord(keys[i]); if (record.getLongValue(V1_TYPEDEF_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { - return record; + return translateRecord(record); } } return null; } + @Override + public DBRecord translateRecord(DBRecord oldRec) { + if (oldRec == null) { + return null; + } + DBRecord rec = TypedefDBAdapter.SCHEMA.createRecord(oldRec.getKey()); + rec.setLongValue(TYPEDEF_DT_ID_COL, oldRec.getLongValue(V1_TYPEDEF_DT_ID_COL)); + // default TYPEDEF_FLAGS_COL to 0 + rec.setString(TYPEDEF_NAME_COL, oldRec.getString(V1_TYPEDEF_NAME_COL)); + rec.setLongValue(TYPEDEF_CAT_COL, oldRec.getLongValue(V1_TYPEDEF_CAT_COL)); + rec.setLongValue(TYPEDEF_SOURCE_ARCHIVE_ID_COL, + oldRec.getLongValue(V1_TYPEDEF_SOURCE_ARCHIVE_ID_COL)); + rec.setLongValue(TYPEDEF_UNIVERSAL_DT_ID_COL, + oldRec.getLongValue(V1_TYPEDEF_UNIVERSAL_DT_ID_COL)); + rec.setLongValue(TYPEDEF_SOURCE_SYNC_TIME_COL, + oldRec.getLongValue(V1_TYPEDEF_SOURCE_SYNC_TIME_COL)); + rec.setLongValue(TYPEDEF_LAST_CHANGE_TIME_COL, + oldRec.getLongValue(V1_TYPEDEF_LAST_CHANGE_TIME_COL)); + return rec; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java new file mode 100644 index 0000000000..39226fa23d --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDBAdapterV2.java @@ -0,0 +1,154 @@ +/* ### + * 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. + */ +package ghidra.program.database.data; + +import java.io.IOException; +import java.util.Date; + +import db.*; +import ghidra.util.UniversalID; +import ghidra.util.exception.VersionException; + +/** + * Version 2 implementation for accessing the Typedef database table. + */ +class TypedefDBAdapterV2 extends TypedefDBAdapter { + static final int VERSION = 2; + static final int V2_TYPEDEF_DT_ID_COL = 0; + static final int V2_TYPEDEF_FLAGS_COL = 1; + static final int V2_TYPEDEF_NAME_COL = 2; + static final int V2_TYPEDEF_CAT_COL = 3; + static final int V2_TYPEDEF_SOURCE_ARCHIVE_ID_COL = 4; + static final int V2_TYPEDEF_UNIVERSAL_DT_ID_COL = 5; + static final int V2_TYPEDEF_SOURCE_SYNC_TIME_COL = 6; + static final int V2_TYPEDEF_LAST_CHANGE_TIME_COL = 7; + static final Schema V2_SCHEMA = new Schema(VERSION, "Typedef ID", + new Field[] { LongField.INSTANCE, ShortField.INSTANCE, StringField.INSTANCE, + LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, + LongField.INSTANCE }, + new String[] { "Data Type ID", "Flags", "Name", "Category ID", "Source Archive ID", + "Universal Data Type ID", "Source Sync Time", "Last Change Time" }); + private Table table; + + /** + * Gets a version 1 adapter for the Typedef database table. + * @param handle handle to the database containing the table. + * @param create true if this constructor should create the table. + * @throws VersionException if the the table's version does not match the expected version + * for this adapter. + * @throws IOException if IO error occurs + */ + public TypedefDBAdapterV2(DBHandle handle, boolean create) + throws VersionException, IOException { + + if (create) { + table = handle.createTable(TYPEDEF_TABLE_NAME, V2_SCHEMA, + new int[] { V2_TYPEDEF_CAT_COL, V2_TYPEDEF_UNIVERSAL_DT_ID_COL }); + } + else { + table = handle.getTable(TYPEDEF_TABLE_NAME); + if (table == null) { + throw new VersionException("Missing Table: " + TYPEDEF_TABLE_NAME); + } + int version = table.getSchema().getVersion(); + if (version != VERSION) { + String msg = "Expected version " + VERSION + " for table " + TYPEDEF_TABLE_NAME + + " but got " + table.getSchema().getVersion(); + if (version < VERSION) { + throw new VersionException(msg, VersionException.OLDER_VERSION, true); + } + throw new VersionException(msg, VersionException.NEWER_VERSION, false); + } + } + } + + @Override + void deleteTable(DBHandle handle) throws IOException { + handle.deleteTable(TYPEDEF_TABLE_NAME); + } + + @Override + public DBRecord createRecord(long dataTypeID, String name, short flags, long categoryID, + long sourceArchiveID, long sourceDataTypeID, long lastChangeTime) throws IOException { + + long tableKey = table.getKey(); +// if (tableKey <= DataManager.VOID_DATATYPE_ID) { +// tableKey = DataManager.VOID_DATATYPE_ID +1; +// } + long key = DataTypeManagerDB.createKey(DataTypeManagerDB.TYPEDEF, tableKey); + + DBRecord record = V2_SCHEMA.createRecord(key); + record.setLongValue(V2_TYPEDEF_DT_ID_COL, dataTypeID); + record.setShortValue(V2_TYPEDEF_FLAGS_COL, flags); + record.setString(V2_TYPEDEF_NAME_COL, name); + record.setLongValue(V2_TYPEDEF_CAT_COL, categoryID); + record.setLongValue(V2_TYPEDEF_SOURCE_ARCHIVE_ID_COL, sourceArchiveID); + record.setLongValue(V2_TYPEDEF_UNIVERSAL_DT_ID_COL, sourceDataTypeID); + record.setLongValue(V2_TYPEDEF_SOURCE_SYNC_TIME_COL, lastChangeTime); + record.setLongValue(V2_TYPEDEF_LAST_CHANGE_TIME_COL, lastChangeTime); + table.putRecord(record); + return record; + } + + @Override + public DBRecord getRecord(long typedefID) throws IOException { + return table.getRecord(typedefID); + } + + @Override + public RecordIterator getRecords() throws IOException { + return table.iterator(); + } + + @Override + public void updateRecord(DBRecord record, boolean setLastChangeTime) throws IOException { + if (setLastChangeTime) { + record.setLongValue(TypedefDBAdapter.TYPEDEF_LAST_CHANGE_TIME_COL, + (new Date()).getTime()); + } + table.putRecord(record); + } + + @Override + public boolean removeRecord(long dataID) throws IOException { + return table.deleteRecord(dataID); + } + + @Override + public Field[] getRecordIdsInCategory(long categoryID) throws IOException { + return table.findRecords(new LongField(categoryID), V2_TYPEDEF_CAT_COL); + } + + @Override + Field[] getRecordIdsForSourceArchive(long archiveID) throws IOException { + return table.findRecords(new LongField(archiveID), V2_TYPEDEF_SOURCE_ARCHIVE_ID_COL); + } + + @Override + DBRecord getRecordWithIDs(UniversalID sourceID, UniversalID datatypeID) throws IOException { + Field[] keys = + table.findRecords(new LongField(datatypeID.getValue()), V2_TYPEDEF_UNIVERSAL_DT_ID_COL); + + for (int i = 0; i < keys.length; i++) { + DBRecord record = table.getRecord(keys[i]); + if (record.getLongValue(V2_TYPEDEF_SOURCE_ARCHIVE_ID_COL) == sourceID.getValue()) { + return record; + } + } + return null; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java index 3fa67ebacd..4d09c023f1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractDataType.java @@ -89,7 +89,8 @@ public abstract class AbstractDataType implements DataType { @Override public DataTypePath getDataTypePath() { - return new DataTypePath(categoryPath, name); + // use methods instead of fields since they mey be overriden + return new DataTypePath(getCategoryPath(), getName()); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractImageBaseOffsetDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractImageBaseOffsetDataType.java deleted file mode 100644 index 22d0306068..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractImageBaseOffsetDataType.java +++ /dev/null @@ -1,125 +0,0 @@ -/* ### - * 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. - */ -package ghidra.program.model.data; - -import ghidra.docking.settings.Settings; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressOutOfBoundsException; -import ghidra.program.model.mem.MemBuffer; -import ghidra.program.model.scalar.Scalar; - -abstract class AbstractImageBaseOffsetDataType extends BuiltIn { - - AbstractImageBaseOffsetDataType(CategoryPath path, String name, DataTypeManager dtm) { - super(path, name, dtm); - } - - abstract DataType getScalarDataType(); - - static String generateName(DataType dt) { - return "ImageBaseOffset" + dt.getLength() * 8; - } - - static String generateMnemonic(DataType dt) { - return "ibo" + dt.getLength() * 8; - } - - static String generateDescription(DataType dt) { - return (dt.getLength() * 8) + "-bit Image Base Offset"; - } - - @Override - public String getDescription() { - DataType dt = getScalarDataType(); - return generateDescription(dt); - } - - @Override - public String getMnemonic(Settings settings) { - DataType dt = getScalarDataType(); - return generateMnemonic(dt); - } - - @Override - public int getLength() { - return getScalarDataType().getLength(); - } - - @Override - public String getRepresentation(MemBuffer buf, Settings settings, int length) { - Address addr = (Address) getValue(buf, settings, length); - if (addr == null) { // could not create address, so return "Not a pointer (NaP)" - return "NaP"; - } - return addr.toString(); - } - - @Override - public Object getValue(MemBuffer buf, Settings settings, int length) { - DataType dt = getScalarDataType(); - Address imageBase = buf.getMemory().getProgram().getImageBase(); - Scalar value = (Scalar) dt.getValue(buf, settings, length); - if (value != null && value.getUnsignedValue() != 0) { - try { - return imageBase.add(value.getUnsignedValue()); - } - catch (AddressOutOfBoundsException e) { - // ignore - } - } - return null; - } - - @Override - public boolean isEncodable() { - return getScalarDataType().isEncodable(); - } - - @Override - public byte[] encodeValue(Object value, MemBuffer buf, Settings settings, int length) - throws DataTypeEncodeException { - if (!(value instanceof Address)) { - throw new DataTypeEncodeException("Requires Address", value, this); - } - Address addressValue = (Address) value; - Address imageBase = buf.getMemory().getProgram().getImageBase(); - long offset; - try { - offset = addressValue.subtract(imageBase); - } - catch (IllegalArgumentException e) { - throw new DataTypeEncodeException(value, this, e); - } - Scalar scalarOffset = new Scalar(imageBase.getSize(), offset, false); - DataType dt = getScalarDataType(); - return dt.encodeValue(scalarOffset, buf, settings, length); - } - - @Override - public byte[] encodeRepresentation(String repr, MemBuffer buf, Settings settings, int length) - throws DataTypeEncodeException { - Address address = buf.getMemory().getProgram().getAddressFactory().getAddress(repr); - if (address == null) { - throw new DataTypeEncodeException("Cannot parse address", repr, this); - } - return encodeValue(address, buf, settings, length); - } - - @Override - public Class getValueClass(Settings settings) { - return Address.class; - } -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractPointerTypedefDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractPointerTypedefBuiltIn.java similarity index 86% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractPointerTypedefDataType.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractPointerTypedefBuiltIn.java index 4f121ff6e5..a86bedc28a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractPointerTypedefDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/AbstractPointerTypedefBuiltIn.java @@ -25,14 +25,10 @@ import ghidra.util.UniversalID; import ghidra.util.UniversalIdGenerator; /** - * AbstractPointerTypedefDataType provides an abstract implementation for - * a Pointer-Typedef BuiltIn datatype. - *
- * If a generated name is used the name will be locked-in once this datatype is resolved - * and will not automatcally update if any subsequent changes are made to - * {@link TypeDefSettingsDefinition} settings or the name of the referenced datatype. + * AbstractPointerTypedefDataType provides an abstract {@link BuiltIn} datatype + * implementation for a pointer-typedef datatype. */ -public abstract class AbstractPointerTypedefDataType extends BuiltIn implements TypeDef { +public abstract class AbstractPointerTypedefBuiltIn extends BuiltIn implements TypeDef { private String typedefName; private TypedefDataType modelTypedef; @@ -47,12 +43,13 @@ public abstract class AbstractPointerTypedefDataType extends BuiltIn implements * @param pointerSize pointer size in bytes or -1 for default pointer size * @param dtm data-type manager whose data organization should be used */ - public AbstractPointerTypedefDataType(String name, DataType referencedDataType, + protected AbstractPointerTypedefBuiltIn(String name, DataType referencedDataType, int pointerSize, DataTypeManager dtm) { super(getCategoryPath(referencedDataType), getTempNameIfNeeded(name), dtm); setTypedefName(name); modelTypedef = new TypedefDataType("TEMP", new PointerDataType(referencedDataType, pointerSize, dtm)); + setDefaultSettings(modelTypedef.getDefaultSettings()); } /** @@ -63,13 +60,20 @@ public abstract class AbstractPointerTypedefDataType extends BuiltIn implements * @param pointerDataType associated pointer datatype (required) * @param dtm data-type manager whose data organization should be used */ - public AbstractPointerTypedefDataType(String name, Pointer pointerDataType, + protected AbstractPointerTypedefBuiltIn(String name, Pointer pointerDataType, DataTypeManager dtm) { super(pointerDataType.getCategoryPath(), getTempNameIfNeeded(name), dtm); setTypedefName(name); modelTypedef = new TypedefDataType("TEMP", pointerDataType.clone(dtm)); + setDefaultSettings(modelTypedef.getDefaultSettings()); } + @Override + public void enableAutoNaming() { + typedefName = null; + } + + @Override public boolean isAutoNamed() { return typedefName == null; } @@ -102,7 +106,7 @@ public abstract class AbstractPointerTypedefDataType extends BuiltIn implements } void setTypedefName(String name) { - if (name != null) { + if (name != null && !DataUtilities.isValidDataTypeName(name)) { throw new IllegalArgumentException("Invalid DataType name: " + name); } this.typedefName = name; @@ -158,17 +162,7 @@ public abstract class AbstractPointerTypedefDataType extends BuiltIn implements @Override public SettingsDefinition[] getBuiltInSettingsDefinitions() { - return getTypeDefSettingsDefinitions(); - } - - @Override - public TypeDefSettingsDefinition[] getTypeDefSettingsDefinitions() { - return modelTypedef.getTypeDefSettingsDefinitions(); - } - - @Override - public Settings getDefaultSettings() { - return modelTypedef.getDefaultSettings(); + return modelTypedef.getSettingsDefinitions(); } @Override @@ -183,6 +177,11 @@ public abstract class AbstractPointerTypedefDataType extends BuiltIn implements getDataType().getName(); } + @Override + public Class getValueClass(Settings settings) { + return modelTypedef.getValueClass(settings); + } + @Override public Object getValue(MemBuffer buf, Settings settings, int length) { return modelTypedef.getValue(buf, settings, length); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharsetSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharsetSettingsDefinition.java index 42892bd606..2381d25487 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharsetSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/CharsetSettingsDefinition.java @@ -128,6 +128,11 @@ public class CharsetSettingsDefinition implements EnumSettingsDefinition { return CHARSET_NAME; } + @Override + public String getStorageKey() { + return CHARSET_SETTING_NAME; + } + @Override public String getDescription() { return "Character set"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ComponentOffsetSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ComponentOffsetSettingsDefinition.java index 60d80d5c49..563fbf5b56 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ComponentOffsetSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ComponentOffsetSettingsDefinition.java @@ -114,7 +114,7 @@ public class ComponentOffsetSettingsDefinition public String getAttributeSpecification(Settings settings) { if (hasValue(settings)) { long offset = getValue(settings); - return "offset(" + Long.toString(offset) + ")"; + return "offset(0x" + Long.toHexString(offset) + ")"; } return null; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java index 883e292c25..0e6f8b35a3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataType.java @@ -51,6 +51,9 @@ public interface DataType { public final static String CONFLICT_SUFFIX = ".conflict"; + public final static String TYPEDEF_ATTRIBUTE_PREFIX = "__(("; + public final static String TYPEDEF_ATTRIBUTE_SUFFIX = "))"; + static final long NO_SOURCE_SYNC_TIME = 0L; static final long NO_LAST_CHANGE_TIME = 0L; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java index 144eb8cfa9..b3deabd11a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java @@ -246,14 +246,6 @@ public interface DataTypeManager { */ public Category getCategory(CategoryPath path); - /** - * Notification when data type is changed. - * @param dataType data type that is changed - * @param isAutoChange true if change was an automatic change in response to - * another datatype's change (e.g., size, alignment). - */ - public void dataTypeChanged(DataType dataType, boolean isAutoChange); - /** * Add a listener that is notified when the dataTypeManger changes. * @param l the listener diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeMnemonicSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeMnemonicSettingsDefinition.java index 3f77587c19..0b57bfd833 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeMnemonicSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeMnemonicSettingsDefinition.java @@ -88,6 +88,11 @@ public class DataTypeMnemonicSettingsDefinition implements EnumSettingsDefinitio return "Mnemonic-style"; } + @Override + public String getStorageKey() { + return MNEMONIC; + } + @Override public String getDescription() { return "Selects the data-type mnemonic style"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EndianSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EndianSettingsDefinition.java index c655139e4a..6939ea0bab 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EndianSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EndianSettingsDefinition.java @@ -108,6 +108,11 @@ public class EndianSettingsDefinition implements EnumSettingsDefinition { return "Endian"; } + @Override + public String getStorageKey() { + return ENDIAN_SETTING_NAME; + } + @Override public String getDescription() { return "Selects the endianess of the data"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO32DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO32DataType.java index 517b41e39a..2528afd4e1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO32DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO32DataType.java @@ -15,27 +15,43 @@ */ package ghidra.program.model.data; +import ghidra.docking.settings.Settings; +import ghidra.util.classfinder.ClassTranslator; + /** * IBO32DataType provides a Pointer-Typedef BuiltIn for - * a 32-bit Image Base Offset Relative Pointer. + * a 32-bit Image Base Offset Relative Pointer. This {@link TypeDef} implementation + * specifies the {@link PointerType#IMAGE_BASE_RELATIVE} attribute/setting + * associated with a 32-bit {@link Pointer}. + *
+ * This class replaces the use of the old ImageBaseOffset32DataType + * which did not implement the Pointer interface. This is an alternative + * {@link BuiltIn} implementation to using the more general {@link PointerTypedef} + * datatype with an unspecified referenced datatype. {@link PointerTypedef} should + * be used for other cases + * (see {@link #createIBO32PointerTypedef(DataType)}). */ -public class IBO32DataType extends AbstractPointerTypedefDataType { +public class IBO32DataType extends AbstractPointerTypedefBuiltIn { - static final String NAME = "ibo32"; + public static final IBO32DataType dataType = new IBO32DataType(); -// TODO: remove old ImageBaseOffset32DataType implementation and uncomment -// static { -// ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset32", -// IBO32DataType.class.getName()); -// ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset32DataType", -// IBO32DataType.class.getName()); -// } + static final String NAME = "ImageBaseOffset32"; + + private static TypeDefSettingsDefinition[] IBO_TYPEDEF_SETTINGS_DEFS = + { PointerTypeSettingsDefinition.DEF }; + + static { + ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset32", + IBO32DataType.class.getName()); + ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset32DataType", + IBO32DataType.class.getName()); + } /** * Constructs a 32-bit Image Base Offset relative pointer-typedef. */ public IBO32DataType() { - this(DataType.DEFAULT, null); + this(null); } /** @@ -43,25 +59,9 @@ public class IBO32DataType extends AbstractPointerTypedefDataType { * @param dtm data-type manager whose data organization should be used */ public IBO32DataType(DataTypeManager dtm) { - this(DataType.DEFAULT, dtm); - } - - /** - * Constructs a 32-bit Image Base Offset relative pointer-typedef. - * @param referencedDataType data type this pointer-typedef points to - */ - public IBO32DataType(DataType referencedDataType) { - this(referencedDataType, null); - } - - /** - * Constructs a 32-bit Image Base Offset relative pointer-typedef. - * @param referencedDataType data type this pointer-typedef points to - * @param dtm data-type manager whose data organization should be used - */ - public IBO32DataType(DataType referencedDataType, DataTypeManager dtm) { - super(null, referencedDataType, 4, dtm); - PointerTypeSettingsDefinition.DEF.setType(getDefaultSettings(), PointerType.IBO); + super(NAME, null, 4, dtm); + PointerTypeSettingsDefinition.DEF.setType(getDefaultSettings(), + PointerType.IMAGE_BASE_RELATIVE); } @Override @@ -74,18 +74,30 @@ public class IBO32DataType extends AbstractPointerTypedefDataType { if (dataMgr == dtm) { return this; } - IBO32DataType td = new IBO32DataType(getReferencedDataType(), dtm); - TypedefDataType.copyTypeDefSettings(this, td, false); - return td; + return new IBO32DataType(dtm); } @Override - public String getName() { - DataType dt = getReferencedDataType(); - if (dt == null || Undefined.isUndefined(dt) || (dt instanceof VoidDataType)) { - return NAME; // use simple ibo name - } - return super.getName(); // use generated named + public String getMnemonic(Settings settings) { + return "ibo32"; + } + + @Override + public TypeDefSettingsDefinition[] getBuiltInSettingsDefinitions() { + return IBO_TYPEDEF_SETTINGS_DEFS; + } + + /** + * Create a IBO32 {@link PointerTypedef} with auto-naming. If needed, a name and category + * may be assigned to the returned instance. Unlike using an immutable {@link IBO32DataType} instance + * the returned instance is mutable. + * @param referencedDataType referenced datatype or null + * @return new IBO32 pointer-typedef + */ + public static PointerTypedef createIBO32PointerTypedef(DataType referencedDataType) { + return new PointerTypedef(null, referencedDataType, 4, + referencedDataType != null ? referencedDataType.getDataTypeManager() : null, + PointerType.IMAGE_BASE_RELATIVE); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO64DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO64DataType.java index cc78d5641d..894cb3c7a6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO64DataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/IBO64DataType.java @@ -15,27 +15,43 @@ */ package ghidra.program.model.data; +import ghidra.docking.settings.Settings; +import ghidra.util.classfinder.ClassTranslator; + /** * IBO64DataType provides a Pointer-Typedef BuiltIn for - * a 64-bit Image Base Offset Relative Pointer. + * a 64-bit Image Base Offset Relative Pointer. This {@link TypeDef} implementation + * specifies the {@link PointerType#IMAGE_BASE_RELATIVE} attribute/setting + * associated with a 64-bit {@link Pointer}. + *
+ * This class replaces the use of the old ImageBaseOffset64DataType + * which did not implement the Pointer interface. This is an alternative + * {@link BuiltIn} implementation to using the more general {@link PointerTypedef} + * datatype with an unspecified referenced datatype. {@link PointerTypedef} should + * be used for other cases + * (see {@link #createIBO64PointerTypedef(DataType)}). */ -public class IBO64DataType extends AbstractPointerTypedefDataType { +public class IBO64DataType extends AbstractPointerTypedefBuiltIn { - static final String NAME = "ibo64"; + public static final IBO64DataType dataType = new IBO64DataType(); -// TODO: remove old ImageBaseOffset64DataType implementation and uncomment -// static { -// ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset64", -// IBO64DataType.class.getName()); -// ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset64DataType", -// IBO64DataType.class.getName()); -// } + static final String NAME = "ImageBaseOffset64"; + + private static TypeDefSettingsDefinition[] IBO_TYPEDEF_SETTINGS_DEFS = + { PointerTypeSettingsDefinition.DEF }; + + static { + ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset64", + IBO64DataType.class.getName()); + ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset64DataType", + IBO64DataType.class.getName()); + } /** * Constructs a 64-bit Image Base Offset relative pointer-typedef. */ public IBO64DataType() { - this(DataType.DEFAULT, null); + this(null); } /** @@ -43,25 +59,8 @@ public class IBO64DataType extends AbstractPointerTypedefDataType { * @param dtm data-type manager whose data organization should be used */ public IBO64DataType(DataTypeManager dtm) { - this(DataType.DEFAULT, dtm); - } - - /** - * Constructs a 64-bit Image Base Offset relative pointer-typedef. - * @param referencedDataType data type this pointer-typedef points to - */ - public IBO64DataType(DataType referencedDataType) { - this(referencedDataType, null); - } - - /** - * Constructs a 64-bit Image Base Offset relative pointer-typedef. - * @param referencedDataType data type this pointer-typedef points to - * @param dtm data-type manager whose data organization should be used - */ - public IBO64DataType(DataType referencedDataType, DataTypeManager dtm) { - super(null, referencedDataType, 8, dtm); - PointerTypeSettingsDefinition.DEF.setType(getDefaultSettings(), PointerType.IBO); + super(NAME, null, 8, dtm); + PointerTypeSettingsDefinition.DEF.setType(getDefaultSettings(), PointerType.IMAGE_BASE_RELATIVE); } @Override @@ -74,18 +73,30 @@ public class IBO64DataType extends AbstractPointerTypedefDataType { if (dataMgr == dtm) { return this; } - IBO64DataType td = new IBO64DataType(getReferencedDataType(), dtm); - TypedefDataType.copyTypeDefSettings(this, td, false); - return td; + return new IBO64DataType(dtm); } @Override - public String getName() { - DataType dt = getReferencedDataType(); - if (dt == null || Undefined.isUndefined(dt) || (dt instanceof VoidDataType)) { - return NAME; // use simple ibo name - } - return super.getName(); // use generated named + public String getMnemonic(Settings settings) { + return "ibo64"; + } + + @Override + public TypeDefSettingsDefinition[] getBuiltInSettingsDefinitions() { + return IBO_TYPEDEF_SETTINGS_DEFS; + } + + /** + * Create a IBO64 {@link PointerTypedef} with auto-naming. If needed, a name and category + * may be assigned to the returned instance. Unlike using an immutable {@link IBO32DataType} instance + * the returned instance is mutable. + * @param referencedDataType referenced datatype or null + * @return new IBO64 pointer-typedef + */ + public static PointerTypedef createIBO64PointerTypedef(DataType referencedDataType) { + return new PointerTypedef(null, referencedDataType, 8, + referencedDataType != null ? referencedDataType.getDataTypeManager() : null, + PointerType.IMAGE_BASE_RELATIVE); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ImageBaseOffset32DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ImageBaseOffset32DataType.java deleted file mode 100644 index 8505ae9422..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ImageBaseOffset32DataType.java +++ /dev/null @@ -1,48 +0,0 @@ -/* ### - * 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. - */ -package ghidra.program.model.data; - -import ghidra.util.classfinder.*; - -public class ImageBaseOffset32DataType extends AbstractImageBaseOffsetDataType { - static { - ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset32", - ImageBaseOffset32DataType.class.getName()); - } - - private static DataType datatype = DWordDataType.dataType; - - public ImageBaseOffset32DataType() { - this(null); - } - - public ImageBaseOffset32DataType(DataTypeManager dtm) { - super(null, generateName(datatype), dtm); - } - - @Override - DataType getScalarDataType() { - return datatype; - } - - public DataType clone(DataTypeManager dtm) { - if (dtm == getDataTypeManager()) { - return this; - } - return new ImageBaseOffset32DataType(dtm); - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ImageBaseOffset64DataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ImageBaseOffset64DataType.java deleted file mode 100644 index a8e69fdb45..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ImageBaseOffset64DataType.java +++ /dev/null @@ -1,48 +0,0 @@ -/* ### - * 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. - */ -package ghidra.program.model.data; - -import ghidra.util.classfinder.*; - -public class ImageBaseOffset64DataType extends AbstractImageBaseOffsetDataType { - static { - ClassTranslator.put("ghidra.program.model.data.ImageBaseOffset64", - ImageBaseOffset64DataType.class.getName()); - } - - private static DataType datatype = QWordDataType.dataType; - - public ImageBaseOffset64DataType() { - this(null); - } - - public ImageBaseOffset64DataType(DataTypeManager dtm) { - super(null, generateName(datatype), dtm); - } - - @Override - DataType getScalarDataType() { - return datatype; - } - - public DataType clone(DataTypeManager dtm) { - if (dtm == getDataTypeManager()) { - return this; - } - return new ImageBaseOffset64DataType(dtm); - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MutabilitySettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MutabilitySettingsDefinition.java index 7ab887e13d..c7cf3a30bd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MutabilitySettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/MutabilitySettingsDefinition.java @@ -87,6 +87,11 @@ public class MutabilitySettingsDefinition implements EnumSettingsDefinition { return "Mutability"; } + @Override + public String getStorageKey() { + return MUTABILITY; + } + @Override public String getDescription() { return "Selects the data mutability"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PaddingSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PaddingSettingsDefinition.java index d37bf58d24..a8cf53d6fd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PaddingSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PaddingSettingsDefinition.java @@ -23,8 +23,9 @@ import ghidra.docking.settings.Settings; */ public class PaddingSettingsDefinition implements EnumSettingsDefinition { - private static final int PADDED_VALUE = 1; - private static final int UNPADDED_VALUE = 0; + public static final int PADDED_VALUE = 1; + public static final int UNPADDED_VALUE = 0; + private static final String[] choices = { "unpadded", "padded" }; private static final String PADDED = "padded"; @@ -86,6 +87,11 @@ public class PaddingSettingsDefinition implements EnumSettingsDefinition { return "Padding"; } + @Override + public String getStorageKey() { + return PADDED; + } + @Override public String getDescription() { return "Selects if the data is padded or not"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java index 8d2810c556..b102e2fea7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerDataType.java @@ -403,7 +403,7 @@ public class PointerDataType extends BuiltIn implements Pointer { try { PointerType choice = PointerTypeSettingsDefinition.DEF.getType(settings); - if (choice == PointerType.IBO && mem != null) { + if (choice == PointerType.IMAGE_BASE_RELATIVE && mem != null) { // must ignore AddressSpaceSettingsDefinition Address imageBase = mem.getProgram().getImageBase(); targetSpace = imageBase.getAddressSpace(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerType.java index b56932da4e..391e67e033 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerType.java @@ -33,7 +33,7 @@ public enum PointerType { /** * Pointer offset relative to program image base. */ - IBO(1), + IMAGE_BASE_RELATIVE(1), /** * Pointer offset relative to pointer storage address. * NOTE: This type has limited usefulness since it can only be applied to diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypeSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypeSettingsDefinition.java index c47a5a2700..5b74e32447 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypeSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypeSettingsDefinition.java @@ -31,6 +31,7 @@ public class PointerTypeSettingsDefinition "Specifies the pointer type which affects interpretation of offset"; private static final String DISPLAY_NAME = "Pointer Type"; + // Choices correspond to the enumerated PointerType values private static final String[] choices = { "default", "image-base-relative", "relative", "file-offset" }; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedef.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedef.java index 7ecc1596e7..2a417ef12a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedef.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedef.java @@ -17,7 +17,15 @@ package ghidra.program.model.data; import java.util.Objects; +import org.apache.commons.lang3.StringUtils; + +import ghidra.docking.settings.Settings; +import ghidra.docking.settings.SettingsDefinition; +import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.mem.MemBuffer; +import ghidra.util.UniversalID; +import ghidra.util.UniversalIdGenerator; /** * PointerTypedef provides a Pointer-Typedef template datatype @@ -30,59 +38,151 @@ import ghidra.program.model.address.AddressSpace; * since it does not implement a default constructor so it may not be treated * like other {@link BuiltIn} datatypes which are managed by the * <{@link BuiltInDataTypeManager}. + *
+ * NOTE: As a {@link BuiltIn} datatype the use of {@link #setName(String)} and + * {@link #setNameAndCategory(CategoryPath, String)} is disabled. The datatype + * instance must be instantiated with the correct typedef name. */ -public class PointerTypedef extends AbstractPointerTypedefDataType { +public class PointerTypedef extends GenericDataType implements TypeDef { + + private boolean isAutoNamed; + private TypedefDataType modelTypedef; + private UniversalID universalId = UniversalIdGenerator.nextID(); /** * Constructs a pointer-typedef which dereferences into a specific address space. - * @param typeDefName name of this pointer-typedef or null to force name generation. - * @param referencedDataType data type this pointer-typedef points to - * @param pointerSize pointer size in bytes or -1 for default pointer size based upon datatype manager + * @param typeDefName name of this pointer-typedef or null to use auto-named typedef. + * @param referencedDataType data type this pointer-typedef points to or null + * @param pointerSize pointer size in bytes or -1 for default pointer size based upon specified + * address space and datatype manager * @param dtm data-type manager whose data organization should be used (highly recommended, may be null) * @param space address space to be used when dereferencing pointer offset */ public PointerTypedef(String typeDefName, DataType referencedDataType, int pointerSize, DataTypeManager dtm, AddressSpace space) { - super(typeDefName, referencedDataType, pointerSize, dtm); - Objects.requireNonNull(space, "Address space must be specified"); + this(typeDefName, referencedDataType, getPreferredPointerSize(pointerSize, dtm, space), + dtm); AddressSpaceSettingsDefinition.DEF.setValue(getDefaultSettings(), space.getName()); } /** * Constructs a pointer-typedef of a specific type - * @param typeDefName name of this pointer-typedef or null to force name generation. - * @param referencedDataType data type this pointer-typedef points to + * @param typeDefName name of this pointer-typedef or null to use auto-named typedef. + * @param referencedDataType data type this pointer-typedef points to or null * @param pointerSize pointer size in bytes or -1 for default pointer size based upon datatype manager * @param dtm data-type manager whose data organization should be used (highly recommended, may be null) * @param type pointer type (IBO, RELATIVE, FILE_OFFSET) */ public PointerTypedef(String typeDefName, DataType referencedDataType, int pointerSize, DataTypeManager dtm, PointerType type) { - super(typeDefName, referencedDataType, pointerSize, dtm); + this(typeDefName, referencedDataType, pointerSize, dtm); Objects.requireNonNull(type, "Pointer type required"); PointerTypeSettingsDefinition.DEF.setType(getDefaultSettings(), type); } /** - * Constructs a pointer-typedef without any settings - * @param typeDefName name of this pointer-typedef or null to force name generation. - * @param referencedDataType data type this pointer-typedef points to + * Constructs a offset-pointer-typedef + * @param typeDefName name of this pointer-typedef or null to use auto-named typedef. + * @param referencedDataType data type this pointer-typedef points to or null * @param pointerSize pointer size in bytes or -1 for default pointer size based upon datatype manager * @param dtm data-type manager whose data organization should be used (highly recommended, may be null) + * @param componentOffset signed component offset setting value (see {@link ComponentOffsetSettingsDefinition} */ - /* package */ PointerTypedef(String typeDefName, DataType referencedDataType, int pointerSize, - DataTypeManager dtm) { - super(typeDefName, referencedDataType, pointerSize, dtm); + public PointerTypedef(String typeDefName, DataType referencedDataType, int pointerSize, + DataTypeManager dtm, long componentOffset) { + this(typeDefName, referencedDataType, pointerSize, dtm); + ComponentOffsetSettingsDefinition.DEF.setValue(getDefaultSettings(), componentOffset); } /** * Constructs a pointer-typedef without any settings - * @param typeDefName name of this pointer-typedef or null to force name generation. + * @param typeDefName name of this pointer-typedef or null to use auto-named typedef. + * @param referencedDataType data type this pointer-typedef points to or null + * @param pointerSize pointer size in bytes or -1 for default pointer size based upon datatype manager + * @param dtm data-type manager whose data organization should be used (highly recommended, may be null) + */ + public PointerTypedef(String typeDefName, DataType referencedDataType, int pointerSize, + DataTypeManager dtm) { + super(getCategoryPath(referencedDataType), getTempNameIfNeeded(typeDefName), dtm); + isAutoNamed = StringUtils.isBlank(typeDefName); + modelTypedef = + new TypedefDataType("TEMP", new PointerDataType(referencedDataType, pointerSize, dtm)); + } + + /** + * Constructs a pointer-typedef without any settings + * @param typeDefName name of this pointer-typedef or null to use auto-named typedef. * @param pointerDataType associated pointer datatype * @param dtm data-type manager whose data organization should be used (highly recommended, may be null) */ - /* package */ PointerTypedef(String typeDefName, Pointer pointerDataType, DataTypeManager dtm) { - super(typeDefName, pointerDataType, dtm); + public PointerTypedef(String typeDefName, Pointer pointerDataType, DataTypeManager dtm) { + super(pointerDataType.getCategoryPath(), getTempNameIfNeeded(typeDefName), dtm); + isAutoNamed = StringUtils.isBlank(typeDefName); + modelTypedef = new TypedefDataType("TEMP", pointerDataType.clone(dtm)); + } + + private static CategoryPath getCategoryPath(DataType referencedDataType) { + return referencedDataType != null ? referencedDataType.getCategoryPath() + : CategoryPath.ROOT; + } + + private static String getTempNameIfNeeded(String baseName) { + return StringUtils.isBlank(baseName) ? "TEMP" : baseName; + } + + private static int getPreferredPointerSize(int pointerSize, DataTypeManager dtm, + AddressSpace space) { + Objects.requireNonNull(space, "Address space must be specified"); + if (pointerSize > 0) { + return pointerSize; + } + pointerSize = space.getSize() / 8; + if (dtm.getDataOrganization().getPointerSize() == pointerSize) { + pointerSize = -1; + } + return pointerSize; + } + + @Override + public void enableAutoNaming() { + isAutoNamed = true; + } + + @Override + public boolean isAutoNamed() { + return isAutoNamed; + } + + /** + * Get the referenced datatype used to construct this datatype + * (datatype which pointer references). + * @return referenced datatype + */ + protected DataType getReferencedDataType() { + Pointer ptrType = (Pointer) getDataType(); + return ptrType.getDataType(); + } + + public UniversalID getUniversalID() { + return universalId; + } + + @Override + public boolean isEquivalent(DataType obj) { + if (obj == this) { + return true; + } + if (obj == null || !(obj instanceof TypeDef)) { + return false; + } + TypeDef td = (TypeDef) obj; + if (!DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) { + return false; + } + if (!hasSameTypeDefSettings(td)) { + return false; + } + return DataTypeUtilities.isSameOrEquivalentDataType(getDataType(), td.getDataType()); } @Override @@ -91,12 +191,92 @@ public class PointerTypedef extends AbstractPointerTypedefDataType { } @Override - public DataType clone(DataTypeManager dtm) { + public String getName() { + if (isAutoNamed) { + // Do not cache name since we do not have listeners to detect + // settings change which may impact name generation. + return TypedefDataType.generateTypedefName(this); + } + return super.getName(); // use name provided at instantiation + } + + @Override + public boolean hasLanguageDependantLength() { + return modelTypedef.hasLanguageDependantLength(); + } + + @Override + public int getLength() { + return modelTypedef.getLength(); + } + + @Override + public DataType getDataType() { + return modelTypedef.getDataType(); + } + + @Override + public DataType getBaseDataType() { + return modelTypedef.getBaseDataType(); + } + + @Override + public SettingsDefinition[] getSettingsDefinitions() { + return modelTypedef.getSettingsDefinitions(); + } + + @Override + public TypeDefSettingsDefinition[] getTypeDefSettingsDefinitions() { + return modelTypedef.getTypeDefSettingsDefinitions(); + } + + @Override + public Settings getDefaultSettings() { + return modelTypedef.getDefaultSettings(); + } + + @Override + public boolean dependsOn(DataType dt) { + DataType myDt = getDataType(); + return (myDt == dt || myDt.dependsOn(dt)); + } + + @Override + public String toString() { + if (isAutoNamed) { + return getClass().getSimpleName() + ": " + getName(); + } + return getClass().getSimpleName() + ": typedef " + getName() + " " + + getDataType().getName(); + } + + @Override + public Class getValueClass(Settings settings) { + return modelTypedef.getValueClass(settings); + } + + @Override + public Object getValue(MemBuffer buf, Settings settings, int length) { + return modelTypedef.getValue(buf, settings, length); + } + + @Override + public String getRepresentation(MemBuffer buf, Settings settings, int length) { + return modelTypedef.getRepresentation(buf, settings, length); + } + + @Override + public PointerTypedef clone(DataTypeManager dtm) { if (dataMgr == dtm) { return this; } + return copy(dtm); + } + + @Override + public PointerTypedef copy(DataTypeManager dtm) { Pointer ptrType = (Pointer) getDataType(); - String n = hasGeneratedNamed() ? null : getName(); + String n = isAutoNamed ? null : getName(); PointerTypedef td = new PointerTypedef(n, ptrType, getDataTypeManager()); TypedefDataType.copyTypeDefSettings(this, td, false); return td; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedefBuilder.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedefBuilder.java index 0057a40d94..f5c45e23c6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedefBuilder.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/PointerTypedefBuilder.java @@ -20,6 +20,7 @@ import java.util.Objects; import ghidra.program.database.data.PointerTypedefInspector; import ghidra.program.model.address.AddressSpace; import ghidra.program.model.symbol.OffsetReference; +import ghidra.util.InvalidNameException; /** * PointerTypedefBuilder provides a builder for creating {@link Pointer} - {@link TypeDef}s. @@ -62,9 +63,10 @@ public class PointerTypedefBuilder { * upon the associated pointer type and the specified settings. * @param name typedef name * @return this builder + * @throws InvalidNameException if name contains unsupported characters */ - public PointerTypedefBuilder name(String name) { - typedef.setTypedefName(name); + public PointerTypedefBuilder name(String name) throws InvalidNameException { + typedef.setName(name); return this; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java index c9ef0e0286..1fc1eda8f3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/ProgramBasedDataTypeManager.java @@ -15,7 +15,9 @@ */ package ghidra.program.model.data; +import ghidra.docking.settings.SettingsDefinition; import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Data; import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -33,78 +35,102 @@ public interface ProgramBasedDataTypeManager extends DomainFileBasedDataTypeMana Program getProgram(); /** - * Set the long value for instance settings. + * Determine if a settings change is permitted for the specified settingsDefinition. + * @param data data code unit + * @param settingsDefinition settings definition + * @return true if change permitted else false + */ + public boolean isChangeAllowed(Data data, SettingsDefinition settingsDefinition); + + /** + * Set the long value for data instance settings. * - * @param dataAddr min address of data + * @param data data code unit * @param name settings name * @param value value of setting * @return true if the settings actually changed */ - public boolean setLongSettingsValue(Address dataAddr, String name, long value); + public boolean setLongSettingsValue(Data data, String name, long value); /** - * Set the string value for instance settings. + * Set the string value for data instance settings. * - * @param dataAddr min address of data + * @param data data code unit * @param name settings name * @param value value of setting * @return true if the settings actually changed */ - public boolean setStringSettingsValue(Address dataAddr, String name, String value); + public boolean setStringSettingsValue(Data data, String name, String value); /** - * Set the Object settings. + * Set the Object value for data instance settings. * - * @param dataAddr min address of data + * @param data data code unit * @param name the name of the settings * @param value the value for the settings, must be either a String, byte[] * or Long * @return true if the settings were updated */ - public boolean setSettings(Address dataAddr, String name, Object value); + public boolean setSettings(Data data, String name, Object value); /** - * Get the long value for an instance setting. + * Get the long value for data instance settings. * - * @param dataAddr min address of data + * @param data data code unit * @param name settings name * @return null if the named setting was not found */ - public Long getLongSettingsValue(Address dataAddr, String name); + public Long getLongSettingsValue(Data data, String name); /** - * Get the String value for an instance setting. + * Get the String value for data instance settings. * - * @param dataAddr min address of data + * @param data data code unit * @param name settings name * @return null if the named setting was not found */ - public String getStringSettingsValue(Address dataAddr, String name); + public String getStringSettingsValue(Data data, String name); /** - * Gets the value of a settings as an object (either String, byte[], or Long). + * Gets the value for data instance settings in Object form. * - * @param dataAddr the address of the data for this settings + * @param data data code unit * @param name the name of settings. * @return the settings object */ - public Object getSettings(Address dataAddr, String name); + public Object getSettings(Data data, String name); /** - * Clear the setting + * Clear the specified setting for the given data * - * @param dataAddr min address of data + * @param data data code unit * @param name settings name * @return true if the settings were cleared */ - public boolean clearSetting(Address dataAddr, String name); + public boolean clearSetting(Data data, String name); /** - * Clear all settings at the given address. + * Clear all settings for the given data. * - * @param dataAddr the address for this settings. + * @param data data code unit */ - public void clearAllSettings(Address dataAddr); + public void clearAllSettings(Data data); + + /** + * Returns all the instance Settings names used for the specified data + * + * @param data data code unit + * @return the names + */ + public String[] getInstanceSettingsNames(Data data); + + /** + * Returns true if no settings are set for the given data + * + * @param data data code unit + * @return true if not settings + */ + public boolean isEmptySetting(Data data); /** * Move the settings in the range to the new start address @@ -118,22 +144,6 @@ public interface ProgramBasedDataTypeManager extends DomainFileBasedDataTypeMana public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) throws CancelledException; - /** - * Returns all the instance Settings names used at the given address - * - * @param dataAddr the address - * @return the names - */ - public String[] getInstanceSettingsNames(Address dataAddr); - - /** - * Returns true if no settings are set for the given address - * - * @param dataAddr the address to test - * @return true if not settings - */ - public boolean isEmptySetting(Address dataAddr); - /** * Removes all settings in the range * diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TerminatedSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TerminatedSettingsDefinition.java index 1228652316..c74dfbb86b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TerminatedSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TerminatedSettingsDefinition.java @@ -82,6 +82,11 @@ public class TerminatedSettingsDefinition implements EnumSettingsDefinition { return "Termination"; } + @Override + public String getStorageKey() { + return TERMINATED; + } + @Override public String getDescription() { return "Selects if the string is terminated or unterminated"; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDef.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDef.java index cb9419a6a3..c0492db5bf 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDef.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDef.java @@ -23,15 +23,33 @@ import ghidra.docking.settings.SettingsDefinition; */ public interface TypeDef extends DataType { + /** + * Determine if this datatype use auto-naming (e.g., see {@link PointerTypedef}). + * If true, any change to associated {@link TypeDefSettingsDefinition} settings + * or naming of the pointer-referenced datatype will cause a automatic renaming + * of this datatype. + * @return true if auto-named, else false. + */ + public boolean isAutoNamed(); + + /** + * Enable auto-naming for this typedef. This will force naming to reflect the name of + * associated datatype plus an attribute list which corresponds to any + * {@link TypeDefSettingsDefinition} settings which may be set. + */ + public void enableAutoNaming(); + /** * Returns the dataType that this typedef is based on. This could be * another typedef + * @return the datatype which this typedef is based on (may be another {@link TypeDef}). */ public DataType getDataType(); /** * Returns the non-typedef dataType that this typedef is based on, following * chains of typedefs as necessary. + * @return the datatype which this typedef is based on (will not be another {@link TypeDef}). */ public DataType getBaseDataType(); @@ -78,4 +96,5 @@ public interface TypeDef extends DataType { } return true; } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDefSettingsDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDefSettingsDefinition.java index 95f05bebe9..456393b106 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDefSettingsDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypeDefSettingsDefinition.java @@ -33,13 +33,6 @@ import ghidra.docking.settings.SettingsDefinition; */ public interface TypeDefSettingsDefinition extends SettingsDefinition { - /** - * Get the storage key which should be used when storing a key/value - * map entry which corresponds to this settings definition. - * @return settings storage key - */ - String getStorageKey(); - /** * Get the {@link TypeDef} attribute specification for this setting and its * current value. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java index 3a495f0550..840fd731d7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/TypedefDataType.java @@ -18,6 +18,7 @@ package ghidra.program.model.data; import ghidra.docking.settings.*; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.model.mem.MemBuffer; +import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; /** @@ -30,6 +31,7 @@ public class TypedefDataType extends GenericDataType implements TypeDef { private DataType dataType; private SettingsDefinition[] settingsDef; + private boolean isAutoNamed = false; private boolean deleted = false; /** @@ -104,6 +106,20 @@ public class TypedefDataType extends GenericDataType implements TypeDef { } } + @Override + public void enableAutoNaming() { + if (isAutoNamed) { + return; + } + isAutoNamed = true; + notifyNameChanged(name); + } + + @Override + public boolean isAutoNamed() { + return isAutoNamed; + } + @Override public String getDefaultLabelPrefix() { return getName(); @@ -124,7 +140,10 @@ public class TypedefDataType extends GenericDataType implements TypeDef { } if (obj instanceof TypeDef) { TypeDef td = (TypeDef) obj; - if (!DataTypeUtilities.equalsIgnoreConflict(name, td.getName())) { + if (isAutoNamed != td.isAutoNamed()) { + return false; + } + if (!isAutoNamed && !DataTypeUtilities.equalsIgnoreConflict(getName(), td.getName())) { return false; } if (!hasSameTypeDefSettings(td)) { @@ -175,23 +194,66 @@ public class TypedefDataType extends GenericDataType implements TypeDef { return dataType.getValueClass(settings); } + public static TypeDef clone(TypeDef typedef, DataTypeManager dtm) { + if (typedef.getDataTypeManager() == dtm) { + return typedef; + } + TypedefDataType newTypedef = + new TypedefDataType(typedef.getCategoryPath(), typedef.getName(), typedef.getDataType(), + typedef.getUniversalID(), + typedef.getSourceArchive(), typedef.getLastChangeTime(), + typedef.getLastChangeTimeInSourceArchive(), dtm); + copyTypeDefSettings(typedef, newTypedef, false); + newTypedef.isAutoNamed = typedef.isAutoNamed(); + return newTypedef; + } + + public static TypedefDataType copy(TypeDef typedef, DataTypeManager dtm) { + TypedefDataType newTypedef = new TypedefDataType(typedef.getCategoryPath(), + typedef.getName(), typedef.getDataType(), dtm); + copyTypeDefSettings(typedef, newTypedef, false); + newTypedef.isAutoNamed = typedef.isAutoNamed(); + return newTypedef; + } + @Override public TypedefDataType clone(DataTypeManager dtm) { - if (getDataTypeManager() == dtm) { - return this; - } - TypedefDataType typeDef = - new TypedefDataType(categoryPath, name, dataType, getUniversalID(), - getSourceArchive(), getLastChangeTime(), getLastChangeTimeInSourceArchive(), dtm); - copyTypeDefSettings(this, typeDef, false); - return typeDef; + return (TypedefDataType) clone(this, dtm); } @Override public TypedefDataType copy(DataTypeManager dtm) { - TypedefDataType typeDef = new TypedefDataType(categoryPath, name, dataType, dtm); - copyTypeDefSettings(this, typeDef, false); - return typeDef; + return copy(this, dtm); + } + + @Override + public String getName() { + if (isAutoNamed()) { + return generateTypedefName(this); + } + return super.getName(); + } + + @Override + public void setName(String name) throws InvalidNameException { + super.setName(name); + isAutoNamed = false; + } + + @Override + public void setCategoryPath(CategoryPath path) { + if (isAutoNamed()) { + return; // ignore category change if auto-naming enabled + } + super.setCategoryPath(path); + } + + @Override + public CategoryPath getCategoryPath() { + if (isAutoNamed()) { + return getDataType().getCategoryPath(); + } + return super.getCategoryPath(); } @Override @@ -316,6 +378,9 @@ public class TypedefDataType extends GenericDataType implements TypeDef { @Override public String toString() { + if (isAutoNamed()) { + return getName(); + } return "typedef " + getName() + " " + dataType.getName(); } @@ -351,9 +416,9 @@ public class TypedefDataType extends GenericDataType implements TypeDef { public static String generateTypedefName(TypeDef modelType) { // Examples: - // string *32 __attribute__((relative)) - // char *32 __attribute__((image-base-relative)) - // char *16 __attribute__((space(data))) + // string *32 __((relative)) + // char *32 __((image-base-relative)) + // char *16 __((space(data))) Settings settings = modelType.getDefaultSettings(); StringBuilder attributesBuf = new StringBuilder(); @@ -367,9 +432,11 @@ public class TypedefDataType extends GenericDataType implements TypeDef { } } StringBuilder buf = new StringBuilder(modelType.getDataType().getName()); - buf.append(" __attribute__(("); + buf.append(' '); + buf.append(DataType.TYPEDEF_ATTRIBUTE_PREFIX); buf.append(attributesBuf); - buf.append("))"); + buf.append(DataType.TYPEDEF_ATTRIBUTE_SUFFIX); return buf.toString(); } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java index 0131532cdd..237039f40b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/DataStub.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import ghidra.docking.settings.Settings; +import ghidra.docking.settings.SettingsDefinition; import ghidra.program.model.address.Address; import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataTypeDisplayOptions; @@ -322,6 +323,11 @@ public class DataStub implements Data { throw new UnsupportedOperationException(); } + @Override + public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + throw new UnsupportedOperationException(); + } + @Override public Long getLong(String name) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/SettingsBuilder.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/SettingsBuilder.java index 39b02d70b9..8a61f66d3b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/SettingsBuilder.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/SettingsBuilder.java @@ -17,8 +17,7 @@ package ghidra.program.model.data; import java.nio.charset.Charset; -import ghidra.docking.settings.Settings; -import ghidra.docking.settings.SettingsImpl; +import ghidra.docking.settings.*; import ghidra.program.model.data.RenderUnicodeSettingsDefinition.RENDER_ENUM; import ghidra.program.model.data.TranslationSettingsDefinition.TRANSLATION_ENUM; @@ -36,6 +35,11 @@ public class SettingsBuilder implements Settings { // nada } + @Override + public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { + return settings.isChangeAllowed(settingsDefinition); + } + /** * Sets the {@link CharsetSettingsDefinition}. * diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java index f5687c7a1c..93b977a761 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDoubleDataTypeManager.java @@ -141,11 +141,6 @@ public class TestDoubleDataTypeManager implements DataTypeManager { throw new UnsupportedOperationException(); } - @Override - public void dataTypeChanged(DataType dataType, boolean isAutoChange) { - throw new UnsupportedOperationException(); - } - @Override public void addDataTypeManagerListener(DataTypeManagerChangeListener l) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java index 5dbb81a14d..142c21800c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/TestDummyDataTypeManager.java @@ -155,12 +155,6 @@ public class TestDummyDataTypeManager implements DataTypeManager { return null; } - @Override - public void dataTypeChanged(DataType dataType, boolean isAutoChange) { - // stub - - } - @Override public void addDataTypeManagerListener(DataTypeManagerChangeListener l) { // stub