diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java
index 1214d2ac23..3680708d10 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/DBTrace.java
@@ -161,8 +161,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
// TODO: Should there be a TraceCompilerSpec?
this.baseCompilerSpec =
baseLanguage.getCompilerSpecByID(baseCompilerSpec.getCompilerSpecID());
- this.baseAddressFactory =
- new TraceAddressFactory(this.baseLanguage, this.baseCompilerSpec);
+ this.baseAddressFactory = new TraceAddressFactory(this.baseLanguage, this.baseCompilerSpec,
+ space -> getAddressSet(space));
try (Transaction tx = this.openTransaction("Create")) {
initOptions(DBOpenMode.CREATE);
@@ -182,6 +182,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
programView = createProgramView(0);
}
+ private AddressSetView getAddressSet(OverlayAddressSpace space) {
+ // use entire space
+ return new AddressSet(space.getMinAddress(), space.getMaxAddress());
+ }
+
public DBTrace(DBHandle dbh, DBOpenMode openMode, TaskMonitor monitor, Object consumer)
throws CancelledException, VersionException, IOException, LanguageNotFoundException {
super(dbh, openMode, monitor, "Untitled", DB_TIME_INTERVAL, DB_BUFFER_SIZE, consumer);
@@ -214,11 +219,11 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
else {
name = traceInfo.getString(NAME, "Unnamed?");
baseLanguage = DefaultLanguageService.getLanguageService()
- .getLanguage(
- new LanguageID(traceInfo.getString(BASE_LANGUAGE, null)));
+ .getLanguage(new LanguageID(traceInfo.getString(BASE_LANGUAGE, null)));
baseCompilerSpec = baseLanguage.getCompilerSpecByID(
new CompilerSpecID(traceInfo.getString(BASE_COMPILER, null)));
- baseAddressFactory = new TraceAddressFactory(baseLanguage, baseCompilerSpec);
+ baseAddressFactory = new TraceAddressFactory(baseLanguage, baseCompilerSpec,
+ space -> getAddressSet(space));
}
}
@@ -247,9 +252,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
return;
}
if (baseAddressFactory.getAddressSpace(as.getSpaceID()) != as) {
- throw new IllegalArgumentException(
- "AddressSpace '" + as + "' is not in this trace (language=" + getBaseLanguage() +
- ")");
+ throw new IllegalArgumentException("AddressSpace '" + as +
+ "' is not in this trace (language=" + getBaseLanguage() + ")");
}
}
@@ -329,9 +333,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService
protected DBTraceDataTypeManager createDataTypeManager()
throws CancelledException, IOException {
- return createTraceManager("Data Type Manager",
- (openMode, monitor) -> new DBTraceDataTypeManager(dbh, openMode, rwLock, monitor,
- this));
+ return createTraceManager("Data Type Manager", (openMode,
+ monitor) -> new DBTraceDataTypeManager(dbh, openMode, rwLock, monitor, this));
}
@DependentService
@@ -366,8 +369,7 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
}
@DependentService
- protected DBTraceObjectManager createObjectManager()
- throws CancelledException, IOException {
+ protected DBTraceObjectManager createObjectManager() throws CancelledException, IOException {
return createTraceManager("Object Manager",
(openMode, monitor) -> new DBTraceObjectManager(dbh, openMode, rwLock, monitor,
baseLanguage, this));
@@ -376,9 +378,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService
protected DBTraceOverlaySpaceAdapter createOverlaySpaceAdapter()
throws CancelledException, IOException {
- return createTraceManager("Overlay Space Adapter",
- (openMode, monitor) -> new DBTraceOverlaySpaceAdapter(dbh, openMode, rwLock, monitor,
- this));
+ return createTraceManager("Overlay Space Adapter", (openMode,
+ monitor) -> new DBTraceOverlaySpaceAdapter(dbh, openMode, rwLock, monitor, this));
}
@DependentService
@@ -409,9 +410,9 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService
protected DBTraceStaticMappingManager createStaticMappingManager(
DBTraceOverlaySpaceAdapter overlayAdapter) throws CancelledException, IOException {
- return createTraceManager("Static Mapping Manager", (openMode,
- monitor) -> new DBTraceStaticMappingManager(dbh, openMode, rwLock, monitor, this,
- overlayAdapter));
+ return createTraceManager("Static Mapping Manager",
+ (openMode, monitor) -> new DBTraceStaticMappingManager(dbh, openMode, rwLock, monitor,
+ this, overlayAdapter));
}
@DependentService
@@ -434,9 +435,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
@DependentService
protected DBTraceTimeManager createTimeManager(DBTraceThreadManager threadManager)
throws IOException, CancelledException {
- return createTraceManager("Time Manager",
- (openMode, monitor) -> new DBTraceTimeManager(dbh, openMode, rwLock, monitor, this,
- threadManager));
+ return createTraceManager("Time Manager", (openMode, monitor) -> new DBTraceTimeManager(dbh,
+ openMode, rwLock, monitor, this, threadManager));
}
@Override
@@ -830,13 +830,13 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
allViews(v -> v.updateMemoryChangeRegionBlockFlags(region, lifespan));
}
- public void updateViewsChangeRegionBlockRange(TraceMemoryRegion region,
- AddressRange oldRange, AddressRange newRange) {
+ public void updateViewsChangeRegionBlockRange(TraceMemoryRegion region, AddressRange oldRange,
+ AddressRange newRange) {
allViews(v -> v.updateMemoryChangeRegionBlockRange(region, oldRange, newRange));
}
- public void updateViewsChangeRegionBlockLifespan(TraceMemoryRegion region,
- Lifespan oldLifespan, Lifespan newLifespan) {
+ public void updateViewsChangeRegionBlockLifespan(TraceMemoryRegion region, Lifespan oldLifespan,
+ Lifespan newLifespan) {
allViews(v -> v.updateMemoryChangeRegionBlockLifespan(region, oldLifespan, newLifespan));
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/address/DBTraceOverlaySpaceAdapter.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/address/DBTraceOverlaySpaceAdapter.java
index 0645e04413..9d4bbf4790 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/address/DBTraceOverlaySpaceAdapter.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/address/DBTraceOverlaySpaceAdapter.java
@@ -23,7 +23,10 @@ import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import db.*;
-import ghidra.program.model.address.*;
+import ghidra.program.database.ProgramAddressFactory;
+import ghidra.program.database.ProgramOverlayAddressSpace;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSpace;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceManager;
import ghidra.trace.model.Trace.TraceOverlaySpaceChangeType;
@@ -118,8 +121,6 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
static final String NAME_COLUMN_NAME = "Name";
static final String BASE_COLUMN_NAME = "Base";
- // NOTE: I don't care to record min/max limit
-
@DBAnnotatedColumn(NAME_COLUMN_NAME)
static DBObjectColumn NAME_COLUMN;
@DBAnnotatedColumn(BASE_COLUMN_NAME)
@@ -148,8 +149,6 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
protected final DBCachedObjectStore
Overlay - Each of the above memory block types may optionally be specified as an Overlay at the - time of creation. If this option is selected, the block is created in a new +
Overlay - Each of the above memory block types may + optionally be created as an Overlay block. One or more memory blocks may be defined + within the same overlay address space. + + + An overlay memory block may be created in two ways: + *
Each row displays information about one of the memory blocks. The following summarizes the information about each block.
+ +Many of the Memory Map table fields may be
+ modified to alter the memory block specification (such fields are marked with an '*').
-+Name - Name of the memory block.
+Name * - Name of the memory block.
-Start - The starting address (in hex) of the memory block.
+Start - The starting address (in hex) of the memory block. For overlay blocks + this will reflect an overlay address which includes the name of the overlay address space.
-End - The Ending address (in hex) of the memory block.
+End - The ending address (in hex) of the memory block. For overlay blocks + this will reflect an overlay address which includes the name of the overlay address space.
Length - The length (in hex) of the memory block.
-R - Indicates read permission.
+R * - Indicates read permission.
-W - Indicates write permission.
+W * - Indicates write permission.
-X - Indicates execute permission.
+X * - Indicates execute permission.
-Volatile - Indicates a region of volatile I/O Memory.
+Volatile * - Indicates a region of volatile I/O Memory.
-Overlay - Indicates if block is defined as a memory overlay.
+Overlayed Space - If the block is an overlay block this column indicates the name + of the overlayed physical memory space. This field will be empty for non-overlay blocks.
Type - Indicates whether the block is a Default, Bit Mapped or Byte Mapped type of block.
-Initialized - Indicates whether the block has been initialized with values; +
Initialized * - Indicates whether the block has been initialized with values; this property applies to Default and Overlay blocks.
Byte Source - Provides information about the source of the bytes in this @@ -121,11 +142,26 @@
Source - Description of block origination.
-Comment - User added comment about this memory block.
+Comment * - User added comment about this memory block.
++An overlay address space may be renamed by selecting an overlay block within the table. + Its current overlay address space name should appear as the prefix to its Start address + (e.g., OV1::00001000 where OV1 is the current name of the overlay space containing the + overlay block). The popup menu action Rename Overlay Space may be selected after + a right-click on the selected overlay block row. This will popup a window from which the + overlay space may be renamed.
+ ++
An important consideration when renaming overlay memory spaces is that any Diff operation + between the affected Program and another Program may not behave as expected until after the + Program is closed and re-opened. This is caused by address comparison and ordering which + will continue to use the original overlay space name until the program is re-opened.
@@ -191,10 +227,8 @@
Add
-Select Add to bring up the Add Memory Block dialog. Fill in the requested information and select the OK button.
-@@ -210,7 +244,6 @@
--
Block Name - Enter the name of the new memory block.
@@ -218,8 +251,9 @@Start Addr - Enter the start address of the new memory block. If the program language defines multiple address spaces, the address space must also be specified. The address space selection will not appear if only one is defined. - Overlay spaces are not included in the list of spaces. Within the defaul address - space, a block may not span across the current Image Base Address.
+ If creating an overlay memory block within an existing overlay address space that + space should be selected. A block within the default address space may not span across + the current Image Base Address.
Length - Enter the length of the new memory block.
@@ -234,8 +268,16 @@Volatile - Marks this block as volatile I/O memory.
-Overlay - Creates the block as an overlay within a corresponding overlay address space.
- +Overlay - Creates the block as an overlay block. An overlay memory block may be + created in two ways: +
+
+- Specifying a Start Addr within an existing overlay address space + (this Overlay option is redudant and ignored), or
+- Specifying a Start Addr within a physical memory address space and enabling + this Overlay option. This use case will force the creation of a new unique overlay + address space.
+Block Types - Select the block type from the combo box: Default, Bit Mapped or Byte Mapped.
@@ -290,7 +332,6 @@ to the left value (e.g., 2:4).
-Select Move to bring up the Move Memory Block dialog. The Move action is enabled when exactly one memory block is selected. Enter either a new start or end address to cause the block to be moved.
-
@@ -328,7 +367,6 @@
-
Name - Name of the memory block to be moved (not editable).
@@ -356,13 +394,11 @@The block is an Overlay block. -
-Select Split to bring up the Split Block Dialog. The Split action is enabled when exactly one memory block is selected. Use the Split Block Dialog to split a memory block into two smaller memory blocks. There are four ways to @@ -377,7 +413,6 @@
Enter a length for the second block (new block). -
@@ -395,7 +430,6 @@
-Block to Split
@@ -423,20 +457,17 @@-
Overlay type blocks cannot be split.
Expand Up
-Select Expand Up to bring up the Expand Block Up Dialog. The Expand Up action is enabled when exactly one memory block is selected. Use the Expand Block Up Dialog to cause a memory block to grow by adding additional bytes BEFORE the memory block. The block can be expanded by either entering a new start address or a new length.
-@@ -452,7 +483,6 @@
-New Start Address - A new start address can be entered here. It must be before the current start address.
@@ -463,20 +493,17 @@-
Overlay type blocks cannot be expanded.
Expand Down
-Select Expand Down to bring up the Expand Block Down Dialog. The Expand Down action is enabled when exactly one memory block is selected. Use the Expand Block Down Dialog to cause a memory block to grow by adding additional bytes AFTER the memory block. The block can be expanded by either entering a new end address or a new length.
-@@ -492,7 +519,6 @@
-Start Address - Displays the start address of the block (not editable).
@@ -504,13 +530,11 @@-
Overlay type blocks cannot be expanded.
Merge
-The Merge action is enabled when two or more memory blocks are selected. It attempts to merge all selected blocks into one block. Any "gaps" will be "filled in" with 0s.
@@ -524,13 +548,11 @@
Only adjacent Default blocks of the same initialization state can be merged.
-
Overlay type blocks cannot be merged.
Delete
-+ +The Delete action is enabled when one or more memory blocks are selected. All selected blocks will be deleted. If the blocks contained defined data or instructions, a confirmation dialog is displayed; select "yes" on the dialog to delete the block. A @@ -539,14 +561,16 @@
-
For large blocks that may contain many symbols, references, instructions, etc., the delete operation may take a while to complete. You can cancel the delete operation at any time.
When removing an overlay memory block the + corresponding overlay address space will also be removed if no other overlay blocks + exist within that space.
Set Image Base
-The Set Image Base action allows you to change the base address of a program. This action is useful when working with relocatable code such as DLLs or shared objects. All addresses, code units, references, etc. will immediately be @@ -559,7 +583,6 @@
To change the image base, enter a new image base in the text field and click on the OK button.
-
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png index ce072eaed7..e1614e91a3 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java index 9f69289502..595e9891db 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/assembler/AbstractPatchAction.java @@ -23,6 +23,7 @@ import docking.ActionContext; import docking.ComponentProvider; import docking.action.DockingAction; import docking.widgets.fieldpanel.*; +import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.support.FieldLocation; import ghidra.GhidraOptions; import ghidra.app.context.ListingActionContext; @@ -268,8 +269,9 @@ public abstract class AbstractPatchAction extends DockingAction { BigInteger index = adapter.getAddressIndexMap().getIndex(address); int count = layout.getNumFields(); for (int i = 0; i < count; i++) { - ListingField field = (ListingField) layout.getField(i); - if (field.getFieldFactory().getFieldName().equals(fieldName)) { + Field f = layout.getField(i); + if ((f instanceof ListingField field) && + field.getFieldFactory().getFieldName().equals(fieldName)) { return new FieldLocation(index, i); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java index 64ea6b14b5..38945157ff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/AbstractCodeBrowserPlugin.java @@ -734,8 +734,9 @@ public abstract class AbstractCodeBrowserPluginex int instanceNum = 0; for (int i = 0; i < layout.getNumFields(); i++) { - ListingField bf = (ListingField) layout.getField(i); - if (bf.getFieldFactory().getFieldName().equals(fieldName)) { + Field f = layout.getField(i); + if ((f instanceof ListingField bf) && + bf.getFieldFactory().getFieldName().equals(fieldName)) { if (instanceNum++ == occurrence) { fieldNum = i; break; @@ -774,7 +775,7 @@ public abstract class AbstractCodeBrowserPlugin
ex public boolean goTo(ProgramLocation location, boolean centerOnScreen) { return Swing - .runNow(() -> connectedProvider.getListingPanel().goTo(location, centerOnScreen)); + .runNow(() -> connectedProvider.getListingPanel().goTo(location, centerOnScreen)); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java index 3b4ac5d01c..764466b80b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java @@ -544,7 +544,8 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { addrField = new AddressInput(); addrField.setName("Start Addr"); addrFactory = model.getProgram().getAddressFactory(); - addrField.setAddressFactory(addrFactory, true, true); + addrField.setAddressFactory(addrFactory, + AddressInput.INCLUDE_OTHER_AND_LOADED_MEMORY_SPACES); addrField.addChangeListener(ev -> addrChanged()); return addrField; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/ExpandBlockModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/ExpandBlockModel.java index 6aa522aaa9..685d11a4e8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/ExpandBlockModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/ExpandBlockModel.java @@ -161,11 +161,12 @@ abstract class ExpandBlockModel implements DomainObjectListener { Program prog = (Program) obj; Memory memory = prog.getMemory(); try { + String blockName = expandBlock.getName(); MemoryBlock newBlock = memory.createBlock(expandBlock, expandBlock.getName() + ".exp", startAddr, length); MemoryBlock b = memory.join(expandBlock, newBlock); - if (!b.getName().endsWith(".exp")) { - b.setName(b.getName() + ".exp"); + if (!b.getName().equals(blockName)) { + b.setName(blockName); // preserve block name } return true; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java index 8cfda7f856..48f38bdf99 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java @@ -121,9 +121,10 @@ class MemoryMapManager { // make sure that the block after the first block is the second block Address nextStart = blockA.getEnd(); AddressSpace space = nextStart.getAddressSpace(); - if (space.isOverlaySpace()) { + if (space.isOverlaySpace() && space.isNonLoadedMemorySpace()) { + // impose convention-based restriction Msg.showError(this, plugin.getMemoryMapProvider().getComponent(), - "Merge Blocks Failed", "Can't merge overlay blocks"); + "Merge Blocks Failed", "Cannot merge OTHER overlay blocks"); return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java index 7b0668e17b..4190477859 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java @@ -66,7 +66,7 @@ class MemoryMapModel extends AbstractSortedTableModel
+ *implements Pr final static String WRITE_COL = "W"; final static String EXECUTE_COL = "X"; final static String VOLATILE_COL = "Volatile"; - final static String OVERLAY_COL = "Overlay"; + final static String OVERLAY_COL = "Overlayed Space"; final static String BLOCK_TYPE_COL = "Type"; final static String INIT_COL = "Initialized"; final static String BYTE_SOURCE_COL = "Byte Source"; @@ -124,7 +124,7 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr @Override public boolean isSortable(int columnIndex) { if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE || - columnIndex == VOLATILE || columnIndex == OVERLAY || columnIndex == INIT) { + columnIndex == VOLATILE || columnIndex == INIT) { return false; } return true; @@ -163,7 +163,7 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr @Override public Class> getColumnClass(int columnIndex) { if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE || - columnIndex == VOLATILE || columnIndex == OVERLAY || columnIndex == INIT) { + columnIndex == VOLATILE || columnIndex == INIT) { return Boolean.class; } return String.class; @@ -198,12 +198,6 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr } private String getAddressString(Address address) { - AddressSpace space = address.getAddressSpace(); - if (space.isOverlaySpace()) { - OverlayAddressSpace ovSpace = (OverlayAddressSpace) space; - AddressSpace baseSpace = ovSpace.getOverlayedSpace(); - address = baseSpace.getAddress(address.getOffset()); - } return address.toString(); } @@ -435,7 +429,7 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr case VOLATILE: return block.isVolatile() ? Boolean.TRUE : Boolean.FALSE; case OVERLAY: - return block.isOverlay() ? Boolean.TRUE : Boolean.FALSE; + return getOverlayBaseSpaceName(block); case INIT: MemoryBlockType blockType = block.getType(); if (blockType == MemoryBlockType.BIT_MAPPED) { @@ -532,6 +526,14 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr return program; } + private String getOverlayBaseSpaceName(MemoryBlock block) { + AddressSpace space = block.getStart().getAddressSpace(); + if (space instanceof OverlayAddressSpace ovSpace) { + return ovSpace.getOverlayedSpace().getName(); + } + return ""; + } + private class MemoryMapComparator implements Comparator { private final int sortColumn; @@ -568,9 +570,9 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr int b2v = (b2.isVolatile() ? 1 : -1); return (b1v - b2v); case OVERLAY: - int b1o = (b1.isOverlay() ? 1 : -1); - int b2o = (b2.isOverlay() ? 1 : -1); - return (b1o - b2o); + String ov1 = getOverlayBaseSpaceName(b1); + String ov2 = getOverlayBaseSpaceName(b2); + return ov1.compareTo(ov2); case INIT: int b1init = (b1.isInitialized() ? 1 : -1); int b2init = (b2.isInitialized() ? 1 : -1); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java index e5f3758566..d10b3c4dfd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.memory; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; +import java.util.List; import javax.swing.*; import javax.swing.table.TableColumn; @@ -26,6 +27,8 @@ import javax.swing.table.TableModel; import docking.ActionContext; import docking.action.DockingAction; import docking.action.ToolBarData; +import docking.action.builder.ActionBuilder; +import docking.widgets.OptionDialog; import docking.widgets.table.*; import docking.widgets.textfield.GValidatedTextField.MaxLengthField; import generic.theme.GIcon; @@ -34,11 +37,12 @@ import ghidra.framework.model.DomainFile; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.Address; +import ghidra.program.model.address.OverlayAddressSpace; import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.mem.MemoryBlockType; +import ghidra.program.model.mem.*; import ghidra.util.HelpLocation; import ghidra.util.Msg; +import ghidra.util.exception.UsrException; import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTableFilterPanel; import ghidra.util.table.actions.MakeProgramSelectionAction; @@ -149,8 +153,6 @@ class MemoryMapProvider extends ComponentProviderAdapter { column.setCellRenderer(new GBooleanCellRenderer()); column = table.getColumn(MemoryMapModel.VOLATILE_COL); column.setCellRenderer(new GBooleanCellRenderer()); - column = table.getColumn(MemoryMapModel.OVERLAY_COL); - column.setCellRenderer(new GBooleanCellRenderer()); column = table.getColumn(MemoryMapModel.INIT_COL); column.setCellRenderer(new GBooleanCellRenderer()); @@ -182,10 +184,25 @@ class MemoryMapProvider extends ComponentProviderAdapter { return memPanel; } + private boolean canRenameOverlaySpace(ActionContext context) { + if (context.getContextObject() != getTable()) { + return false; + } + MemoryBlock block = getSelectedBlock(); + return block != null && block.isOverlay(); + } + private void addLocalActions() { - Icon addImage = new GIcon("icon.plugin.memorymap.add"); + // Add popup menu action for renaming overlay space on selected overlay block + new ActionBuilder("Rename Overlay Space", plugin.getName()) + .helpLocation(new HelpLocation("MemoryMapPlugin", "OverlaySpaceRename")) + .popupMenuPath("Rename Overlay Space") + .enabledWhen(c -> canRenameOverlaySpace(c)) + .onAction(c -> renameOverlaySpace(c)) + .buildAndInstallLocal(this); + Icon addImage = new GIcon("icon.plugin.memorymap.add"); addAction = new MemoryMapAction("Add Block", addImage) { @Override public void actionPerformed(ActionContext context) { @@ -300,6 +317,24 @@ class MemoryMapProvider extends ComponentProviderAdapter { tool.addLocalAction(this, action); } + private boolean checkExclusiveAccess() { + if (program.hasExclusiveAccess()) { + return true; + } + String msg = "Close the file and undo your checkout,\n" + + "then do a checkout with the exclusive lock."; + + DomainFile df = program.getDomainFile(); + if (df.modifiedSinceCheckout() || df.isChanged()) { + msg = "Check in this file, then do a checkout with the\n" + "exclusive lock."; + } + + Msg.showInfo(getClass(), MemoryMapProvider.this.getComponent(), + "Exclusive Checkout Required", "An exclusive checkout is required in order to\n" + + "manipulate memory blocks or change the image base.\n" + msg); + return false; + } + private void setBase() { ImageBaseDialog dialog = new ImageBaseDialog(tool, program, program.getImageBase()); tool.showDialog(dialog, this); @@ -445,23 +480,16 @@ class MemoryMapProvider extends ComponentProviderAdapter { column.setResizable(false); } - column = table.getColumn(MemoryMapModel.OVERLAY_COL); - if (column != null) { - column.setMaxWidth(65); - column.setMinWidth(65); - column.setResizable(false); - } - column = table.getColumn(MemoryMapModel.BLOCK_TYPE_COL); if (column != null) { - column.setMinWidth(60); + column.setMinWidth(25); // column.setResizable(true); } column = table.getColumn(MemoryMapModel.INIT_COL); if (column != null) { - column.setMaxWidth(80); - column.setMinWidth(80); + column.setMaxWidth(25); + column.setMinWidth(25); column.setResizable(false); } } @@ -522,6 +550,37 @@ class MemoryMapProvider extends ComponentProviderAdapter { return tableModel.getBlockAt(row); } + private void renameOverlaySpace(ActionContext c) { + if (!checkExclusiveAccess()) { + return; + } + if (!program.canLock()) { + setStatusText("Program is busy, try again later"); + return; + } + MemoryBlock block = getSelectedBlock(); + if (block == null || !block.isOverlay()) { + return; + } + OverlayAddressSpace overlaySpace = (OverlayAddressSpace) block.getStart().getAddressSpace(); + String oldName = overlaySpace.getName(); + + String newName = OptionDialog.showInputSingleLineDialog(getComponent(), + "Rename Overlay Space", "New Name:", oldName); + if (newName == null || oldName.equals(newName)) { + return; + } + + try { + program.withTransaction("Rename Overlay Space: " + oldName, () -> { + program.renameOverlaySpace(oldName, newName); + }); + } + catch (UsrException e) { + Msg.showError(this, getComponent(), "Rename Overlay Error", e.getMessage()); + } + } + /** * Delete the selected blocks. */ @@ -560,13 +619,22 @@ class MemoryMapProvider extends ComponentProviderAdapter { if (block == null) { return; } - if (block.isOverlay()) { - Msg.showInfo(getClass(), getComponent(), "Expand Overlay Block Not Allowed", - "Overlay blocks cannot be expanded."); - } - else { - showExpandBlockDialog(dialogType, block); + + // Check for expansion of FileBytes use + List sourceInfos = block.getSourceInfos(); + int sourceIndex = dialogType == ExpandBlockDialog.EXPAND_UP ? 0 : (sourceInfos.size() - 1); + MemoryBlockSourceInfo sourceInfo = sourceInfos.get(sourceIndex); + if (sourceInfo.getFileBytes().isPresent()) { + int choice = OptionDialog.showOptionDialogWithCancelAsDefaultButton(getComponent(), + "Expanding File Bytes Block", + "Block use of File Bytes will be expanded with a 0-filled region. Continue?", + "Continue..."); + if (choice != OptionDialog.OPTION_ONE) { + return; + } } + + showExpandBlockDialog(dialogType, block); } private void moveBlock() { @@ -579,9 +647,10 @@ class MemoryMapProvider extends ComponentProviderAdapter { return; } - if (block.isOverlay()) { - Msg.showInfo(getClass(), getComponent(), "Move Overlay Block Not Allowed", - "Overlay blocks cannot be moved."); + if (block.isOverlay() && block.getStart().isNonLoadedMemoryAddress()) { + // impose convention-based restriction + Msg.showInfo(getClass(), getComponent(), "Moving OTHER Overlay Block Not Allowed", + "OTHER overlay blocks cannot be moved."); } else { showMoveBlockDialog(block); @@ -596,9 +665,10 @@ class MemoryMapProvider extends ComponentProviderAdapter { if (block == null) { return; } - if (block.isOverlay()) { - Msg.showInfo(getClass(), getComponent(), "Split Overlay Block Not Allowed", - "Overlay blocks cannot be split."); + if (block.isOverlay() && block.getStart().isNonLoadedMemoryAddress()) { + // impose convention-based restriction + Msg.showInfo(getClass(), getComponent(), "Split OTHER Overlay Block Not Allowed", + "OTHER overlay blocks can not be split."); } else { SplitBlockDialog d = new SplitBlockDialog(plugin, block, program.getAddressFactory()); @@ -690,23 +760,5 @@ class MemoryMapProvider extends ComponentProviderAdapter { super(name, plugin.getName()); this.setToolBarData(new ToolBarData(icon, "A")); } - - public boolean checkExclusiveAccess() { - if (program.hasExclusiveAccess()) { - return true; - } - String msg = "Close the file and undo your checkout,\n" + - "then do a checkout with the exclusive lock."; - - DomainFile df = program.getDomainFile(); - if (df.modifiedSinceCheckout() || df.isChanged()) { - msg = "Check in this file, then do a checkout with the\n" + "exclusive lock."; - } - - Msg.showInfo(getClass(), MemoryMapProvider.this.getComponent(), - "Exclusive Checkout Required", "An exclusive checkout is required in order to\n" + - "manipulate memory blocks or change the image base.\n" + msg); - return false; - } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java index 6731b7fd78..d258e42662 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MoveBlockDialog.java @@ -83,8 +83,8 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc changing = true; if (!isVisible()) { AddressFactory factory = model.getAddressFactory(); - newStartField.setAddressFactory(factory, true, false); - newEndField.setAddressFactory(factory, true, false); + newStartField.setAddressFactory(factory); + newEndField.setAddressFactory(factory); } Address newStart = model.getNewStartAddress(); if (newStart != null) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/EditMemoryReferencePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/EditMemoryReferencePanel.java index 9eb5dd2f9b..47d0f284e3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/EditMemoryReferencePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/references/EditMemoryReferencePanel.java @@ -19,8 +19,9 @@ import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.*; +import java.util.ArrayList; import java.util.List; +import java.util.WeakHashMap; import javax.swing.*; import javax.swing.border.LineBorder; @@ -198,7 +199,7 @@ class EditMemoryReferencePanel extends EditReferencePanel { toAddr = toAddr.subtractWrap(defaultOffset); } - toAddressField.setAddressFactory(fromCu.getProgram().getAddressFactory(), false, false); + toAddressField.setAddressFactory(fromCu.getProgram().getAddressFactory()); toAddressField.setAddress(toAddr); enableOffsetField(editReference.isOffsetReference()); @@ -223,7 +224,7 @@ class EditMemoryReferencePanel extends EditReferencePanel { addrHistoryButton.setEnabled(getAddressHistorySize(p) != 0); - toAddressField.setAddressFactory(p.getAddressFactory(), false, false); + toAddressField.setAddressFactory(p.getAddressFactory()); Address cuAddr = fromCu.getMinAddress(); @@ -297,8 +298,7 @@ class EditMemoryReferencePanel extends EditReferencePanel { } if (toAddr != null) { Reference r = p.getReferenceManager() - .getReference(fromCu.getMinAddress(), toAddr, - fromOpIndex); + .getReference(fromCu.getMinAddress(), toAddr, fromOpIndex); if (r != null) { toAddr = null; if (r.isOffsetReference()) { @@ -582,25 +582,23 @@ class EditMemoryReferencePanel extends EditReferencePanel { historyWin.setLocation(p); KeyboardFocusManager.getCurrentKeyboardFocusManager() - .addPropertyChangeListener( - "focusOwner", new PropertyChangeListener() { - boolean hasFocus = false; + .addPropertyChangeListener("focusOwner", new PropertyChangeListener() { + boolean hasFocus = false; - @Override - public void propertyChange(PropertyChangeEvent evt) { - Object focusOwner = evt.getNewValue(); - if (focusOwner == displayTable || focusOwner == historyWin) { - hasFocus = true; - } - else if (hasFocus) { - hasFocus = false; - KeyboardFocusManager.getCurrentKeyboardFocusManager() - .removePropertyChangeListener( - "focusOwner", this); - hideAddressHistoryPopup(); - } + @Override + public void propertyChange(PropertyChangeEvent evt) { + Object focusOwner = evt.getNewValue(); + if (focusOwner == displayTable || focusOwner == historyWin) { + hasFocus = true; } - }); + else if (hasFocus) { + hasFocus = false; + KeyboardFocusManager.getCurrentKeyboardFocusManager() + .removePropertyChangeListener("focusOwner", this); + hideAddressHistoryPopup(); + } + } + }); historyWin.setVisible(true); @@ -669,17 +667,13 @@ class EditMemoryReferencePanel extends EditReferencePanel { @SuppressWarnings("unchecked") void readXmlDataState(Element element) { List programElements = element.getChildren("ADDR_HISTORY"); - Iterator iter = programElements.iterator(); - while (iter.hasNext()) { - Element programElement = iter.next(); + for (Element programElement : programElements) { String programName = programElement.getAttributeValue("PROGRAM"); Program program = getOpenProgram(programName); if (program != null) { AddressFactory addrFactory = program.getAddressFactory(); List addrElements = programElement.getChildren("ADDRESS"); - Iterator addrIter = addrElements.iterator(); - while (addrIter.hasNext()) { - Element addrElement = addrIter.next(); + for (Element addrElement : addrElements) { String addrStr = addrElement.getAttributeValue("VALUE"); if (addrStr != null) { Address addr = addrFactory.getAddress(addrStr); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/ListingDisplaySearcher.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/ListingDisplaySearcher.java index d452c2bbcc..e29cbf89e2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/ListingDisplaySearcher.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchtext/ListingDisplaySearcher.java @@ -21,6 +21,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import docking.widgets.fieldpanel.Layout; +import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.RowColLocation; import ghidra.app.plugin.core.searchtext.iterators.*; @@ -287,15 +288,15 @@ class ListingDisplaySearcher implements Searcher { } if (options.isForward()) { - while (!monitor.isCancelled() && results.size() == 0 && - currentLayout != null && currentFieldIndex < currentLayout.getNumFields()) { + while (!monitor.isCancelled() && results.size() == 0 && currentLayout != null && + currentFieldIndex < currentLayout.getNumFields()) { findNextMatch(); } } else { currentFieldIndex = currentLayout.getNumFields() - 1; - while (!monitor.isCancelled() && results.size() == 0 && - currentLayout != null && currentFieldIndex >= 0) { + while (!monitor.isCancelled() && results.size() == 0 && currentLayout != null && + currentFieldIndex >= 0) { findNextMatch(); } } @@ -343,7 +344,10 @@ class ListingDisplaySearcher implements Searcher { * Returns the number of fields used in the match. */ private int findLocations(int fieldIndex) { - ListingField field = (ListingField) currentLayout.getField(fieldIndex); + Field f = currentLayout.getField(fieldIndex); + if (!(f instanceof ListingField field)) { + return 0; + } FieldFactory ff = field.getFieldFactory(); String fieldName = ff.getFieldName(); if (!doSearchField(fieldName)) { @@ -360,9 +364,13 @@ class ListingDisplaySearcher implements Searcher { options.searchBothInstructionMnemonicAndOperands(); if (isMnemonic && isInstructionsOrData) { if (currentFieldIndex <= currentLayout.getNumFields() - 2) { - ListingField opField = (ListingField) currentLayout.getField(fieldIndex + 1); - findMnemonicOperandLocations(field, opField); - fieldCount = 2; // if we match here, then signal that we matched across two fields + Field f2 = currentLayout.getField(fieldIndex + 1); + if ((f2 instanceof ListingField opField) && opField.getFieldFactory() + .getFieldName() + .equals(OperandFieldFactory.FIELD_NAME)) { + findMnemonicOperandLocations(field, opField); + fieldCount = 2; // if we match here, then signal that we matched across two fields + } } else { findLocations(field); @@ -389,8 +397,8 @@ class ListingDisplaySearcher implements Searcher { if (startLocation.getAddress().equals(currentAddress)) { // set the current Field index to correspond to the program location for (int i = 0; i < currentLayout.getNumFields(); i++) { - ListingField field = (ListingField) currentLayout.getField(i); - if (getFieldForLocation(field, i)) { + Field f = currentLayout.getField(i); + if ((f instanceof ListingField field) && getFieldForLocation(field, i)) { break; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java index 76c097d1b2..87862d1f05 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java @@ -248,8 +248,6 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { clearStatusText(); - // the number value is a byte size, which means we need to adjust that value by - // the addressable unit size of the processor Address currentAddress = navigatable.getLocation().getAddress(); AddressSet addressSet = new AddressSet(navigatable.getSelection()); @@ -257,14 +255,15 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { if (addressSet.isEmpty()) { addressSet.addRange(currentAddress, currentAddress); } - length *= currentAddress.getAddressSpace().getAddressableUnitSize(); AddressRangeIterator aiter = addressSet.getAddressRanges(); AddressSet newSet = new AddressSet(); while (aiter.hasNext()) { AddressRange range = aiter.next(); Address toAddress = createForwardToAddress(range.getMinAddress(), length - 1); - newSet.addRange(range.getMinAddress(), toAddress); + if (toAddress != null) { + newSet.addRange(range.getMinAddress(), toAddress); + } } ProgramSelection selection = new ProgramSelection(newSet); NavigationUtils.setSelection(tool, navigatable, selection); @@ -279,11 +278,7 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { } clearStatusText(); - // the number value is a byte size, which means we need to adjust that value by - // the addressable unit size of the processor Address currentAddress = navigatable.getLocation().getAddress(); - length *= currentAddress.getAddressSpace().getAddressableUnitSize(); - AddressSet addressSet = new AddressSet(navigatable.getSelection()); if (addressSet.isEmpty()) { addressSet.addRange(currentAddress, currentAddress); @@ -295,54 +290,69 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { AddressRange range = aiter.next(); Address fromAddress = createBackwardToAddress(range.getMaxAddress(), length - 1); - newSet.addRange(fromAddress, range.getMaxAddress()); + if (fromAddress != null) { + newSet.addRange(fromAddress, range.getMaxAddress()); + } } ProgramSelection selection = new ProgramSelection(newSet); NavigationUtils.setSelection(tool, navigatable, selection); } - private Address createBackwardToAddress(Address startAddress, long length) { - AddressSpace addressSpace = startAddress.getAddressSpace(); + private Address createBackwardToAddress(Address toAddress, long length) { + AddressSpace addressSpace = toAddress.getAddressSpace(); if (addressSpace.isOverlaySpace()) { OverlayAddressSpace oas = (OverlayAddressSpace) addressSpace; - if (startAddress.getOffset() - length < oas.getMinOffset()) { + AddressRange range = oas.getOverlayAddressSet().getRangeContaining(toAddress); + if (range == null) { showWarningDialog(OVERFLOW_SELECTION_WARNING); - return addressSpace.getAddress(oas.getMinOffset()); + return toAddress; + } + long avail = toAddress.subtract(range.getMinAddress()); + if (avail < (length - 1)) { + showWarningDialog(OVERFLOW_SELECTION_WARNING); + return range.getMinAddress(); } } - Address toAddress = null; + Address addr = null; try { - toAddress = startAddress.subtract(length); + addr = toAddress.subtractNoWrap(length); } - catch (AddressOutOfBoundsException aoobe) { + catch (AddressOverflowException aoobe) { showWarningDialog(OVERFLOW_SELECTION_WARNING); - toAddress = addressSpace.getMinAddress(); + addr = addressSpace.getMinAddress(); } - return toAddress; + return addr; } - private Address createForwardToAddress(Address startAddress, long length) { - AddressSpace addressSpace = startAddress.getAddressSpace(); + private Address createForwardToAddress(Address fromAddress, long length) { + + AddressSpace addressSpace = fromAddress.getAddressSpace(); if (addressSpace.isOverlaySpace()) { OverlayAddressSpace oas = (OverlayAddressSpace) addressSpace; - if (startAddress.getOffset() + length > oas.getMaxOffset()) { + AddressRange range = oas.getOverlayAddressSet().getRangeContaining(fromAddress); + if (range == null) { showWarningDialog(OVERFLOW_SELECTION_WARNING); - return addressSpace.getAddress(oas.getMaxOffset()); + return fromAddress; + } + long avail = range.getMaxAddress().subtract(fromAddress); + if (avail < (length - 1)) { + showWarningDialog(OVERFLOW_SELECTION_WARNING); + return range.getMaxAddress(); } } - Address toAddress = null; + Address addr = null; try { - toAddress = startAddress.add(length); + addr = fromAddress.addNoWrap(length); } - catch (AddressOutOfBoundsException aoobe) { + catch (AddressOverflowException aoobe) { showWarningDialog(OVERFLOW_SELECTION_WARNING); - toAddress = addressSpace.getMaxAddress(); + addr = addressSpace.getMaxAddress(); } - return toAddress; + return addr; } private void showWarningDialog(final String text) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddressInput.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddressInput.java index 4e6d8febf7..5b0c22d436 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddressInput.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/AddressInput.java @@ -20,6 +20,7 @@ import java.awt.FontMetrics; import java.awt.event.ActionListener; import java.util.Arrays; import java.util.Comparator; +import java.util.function.Predicate; import javax.swing.*; import javax.swing.border.Border; @@ -44,18 +45,17 @@ public class AddressInput extends JPanel implements FocusableEditor { private boolean stateChanging; private JTextField spaceField; - private static final Comparator ADDRESS_SPACE_SORT_COMPARATOR = - (s1, s2) -> { - if (s1.isOverlaySpace()) { - if (!s2.isOverlaySpace()) { - return 1; - } + private static final Comparator ADDRESS_SPACE_SORT_COMPARATOR = (s1, s2) -> { + if (s1.isOverlaySpace()) { + if (!s2.isOverlaySpace()) { + return 1; } - else if (s2.isOverlaySpace()) { - return -1; - } - return s1.getName().compareTo(s2.getName()); - }; + } + else if (s2.isOverlaySpace()) { + return -1; + } + return s1.getName().compareTo(s2.getName()); + }; /** * Constructor for AddressInput. @@ -160,30 +160,47 @@ public class AddressInput extends JPanel implements FocusableEditor { return textField.getText().length() != 0; } - /** - * Set the address factory to be used to parse addresses. Also - * used to set the combo box with the list of valid address spaces - * if there is more than one space. - */ - public void setAddressFactory(AddressFactory factory) { - setAddressFactory(factory, false, false); - } - public AddressFactory getAddressFactory() { return addrFactory; } /** - * Set the address factory to be used to parse addresses. Also used to set the combo box - * with the list of valid address spaces if there is more than one space. - * @param factory address factory to use - * @param filterOverlaySpaces true if overlay spaces should not appear in the combo box - * for the address spaces. - * @param allowOtherSpace true if the OTHER space should appear in the combo box for - * the address spaces + * Address Space predicate which includes all loaded memory spaces. + * See {@link AddressSpace#isLoadedMemorySpace()}. + * Intended for use with {@link #setAddressFactory(AddressFactory, Predicate)}. */ - public void setAddressFactory(AddressFactory factory, boolean filterOverlaySpaces, - boolean allowOtherSpace) { + public final static Predicate INCLUDE_LOADED_MEMORY_SPACES = (s) -> { + return s.isLoadedMemorySpace(); + }; + + /** + * Address Space predicate which include all loaded memory spaces plus the + * {@link AddressSpace#OTHER_SPACE}. See {@link AddressSpace#isLoadedMemorySpace()}. + * Intended for use with {@link #setAddressFactory(AddressFactory, Predicate)}. + */ + public final static Predicate INCLUDE_OTHER_AND_LOADED_MEMORY_SPACES = (s) -> { + return s.isLoadedMemorySpace() || s.equals(AddressSpace.OTHER_SPACE); + }; + + /** + * Set the address factory to be used to parse addresses. Also + * used to set the combo box with the list of valid address spaces + * if there is more than one space. Only loaded memory spaces + * will be allowed (see {@link AddressSpace#isLoadedMemorySpace()}). + * @param factory address factory to use + */ + public void setAddressFactory(AddressFactory factory) { + setAddressFactory(factory, INCLUDE_LOADED_MEMORY_SPACES); + } + + /** + * Set the address factory to be used to parse addresses. Also used to set the combo box + * with the list of valid address spaces if there is more than one space. The specified + * predicate will be used to determine if an address space should be included. + * @param factory address factory to use + * @param predicate callback used to determine if an address space should be included for selection + */ + public void setAddressFactory(AddressFactory factory, Predicate predicate) { this.addrFactory = factory; AddressSpace[] spaces = factory.getAddressSpaces(); @@ -194,14 +211,9 @@ public class AddressInput extends JPanel implements FocusableEditor { FontMetrics fm = combo.getFontMetrics(combo.getFont()); int width = 0; for (AddressSpace space : spaces) { - if (filterOverlaySpaces && space.isOverlaySpace()) { + if (!predicate.test(space)) { continue; } - - if (!allowOtherSpace && space.equals(AddressSpace.OTHER_SPACE)) { - continue; - } - String s = space.toString(); width = Math.max(width, fm.stringWidth(s)); @@ -260,13 +272,25 @@ public class AddressInput extends JPanel implements FocusableEditor { } /** - * Set the offset part of the address field. + * Set the offset part of the address offset field without changing address space. + * NOTE: This method is intended for test use only and mimicks user input. * @param value the offset value string */ public void setValue(String value) { textField.setText(value); } + /** + * Set the address space and offset. + * NOTE: Unlike {@link #setAddress(Address)} this method is intended for test use only + * and mimicks user input with {@link #stateChanged()} notification. + * @param addr the address value + */ + public void setValue(Address addr) { + setAddress(addr); + stateChanged(); + } + @Override public boolean isEnabled() { return textField.isEnabled(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java index 09122d9618..87bc1e0cc9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingHoverProvider.java @@ -49,11 +49,10 @@ public class ListingHoverProvider extends AbstractHoverProvider { Rectangle fieldBounds, MouseEvent event) { ProgramLocation loc = null; - if (field instanceof ListingField) { - ListingField listingField = (ListingField) field; + if (field instanceof ListingField listingField) { loc = listingField.getFieldFactory() - .getProgramLocation(fieldLocation.getRow(), - fieldLocation.getCol(), listingField); + .getProgramLocation(fieldLocation.getRow(), fieldLocation.getCol(), + listingField); } return loc; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java index f26a3bb432..f33399bb02 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingModelAdapter.java @@ -247,14 +247,17 @@ public class ListingModelAdapter implements LayoutModel, ListingModelListener { int defaultFieldIndex = 0; for (int i = 0; i < layout.getNumFields(); i++) { - ListingField f = (ListingField) layout.getField(i); - FieldFactory factory = f.getFieldFactory(); + Field f = layout.getField(i); + if (!(f instanceof ListingField field)) { + continue; + } + FieldFactory factory = field.getFieldFactory(); if (factory.getClass() == defaultFieldFactoryClass) { defaultFieldIndex = i; } // this method only returns a location if all information matches. - FieldLocation floc = factory.getFieldLocation(f, index, i, location); + FieldLocation floc = factory.getFieldLocation(field, index, i, location); if (floc != null) { return floc; } @@ -297,21 +300,18 @@ public class ListingModelAdapter implements LayoutModel, ListingModelListener { return addr != null ? new ProgramLocation(model.getProgram(), addr) : null; } - ListingField bf = null; - + Field f; if (floc.getFieldNum() >= layout.getNumFields()) { - bf = (ListingField) layout.getField(0); + f = layout.getField(0); } else { - bf = (ListingField) layout.getField(floc.getFieldNum()); + f = layout.getField(floc.getFieldNum()); } - - return getProgramLocation(floc, bf); + return (f instanceof ListingField bf) ? getProgramLocation(floc, bf) : null; } public ProgramLocation getProgramLocation(FieldLocation location, Field field) { - ListingField lf = (ListingField) field; - if (lf != null) { + if (field instanceof ListingField lf) { FieldFactory factory = lf.getFieldFactory(); ProgramLocation pLoc = @@ -502,7 +502,6 @@ public class ListingModelAdapter implements LayoutModel, ListingModelListener { } } - public Layout getLayout(Address addr) { BigInteger index = addressToIndexMap.getIndex(addr); return getLayout(index); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java index 9bb31eb45f..d93690e3ba 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java @@ -238,8 +238,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc if (show) { headerPanel = new FieldHeader(formatManager, scroller, fieldPanel); // set the model to that of the field at the cursor location - ListingField currentField = (ListingField) fieldPanel.getCurrentField(); - if (currentField != null) { + Field f = fieldPanel.getCurrentField(); + if (f instanceof ListingField currentField) { headerPanel.setSelectedFieldFactory(currentField.getFieldFactory()); } } @@ -771,12 +771,12 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc @Override public void buttonPressed(FieldLocation fieldLocation, Field field, MouseEvent mouseEvent) { - if (fieldLocation == null || field == null) { + if (fieldLocation == null || !(field instanceof ListingField listingField)) { return; } - ListingField listingField = (ListingField) field; - ProgramLocation programLocation = layoutModel.getProgramLocation(fieldLocation, field); + ProgramLocation programLocation = + layoutModel.getProgramLocation(fieldLocation, listingField); if (programLocation == null) { return; } @@ -798,8 +798,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc @Override public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) { - ListingField lf = (ListingField) field; - if (lf == null) { + if (!(field instanceof ListingField lf)) { return; } @@ -905,10 +904,9 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc */ public ProgramLocation getProgramLocation(Point point) { FieldLocation dropLoc = new FieldLocation(); - ListingField field = (ListingField) fieldPanel.getFieldAt(point.x, point.y, dropLoc); - if (field != null) { - return field.getFieldFactory() - .getProgramLocation(dropLoc.getRow(), dropLoc.getCol(), field); + Field field = fieldPanel.getFieldAt(point.x, point.y, dropLoc); + if (field instanceof ListingField lf) { + return lf.getFieldFactory().getProgramLocation(dropLoc.getRow(), dropLoc.getCol(), lf); } return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiLayout.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiLayout.java index 3ce4805ac3..1a64541487 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiLayout.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/multilisting/MultiLayout.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,13 +15,13 @@ */ package ghidra.app.util.viewer.multilisting; -import ghidra.app.util.viewer.field.DummyFieldFactory; -import ghidra.app.util.viewer.format.FormatManager; -import ghidra.app.util.viewer.proxy.EmptyProxy; import docking.widgets.fieldpanel.Layout; import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.support.MultiRowLayout; import docking.widgets.fieldpanel.support.RowLayout; +import ghidra.app.util.viewer.field.DummyFieldFactory; +import ghidra.app.util.viewer.format.FormatManager; +import ghidra.app.util.viewer.proxy.EmptyProxy; class MultiLayout { private Layout[] layouts; @@ -30,10 +29,6 @@ class MultiLayout { public MultiLayout() { } - /** - * @param index2 - * @param layouts - */ public MultiLayout(Layout[] layouts, FormatManager formatMgr, DummyFieldFactory factory) { this.layouts = layouts; int[] rowHeights = new int[formatMgr.getMaxNumRows()]; @@ -41,17 +36,15 @@ class MultiLayout { for (int i = 0; i < layouts.length; i++) { MultiRowLayout layout = (MultiRowLayout) layouts[i]; if (layout == null) { - layout = - new MultiRowLayout(new RowLayout(new Field[] { factory.getField( - EmptyProxy.EMPTY_PROXY, 0) }, id), 1); + layout = new MultiRowLayout( + new RowLayout(new Field[] { factory.getField(EmptyProxy.EMPTY_PROXY, 0) }, id), + 1); layouts[i] = layout; } layout.fillHeights(rowHeights); - } - for (int i = 0; i < layouts.length; i++) { - MultiRowLayout layout = (MultiRowLayout) layouts[i]; - layout.align(rowHeights); + for (Layout layout : layouts) { + ((MultiRowLayout) layout).align(rowHeights); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/AddressRangeIteratorConverter.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/AddressRangeIteratorConverter.java index f8467621de..b4c9d99e5c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/AddressRangeIteratorConverter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/AddressRangeIteratorConverter.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. @@ -18,29 +17,31 @@ package ghidra.program.util; import java.util.Iterator; -import ghidra.program.model.address.AddressRange; -import ghidra.program.model.address.AddressRangeIterator; +import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; public class AddressRangeIteratorConverter implements AddressRangeIterator { - + private AddressRangeIterator iterator; private Program program; AddressRange nextRange; - + public AddressRangeIteratorConverter(AddressRangeIterator iterator, Program program) { this.iterator = iterator; this.program = program; } + @Override public Iterator iterator() { return this; } - + + @Override public void remove() { throw new UnsupportedOperationException(); } - + + @Override public boolean hasNext() { if (nextRange != null) { return true; @@ -48,15 +49,17 @@ public class AddressRangeIteratorConverter implements AddressRangeIterator { while (iterator.hasNext()) { AddressRange range = iterator.next(); // TODO Future change: May want to get as much of the range as you can if you can't get it all. - AddressRange convertedRange = DiffUtility.getCompatibleAddressRange(range, program); - if (convertedRange != null) { - nextRange = convertedRange; + AddressSet convertedRangeSet = + DiffUtility.getCompatibleAddressSet(range, program, true); + if (convertedRangeSet != null && !convertedRangeSet.isEmpty()) { + nextRange = convertedRangeSet.getFirstRange(); return true; } } return false; } + @Override public AddressRange next() { if (nextRange != null) { AddressRange convertedRange = nextRange; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/DefaultAddressTranslator.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/DefaultAddressTranslator.java index 5e748ff9d3..39d3421a0b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/DefaultAddressTranslator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/DefaultAddressTranslator.java @@ -28,23 +28,28 @@ public class DefaultAddressTranslator implements AddressTranslator { this.sourceProgram = sourceProgram; } + @Override public Program getDestinationProgram() { return destinationProgram; } + @Override public Program getSourceProgram() { return sourceProgram; } + @Override public Address getAddress(Address sourceAddress) { return SimpleDiffUtility.getCompatibleAddress(sourceProgram, sourceAddress, destinationProgram); } + @Override public boolean isOneForOneTranslator() { return true; } + @Override public AddressSet getAddressSet(AddressSetView sourceAddressSet) { if (sourceAddressSet == null) { return null; // FIXME @@ -55,7 +60,12 @@ public class DefaultAddressTranslator implements AddressTranslator { @Override public AddressRange getAddressRange(AddressRange sourceAddressRange) throws AddressTranslationException { - return DiffUtility.getCompatibleAddressRange(sourceAddressRange, destinationProgram); + AddressSet destinationRange = + DiffUtility.getCompatibleAddressSet(sourceAddressRange, destinationProgram, true); + if (destinationRange != null && !destinationRange.isEmpty()) { + return destinationRange.getFirstRange(); + } + return null; } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/DiffUtility.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/DiffUtility.java index 4ec68b1072..a70ae5ff68 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/DiffUtility.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/DiffUtility.java @@ -15,6 +15,10 @@ */ package ghidra.program.util; +import java.util.Comparator; + +import org.apache.commons.lang3.Range; + import ghidra.program.model.address.*; import ghidra.program.model.lang.Register; import ghidra.program.model.listing.*; @@ -55,80 +59,55 @@ public class DiffUtility extends SimpleDiffUtility { AddressRangeIterator rangeIter = set.getAddressRanges(); while (rangeIter.hasNext()) { AddressRange range = rangeIter.next(); - AddressRange compatibleAddressRange = getCompatibleAddressRange(range, otherProgram); - if (compatibleAddressRange != null) { - otherSet.add(compatibleAddressRange); + AddressSet compatibleSet = getCompatibleAddressSet(range, otherProgram, false); + if (compatibleSet != null) { + otherSet.add(compatibleSet); } } return otherSet; } - /** - * Reduce an address-set from one program to the set of addresses that are incompatible with - * the specified otherProgram. - * @param set address-set corresponding to one program - * @param otherProgram the addresses are incompatible with this other program. - * @return incompatible address-set - */ - public static AddressSet getNonCompatibleAddressSet(AddressSetView set, Program otherProgram) { - AddressSet nonCompatibleSet = new AddressSet(); - AddressRangeIterator rangeIter = set.getAddressRanges(); - while (rangeIter.hasNext()) { - AddressRange range = rangeIter.next(); - AddressRange compatibleAddressRange = getCompatibleAddressRange(range, otherProgram); - if (compatibleAddressRange == null) { - nonCompatibleSet.add(range); - } + private static final Comparator unsignedLongComparator = new Comparator<>() { + + @Override + public int compare(Long o1, Long o2) { + return Long.compareUnsigned(o1, o2); } - return nonCompatibleSet; - } + }; /** - * Convert an address range from one program to a compatible address range in the + * Convert an address range from one program to a compatible address set in the * specified otherProgram. Only memory addresses will be considered. - * If the entire range cannot be converted then null is returned. + * If none of the range can be converted then null is returned. * @param range address range to convert * @param otherProgram target program which corresponds to the returned address range. - * @return translated address range or null if a compatible range could not be - * determined in the other program. + * @param exactMatchOnly if true and a one-to-one address mapping cannot be identified null + * will be returned, otherwise a partial set may be returned or null if no valid translation + * was found. + * @return compatible address set or null */ - public static AddressRange getCompatibleAddressRange(AddressRange range, Program otherProgram) { - Address nextMin = range.getMinAddress(); - Address nextMax = range.getMaxAddress(); - Address newMinAddress = translateMemoryAddress(nextMin, otherProgram, false); - Address newMaxAddress = translateMemoryAddress(nextMax, otherProgram, true); - try { -// while (newMinAddress == null) { -// nextMin = nextMin.add(1L); -// int compareMinToMax = nextMin.compareTo(nextMax); -// if (compareMinToMax > 0) { -// break; -// } -// newMinAddress = translateMemoryAddress(nextMin, otherProgram, true); -// if (compareMinToMax == 0) { -// break; -// } -// } - while (newMaxAddress == null && nextMin != null) { - nextMax = nextMax.subtract(1L); - int compareMaxToMin = nextMax.compareTo(nextMin); - if (compareMaxToMin < 0) { - break; - } - newMaxAddress = translateMemoryAddress(nextMax, otherProgram, true); - if (compareMaxToMin == 0) { - break; - } - } - } - catch (AddressOutOfBoundsException e) { - // Won't be able to add 1 at end of block or subtract 1 at start of block. - } - if (newMinAddress == null || newMaxAddress == null || - newMinAddress.getOffset() > newMaxAddress.getOffset()) { + public static AddressSet getCompatibleAddressSet(AddressRange range, Program otherProgram, + boolean exactMatchOnly) { + AddressSpace addrSpace = range.getMinAddress().getAddressSpace(); + AddressSpace otherSpace = getCompatibleAddressSpace(addrSpace, otherProgram); + if (otherSpace == null) { return null; } - return new AddressRangeImpl(newMinAddress, newMaxAddress); + Range r = Range.between(range.getMinAddress().getOffset(), + range.getMaxAddress().getOffset(), unsignedLongComparator); + Range otherSpaceRange = Range.between(otherSpace.getMinAddress().getOffset(), + otherSpace.getMaxAddress().getOffset(), unsignedLongComparator); + if (!r.isOverlappedBy(otherSpaceRange)) { + return null; + } + r = r.intersectionWith(otherSpaceRange); + Address min = otherSpace.getAddressInThisSpaceOnly(r.getMinimum()); + Address max = otherSpace.getAddressInThisSpaceOnly(r.getMaximum()); + AddressSet set = new AddressSet(min, max); + if (exactMatchOnly && set.getNumAddresses() != range.getLength()) { + return null; + } + return set; } /** @@ -200,64 +179,6 @@ public class DiffUtility extends SimpleDiffUtility { .createNameSpace(otherParentNamespace, namespace.getName(), source); } -// /** -// * Given a symbol for a specified program, get the corresponding symbol from the -// * specified otherProgram. -// * @param program program which contains the specified symbol instance -// * @param p2Symbol symbol to look for -// * @param otherProgram other program -// * @return corresponding symbol for otherProgram or null if no such symbol exists. -// */ -// public static Symbol getSymbol(AddressTranslator p2ToP1Translator, Symbol p2Symbol) { -// -// if (p2Symbol == null) { -// return null; -// } -// Program otherProgram = p2ToP1Translator.getDestinationProgram(); -// SymbolType st = p2Symbol.getSymbolType(); -// if (st == SymbolType.GLOBAL) { -// return otherProgram.getGlobalNamespace().getSymbol(); -// } -// if (st == SymbolType.FUNCTION) { -// Function func = (Function) p2Symbol.getObject(); -// Address p1Entry = p2ToP1Translator.getAddress(func.getEntryPoint()); -// if (p1Entry == null) { -// return null; -// } -// func = otherProgram.getFunctionManager().getFunctionAt(p1Entry); -// return func != null ? func.getSymbol() : null; -// } -// -// SymbolTable otherSymTable = otherProgram.getSymbolTable(); -// Address addr2 = p2Symbol.getAddress(); -// if (addr2.isVariableAddress()) { -// Variable var2 = (Variable) p2Symbol.getObject(); -// Symbol otherFuncSym = getSymbol(p2ToP1Translator, p2Symbol.getParentSymbol()); -// Address storeAddr = p2ToP1Translator.getAddress(var2.getStorageAddress()); -// return getVariableSymbol(otherSymTable, var2, otherFuncSym, storeAddr); -// } -// -// Symbol parent1 = getSymbol(p2ToP1Translator, p2Symbol.getParentSymbol()); -// if (parent1 == null) { -// return null; -// } -// Namespace namespace = (Namespace)parent1.getObject(); -// String name2 = p2Symbol.getName(); -// -// AddressSpace addrSpace = addr2.getAddressSpace(); -// if (addrSpace.getType() == AddressSpace.TYPE_NONE || addr2.isExternalAddress()) { -// Symbol s = otherSymTable.getSymbol(name2, namespace); -// return (s != null && st == s.getSymbolType()) ? s : null; -// } -// -// if (addr2.isMemoryAddress()) { -// Symbol s = otherSymTable.getSymbol(name2, addr2, namespace); -// return (s != null && st == s.getSymbolType()) ? s : null; -// } -// -// return null; -// } - /** * Determine if the specified variables have overlapping storage. * Variable storage check includes dynamically mapped storage for parameters. This method @@ -341,10 +262,10 @@ public class DiffUtility extends SimpleDiffUtility { } /** - * - * @param p2ToP1Translator - * @param p2Ref - * @return + * Translate reference from program p2 to target program p1 + * @param p2ToP1Translator program address translater + * @param p2Ref original reference to be copied + * @return translated reference or null */ public static Reference getReference(AddressTranslator p2ToP1Translator, Reference p2Ref) { Program program = p2ToP1Translator.getDestinationProgram(); @@ -374,7 +295,10 @@ public class DiffUtility extends SimpleDiffUtility { * @param extLoc existing external location to be copied * @param otherProgram target program * @return new external location - * @throws InvalidInputException + * @throws InvalidInputException if {@code libraryName} is invalid or null, or an invalid + * {@code extlabel} is specified. Names with spaces or the empty string are not permitted. + * Neither {@code extLabel} nor {@code extAddr} was specified properly. + * @throws DuplicateNameException if another non-Library namespace has the same name */ public static ExternalLocation createExtLocation(Program program, ExternalLocation extLoc, Program otherProgram) throws InvalidInputException, DuplicateNameException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiff.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiff.java index 2885f22926..90bae950e1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiff.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramDiff.java @@ -644,8 +644,7 @@ public class ProgramDiff { monitorMsg = "Checking Repeatable Comment Differences"; monitor.setMessage(monitorMsg); as = getCommentDiffs(CodeUnit.REPEATABLE_COMMENT, addrs, - new CommentTypeComparator(CodeUnit.REPEATABLE_COMMENT), - monitor); + new CommentTypeComparator(CodeUnit.REPEATABLE_COMMENT), monitor); break; case ProgramDiffFilter.PRE_COMMENT_DIFFS: monitorMsg = "Checking Pre-Comment Differences"; @@ -928,8 +927,7 @@ public class ProgramDiff { monitorMsg = "Checking Repeatable Comment Differences"; monitor.setMessage(monitorMsg); as = getCommentDiffs(CodeUnit.REPEATABLE_COMMENT, checkAddressSet, - new CommentTypeComparator(CodeUnit.REPEATABLE_COMMENT), - monitor); + new CommentTypeComparator(CodeUnit.REPEATABLE_COMMENT), monitor); break; case ProgramDiffFilter.PRE_COMMENT_DIFFS: monitorMsg = "Checking Pre-Comment Differences"; @@ -1417,17 +1415,16 @@ public class ProgramDiff { */ private AddressSet getLabelDifferences(AddressSetView addressSet, TaskMonitor monitor) throws CancelledException { - SymbolIterator iter1; - SymbolIterator iter2; + if (addressSet == null) { - iter1 = program1.getSymbolTable().getPrimarySymbolIterator(true); - iter2 = program2.getSymbolTable().getPrimarySymbolIterator(true); - } - else { - iter1 = program1.getSymbolTable().getPrimarySymbolIterator(addressSet, true); - AddressSet addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet, program2); - iter2 = program2.getSymbolTable().getPrimarySymbolIterator(addressSet2, true); + addressSet = program1.getMemory(); } + + SymbolIterator iter1 = program1.getSymbolTable().getPrimarySymbolIterator(addressSet, true); + AddressSetView addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet, program2); + SymbolIterator iter2 = + program2.getSymbolTable().getPrimarySymbolIterator(addressSet2, true); + SymbolComparator c = new SymbolComparator(); return c.getObjectDiffs(iter1, iter2, monitor); } @@ -1534,17 +1531,15 @@ public class ProgramDiff { */ private AddressSet getFunctionDifferences(AddressSetView addressSet, TaskMonitor monitor) throws CancelledException { - FunctionIterator iter1; - FunctionIterator iter2; + if (addressSet == null) { - iter1 = program1.getListing().getFunctions(true); - iter2 = program2.getListing().getFunctions(true); - } - else { - iter1 = program1.getListing().getFunctions(addressSet, true); - AddressSet addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet, program2); - iter2 = program2.getListing().getFunctions(addressSet2, true); + addressSet = program1.getMemory(); } + + FunctionIterator iter1 = program1.getListing().getFunctions(addressSet, true); + AddressSetView addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet, program2); + FunctionIterator iter2 = program2.getListing().getFunctions(addressSet2, true); + FunctionComparator c = new FunctionComparator(); return c.getObjectDiffs(iter1, iter2, monitor); } @@ -1588,8 +1583,7 @@ public class ProgramDiff { * @see ghidra.program.model.listing.CodeUnit */ private AddressSet getCuiDiffs(String cuiType, AddressSetView addressSet, - CodeUnitComparator c, - TaskMonitor monitor) throws CancelledException { + CodeUnitComparator c, TaskMonitor monitor) throws CancelledException { CodeUnitIterator iter1 = listing1.getCodeUnitIterator(cuiType, addressSet, true); AddressSet addressSet2 = DiffUtility.getCompatibleAddressSet(addressSet, program2); CodeUnitIterator iter2 = listing2.getCodeUnitIterator(cuiType, addressSet2, true); @@ -2682,8 +2676,9 @@ public class ProgramDiff { return false; } } - Symbol p1Symbol = p2ToP1Translator.getDestinationProgram().getSymbolTable().getSymbol( - p1Ref.getSymbolID()); + Symbol p1Symbol = p2ToP1Translator.getDestinationProgram() + .getSymbolTable() + .getSymbol(p1Ref.getSymbolID()); Symbol p2Symbol = p2ToP1Translator.getSourceProgram().getSymbolTable().getSymbol(p2Ref.getSymbolID()); if (!ProgramDiff.equivalentSymbols(p2ToP1Translator, p1Symbol, p2Symbol)) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java index 65e2f7d2fa..4f501cc3cd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java @@ -29,7 +29,7 @@ import ghidra.util.datastruct.Accumulator; import ghidra.util.datastruct.ListAccumulator; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; -import utility.function.TerminatingConsumer; +import utility.function.TerminatingConsumer; /** * ProgramMemoryUtil
contains some static methods for @@ -152,7 +152,7 @@ public class ProgramMemoryUtil { throws MemoryAccessException { // TODO: Addresses from one program cannot be used with another program (e.g., overlays) -// TODO: Should be using AddressTranslator +// TODO: Should be using AddressTranslator (see DiffUtility.getCompatibleAddressRange) // TODO: Method relies too heavily on caller limiting range to valid initialized memory in both programs // Copy the bytes for this range @@ -737,7 +737,7 @@ public class ProgramMemoryUtil { } } } - + /** * Finds the string in memory indicated by the searchString limited to the indicated * memory blocks and address set. @@ -752,14 +752,14 @@ public class ProgramMemoryUtil { public static List findString(String searchString, Program program, Listblocks, AddressSetView set, TaskMonitor monitor) throws CancelledException { - - List addresses = new ArrayList<>(); - - // just add each found location to the list, no termination of search - TerminatingConsumer collector = (i) -> addresses.add(i); - + + List addresses = new ArrayList<>(); + + // just add each found location to the list, no termination of search + TerminatingConsumer collector = (i) -> addresses.add(i); + locateString(searchString, collector, program, blocks, set, monitor); - + return addresses; } @@ -777,7 +777,8 @@ public class ProgramMemoryUtil { * @param monitor a task monitor to allow * @throws CancelledException if the user cancels */ - public static void locateString(String searchString, TerminatingConsumer foundLocationConsumer, Program program, + public static void locateString(String searchString, + TerminatingConsumer foundLocationConsumer, Program program, List blocks, AddressSetView set, TaskMonitor monitor) throws CancelledException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractProgramBasedTest.java b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractProgramBasedTest.java index 42a00e3f5c..32473b0863 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractProgramBasedTest.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/test/AbstractProgramBasedTest.java @@ -24,6 +24,7 @@ import java.util.List; import org.junit.After; import docking.widgets.fieldpanel.*; +import docking.widgets.fieldpanel.field.Field; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.util.viewer.field.ListingField; import ghidra.app.util.viewer.listingpanel.ListingPanel; @@ -246,8 +247,9 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg int instanceNum = 1; for (int i = 0; i < layout.getNumFields(); i++) { - ListingField bf = (ListingField) layout.getField(i); - if (bf.getFieldFactory().getFieldName().equals(fieldName)) { + Field f = layout.getField(i); + if ((f instanceof ListingField bf) && + bf.getFieldFactory().getFieldName().equals(fieldName)) { if (instanceNum++ == occurrence) { return bf; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java index 45ed039279..406f3083d4 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java @@ -113,7 +113,8 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest table.addRowSelectionInterval(0, 0); Set actions = getActionsByOwner(tool, plugin.getName()); for (DockingActionIf action : actions) { - if (action.getName().equals("Merge Blocks") || action.getName().equals("Local Menu")) { + if (action.getName().equals("Merge Blocks") || action.getName().equals("Local Menu") || + action.getName().equals("Rename Overlay Space")) { assertFalse(action.isEnabled()); } else { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java index 39f7bf95e1..88bf1372ad 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java @@ -19,6 +19,7 @@ import static org.junit.Assert.*; import java.awt.Component; import java.awt.Container; +import java.util.concurrent.atomic.AtomicInteger; import javax.swing.*; import javax.swing.table.TableModel; @@ -207,7 +208,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY)); + assertEquals("", model.getValueAt(0, MemoryMapModel.OVERLAY)); assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.INIT)); assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE)); @@ -281,7 +282,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY)); + assertEquals("", model.getValueAt(0, MemoryMapModel.OVERLAY)); assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.INIT)); assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE)); @@ -544,7 +545,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE)); assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY)); + assertEquals("", model.getValueAt(0, MemoryMapModel.OVERLAY)); assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE)); assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.INIT)); assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE)); @@ -628,15 +629,15 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest MemoryBlock[] blocks = program.getMemory().getBlocks(); int row = blocks.length - 1; assertEquals(".test", model.getValueAt(row, MemoryMapModel.NAME)); - assertEquals("00000000", model.getValueAt(row, MemoryMapModel.START)); + assertEquals(".test::00000000", model.getValueAt(row, MemoryMapModel.START)); assertEquals(".test::00000000", block.getStart().toString()); - assertEquals("000000ff", model.getValueAt(row, MemoryMapModel.END)); + assertEquals(".test::000000ff", model.getValueAt(row, MemoryMapModel.END)); assertEquals(".test::000000ff", block.getEnd().toString()); assertEquals("0x100", model.getValueAt(row, MemoryMapModel.LENGTH)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.WRITE)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.OVERLAY)); + assertEquals("ram", model.getValueAt(row, MemoryMapModel.OVERLAY)); assertEquals(MemoryBlockType.DEFAULT.toString(), model.getValueAt(row, MemoryMapModel.BLOCK_TYPE)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.INIT)); @@ -707,15 +708,15 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest MemoryBlock[] blocks = program.getMemory().getBlocks(); int row = blocks.length - 1; assertEquals(".test", model.getValueAt(row, MemoryMapModel.NAME)); - assertEquals("00000000", model.getValueAt(row, MemoryMapModel.START)); + assertEquals(".test::00000000", model.getValueAt(row, MemoryMapModel.START)); assertEquals(".test::00000000", block.getStart().toString()); - assertEquals("000000ff", model.getValueAt(row, MemoryMapModel.END)); + assertEquals(".test::000000ff", model.getValueAt(row, MemoryMapModel.END)); assertEquals(".test::000000ff", block.getEnd().toString()); assertEquals("0x100", model.getValueAt(row, MemoryMapModel.LENGTH)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.WRITE)); assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.OVERLAY)); + assertEquals("ram", model.getValueAt(row, MemoryMapModel.OVERLAY)); assertEquals(MemoryBlockType.DEFAULT.toString(), model.getValueAt(row, MemoryMapModel.BLOCK_TYPE)); assertEquals(Boolean.FALSE, model.getValueAt(row, MemoryMapModel.INIT)); @@ -792,7 +793,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE)); assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY)); + assertEquals("", model.getValueAt(0, MemoryMapModel.OVERLAY)); assertEquals("Bit Mapped", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE)); assertNull(model.getValueAt(0, MemoryMapModel.INIT)); assertEquals("01001000", model.getValueAt(0, MemoryMapModel.SOURCE)); @@ -868,7 +869,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ)); assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE)); assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE)); - assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY)); + assertEquals("", model.getValueAt(0, MemoryMapModel.OVERLAY)); assertEquals("Byte Mapped", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE)); assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.INIT)); assertEquals("01001000", model.getValueAt(0, MemoryMapModel.SOURCE)); @@ -945,8 +946,8 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest public void testMoveBlockNotAllowed() throws Exception { // create an overlay block tx(program, () -> { - memory.createInitializedBlock(".overlay", getAddr(0), 0x100, (byte) 0xa, - TaskMonitor.DUMMY, true); + memory.createInitializedBlock("other.overlay", AddressSpace.OTHER_SPACE.getAddress(0), + 0x100, (byte) 0xa, TaskMonitor.DUMMY, true); }); int row = table.getModel().getRowCount() - 1; @@ -956,18 +957,45 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest performAction(action, false); OptionDialog d = waitForDialogComponent(OptionDialog.class); assertNotNull(d); - assertEquals("Move Overlay Block Not Allowed", d.getTitle()); + assertEquals("Moving OTHER Overlay Block Not Allowed", d.getTitle()); } @Test public void testMoveBlock() throws Exception { + doTextMoveBlock(false); + } + + @Test + public void testMoveOverlayBlock() throws Exception { + doTextMoveBlock(true); + } + + private void doTextMoveBlock(boolean overlay) throws Exception { // add a block at 0, length 0x100 tx(program, () -> { memory.createInitializedBlock(".test", getAddr(0), 0x100, (byte) 0, TaskMonitor.DUMMY, - false); + overlay); }); - table.setRowSelectionInterval(0, 0); + MemoryBlock block = program.getMemory().getBlock(".test"); + assertNotNull(block); + assertEquals(overlay, block.isOverlay()); + Address startAddr = block.getStart(); + Address endAddr = block.getEnd(); + + // Select correct row + AtomicInteger row = new AtomicInteger(); + runSwing(() -> { + for (int i = 0; i < table.getRowCount(); i++) { + if (".test".equals(table.getModel().getValueAt(i, 0))) { + table.setRowSelectionInterval(i, i); + row.set(i); + break; + } + } + }); + + waitForSwing(); DockingActionIf action = getAction(plugin, "Move Block"); assertTrue(action.isEnabled()); @@ -986,11 +1014,11 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest JLabel origStartLabel = (JLabel) findComponentByName(d.getComponent(), "origStart"); assertNotNull(origStartLabel); - assertEquals(getAddr(0).toString(), origStartLabel.getText()); + assertEquals(startAddr.toString(), origStartLabel.getText()); JLabel origEndLabel = (JLabel) findComponentByName(d.getComponent(), "origEnd"); assertNotNull(origEndLabel); - assertEquals(getAddr(0xffL).toString(), origEndLabel.getText()); + assertEquals(endAddr.toString(), origEndLabel.getText()); JLabel lengthLabel = (JLabel) findComponentByName(d.getComponent(), "length"); assertNotNull(lengthLabel); @@ -999,19 +1027,21 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest AddressInput startField = (AddressInput) findComponentByName(d.getComponent(), "newStart"); assertNotNull(startField); - assertEquals(getAddr(0), startField.getAddress()); + assertEquals(startAddr, startField.getAddress()); AddressInput endField = (AddressInput) findComponentByName(d.getComponent(), "newEnd"); assertNotNull(endField); - assertEquals(getAddr(0xffL), endField.getAddress()); + assertEquals(endAddr, endField.getAddress()); JButton okButton = findButton(d.getComponent(), "OK"); assertNotNull(okButton); assertFalse(okButton.isEnabled()); // move the block to 0x300 - runSwing(() -> startField.setValue(getAddr(0x0300).toString())); - assertEquals(getAddr(0x3ff), endField.getAddress()); + Address newStart = startAddr.getAddressSpace().getAddressInThisSpaceOnly(0x300); + Address newEnd = startAddr.getAddressSpace().getAddressInThisSpaceOnly(0x3ff); + runSwing(() -> startField.setValue(newStart)); + assertEquals(newEnd, endField.getAddress()); assertTrue(okButton.isEnabled()); runSwing(() -> okButton.getActionListeners()[0].actionPerformed(null)); @@ -1024,9 +1054,9 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest program.flushEvents(); waitForSwing(); - assertEquals(".test", model.getValueAt(0, MemoryMapModel.NAME)); - assertEquals(getAddr(0x300).toString(), model.getValueAt(0, MemoryMapModel.START)); - assertEquals(getAddr(0x3ff).toString(), model.getValueAt(0, MemoryMapModel.END)); + assertEquals(".test", model.getValueAt(row.get(), MemoryMapModel.NAME)); + assertEquals(newStart.toString(), model.getValueAt(row.get(), MemoryMapModel.START)); + assertEquals(newEnd.toString(), model.getValueAt(row.get(), MemoryMapModel.END)); } @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider3Test.java index 9174df1489..ff31f1d8e0 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider3Test.java @@ -35,6 +35,7 @@ import ghidra.app.util.AddressInput; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; import ghidra.test.AbstractGhidraHeadedIntegrationTest; @@ -436,8 +437,8 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest public void testSplitNotAllowed() throws Exception { // create an overlay block tx(program, () -> { - memory.createInitializedBlock(".overlay", getAddr(0), 0x100, (byte) 0xa, - TaskMonitor.DUMMY, true); + memory.createInitializedBlock(".overlay", AddressSpace.OTHER_SPACE.getAddress(0), 0x100, + (byte) 0xa, TaskMonitor.DUMMY, true); }); int row = table.getModel().getRowCount() - 1; @@ -446,35 +447,35 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest performAction(action, false); OptionDialog d = waitForDialogComponent(OptionDialog.class); assertNotNull(d); - assertEquals("Split Overlay Block Not Allowed", d.getTitle()); + assertEquals("Split OTHER Overlay Block Not Allowed", d.getTitle()); close(d); } - @Test - public void testExpandBlockNotAllowed() throws Exception { - // create an overlay block - tx(program, () -> { - memory.createInitializedBlock(".overlay", getAddr(0), 0x100, (byte) 0xa, - TaskMonitor.DUMMY, true); - }); - - int row = table.getModel().getRowCount() - 1; - table.setRowSelectionInterval(row, row); - DockingActionIf action = getAction(plugin, "Expand Block Up"); - performAction(action, false); - OptionDialog d = waitForDialogComponent(OptionDialog.class); - assertNotNull(d); - assertEquals("Expand Overlay Block Not Allowed", d.getTitle()); - close(d); - - action = getAction(plugin, "Expand Block Down"); - performAction(action, false); - - OptionDialog d2 = waitForDialogComponent(OptionDialog.class); - assertNotNull(d2); - assertEquals("Expand Overlay Block Not Allowed", d2.getTitle()); - runSwing(() -> d2.close()); - } +// @Test +// public void testExpandBlockNotAllowed() throws Exception { +// // create an overlay block +// tx(program, () -> { +// memory.createInitializedBlock(".overlay", getAddr(0), 0x100, (byte) 0xa, +// TaskMonitor.DUMMY, true); +// }); +// +// int row = table.getModel().getRowCount() - 1; +// table.setRowSelectionInterval(row, row); +// DockingActionIf action = getAction(plugin, "Expand Block Up"); +// performAction(action, false); +// OptionDialog d = waitForDialogComponent(OptionDialog.class); +// assertNotNull(d); +// assertEquals("Expand Overlay Block Not Allowed", d.getTitle()); +// close(d); +// +// action = getAction(plugin, "Expand Block Down"); +// performAction(action, false); +// +// OptionDialog d2 = waitForDialogComponent(OptionDialog.class); +// assertNotNull(d2); +// assertEquals("Expand Overlay Block Not Allowed", d2.getTitle()); +// runSwing(() -> d2.close()); +// } @Test public void testExpandBlockUpSetup() { @@ -536,7 +537,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest runSwing(() -> okButton.getActionListeners()[0].actionPerformed(null)); waitForSwing(); - assertEquals(".text.exp", model.getValueAt(0, MemoryMapModel.NAME)); + assertEquals(".text", model.getValueAt(0, MemoryMapModel.NAME)); assertEquals("00002000", model.getValueAt(0, MemoryMapModel.START)); assertEquals("010075ff", model.getValueAt(0, MemoryMapModel.END)); assertEquals("0x1005600", model.getValueAt(0, MemoryMapModel.LENGTH)); @@ -548,7 +549,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest assertEquals("0x6600", model.getValueAt(0, MemoryMapModel.LENGTH)); redo(program); - assertEquals(".text.exp", model.getValueAt(0, MemoryMapModel.NAME)); + assertEquals(".text", model.getValueAt(0, MemoryMapModel.NAME)); assertEquals("00002000", model.getValueAt(0, MemoryMapModel.START)); assertEquals("010075ff", model.getValueAt(0, MemoryMapModel.END)); assertEquals("0x1005600", model.getValueAt(0, MemoryMapModel.LENGTH)); @@ -606,7 +607,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest runSwing(() -> okButton.getActionListeners()[0].actionPerformed(null)); waitForSwing(); - assertEquals(".text.exp", model.getValueAt(0, MemoryMapModel.NAME)); + assertEquals(".text", model.getValueAt(0, MemoryMapModel.NAME)); assertEquals("01000000", model.getValueAt(0, MemoryMapModel.START)); assertEquals("010075ff", model.getValueAt(0, MemoryMapModel.END)); assertEquals("0x7600", model.getValueAt(0, MemoryMapModel.LENGTH)); @@ -618,7 +619,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest assertEquals("0x6600", model.getValueAt(0, MemoryMapModel.LENGTH)); redo(program); - assertEquals(".text.exp", model.getValueAt(0, MemoryMapModel.NAME)); + assertEquals(".text", model.getValueAt(0, MemoryMapModel.NAME)); assertEquals("01000000", model.getValueAt(0, MemoryMapModel.START)); assertEquals("010075ff", model.getValueAt(0, MemoryMapModel.END)); assertEquals("0x7600", model.getValueAt(0, MemoryMapModel.LENGTH)); @@ -736,7 +737,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest runSwing(() -> okButton.getActionListeners()[0].actionPerformed(null)); waitForSwing(); - assertEquals(".text.exp", model.getValueAt(0, MemoryMapModel.NAME)); + assertEquals(".text", model.getValueAt(0, MemoryMapModel.NAME)); assertEquals("01001000", model.getValueAt(0, MemoryMapModel.START)); assertEquals("01007700", model.getValueAt(0, MemoryMapModel.END)); assertEquals("0x6701", model.getValueAt(0, MemoryMapModel.LENGTH)); @@ -763,7 +764,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest runSwing(() -> okButton.getActionListeners()[0].actionPerformed(null)); waitForSwing(); - assertEquals(".text.exp", model.getValueAt(0, MemoryMapModel.NAME)); + assertEquals(".text", model.getValueAt(0, MemoryMapModel.NAME)); assertEquals("01001000", model.getValueAt(0, MemoryMapModel.START)); assertEquals("010076ff", model.getValueAt(0, MemoryMapModel.END)); assertEquals("0x6700", model.getValueAt(0, MemoryMapModel.LENGTH)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/OverlayAddressSpaceTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/OverlayAddressSpaceTest.java index da047aa866..977e0d006a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/OverlayAddressSpaceTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/OverlayAddressSpaceTest.java @@ -130,7 +130,7 @@ public class OverlayAddressSpaceTest extends AbstractGhidraHeadedIntegrationTest space3 = factory.getAddressSpace(space3.getName()); OverlayAddressSpace space1Overlay = - new OverlayAddressSpace("Overlay1", space1, 4, 0x20, 0x30); + new SingleRangeOverlayAddressSpace("Overlay1", space1, 4, 0x20, 0x30, "Overlay1"); Address space1Address = space1.getAddress(0x20); Address space1OverlayAddress = space1Overlay.getAddress(0x22); @@ -192,7 +192,7 @@ public class OverlayAddressSpaceTest extends AbstractGhidraHeadedIntegrationTest space1 = factory.getAddressSpace(space1.getName()); OverlayAddressSpace space1Overlay = - new OverlayAddressSpace("Overlay1", space1, 4, 0x20, 0x30); + new SingleRangeOverlayAddressSpace("Overlay1", space1, 4, 0x20, 0x30, "Overlay1"); assertEquals(0x25, space1Overlay.truncateOffset(0x25)); assertEquals(0x40, space1Overlay.truncateOffset(0x40)); @@ -223,7 +223,7 @@ public class OverlayAddressSpaceTest extends AbstractGhidraHeadedIntegrationTest } @Test - public void testOverlayRename() throws Exception { + public void testOverlayRenameAndDelete() throws Exception { ProgramBuilder builder = new ProgramBuilder("Test", ProgramBuilder._TOY, this); program = builder.getProgram(); @@ -242,18 +242,42 @@ public class OverlayAddressSpaceTest extends AbstractGhidraHeadedIntegrationTest assertEquals("my_overlay_x", overlayBlock1.getStart().getAddressSpace().getName()); assertEquals("my_overlay_x.1", overlayBlock2.getStart().getAddressSpace().getName()); - overlayBlock1.setName("my new name"); + overlayBlock1.setName("my new name"); // does not rename overlay space assertEquals("my new name", overlayBlock1.getName()); - assertEquals("my_new_name", overlayBlock1.getStart().getAddressSpace().getName()); - assertNull(af.getAddressSpace("my_overlay_x")); - assertNotNull(af.getAddressSpace("my_new_name")); + assertEquals("my_overlay_x", overlayBlock1.getStart().getAddressSpace().getName()); + assertNotNull(af.getAddressSpace("my_overlay_x")); + assertNull(af.getAddressSpace("my_new_name")); - overlayBlock2.setName("my new name"); + overlayBlock2.setName("my new name"); // does not rename overlay space assertEquals("my new name", overlayBlock2.getName()); - assertEquals("my_new_name.1", overlayBlock2.getStart().getAddressSpace().getName()); - assertNull(af.getAddressSpace("my_overlay_x.1")); - assertNotNull(af.getAddressSpace("my_new_name.1")); + assertEquals("my_overlay_x.1", overlayBlock2.getStart().getAddressSpace().getName()); + assertNotNull(af.getAddressSpace("my_overlay_x.1")); + assertNull(af.getAddressSpace("my_new_name.1")); + overlayBlock2.setName("my overlay:x"); // restore non-duplicate block name + + program.renameOverlaySpace("my_overlay_x", "my_overlay_x2"); + program.renameOverlaySpace("my_overlay_x.1", "my_overlay_x2.1"); + + // must reacquire block instances due to imposed memory reload/restore + + overlayBlock1 = program.getMemory().getBlock("my new name"); + assertEquals("my_overlay_x2", overlayBlock1.getStart().getAddressSpace().getName()); + assertNull(af.getAddressSpace("my_overlay_x")); + assertNotNull(af.getAddressSpace("my_overlay_x2")); + + overlayBlock2 = program.getMemory().getBlock("my overlay:x"); + assertEquals("my_overlay_x2.1", overlayBlock2.getStart().getAddressSpace().getName()); + assertNull(af.getAddressSpace("my_overlay_x.1")); + assertNotNull(af.getAddressSpace("my_overlay_x2.1")); + + // Overlay Space removal will occur after block removal + + assertFalse( + program.removeOverlaySpace(overlayBlock1.getStart().getAddressSpace().getName())); + assertNotNull(af.getAddressSpace("my_overlay_x2")); + program.getMemory().removeBlock(overlayBlock1, TaskMonitor.DUMMY); // should remove space + assertNull(af.getAddressSpace("my_overlay_x2")); } finally { program.endTransaction(transactionID, true); @@ -270,8 +294,9 @@ public class OverlayAddressSpaceTest extends AbstractGhidraHeadedIntegrationTest int transactionID = program.startTransaction(testName.getMethodName()); MemoryBlock overlayBlock1 = null; try { - overlayBlock1 = program.getMemory().createInitializedBlock(".overlay1", - af.getAddress("1000"), 0x100, (byte) 0x11, TaskMonitor.DUMMY, true); + overlayBlock1 = program.getMemory() + .createInitializedBlock(".overlay1", af.getAddress("1000"), 0x100, (byte) 0x11, + TaskMonitor.DUMMY, true); } finally { program.endTransaction(transactionID, true); @@ -305,14 +330,15 @@ public class OverlayAddressSpaceTest extends AbstractGhidraHeadedIntegrationTest MemoryBlock overlayBlock3 = null; MemoryBlock overlayBlock4 = null; try { - overlayBlock2 = program.getMemory().createInitializedBlock(".overlay2", - af.getAddress("2000"), 0x200, (byte) 0x22, TaskMonitor.DUMMY, true); + overlayBlock2 = program.getMemory() + .createInitializedBlock(".overlay2", af.getAddress("2000"), 0x200, (byte) 0x22, + TaskMonitor.DUMMY, true); overlayBlock3 = program.getMemory() - .createInitializedBlock("my_overlay_x", - af.getAddress("3000"), 0x300, (byte) 0x33, TaskMonitor.DUMMY, true); + .createInitializedBlock("my_overlay_x", af.getAddress("3000"), 0x300, + (byte) 0x33, TaskMonitor.DUMMY, true); overlayBlock4 = program.getMemory() - .createInitializedBlock("my overlay:x", - af.getAddress("4000"), 0x400, (byte) 0x44, TaskMonitor.DUMMY, true); + .createInitializedBlock("my overlay:x", af.getAddress("4000"), 0x400, + (byte) 0x44, TaskMonitor.DUMMY, true); } finally { program.endTransaction(transactionID, true); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java index ca06c7d962..4eb1bc92b0 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java @@ -48,8 +48,8 @@ public class ByteMappedMemoryBlockTest extends AbstractGhidraHeadedIntegrationTe space = program.getAddressFactory().getDefaultAddressSpace(); transactionID = program.startTransaction("Test"); - block = memory.createInitializedBlock("BYTE_BLOCK", space.getAddress(0), - bytes.length, (byte) 0, TaskMonitor.DUMMY, false); + block = memory.createInitializedBlock("BYTE_BLOCK", space.getAddress(0), bytes.length, + (byte) 0, TaskMonitor.DUMMY, false); memory.setBytes(block.getStart(), bytes); } @@ -101,8 +101,7 @@ public class ByteMappedMemoryBlockTest extends AbstractGhidraHeadedIntegrationTe } MemoryBlock block2 = memory.createInitializedBlock("BYTE_BLOCK2", space.getAddress(0x100), - bytes.length, - (byte) 0, TaskMonitor.DUMMY, false); + bytes.length, (byte) 0, TaskMonitor.DUMMY, false); set.add(addr(0x100), addr(0x1FF)); set.add(addr(0x1080), addr(0x10FF)); @@ -279,11 +278,15 @@ public class ByteMappedMemoryBlockTest extends AbstractGhidraHeadedIntegrationTe MemoryBlock byteMappedBlock = memory.createByteMappedBlock("test", addr(0x1000), addr(0x80), 0x100, new ByteMappingScheme(2, 4), true); assertTrue(byteMappedBlock.isOverlay()); - AddressSpace testSpace = program.getAddressFactory().getAddressSpace("test"); + OverlayAddressSpace testSpace = + (OverlayAddressSpace) program.getAddressFactory().getAddressSpace("test"); assertNotNull(testSpace); assertEquals(space, testSpace.getPhysicalSpace()); - assertEquals(testSpace.getAddress(0x1000), testSpace.getMinAddress()); - assertEquals(testSpace.getAddress(0x10FF), testSpace.getMaxAddress()); + AddressSetView testSet = testSpace.getOverlayAddressSet(); + assertEquals(1, testSet.getNumAddressRanges()); + AddressRange r = testSet.getFirstRange(); + assertEquals(testSpace.getAddress(0x1000), r.getMinAddress()); + assertEquals(testSpace.getAddress(0x10FF), r.getMaxAddress()); assertEquals(0x100, byteMappedBlock.getSize()); assertEquals(testSpace.getAddress(0x1000), byteMappedBlock.getStart()); assertEquals(testSpace.getAddress(0x10FF), byteMappedBlock.getEnd()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java index c9636c6499..9c1a61dd28 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java @@ -314,29 +314,29 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { MemoryBlock block = mem.getBlock(addr(95)); assertEquals(newBlock, block); } - + @Test public void testGetBlockByName() throws Exception { - + MemoryBlock block1 = createBlock("Test1", addr(100), 100); MemoryBlock block2 = createBlock("Test2", addr(300), 100); - + MemoryBlock block = mem.getBlock("Test1"); assertEquals("Test1", block.getName()); assertEquals("get same block", block, block1); mem.split(block, addr(150)); block = mem.getBlock("Test1"); - assertEquals("Test1", block.getName()); + assertEquals("Test1", block.getName()); assertEquals(50, block.getSize()); - + // non-existent block block = mem.getBlock("NoExist"); assertNull(block); - + program.endTransaction(transactionID, true); - transactionID = program.startTransaction("Test"); - + transactionID = program.startTransaction("Test"); + // now exists mem.getBlock("Test1").setName("NoExist"); // Test1 no longer exists @@ -347,28 +347,28 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { mem.removeBlock(block, new TaskMonitorAdapter()); block = mem.getBlock("NoExist"); assertNull("block should be deleted", block); - + // Test1 still doesn't exist block = mem.getBlock("Test1"); assertNull("block deleted", block); - + block = mem.getBlock("Test2"); assertEquals("Test2", block.getName()); - + program.endTransaction(transactionID, true); - + program.undo(); - + // Test1 still doesn't exist block = mem.getBlock("Test1"); assertNotNull("Undo, Test1 exists again", block); - + transactionID = program.startTransaction("Test"); } @Test public void testMemoryMapExecuteSet() throws Exception { - + AddressSetView executeSet = mem.getExecuteSet(); assertTrue(executeSet.isEmpty()); MemoryBlock block1 = createBlock("Test1", addr(100), 100); @@ -381,7 +381,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { MemoryBlock block = mem.getBlock("Test1"); executeSet = mem.getExecuteSet(); assertTrue(executeSet.isEmpty()); - + block.setExecute(false); executeSet = mem.getExecuteSet(); assertTrue(executeSet.isEmpty()); @@ -391,15 +391,15 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { assertTrue(executeSet.isEmpty() != true); Address start = block.getStart(); Address end = block.getEnd(); - assertTrue(executeSet.contains(start,end)); + assertTrue(executeSet.contains(start, end)); // non-existent block block = mem.getBlock("NoExist"); assertNull(block); - + program.endTransaction(transactionID, true); - transactionID = program.startTransaction("Test"); - + transactionID = program.startTransaction("Test"); + // now exists mem.getBlock("Test1").setName("NoExist"); // Test1 no longer exists @@ -408,39 +408,39 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { start = block.getStart(); end = block.getEnd(); // should be same block - assertTrue(executeSet.contains(start,end)); + assertTrue(executeSet.contains(start, end)); block.setExecute(false); executeSet = mem.getExecuteSet(); - assertTrue(executeSet.contains(start,end) == false); - + assertTrue(executeSet.contains(start, end) == false); + block2.setExecute(true); Address start2 = block2.getStart(); Address end2 = block2.getEnd(); mem.removeBlock(block2, new TaskMonitorAdapter()); - + program.endTransaction(transactionID, true); - + program.undo(); - + transactionID = program.startTransaction("Test"); // should be execute set on block2, deleted, then undone executeSet = mem.getExecuteSet(); - assertTrue(executeSet.contains(start2,end2) == false); - + assertTrue(executeSet.contains(start2, end2) == false); + // undid set execute block should now be contained block = mem.getBlock("Test1"); start = block.getStart(); end = block.getEnd(); executeSet = mem.getExecuteSet(); - assertTrue(executeSet.contains(start,end)); - + assertTrue(executeSet.contains(start, end)); + mem.split(block, addr(150)); block = mem.getBlock("Test1"); executeSet = mem.getExecuteSet(); assertTrue(executeSet.isEmpty() != true); assertTrue(executeSet.contains(block.getStart(), block.getEnd())); - + // remove block that was split, should still be executable memory start = block.getStart(); end = block.getEnd(); @@ -449,7 +449,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { assertTrue(executeSet.isEmpty() != true); assertTrue(executeSet.contains(start, end) == false); } - + @Test public void testSave() throws Exception { MemoryBlock block1 = createBlock("Test1", addr(0), 100); @@ -1062,11 +1062,23 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { (byte) 0xa, TaskMonitor.DUMMY, true); try { - mem.join(blockOne, blockTwo); + mem.join(blockOne, blockTwo); // two different overlay spaces Assert.fail("Join should have caused and Exception!"); } - catch (IllegalArgumentException e) { + catch (MemoryBlockException e) { + // expected } + + AddressSpace overlaySpace = blockOne.getStart().getAddressSpace(); + + MemoryBlock blockThree = + mem.createInitializedBlock(".overlay3", overlaySpace.getAddressInThisSpaceOnly(0x1000), + 0x100, (byte) 0xa, TaskMonitor.DUMMY, true); + + MemoryBlock joinedBlock = mem.join(blockOne, blockThree); // same overlay space + assertEquals(".overlay", joinedBlock.getName()); // use name of min block + assertEquals(0x1100, joinedBlock.getSize()); + } @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AbstractProgramDiffTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AbstractProgramDiffTest.java index 4838226191..6b49aabf7c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AbstractProgramDiffTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/AbstractProgramDiffTest.java @@ -15,14 +15,14 @@ */ package ghidra.program.util; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import ghidra.program.model.address.*; import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.*; -import ghidra.test.ClassicSampleX86ProgramBuilder; import ghidra.test.AbstractGhidraHeadedIntegrationTest; +import ghidra.test.ClassicSampleX86ProgramBuilder; import ghidra.util.exception.CancelledException; public class AbstractProgramDiffTest extends AbstractGhidraHeadedIntegrationTest { @@ -33,17 +33,34 @@ public class AbstractProgramDiffTest extends AbstractGhidraHeadedIntegrationTest protected Program p1; protected Program p2; + /** + * Generate address in p1's default address space + * @param offset address offset + * @return address + */ protected Address addr(int offset) { AddressSpace space = p1.getAddressFactory().getDefaultAddressSpace(); return space.getAddress(offset); } - protected Address addr(Program program, int offset) { + /** + * Generate address in specified program's default address space + * @param program target program + * @param offset address offset + * @return address + */ + protected static Address addr(Program program, int offset) { AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); return space.getAddress(offset); } - protected Address addr(Program program, String addrString) { + /** + * Generate address from parsed string within specified program. + * @param program target program + * @param addrString address string (w/ optional space name) to be parsed + * @return addressor null if parse fails + */ + protected static Address addr(Program program, String addrString) { return program.getAddressFactory().getAddress(addrString); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiff2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiff2Test.java index 057204154c..2edb546849 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiff2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiff2Test.java @@ -21,8 +21,7 @@ package ghidra.program.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.*; @@ -35,7 +34,6 @@ import ghidra.program.model.listing.*; import ghidra.program.model.mem.Memory; import ghidra.program.model.symbol.*; import ghidra.test.TestEnv; -import ghidra.util.task.TaskMonitor; /** * ProgramDiffTest
tests theProgramDiff
class @@ -209,9 +207,9 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { Namespace namespace = program.getGlobalNamespace(); try { program.getFunctionManager() - .getFunctionAt(addr(program, "0x100248f")) - .getSymbol() - .setName("Bud", SourceType.IMPORTED); + .getFunctionAt(addr(program, "0x100248f")) + .getSymbol() + .setName("Bud", SourceType.IMPORTED); createDataReference(program, addr(program, "0x01001e81"), addr(program, "0x01001ea0")); Symbol[] symbols = st.getSymbols(addr(program, "0x01001ea0")); @@ -235,9 +233,9 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { Namespace namespace = program.getGlobalNamespace(); try { program.getFunctionManager() - .getFunctionAt(addr(program, "0x100248f")) - .getSymbol() - .setName("Bud", SourceType.ANALYSIS); + .getFunctionAt(addr(program, "0x100248f")) + .getSymbol() + .setName("Bud", SourceType.ANALYSIS); createDataReference(program, addr(program, "0x01001e81"), addr(program, "0x01001ea0"));// Leave this as default. Symbol[] symbols = st.getSymbols(addr(program, "0x01001ea0")); @@ -710,7 +708,9 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { // P1 program try { Memory memory = program.getMemory(); - memory.createInitializedBlock("Foo", addr(program, "0x01000000"), 0x200L, + memory.createInitializedBlock("Foo1", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + memory.createInitializedBlock("Foo2", addr(program, "0x01000000"), 0x200L, (byte) 0x0, null, true); } catch (Exception e) { @@ -723,7 +723,9 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { // P2 program try { Memory memory = program.getMemory(); - memory.createInitializedBlock("Foo", addr(program, "0x01000000"), 0x200L, + memory.createInitializedBlock("Foo2", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + memory.createInitializedBlock("Foo1", addr(program, "0x01000000"), 0x200L, (byte) 0x0, null, true); } catch (Exception e) { @@ -742,14 +744,16 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { } @Test - public void testDiffOverlaysDiffNames() throws Exception { + public void testDiffOverlaysDiffRegion() throws Exception { mtf.initialize("NotepadMergeListingTest", new ProgramModifierListener() { @Override public void modifyLatest(ProgramDB program) { // P1 program try { Memory memory = program.getMemory(); - memory.createInitializedBlock("Foo", addr(program, "0x01000000"), 0x200L, + memory.createInitializedBlock("Foo1", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + memory.createInitializedBlock("Foo2", addr(program, "0x01000000"), 0x200L, (byte) 0x0, null, true); } catch (Exception e) { @@ -762,7 +766,9 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { // P2 program try { Memory memory = program.getMemory(); - memory.createInitializedBlock("Bar", addr(program, "0x01000000"), 0x200L, + memory.createInitializedBlock("Foo2", addr(program, "0x02000000"), 0x200L, + (byte) 0x0, null, true); + memory.createInitializedBlock("Foo1", addr(program, "0x02000000"), 0x200L, (byte) 0x0, null, true); } catch (Exception e) { @@ -776,7 +782,57 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { programDiff = new ProgramDiff(p1, p2); AddressSet as = new AddressSet(); - as.addRange(addr(p1, "Foo:0x01000000"), addr(p1, "Foo:0x010001ff")); + // All overlay blocks in common Foo overlay space is considered + as.addRange(addr(p1, "Foo1:0x01000000"), addr(p1, "Foo1:0x010001ff")); + as.addRange(addr(p1, "Foo1::02000000"), addr(p1, "Foo1::020001ff")); + as.addRange(addr(p1, "Foo2:0x01000000"), addr(p1, "Foo2:0x010001ff")); + as.addRange(addr(p1, "Foo2::02000000"), addr(p1, "Foo2::020001ff")); + programDiff.setFilter(new ProgramDiffFilter(ProgramDiffFilter.ALL_DIFFS)); + assertEquals(as, programDiff.getDifferences(programDiff.getFilter(), null)); + } + + @Test + public void testDiffOverlaysDiffNames() throws Exception { + mtf.initialize("NotepadMergeListingTest", new ProgramModifierListener() { + @Override + public void modifyLatest(ProgramDB program) { + // P1 program + try { + Memory memory = program.getMemory(); + memory.createInitializedBlock("Foo1", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + memory.createInitializedBlock("Foo2", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + // P2 program + try { + Memory memory = program.getMemory(); + memory.createInitializedBlock("Bar1", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + memory.createInitializedBlock("Bar2", addr(program, "0x01000000"), 0x200L, + (byte) 0x0, null, true); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + }); + + p1 = mtf.getResultProgram(); + p2 = mtf.getPrivateProgram(); + + programDiff = new ProgramDiff(p1, p2); + AddressSet as = new AddressSet(); + // Only block regions in p1 are considered + as.addRange(addr(p1, "Foo1:0x01000000"), addr(p1, "Foo1:0x010001ff")); + as.addRange(addr(p1, "Foo2:0x01000000"), addr(p1, "Foo2:0x010001ff")); programDiff.setFilter(new ProgramDiffFilter(ProgramDiffFilter.ALL_DIFFS)); assertEquals(as, programDiff.getDifferences(programDiff.getFilter(), null)); } @@ -794,8 +850,8 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { SymbolTable st = program.getSymbolTable(); Namespace globalNamespace = program.getGlobalNamespace(); - st.createLabel(addr(program, "Foo:0x01000030"), "Sample0030", - globalNamespace, SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000030"), "Sample0030", globalNamespace, + SourceType.USER_DEFINED); } catch (Exception e) { Assert.fail(e.getMessage()); @@ -812,8 +868,8 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { SymbolTable st = program.getSymbolTable(); Namespace globalNamespace = program.getGlobalNamespace(); - st.createLabel(addr(program, "Foo:0x01000050"), "Other0050", - globalNamespace, SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000050"), "Other0050", globalNamespace, + SourceType.USER_DEFINED); } catch (Exception e) { Assert.fail(e.getMessage()); @@ -839,18 +895,19 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { public void modifyLatest(ProgramDB program) { // P1 program try { + // Overlay Foo 0x01000000 - 0x0100017f Memory memory = program.getMemory(); memory.createInitializedBlock("Foo", addr(program, "0x01000000"), 0x180L, (byte) 0x0, null, true); SymbolTable st = program.getSymbolTable(); Namespace globalNamespace = program.getGlobalNamespace(); - st.createLabel(addr(program, "Foo:0x01000030"), "Sample0030", - globalNamespace, SourceType.USER_DEFINED); - st.createLabel(addr(program, "Foo:0x01000079"), "Sample0079", - globalNamespace, SourceType.USER_DEFINED); - st.createLabel(addr(program, "Foo:0x0100017f"), "Sample017f", - globalNamespace, SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000030"), "Sample0030", globalNamespace, + SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000179"), "Sample0179", globalNamespace, + SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x0100017f"), "Sample017f", globalNamespace, + SourceType.USER_DEFINED); } catch (Exception e) { Assert.fail(e.getMessage()); @@ -861,18 +918,19 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { public void modifyPrivate(ProgramDB program) { // P2 program try { + // Overlapping overlay with the same name 0x01000080 - 0x0100017f Memory memory = program.getMemory(); memory.createInitializedBlock("Foo", addr(program, "0x01000080"), 0x180L, (byte) 0x0, null, true); SymbolTable st = program.getSymbolTable(); Namespace globalNamespace = program.getGlobalNamespace(); - st.createLabel(addr(program, "Foo:0x01000080"), "Other0080", - globalNamespace, SourceType.USER_DEFINED); - st.createLabel(addr(program, "Foo:0x01000180"), "Other0180", - globalNamespace, SourceType.USER_DEFINED); - st.createLabel(addr(program, "Foo:0x01000200"), "Other0200", - globalNamespace, SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000080"), "Other0080", globalNamespace, + SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000162"), "Other0162", globalNamespace, + SourceType.USER_DEFINED); + st.createLabel(addr(program, "Foo:0x01000200"), "Other0200", globalNamespace, + SourceType.USER_DEFINED); } catch (Exception e) { Assert.fail(e.getMessage()); @@ -883,13 +941,17 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { p1 = mtf.getResultProgram(); p2 = mtf.getPrivateProgram(); - // No addresses should be considered to be in common for the overlays. + // All overlay blocks in common Foo overlay space is considered programDiff = new ProgramDiff(p1, p2); - AddressSet as = new AddressSet(); - as.addRange(addr(p1, "Foo:0x01000000"), addr(p1, "Foo:0x0100017f")); programDiff.setFilter(new ProgramDiffFilter(ProgramDiffFilter.ALL_DIFFS)); AddressSetView diffs = programDiff.getDifferences(programDiff.getFilter(), null); - assertEquals(as, diffs); + + AddressSet expectedDiffAs = new AddressSet(); + expectedDiffAs.addRange(addr(p1, "Foo:0x01000000"), addr(p1, "Foo:0x01000080")); // not found in p2 + expectedDiffAs.add(addr(p1, "Foo:0x01000162")); // Other0162 not found in p1 + expectedDiffAs.add(addr(p1, "Foo:0x01000179")); // Sample0179 not found in p2 + expectedDiffAs.addRange(addr(p1, "Foo:0x0100017f"), addr(p1, "Foo:0x010001ff")); // not found in p1 + assertEquals(expectedDiffAs, diffs); } @Test @@ -1084,61 +1146,6 @@ public class ProgramDiff2Test extends AbstractProgramDiffTest { assertEquals(as, programDiff.getDifferences(programDiff.getFilter(), null)); } - @Test - public void testDiffOverlayOrder() throws Exception { - mtf.initialize("overlayCalc", new ProgramModifierListener() { - @Override - public void modifyLatest(ProgramDB program) { - // P1 program - SymbolTable st = program.getSymbolTable(); - try { - program.getMemory().createInitializedBlock("SomeOverlay", - addr(program, "0x01001630"), 0x200, (byte) 0, TaskMonitor.DUMMY, true); - program.getMemory().createInitializedBlock("OtherOverlay", - addr(program, "0x01001630"), 0x300, (byte) 0, TaskMonitor.DUMMY, true); - st.createLabel(addr(program, "SomeOverlay::01001630"), "OVL1630", - SourceType.USER_DEFINED); - st.createLabel(addr(program, "OtherOverlay::01001866"), "OVL1866", - SourceType.USER_DEFINED); - } - catch (Exception e) { - Assert.fail(e.getMessage()); - } - } - - @Override - public void modifyPrivate(ProgramDB program) { - // P2 program - SymbolTable st = program.getSymbolTable(); - try { - program.getMemory().createInitializedBlock("OtherOverlay", - addr(program, "0x01001630"), 0x200, (byte) 0, TaskMonitor.DUMMY, true); - program.getMemory().createInitializedBlock("SomeOverlay", - addr(program, "0x01001630"), 0x300, (byte) 0, TaskMonitor.DUMMY, true); - st.createLabel(addr(program, "SomeOverlay::01001889"), "OVL1889", - SourceType.USER_DEFINED); - st.createLabel(addr(program, "OtherOverlay::01001646"), "OVL1646", - SourceType.USER_DEFINED); - } - catch (Exception e) { - Assert.fail(e.getMessage()); - } - } - }); - - p1 = mtf.getResultProgram(); - p2 = mtf.getPrivateProgram(); - - programDiff = new ProgramDiff(p1, p2); - AddressSet as = new AddressSet(); - as.addRange(addr(p1, "SomeOverlay::01001630"), addr(p1, "SomeOverlay::01001630")); - // Diff won't detect SomeOverlay::01001889 because it isn't in p1. - as.addRange(addr(p1, "OtherOverlay::01001646"), addr(p1, "OtherOverlay::01001646")); - as.addRange(addr(p1, "OtherOverlay::01001866"), addr(p1, "OtherOverlay::01001866")); - programDiff.setFilter(new ProgramDiffFilter(ProgramDiffFilter.SYMBOL_DIFFS)); - assertEquals(as, programDiff.getDifferences(programDiff.getFilter(), null)); - } - // private void printAddressSet(AddressSetView diffAs) { // System.out.println("====="); // for (AddressRange addressRange : diffAs) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiffMergeOverlayTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiffMergeOverlayTest.java new file mode 100644 index 0000000000..002d886317 --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramDiffMergeOverlayTest.java @@ -0,0 +1,325 @@ +/* ### + * 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. + */ +/* + * ProgramDiffTest.java + * + * Created on January 3, 2002, 9:55 AM + */ + +package ghidra.program.util; + +import static org.junit.Assert.*; + +import java.util.Set; + +import org.junit.*; + +import ghidra.program.database.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.lang.CompilerSpec; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.SymbolTable; +import ghidra.test.TestEnv; +import ghidra.util.task.TaskMonitor; + +/** + *ProgramDiffTest
tests theProgramDiff
class + * to verify it correctly determines various types of program differences. + * The setup for this test class loads two programs that were saved to the + * testdata directory as XML. The tests will determine the differences between + * these two programs. + */ +public class ProgramDiffMergeOverlayTest extends AbstractProgramDiffTest { + + protected MergeTestFacilitator mtf; + protected Program originalProgram; + protected Program latestProgram; + protected Program myProgram; + protected Program resultProgram; + + public ProgramDiffMergeOverlayTest() { + super(); + } + + @Before + public void setUp() throws Exception { + + fixupGUI(); + mtf = new MergeTestFacilitator(); + TestEnv testEnv = mtf.getTestEnvironment(); + testEnv.getTool().setToolName("TestTool"); + } + + @After + public void tearDown() throws Exception { + try { + if (resultProgram != null) { + resultProgram.flushEvents(); + } + waitForSwing(); + + } + catch (Exception e) { + e.printStackTrace(); + } + mtf.dispose();// Get rid of the merge environment. + + } + + @Test + public void testDiffMergeOverlayFunctionTags() throws Exception { + mtf.initialize("overlayCalc", new MultiOverlayProgramModifierListener() { + @Override + public void modifyLatest(ProgramDB program) { + // P1 program + super.modifyLatest(program); + try { + Listing listing = program.getListing(); + + Function f = listing.getFunctionAt(addr(program, "OtherOverlay:0x01001680")); + f.addTag("Tag1"); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + // P2 program + super.modifyPrivate(program); + try { + Listing listing = program.getListing(); + Function f = listing.getFunctionAt(addr(program, "SomeOverlay:0x01001780")); + f.addTag("Tag2"); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + }); + + p1 = mtf.getResultProgram(); + p2 = mtf.getPrivateProgram(); + + AddressSet as = new AddressSet(); + as.addRange(addr(p1, "SomeOverlay::01001780"), addr(p1, "SomeOverlay::01001780")); + as.addRange(addr(p1, "OtherOverlay::01001680"), addr(p1, "OtherOverlay::01001680")); + + ProgramMergeManager programMerge = new ProgramMergeManager(p1, p2, TaskMonitor.DUMMY); + programMerge.setDiffFilter(new ProgramDiffFilter( + ProgramDiffFilter.FUNCTION_DIFFS | ProgramDiffFilter.FUNCTION_TAG_DIFFS)); + programMerge.setMergeFilter( + new ProgramMergeFilter(ProgramMergeFilter.FUNCTIONS | ProgramMergeFilter.FUNCTION_TAGS, + ProgramMergeFilter.REPLACE)); + assertEquals(as, programMerge.getFilteredDifferences()); + + p1.withTransaction("merge", () -> programMerge.merge(as, TaskMonitor.DUMMY)); + assertEquals(new AddressSet(), programMerge.getFilteredDifferences()); + + Listing listing1 = p1.getListing(); + Function f = listing1.getFunctionAt(addr(p1, "SomeOverlay:0x01001780")); + assertNotNull(f); + Settags = f.getTags(); + assertEquals(1, tags.size()); + FunctionTag tag = tags.iterator().next(); + assertEquals("Tag2", tag.getName()); + } + + @Test + public void testDiffMergeOverlayFunctions() throws Exception { + mtf.initialize("overlayCalc", new MultiOverlayProgramModifierListener() { + @Override + public void modifyLatest(ProgramDB program) { + // P1 program + super.modifyLatest(program); + try { + Listing listing = program.getListing(); + Function f = listing.getFunctionAt(addr(program, "OtherOverlay:0x01001680")); + f.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + // P2 program + super.modifyPrivate(program); + try { + Listing listing = program.getListing(); + Function f = listing.getFunctionAt(addr(program, "SomeOverlay:0x01001780")); + f.setCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + }); + + p1 = mtf.getResultProgram(); + p2 = mtf.getPrivateProgram(); + + programDiff = new ProgramDiff(p1, p2); + AddressSet as = new AddressSet(); + as.addRange(addr(p1, "SomeOverlay::01001780"), addr(p1, "SomeOverlay::01001780")); + as.addRange(addr(p1, "OtherOverlay::01001680"), addr(p1, "OtherOverlay::01001680")); + programDiff.setFilter(new ProgramDiffFilter(ProgramDiffFilter.FUNCTION_DIFFS)); + assertEquals(as, programDiff.getDifferences(programDiff.getFilter(), null)); + + ProgramMergeManager programMerge = new ProgramMergeManager(p1, p2, TaskMonitor.DUMMY); + programMerge.setDiffFilter(new ProgramDiffFilter(ProgramDiffFilter.FUNCTION_DIFFS)); + programMerge.setMergeFilter( + new ProgramMergeFilter(ProgramMergeFilter.FUNCTIONS, ProgramMergeFilter.REPLACE)); + assertEquals(as, programMerge.getFilteredDifferences()); + + p1.withTransaction("merge", () -> programMerge.merge(as, TaskMonitor.DUMMY)); + assertEquals(new AddressSet(), programMerge.getFilteredDifferences()); + + Listing listing1 = p1.getListing(); + assertNotNull(listing1.getFunctionAt(addr(p1, "OtherOverlay:0x01001680"))); + assertNotNull(listing1.getFunctionAt(addr(p1, "SomeOverlay:0x01001780"))); + } + + @Test + public void testDiffMergeOverlayLabels() throws Exception { + mtf.initialize("overlayCalc", new MultiOverlayProgramModifierListener() { + @Override + public void modifyLatest(ProgramDB program) { + // P1 program + super.modifyLatest(program); + try { + SymbolTable st = program.getSymbolTable(); + st.createLabel(addr(program, "SomeOverlay::01001630"), "OVL1630", + SourceType.USER_DEFINED); + st.createLabel(addr(program, "OtherOverlay::01001866"), "OVL1866", + SourceType.USER_DEFINED); + + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + // P2 program + super.modifyPrivate(program); + try { + SymbolTable st = program.getSymbolTable(); + st.createLabel(addr(program, "SomeOverlay::01001889"), "OVL1889", + SourceType.USER_DEFINED); + st.createLabel(addr(program, "OtherOverlay::01001646"), "OVL1646", + SourceType.USER_DEFINED); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + }); + + p1 = mtf.getResultProgram(); + p2 = mtf.getPrivateProgram(); + + programDiff = new ProgramDiff(p1, p2); + final AddressSet as = new AddressSet(); + as.addRange(addr(p1, "SomeOverlay::01001630"), addr(p1, "SomeOverlay::01001630")); + as.addRange(addr(p1, "SomeOverlay::01001889"), addr(p1, "SomeOverlay::01001889")); + as.addRange(addr(p1, "OtherOverlay::01001646"), addr(p1, "OtherOverlay::01001646")); + as.addRange(addr(p1, "OtherOverlay::01001866"), addr(p1, "OtherOverlay::01001866")); + programDiff.setFilter(new ProgramDiffFilter(ProgramDiffFilter.SYMBOL_DIFFS)); + assertEquals(as, programDiff.getDifferences(programDiff.getFilter(), null)); + + ProgramMergeManager programMerge = new ProgramMergeManager(p1, p2, TaskMonitor.DUMMY); + programMerge.setDiffFilter(new ProgramDiffFilter(ProgramDiffFilter.SYMBOL_DIFFS)); + programMerge.setMergeFilter( + new ProgramMergeFilter(ProgramMergeFilter.SYMBOLS | ProgramMergeFilter.PRIMARY_SYMBOL, + ProgramMergeFilter.REPLACE)); + assertEquals(as, programMerge.getFilteredDifferences()); + + p1.withTransaction("merge", () -> programMerge.merge(as, TaskMonitor.DUMMY)); + AddressSet as2 = new AddressSet(); + // Symbol removal not handled - only replace or add + as2.addRange(addr(p1, "OtherOverlay::01001866"), addr(p1, "OtherOverlay::01001866")); + assertEquals(as2, programMerge.getFilteredDifferences()); + + SymbolTable st1 = p1.getSymbolTable(); + assertNotNull(st1.getSymbol("OVL1889", addr(p1, "SomeOverlay::01001889"), null)); + assertNotNull(st1.getSymbol("OVL1646", addr(p1, "OtherOverlay::01001646"), null)); + } + +// private void printAddressSet(AddressSetView as) { +// System.out.println("====="); +// for (AddressRange r : as) { +// System.out.println("[" + r.getMinAddress() + "," + r.getMaxAddress() + "]"); +// } +// System.out.println("-----"); +// } + + private class MultiOverlayProgramModifierListener implements ProgramModifierListener { + + @Override + public void modifyLatest(ProgramDB program) { + // P1 program + try { + program.getMemory() + .createInitializedBlock("SomeOverlay", addr(program, "0x01001630"), 0x200, + (byte) 0, TaskMonitor.DUMMY, true); + program.getMemory() + .createInitializedBlock("OtherOverlay", addr(program, "0x01001630"), 0x300, + (byte) 0, TaskMonitor.DUMMY, true); + + initProgramCommon(program); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + + @Override + public void modifyPrivate(ProgramDB program) { + // P2 program + try { + program.getMemory() + .createInitializedBlock("OtherOverlay", addr(program, "0x01001630"), 0x200, + (byte) 0, TaskMonitor.DUMMY, true); + program.getMemory() + .createInitializedBlock("SomeOverlay", addr(program, "0x01001630"), 0x300, + (byte) 0, TaskMonitor.DUMMY, true); + + initProgramCommon(program); + } + catch (Exception e) { + Assert.fail(e.getMessage()); + } + } + } + + private static void initProgramCommon(Program program) throws Exception { + Listing listing = program.getListing(); + + Address a = addr(program, "OtherOverlay:0x01001680"); + listing.createFunction("oFunc", a, new AddressSet(a, a), SourceType.USER_DEFINED); + + a = addr(program, "SomeOverlay:0x01001780"); + listing.createFunction("sFunc", a, new AddressSet(a, a), SourceType.USER_DEFINED); + + } + +} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramMerge2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramMerge2Test.java index 5652ff4e4a..07f42c3a8a 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramMerge2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/util/ProgramMerge2Test.java @@ -29,8 +29,7 @@ import org.junit.*; import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.program.database.*; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSet; +import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.listing.*; @@ -3221,6 +3220,8 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { SourceType.USER_DEFINED); st.createLabel(addr(program, "Foo:0x01000180"), "Other0180", globalNamespace, SourceType.USER_DEFINED); + // TODO: No CodeUnit at Foo:0x01000200 - outside memory block + // Should symbols be handled by Diff/Merge outside of memory blocks? st.createLabel(addr(program, "Foo:0x01000200"), "Other0200", globalNamespace, SourceType.USER_DEFINED); } @@ -3238,16 +3239,32 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { try { programMerge = new ProgramMergeManager(p1, p2, null, TaskMonitor.DUMMY); - programMerge.setDiffFilter(new ProgramDiffFilter( - ProgramDiffFilter.SYMBOL_DIFFS | ProgramDiffFilter.FUNCTION_DIFFS)); - programMerge.setMergeFilter( - new ProgramMergeFilter(ProgramMergeFilter.FUNCTIONS, ProgramMergeFilter.REPLACE)); - // Only program1's symbol Diffs are found since program2's overlay is not compatible with program1. + programMerge.setDiffFilter(new ProgramDiffFilter(ProgramDiffFilter.SYMBOL_DIFFS)); + + AddressSetView diffSet = programMerge.getFilteredDifferences(); + AddressSet expectedDiffs = new AddressSet(); expectedDiffs.addRange(addr(p1, "Foo:0x01000030"), addr(p1, "Foo:0x01000030")); expectedDiffs.addRange(addr(p1, "Foo:0x01000079"), addr(p1, "Foo:0x01000079")); - expectedDiffs.addRange(addr(p1, "Foo:0x0100017f"), addr(p1, "Foo:0x0100017f")); - assertEquals(expectedDiffs, programMerge.getFilteredDifferences()); + expectedDiffs.addRange(addr(p1, "Foo:0x01000080"), addr(p1, "Foo:0x01000080")); + expectedDiffs.addRange(addr(p1, "Foo:0x0100017f"), addr(p1, "Foo:0x01000180")); + assertEquals(expectedDiffs, diffSet); + + programMerge.setMergeFilter(new ProgramMergeFilter( + ProgramMergeFilter.SYMBOLS | ProgramMergeFilter.PRIMARY_SYMBOL, + ProgramMergeFilter.REPLACE)); + + // must intersect diff set with p1 memory to avoid skipping merge + programMerge.merge(expectedDiffs.intersect(p1.getMemory()), TaskMonitor.DUMMY); + + diffSet = programMerge.getFilteredDifferences(); + expectedDiffs = new AddressSet(); + expectedDiffs.addRange(addr(p1, "Foo:0x01000030"), addr(p1, "Foo:0x01000030")); + expectedDiffs.addRange(addr(p1, "Foo:0x01000079"), addr(p1, "Foo:0x01000079")); + // Foo:0x01000180 not applied to p1 since outside defined memory block + expectedDiffs.addRange(addr(p1, "Foo:0x01000180"), addr(p1, "Foo:0x01000180")); + assertEquals(expectedDiffs, diffSet); + commit = true; } finally { @@ -4395,6 +4412,8 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { AddressSet expectedDiffs = new AddressSet(); expectedDiffs.addRange(addr(p1, "SomeOverlay::01001630"), addr(p1, "SomeOverlay::01001630")); + expectedDiffs.addRange(addr(p1, "SomeOverlay::01001889"), + addr(p1, "SomeOverlay::01001889")); expectedDiffs.addRange(addr(p1, "OtherOverlay::01001646"), addr(p1, "OtherOverlay::01001646")); expectedDiffs.addRange(addr(p1, "OtherOverlay::01001866"), @@ -4426,7 +4445,7 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { assertEquals("OVL1630", symbols[0].getName()); symbols = resultSymTab.getSymbols(addr(p1, "SomeOverlay::01001889")); - assertEquals(0, symbols.length); // Not part of the merge set. + assertEquals(1, symbols.length); symbols = resultSymTab.getSymbols(addr(p1, "OtherOverlay::01001646")); assertEquals(1, symbols.length); @@ -4502,6 +4521,8 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { AddressSet expectedDiffs = new AddressSet(); expectedDiffs.addRange(addr(p1, "SomeOverlay::01001630"), addr(p1, "SomeOverlay::01001630")); + expectedDiffs.addRange(addr(p1, "SomeOverlay::01001889"), + addr(p1, "SomeOverlay::01001889")); expectedDiffs.addRange(addr(p1, "OtherOverlay::01001646"), addr(p1, "OtherOverlay::01001646")); expectedDiffs.addRange(addr(p1, "OtherOverlay::01001866"), @@ -4530,7 +4551,7 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { assertEquals(0, symbols.length); symbols = resultSymTab.getSymbols(addr(p1, "SomeOverlay::01001889")); - assertEquals(0, symbols.length); // Not part of the merge set. + assertEquals(1, symbols.length); symbols = resultSymTab.getSymbols(addr(p1, "OtherOverlay::01001646")); assertEquals(1, symbols.length); @@ -4649,9 +4670,11 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { // P1 program SymbolTable st = program.getSymbolTable(); try { + // SomeOverlay 0x01001630 - 0x0100182f program.getMemory() .createInitializedBlock("SomeOverlay", addr(program, "0x01001630"), 0x200, (byte) 0, TaskMonitor.DUMMY, true); + // OtherOverlay 0x01001630 - 0x0100192f program.getMemory() .createInitializedBlock("OtherOverlay", addr(program, "0x01001630"), 0x300, (byte) 0, TaskMonitor.DUMMY, true); @@ -4712,13 +4735,30 @@ public class ProgramMerge2Test extends AbstractGhidraHeadedIntegrationTest { addr(p1, "OtherOverlay::01001646")); assertEquals(expectedDiffs, programMerge.getFilteredDifferences()); + SymbolTable st1 = p1.getSymbolTable(); + Address a1 = addr(p1, "SomeOverlay::01001630"); + Address a2 = addr(p1, "OtherOverlay::01001646"); + System.out.println("Before:"); + for (Symbol s : st1.getSymbols(a1)) { + System.out.println(a1 + " " + s.getName()); + } + for (Symbol s : st1.getSymbols(a2)) { + System.out.println(a2 + " " + s.getName()); + } + programMerge.setMergeFilter( new ProgramMergeFilter(ProgramMergeFilter.SYMBOLS, ProgramMergeFilter.REPLACE)); programMerge.merge(expectedDiffs, TaskMonitor.DUMMY); - AddressSet expectedPostMergeDiffs = new AddressSet(); - assertEquals(expectedPostMergeDiffs, programMerge.getFilteredDifferences()); + System.out.println("After:"); + for (Symbol s : st1.getSymbols(a1)) { + System.out.println(a1 + " " + s.getName()); + } + for (Symbol s : st1.getSymbols(a2)) { + System.out.println(a2 + " " + s.getName()); + } + AddressSet expectedPostMergeDiffs = new AddressSet(); assertEquals(expectedPostMergeDiffs, programMerge.getFilteredDifferences()); commit = true; } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java index 233be23748..36c4fb80f5 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java @@ -136,12 +136,13 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest { assertNotNull(block); assertEquals(100, block.getSize()); AddressSpace space = x08.getAddressFactory().getAddressSpace(".testBit"); - assertNotNull(space); + assertTrue(space instanceof OverlayAddressSpace); + OverlayAddressSpace ospace = (OverlayAddressSpace) space; assertTrue(space.isOverlaySpace()); assertEquals(space.getAddress(0x3000), block.getStart()); assertEquals(space.getAddress(0x3063), block.getEnd()); - assertEquals(block.getStart(), space.getMinAddress()); - assertEquals(block.getEnd(), space.getMaxAddress()); + assertEquals(block.getStart(), ospace.getOverlayAddressSet().getMinAddress()); + assertEquals(block.getEnd(), ospace.getOverlayAddressSet().getMaxAddress()); MemoryBlockSourceInfo info = block.getSourceInfos().get(0); AddressRange mappedRange = info.getMappedRange().get(); assertEquals(13, mappedRange.getLength()); @@ -198,12 +199,13 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest { assertNotNull(block); assertEquals(100, block.getSize()); AddressSpace space = x08.getAddressFactory().getAddressSpace(".testByte"); - assertNotNull(space); + assertTrue(space instanceof OverlayAddressSpace); + OverlayAddressSpace ospace = (OverlayAddressSpace) space; assertTrue(space.isOverlaySpace()); assertEquals(space.getAddress(0x3000), block.getStart()); assertEquals(space.getAddress(0x3063), block.getEnd()); - assertEquals(block.getStart(), space.getMinAddress()); - assertEquals(block.getEnd(), space.getMaxAddress()); + assertEquals(block.getStart(), ospace.getOverlayAddressSet().getMinAddress()); + assertEquals(block.getEnd(), ospace.getOverlayAddressSet().getMaxAddress()); MemoryBlockSourceInfo info = block.getSourceInfos().get(0); AddressRange mappedRange = info.getMappedRange().get(); assertEquals(100, mappedRange.getLength()); @@ -226,12 +228,13 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest { assertNotNull(block); assertEquals(100, block.getSize()); AddressSpace space = x08.getAddressFactory().getAddressSpace(".testByte"); - assertNotNull(space); + assertTrue(space instanceof OverlayAddressSpace); + OverlayAddressSpace ospace = (OverlayAddressSpace) space; assertTrue(space.isOverlaySpace()); assertEquals(space.getAddress(0x3000), block.getStart()); assertEquals(space.getAddress(0x3063), block.getEnd()); - assertEquals(block.getStart(), space.getMinAddress()); - assertEquals(block.getEnd(), space.getMaxAddress()); + assertEquals(block.getStart(), ospace.getOverlayAddressSet().getMinAddress()); + assertEquals(block.getEnd(), ospace.getOverlayAddressSet().getMaxAddress()); MemoryBlockSourceInfo info = block.getSourceInfos().get(0); AddressRange mappedRange = info.getMappedRange().get(); assertEquals(198, mappedRange.getLength()); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramAddressFactoryOverlayTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramAddressFactoryOverlayTest.java new file mode 100644 index 0000000000..e216d62b0f --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramAddressFactoryOverlayTest.java @@ -0,0 +1,117 @@ +/* ### + * 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; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGenericTest; +import ghidra.program.model.address.AddressSpace; + +public class ProgramAddressFactoryOverlayTest extends AbstractGenericTest { + + private ProgramDB p; + ProgramAddressFactory factory; + private AddressSpace defaultSpace; + + public ProgramAddressFactoryOverlayTest() { + super(); + } + + @Before + public void setUp() throws Exception { + ProgramBuilder builder = new ProgramBuilder("Test", ProgramBuilder._TOY); + p = builder.getProgram(); + factory = p.getAddressFactory(); + defaultSpace = p.getAddressFactory().getDefaultAddressSpace(); + p.addConsumer(this); + builder.dispose(); + + p.withTransaction("Add Overlays", () -> { + p.createOverlaySpace("A", defaultSpace); + p.createOverlaySpace("B", defaultSpace); + p.createOverlaySpace("C", defaultSpace); + }); + } + + @After + public void tearDown() throws Exception { + p.release(this); + } + + private int getSpaceId(String spaceName) { + AddressSpace space = factory.getAddressSpace(spaceName); + assertNotNull("Space " + spaceName + " not found", space); + return System.identityHashCode(space); + } + + @Test + public void testOverlayRename() throws Exception { + + AddressSpace aSpace = factory.getAddressSpace("A"); + int aId = System.identityHashCode(aSpace); + AddressSpace bSpace = factory.getAddressSpace("B"); + int bId = System.identityHashCode(bSpace); + AddressSpace cSpace = factory.getAddressSpace("C"); + int cId = System.identityHashCode(cSpace); + + // Perform extensive renames within single transaction + p.withTransaction("Add Overlays", () -> { + p.renameOverlaySpace("C", "Ctmp"); + p.renameOverlaySpace("A", "C"); + p.renameOverlaySpace("B", "A"); + p.renameOverlaySpace("Ctmp", "B"); + + p.createOverlaySpace("D", defaultSpace); + }); + + assertEquals(aId, getSpaceId("C")); + assertEquals(bId, getSpaceId("A")); + assertEquals(cId, getSpaceId("B")); + + assertEquals("C", aSpace.getName()); + assertEquals("A", bSpace.getName()); + assertEquals("B", cSpace.getName()); + + assertNotNull(factory.getAddressSpace("D")); + + p.undo(); + + assertEquals(aId, getSpaceId("A")); + assertEquals(bId, getSpaceId("B")); + assertEquals(cId, getSpaceId("C")); + + assertEquals("A", aSpace.getName()); + assertEquals("B", bSpace.getName()); + assertEquals("C", cSpace.getName()); + + assertNull(factory.getAddressSpace("D")); + + p.redo(); + + assertEquals(aId, getSpaceId("C")); + assertEquals(bId, getSpaceId("A")); + assertEquals(cId, getSpaceId("B")); + + assertEquals("C", aSpace.getName()); + assertEquals("A", bSpace.getName()); + assertEquals("B", cSpace.getName()); + + assertNotNull(factory.getAddressSpace("D")); + + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramOverlaysTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramOverlaysTest.java index 11a54356c7..3452003c70 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramOverlaysTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/database/ProgramOverlaysTest.java @@ -71,8 +71,9 @@ public class ProgramOverlaysTest extends AbstractGenericTest { AddressSpace space = p.getAddressFactory().getAddressSpace("OV1"); assertNotNull(space); memory.setBytes(space.getAddress(0x100), fillB); - p.getReferenceManager().addMemoryReference(addr(p, "0x1001003"), addr(p, "OV1:0x100"), - RefType.DATA, SourceType.USER_DEFINED, 0); + p.getReferenceManager() + .addMemoryReference(addr(p, "0x1001003"), addr(p, "OV1:0x100"), RefType.DATA, + SourceType.USER_DEFINED, 0); p.endTransaction(id, true); return space; @@ -140,18 +141,18 @@ public class ProgramOverlaysTest extends AbstractGenericTest { assertNotNull(block); int id = p.startTransaction(""); - block.setName("BOB"); + block.setName("BOB"); // does not affect name of overlay which was created with block creation p.endTransaction(id, true); assertEquals("BOB", block.getName()); assertEquals(block, memory.getBlock("BOB")); - assertEquals("BOB", space.getName()); - assertEquals(space, p.getAddressFactory().getAddressSpace("BOB")); + assertEquals("OV1", space.getName()); + assertEquals(space, p.getAddressFactory().getAddressSpace("OV1")); Reference[] refs = p.getReferenceManager().getReferencesFrom(addr(p, "0x1001003")); assertEquals(1, refs.length); - assertEquals("BOB::00000100", refs[0].getToAddress().toString()); + assertEquals("OV1::00000100", refs[0].getToAddress().toString()); } diff --git a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java index 77144e063d..84a4e146ef 100644 --- a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java +++ b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java @@ -300,9 +300,8 @@ public class ProgramDiffPlugin extends ProgramPlugin DiffUtility.getCompatibleAddressSet(p1AddressSet, secondaryDiffProgram); AddressIndexMap p2IndexMap = new AddressIndexMap(p1AddressSetAsP2); markerManager.getOverviewProvider().setProgram(secondaryDiffProgram, p2IndexMap); - fp.setBackgroundColorModel( - new MarkerServiceBackgroundColorModel(markerManager, secondaryDiffProgram, - p2IndexMap)); + fp.setBackgroundColorModel(new MarkerServiceBackgroundColorModel(markerManager, + secondaryDiffProgram, p2IndexMap)); currentSelection = previousP1Selection; p2DiffHighlight = previousP2DiffHighlight; @@ -1062,9 +1061,7 @@ public class ProgramDiffPlugin extends ProgramPlugin setProgram2Selection(p2Selection); clearDiff(); if (secondaryDiffProgram != null) { - Iterator iter = bookmarkMap.values().iterator(); - while (iter.hasNext()) { - BookmarkNavigator nav = iter.next(); + for (BookmarkNavigator nav : bookmarkMap.values()) { nav.dispose(); } bookmarkMap.clear(); @@ -1140,6 +1137,10 @@ public class ProgramDiffPlugin extends ProgramPlugin } private void selectAndOpenProgram2() { + if (checkStaleOverlays(currentProgram)) { + return; + } + final OpenVersionedFileDialog dialog = getOpenVersionedFileDialog(); List openProgramList = getOpenProgramList(); @@ -1161,6 +1162,30 @@ public class ProgramDiffPlugin extends ProgramPlugin dialog.showComponent(); } + private boolean hasStaleOverlays(Program p) { + return p.getAddressFactory().hasStaleOverlayCondition(); + } + + /** + * Check program's address factory for stale overlay condition. + * @param p program to check + * @return true if user chose to cancel operation due to stale overlays + */ + private boolean checkStaleOverlays(Program p) { + if (!hasStaleOverlays(p)) { + return false; + } + + String usage = (p == currentProgram) ? "current" : "selected"; + + int rc = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, "Diff Warning", + "The " + usage + + " program has recently had an overlay space renamed which may prevent an accurate Diff.\n" + + "It is recommended that the program be closed and re-opened before performing Diff.", + "Continue"); + return (rc != OptionDialog.OPTION_ONE); + } + private OpenVersionedFileDialog getOpenVersionedFileDialog() { if (openVersionedFileDialog != null) { @@ -1567,6 +1592,11 @@ public class ProgramDiffPlugin extends ProgramPlugin newProgram.release(this); return false; } + + if (!hasStaleOverlays(currentProgram) && checkStaleOverlays(newProgram)) { + return false; + } + ProgramMemoryComparator programMemoryComparator = null; try { programMemoryComparator = new ProgramMemoryComparator(currentProgram, newProgram); @@ -1629,14 +1659,19 @@ public class ProgramDiffPlugin extends ProgramPlugin secondaryDiffProgram); actionManager.secondProgramOpened(); actionManager.addActions(); - diffListingPanel.goTo(currentLocation); MarkerSet cursorMarkers = getCursorMarkers(); Address currentP2Address = currentLocation.getAddress(); + ProgramLocation current2PLocation = currentLocation; if (currentLocation.getProgram() != secondaryDiffProgram) { // Make sure address is from P2. currentP2Address = SimpleDiffUtility.getCompatibleAddress(currentLocation.getProgram(), currentLocation.getAddress(), secondaryDiffProgram); + if (currentP2Address != null) { + current2PLocation = ProgramLocation.getTranslatedCopy(currentLocation, + secondaryDiffProgram, currentP2Address); + } } + diffListingPanel.goTo(current2PLocation); if (currentP2Address != null) { cursorMarkers.setAddressSet(new AddressSet(currentP2Address)); } @@ -1812,10 +1847,11 @@ public class ProgramDiffPlugin extends ProgramPlugin return; } - ListingField lf = (ListingField) field; - FieldFactory factory = lf.getFieldFactory(); - ProgramLocation pLoc = - factory.getProgramLocation(location.getRow(), location.getCol(), lf); + ProgramLocation pLoc = null; + if (field instanceof ListingField lf) { + FieldFactory factory = lf.getFieldFactory(); + pLoc = factory.getProgramLocation(location.getRow(), location.getCol(), lf); + } // if clicked in dummy field, try and find the address for the white space. if (pLoc == null) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/MultiRowLayout.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/MultiRowLayout.java index 41deea1f8a..0acd14db69 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/MultiRowLayout.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/MultiRowLayout.java @@ -17,10 +17,13 @@ package docking.widgets.fieldpanel.support; import java.awt.Graphics; import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; import javax.swing.JComponent; import docking.widgets.fieldpanel.Layout; +import docking.widgets.fieldpanel.field.EmptyTextField; import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.internal.*; @@ -54,9 +57,9 @@ public class MultiRowLayout implements Layout { this.indexSize = indexSize; this.layouts = layouts; int height = 0; - for (int i = 0; i < layouts.length; i++) { - numFields += layouts[i].getNumFields(); - height += layouts[i].getHeight(); + for (RowLayout layout : layouts) { + numFields += layout.getNumFields(); + height += layout.getHeight(); } heightAbove = layouts[0].getHeightAbove(); heightBelow = height - heightAbove; @@ -313,65 +316,6 @@ public class MultiRowLayout implements Layout { return 0; } - /** - * synchronize
adjusts this layout and the layout passed as a - * parameter by adding and resizing rows as necessary to make their - * vertical layouts match. - * - * @param layout the other multi-row layout that is to be synchronized with. - * @param dummyField empty field used for spacing. - */ - public void align(MultiRowLayout layout, Field dummyField) { - int myNumRows = layouts.length; - int otherNumRows = layout.layouts.length; - int myRow = 0; - int otherRow = 0; - int myHeight = layouts[myRow].getHeight(); - int otherHeight = layout.layouts[otherRow].getHeight(); - primaryOffset = -1; - - while (myRow < myNumRows && otherRow < otherNumRows) { - int myId = layouts[myRow].getRowID(); - int otherId = layout.layouts[otherRow].getRowID(); - - if (myId < otherId) { - layout.layouts[otherRow].insertSpaceAbove(myHeight); - if (++myRow < myNumRows) - myHeight = layouts[myRow].getHeight(); - } - else if (myId > otherId) { - layouts[myRow].insertSpaceAbove(otherHeight); - if (++otherRow < otherNumRows) - otherHeight = layout.layouts[otherRow].getHeight(); - } - else { - int myEnd = layouts[myRow].getHeight(); - int otherEnd = layout.layouts[otherRow].getHeight(); - if (myEnd > otherEnd) { - layout.layouts[otherRow].insertSpaceBelow(myEnd - otherEnd); - } - else if (otherEnd > myEnd) { - layouts[myRow].insertSpaceBelow(otherEnd - myEnd); - } - if (++myRow < myNumRows) - myHeight = layouts[myRow].getHeight(); - if (++otherRow < otherNumRows) - otherHeight = layout.layouts[otherRow].getHeight(); - } - } - while (myRow < myNumRows) { - layout.layouts[otherNumRows - 1].insertSpaceBelow(myHeight); - if (++myRow < myNumRows) - myHeight = layouts[myRow].getHeight(); - } - - while (otherRow < otherNumRows) { - layouts[myNumRows - 1].insertSpaceBelow(otherHeight); - if (++otherRow < otherNumRows) - otherHeight = layout.layouts[otherRow].getHeight(); - } - } - @Override public int getPrimaryOffset() { if (primaryOffset == -1) { @@ -382,11 +326,11 @@ public class MultiRowLayout implements Layout { private void findPrimaryOffset() { primaryOffset = 0; - for (int i = 0; i < layouts.length; i++) { - if (layouts[i].isPrimary()) { + for (RowLayout layout : layouts) { + if (layout.isPrimary()) { return; } - primaryOffset += layouts[i].getHeight(); + primaryOffset += layout.getHeight(); } primaryOffset = 0; } @@ -426,17 +370,17 @@ public class MultiRowLayout implements Layout { public void fillHeights(int[] rowHeights) { int lastId = -1; int height = 0; - for (int i = 0; i < layouts.length; i++) { - int id = layouts[i].getRowID(); + for (RowLayout layout : layouts) { + int id = layout.getRowID(); if (id == lastId) { - height += layouts[i].getHeight(); + height += layout.getHeight(); } else { if (lastId >= 0) { rowHeights[lastId] = Math.max(rowHeights[lastId], height); } lastId = id; - height = layouts[i].getHeight(); + height = layout.getHeight(); } } if (lastId >= 0) { @@ -444,6 +388,17 @@ public class MultiRowLayout implements Layout { } } + private class EmptyRowLayout extends RowLayout { + + public EmptyRowLayout(int rowId, int height) { + super(getEmptyFields(height), rowId); + } + + private static Field[] getEmptyFields(int height) { + return new Field[] { new EmptyTextField(height, 0, 0, 0) }; + } + } + /** * Aligns the heights in this MultiRowLayout to match those in the give row heights array. * Extra is inserted to align the rows in this layout to match those specified in the given array. @@ -451,36 +406,39 @@ public class MultiRowLayout implements Layout { */ public void align(int[] rowHeights) { int row = 0; - int totalAbove = 0; - int lastId = -1; - for (int i = 0; i < layouts.length; i++) { - int id = layouts[i].getRowID(); - if (id != lastId) { - for (; row < id; row++) { - totalAbove += rowHeights[row]; + ListupdatedRows = new ArrayList<>(); + for (RowLayout layout : layouts) { + int id = layout.getRowID(); + for (; row <= id; row++) { + if (rowHeights[row] == 0) { + continue; + } + if (row == id) { + layout.insertSpaceBelow(rowHeights[id] - layout.getHeight()); + updatedRows.add(layout); + } + else { + updatedRows.add(new EmptyRowLayout(row, rowHeights[row])); } - int origHeight = layouts[i].getHeight(); - layouts[i].insertSpaceAbove(totalAbove); - totalAbove = rowHeights[id] - origHeight; - lastId = id; - row++; - } - else { - totalAbove -= layouts[i].getHeight(); } } - int totalBelow = totalAbove; + for (; row < rowHeights.length; row++) { - totalBelow += rowHeights[row]; + if (rowHeights[row] != 0) { + updatedRows.add(new EmptyRowLayout(row, rowHeights[row])); + } } - insertSpaceBelow(totalBelow); int height = 0; + layouts = new RowLayout[updatedRows.size()]; for (int i = 0; i < layouts.length; i++) { + layouts[i] = updatedRows.get(i); height += layouts[i].getHeight(); } + heightAbove = layouts[0].getHeightAbove(); heightBelow = height - heightAbove; + buildOffsets(); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlayRegionSupplier.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlayRegionSupplier.java new file mode 100644 index 0000000000..40a167e21e --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlayRegionSupplier.java @@ -0,0 +1,36 @@ +/* ### + * 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; + +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.address.OverlayAddressSpace; + +/** + * {@link OverlayRegionSupplier} provides a callback mechanism which allows a + * {@link ProgramOverlayAddressSpace} to identify defined memory regions within its + * space so that it may properly implement the {@link OverlayAddressSpace#contains(long)} + * method. + */ +public interface OverlayRegionSupplier { + + /** + * Get the set of memory address defined within the specified overlay space. + * @param overlaySpace overlay address space + * @return set of memory address defined within the specified overlay space or null + */ + AddressSetView getOverlayAddressSet(OverlayAddressSpace overlaySpace); + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java deleted file mode 100644 index d0a7aced1b..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceAdapterDB.java +++ /dev/null @@ -1,224 +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.database; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import db.*; -import ghidra.program.model.address.AddressSpace; -import ghidra.program.model.address.OverlayAddressSpace; -import ghidra.program.model.lang.Language; -import ghidra.program.util.LanguageTranslator; -import ghidra.util.exception.DuplicateNameException; - -class OverlaySpaceAdapterDB { - private static String TABLE_NAME = "Overlay Spaces"; - static final Schema SCHEMA = new Schema(0, "ID", - new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, - LongField.INSTANCE }, - new String[] { "Overlay Space", "Template Space", "Minimum Offset", "Maximum Offset" }); - - private static final int OV_SPACE_NAME_COL = 0; - private static final int OV_SPACE_BASE_COL = 1; - private static final int OV_MIN_OFFSET_COL = 2; - private static final int OV_MAX_OFFSET_COL = 3; - - DBHandle db; - - OverlaySpaceAdapterDB(DBHandle dbHandle) { - this.db = dbHandle; - } - - /** - * Adds existing overlay spaces to the factory. - * @param factory the factory to add overlay spaces to - * @throws IOException - */ - void initializeOverlaySpaces(ProgramAddressFactory factory) throws IOException { - Table table = db.getTable(TABLE_NAME); - if (table != null) { - RecordIterator it = table.iterator(); - while (it.hasNext()) { - DBRecord rec = it.next(); - String spaceName = rec.getString(OV_SPACE_NAME_COL); - String templateSpaceName = rec.getString(OV_SPACE_BASE_COL); - long minOffset = rec.getLongValue(OV_MIN_OFFSET_COL); - long maxOffset = rec.getLongValue(OV_MAX_OFFSET_COL); - AddressSpace space = factory.getAddressSpace(templateSpaceName); - try { - OverlayAddressSpace sp = - factory.addOverlayAddressSpace(spaceName, true, space, minOffset, maxOffset); - sp.setDatabaseKey(rec.getKey()); - } - catch (IllegalArgumentException e) { - throw new RuntimeException( - "Unexpected error initializing overlay address spaces", e); - } - } - } - } - - /** - * Adds a new overlay space to the database - * @param id the name of the new overlay space to add - * @param space the template space used to create the new space. - * @param minOffset the lowest offset in this overlay. - * @param maxOffset the highest offset in this overlay. - * @throws IOException - */ - void addOverlaySpace(OverlayAddressSpace ovSpace) throws IOException { - Table table = db.getTable(TABLE_NAME); - if (table == null) { - table = db.createTable(TABLE_NAME, SCHEMA); - } - DBRecord rec = SCHEMA.createRecord(table.getKey()); - rec.setString(0, ovSpace.getName()); - rec.setString(1, ovSpace.getOverlayedSpace().getName()); - rec.setLongValue(OV_MIN_OFFSET_COL, ovSpace.getMinOffset()); - rec.setLongValue(OV_MAX_OFFSET_COL, ovSpace.getMaxOffset()); - table.putRecord(rec); - ovSpace.setDatabaseKey(rec.getKey()); - } - - /** - * Removes the named space from the database - * @param name the name of the overlay space to remove - * @throws IOException - */ - void removeOverlaySpace(String name) throws IOException { - Table table = db.getTable(TABLE_NAME); - if (table != null) { - RecordIterator it = table.iterator(); - while (it.hasNext()) { - DBRecord rec = it.next(); - String spaceName = rec.getString(0); - if (name.equals(spaceName)) { - it.delete(); - return; - } - } - } - } - - void updateOverlaySpaces(ProgramAddressFactory factory) throws IOException { - Map map = new HashMap<>(); - for (AddressSpace space : factory.getAllAddressSpaces()) { - if (space instanceof OverlayAddressSpace) { - OverlayAddressSpace os = (OverlayAddressSpace) space; - map.put(os.getDatabaseKey(), os); - } - } - Table table = db.getTable(TABLE_NAME); - if (table != null) { - RecordIterator it = table.iterator(); - while (it.hasNext()) { - DBRecord rec = it.next(); - OverlayAddressSpace space = map.remove(rec.getKey()); - if (space != null) { - //maxId = Math.max(maxId, space.getUnique()); - String spaceName = rec.getString(OV_SPACE_NAME_COL); - if (!spaceName.equals(space.getName())) { - factory.removeOverlaySpace(space.getName()); - space.setName(rec.getString(OV_SPACE_NAME_COL)); - try { - factory.addOverlayAddressSpace(space); - } - catch (DuplicateNameException e) { - throw new RuntimeException( - "Unexpected error updating overlay address spaces", e); - } - } - } - else { - String spaceName = rec.getString(OV_SPACE_NAME_COL); - long minOffset = rec.getLongValue(OV_MIN_OFFSET_COL); - long maxOffset = rec.getLongValue(OV_MAX_OFFSET_COL); - AddressSpace origSpace = - factory.getAddressSpace(rec.getString(OV_SPACE_BASE_COL)); - try { - space = factory.addOverlayAddressSpace(spaceName, true, origSpace, - minOffset, - maxOffset); - space.setDatabaseKey(rec.getKey()); - } - catch (IllegalArgumentException e) { - throw new RuntimeException( - "Unexpected error updating overlay address spaces", e); - } - } - } - } - for (OverlayAddressSpace space : map.values()) { - factory.removeOverlaySpace(space.getName()); - } - } - - public void renameOverlaySpace(String oldName, String newName) throws IOException { - Table table = db.getTable(TABLE_NAME); - if (table != null) { - RecordIterator it = table.iterator(); - while (it.hasNext()) { - DBRecord rec = it.next(); - String spaceName = rec.getString(0); - if (oldName.equals(spaceName)) { - it.delete(); - rec.setString(0, newName); - table.putRecord(rec); - return; - } - } - } - } - - /** - * Translate overlay address spaces for a new language provider - * and initialize the new addrFactory with the translated overlay spaces. - * All non-overlay address spaces within the address factory should already - * have been mapped to the new language. - * @param newLanguage new language to be used - * @param addrFactory old address factory - * @param translator language translator to assist with mapping of address spaces - * @throws IOException - */ - void setLanguage(Language newLanguage, ProgramAddressFactory addrFactory, - LanguageTranslator translator) throws IOException { - - Table table = db.getTable(TABLE_NAME); - if (table != null) { - RecordIterator it = table.iterator(); - while (it.hasNext()) { - DBRecord rec = it.next(); - String oldUnderlyingSpaceName = rec.getString(OV_SPACE_BASE_COL); - AddressSpace space = addrFactory.getAddressSpace(oldUnderlyingSpaceName); - if (space != null && space.isNonLoadedMemorySpace()) { - // skip overlays associated with non-loaded spaces such as OTHER space - continue; - } - AddressSpace newSpace = translator.getNewAddressSpace(oldUnderlyingSpaceName); - if (newSpace == null) { - throw new IOException( - "Failed to map old address space: " + oldUnderlyingSpaceName); - } - rec.setString(OV_SPACE_BASE_COL, newSpace.getName()); - table.putRecord(rec); - } - } - initializeOverlaySpaces(addrFactory); - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapter.java new file mode 100644 index 0000000000..00ffe5c17c --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapter.java @@ -0,0 +1,223 @@ +/* ### + * 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; + +import java.io.IOException; + +import db.*; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.Language; +import ghidra.program.util.LanguageTranslator; +import ghidra.util.InvalidNameException; +import ghidra.util.exception.*; +import ghidra.util.task.TaskMonitor; + +abstract class OverlaySpaceDBAdapter { + + // TODO: Duplication of address space names must be avoided. There is the possibility of + // of a language change triggering such duplication with an existing overlay space. + // Such a condition is currently unsupported and may cause severe errors. + + static String TABLE_NAME = "Overlay Spaces"; + static final Schema SCHEMA = OverlaySpaceDBAdapterV1.SCHEMA_V1; + static final int OV_SPACE_NAME_COL = OverlaySpaceDBAdapterV1.OV_SPACE_NAME_COL_V1; + static final int OV_SPACE_BASE_COL = OverlaySpaceDBAdapterV1.OV_SPACE_BASE_COL_V1; + + final DBHandle db; + + OverlaySpaceDBAdapter(DBHandle dbHandle) { + this.db = dbHandle; + } + + static OverlaySpaceDBAdapter getOverlaySpaceAdapter(DBHandle dbHandle, int openMode, + TaskMonitor monitor) throws IOException, VersionException, CancelledException { + try { + return new OverlaySpaceDBAdapterV1(dbHandle, openMode); + } + catch (VersionException e) { + if (openMode == DBConstants.UPGRADE) { + return upgrade(dbHandle, findReadOnlyAdapter(dbHandle), monitor); + } + if (e.isUpgradable() && openMode == DBConstants.READ_ONLY) { + return findReadOnlyAdapter(dbHandle); + } + throw e; + } + } + + private static OverlaySpaceDBAdapter findReadOnlyAdapter(DBHandle handle) + throws VersionException { + + try { + return new OverlaySpaceDBAdapterV0(handle); + } + catch (VersionException e1) { + // failed - can't handle whatever version this is trying to open + } + + throw new VersionException(false); + } + + private static OverlaySpaceDBAdapter upgrade(DBHandle dbHandle, + OverlaySpaceDBAdapter oldAdapter, TaskMonitor monitor) + throws VersionException, IOException, CancelledException { + + monitor.setMessage("Upgrading Overlay Table..."); + monitor.initialize(oldAdapter.getRecordCount() * 2); + + DBHandle tmpHandle = dbHandle.getScratchPad(); + + try { + OverlaySpaceDBAdapter tmpAdapter = + new OverlaySpaceDBAdapterV1(tmpHandle, DBConstants.CREATE); + copyRecords(oldAdapter, tmpAdapter, monitor); + + dbHandle.deleteTable(TABLE_NAME); + + OverlaySpaceDBAdapter newAdapter = + new OverlaySpaceDBAdapterV1(dbHandle, DBConstants.CREATE); + copyRecords(tmpAdapter, newAdapter, monitor); + + tmpHandle.deleteTable(TABLE_NAME); + + return newAdapter; + } + finally { + tmpHandle.deleteTable(TABLE_NAME); + } + } + + private static void copyRecords(OverlaySpaceDBAdapter fromAdapter, + OverlaySpaceDBAdapter toAdapter, TaskMonitor monitor) + throws IOException, CancelledException { + + RecordIterator iter = fromAdapter.getOverlayRecords(); + while (iter.hasNext()) { + monitor.checkCancelled(); + DBRecord rec = iter.next(); + toAdapter.updateOverlayRecord(rec); + monitor.incrementProgress(1); + } + } + + final int getRecordCount() { + Table table = db.getTable(TABLE_NAME); + return table != null ? table.getRecordCount() : 0; + } + + /** + * Adds existing overlay spaces to the factory. + * @param factory the program address factory to add overlay spaces to + * @throws IOException if database error occurs + * @throws RuntimeException for various unsupported address space naming conditions + */ + final void initializeOverlaySpaces(ProgramAddressFactory factory) throws IOException { + + RecordIterator it = getOverlayRecords(); + while (it.hasNext()) { + DBRecord rec = it.next(); + try { + String spaceName = rec.getString(OV_SPACE_NAME_COL); + if (factory.getAddressSpace(spaceName) != null) { + throw new DuplicateNameException("Overlay space '" + spaceName + + "' duplicates name of another address space"); + } + + String baseSpaceName = rec.getString(OV_SPACE_BASE_COL); + AddressSpace space = factory.getAddressSpace(baseSpaceName); + if (space == null) { + throw new RuntimeException("Overlay base space '" + baseSpaceName + + "' not found for overlay space '" + spaceName + "'"); + } + factory.addOverlaySpace(rec.getKey(), spaceName, space); + } + catch (Exception e) { + throw new IOException("Unexpected error initializing overlay address spaces", e); + } + } + } + + /** + * Provide overlay space record iterator. Older adapters will must translate records into + * the latest schema format. + * @return overlay space record iterator + * @throws IOException if database error occurs + */ + abstract RecordIterator getOverlayRecords() throws IOException; + + /** + * Update the overlay database table with the specified record + * @param rec overlay record in latest schema format + * @throws IOException if database error occurs + */ + abstract void updateOverlayRecord(DBRecord rec) throws IOException; + + /** + * Create a new overlay address space and associated record + * @param factory program address factory which retains address spaces + * @param overlayName overlay space name (may not contain `:`, space or other non-printable + * characters. + * @param baseSpace underlying physical/base address space which is to be overlayed + * (must not be an overlay space) + * @return new overlay space (without regions defined) + * @throws IOException if database error occurs + * @throws DuplicateNameException if overlay name duplicates another address space name + * @throws InvalidNameException if specified overlay name is invalid + */ + abstract ProgramOverlayAddressSpace createOverlaySpace(ProgramAddressFactory factory, + String overlayName, AddressSpace baseSpace) + throws IOException, DuplicateNameException, InvalidNameException; + + /** + * Removes the named space from the database. Caller is responsible for updating address + * factory. + * @param name the name of the overlay space to remove + * @return true if overlay record updated, false if not found + * @throws IOException if database error occurs + */ + abstract boolean removeOverlaySpace(String name) throws IOException; + + /** + * Rename the overlay space from oldName to newName. Caller is responsible for updating + * address factory and ensuring that newName does not duplicate that of another address space. + * @param oldName old overlay name + * @param newName new overlay name + * @return true if overlay record updated, false if not found + * @throws IOException if database error occurs + */ + abstract boolean renameOverlaySpace(String oldName, String newName) throws IOException; + + /** + * Reconcile overlay spaces following cache invalidation (e.g., undo/redo) + * @param factory program address factory which retains address spaces + * @throws IOException if database error occurs + */ + abstract void updateOverlaySpaces(ProgramAddressFactory factory) throws IOException; + + /** + * Translate overlay address spaces for a new language provider + * and initialize the new addrFactory with the translated overlay spaces. + * All non-overlay address spaces within the address factory should already + * have been mapped to the new language. + * @param newLanguage new language to be used + * @param addrFactory old address factory + * @param translator language translator to assist with mapping of address spaces + * @throws IOException if database error occurs + */ + abstract void setLanguage(Language newLanguage, ProgramAddressFactory addrFactory, + LanguageTranslator translator) throws IOException; + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapterV0.java new file mode 100644 index 0000000000..8feca2b037 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapterV0.java @@ -0,0 +1,114 @@ +/* ### + * 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; + +import java.io.IOException; + +import javax.help.UnsupportedOperationException; + +import db.*; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.Language; +import ghidra.program.util.LanguageTranslator; +import ghidra.util.exception.VersionException; + +class OverlaySpaceDBAdapterV0 extends OverlaySpaceDBAdapter { + + private static final int VERSION = 0; + +/* Do not remove the following commented out schema! It shows the version 0 overlay table schema. */ +// private static final Schema SCHEMA_V0 = new Schema(VERSION, "ID", +// new Field[] { StringField.INSTANCE, StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE }, +// new String[] { "Overlay Space Name", "Base Space Name", "Minimum Offset", "Maximum Offset" }); + + private static final int OV_SPACE_NAME_COL_V0 = 0; + private static final int OV_SPACE_BASE_COL_V0 = 1; + //private static final int OV_MIN_OFFSET_COL_V0 = 2; // OBSOLETE - Ignored + //private static final int OV_MAX_OFFSET_COL_V0 = 3; // OBSOLETE - Ignored + + private Table table; + + OverlaySpaceDBAdapterV0(DBHandle dbHandle) throws VersionException { + super(dbHandle); + table = dbHandle.getTable(TABLE_NAME); + if (table == null) { + // Missing table case is OK but should be handled by latest version + throw new VersionException("Missing Table: " + TABLE_NAME); + } + if (table.getSchema().getVersion() != VERSION) { + throw new VersionException(VersionException.NEWER_VERSION, false); + } + } + + @Override + ProgramOverlayAddressSpace createOverlaySpace(ProgramAddressFactory factory, String blockName, + AddressSpace baseSpace) { + throw new UnsupportedOperationException(); + } + + @Override + boolean removeOverlaySpace(String name) { + throw new UnsupportedOperationException(); + } + + @Override + boolean renameOverlaySpace(String oldName, String newName) { + throw new UnsupportedOperationException(); + } + + @Override + void updateOverlayRecord(DBRecord rec) { + throw new UnsupportedOperationException(); + } + + @Override + void updateOverlaySpaces(ProgramAddressFactory factory) { + throw new UnsupportedOperationException(); + } + + @Override + void setLanguage(Language newLanguage, ProgramAddressFactory addrFactory, + LanguageTranslator translator) { + throw new UnsupportedOperationException(); + } + + @Override + RecordIterator getOverlayRecords() throws IOException { + return new V0ConvertedRecordIterator(table.iterator()); + } + + private DBRecord convertV0Record(DBRecord v0Rec) { + String overlayName = v0Rec.getString(OV_SPACE_NAME_COL_V0); + String baseSpaceName = v0Rec.getString(OV_SPACE_BASE_COL_V0); + + DBRecord rec = SCHEMA.createRecord(v0Rec.getKey()); + rec.setString(OV_SPACE_NAME_COL, overlayName); + rec.setString(OV_SPACE_BASE_COL, baseSpaceName); + return rec; + } + + private class V0ConvertedRecordIterator extends ConvertedRecordIterator { + + V0ConvertedRecordIterator(RecordIterator originalIterator) { + super(originalIterator, false); + } + + @Override + protected DBRecord convertRecord(DBRecord record) { + return convertV0Record(record); + } + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapterV1.java new file mode 100644 index 0000000000..bb6aaf2b06 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/OverlaySpaceDBAdapterV1.java @@ -0,0 +1,249 @@ +/* ### + * 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; + +import java.io.IOException; +import java.util.*; + +import db.*; +import ghidra.program.database.util.EmptyRecordIterator; +import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.lang.Language; +import ghidra.program.util.LanguageTranslator; +import ghidra.util.InvalidNameException; +import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.VersionException; + +class OverlaySpaceDBAdapterV1 extends OverlaySpaceDBAdapter { + + private static final int VERSION = 1; + + static final Schema SCHEMA_V1 = + new Schema(VERSION, "ID", new Field[] { StringField.INSTANCE, StringField.INSTANCE }, + new String[] { "Overlay Space Name", "Base Space Name" }); + + static final int OV_SPACE_NAME_COL_V1 = 0; + static final int OV_SPACE_BASE_COL_V1 = 1; + + OverlaySpaceDBAdapterV1(DBHandle dbHandle, int openMode) throws IOException, VersionException { + super(dbHandle); + + Table table = dbHandle.getTable(TABLE_NAME); + if (openMode == DBConstants.CREATE) { + if (table != null) { + throw new IOException("Table already exists: " + TABLE_NAME); + } + return; // lazy table creation + } + + if (table != null && table.getSchema().getVersion() != VERSION) { + int version = table.getSchema().getVersion(); + if (version < VERSION) { + throw new VersionException(true); + } + throw new VersionException(VersionException.NEWER_VERSION, false); + } + } + + @Override + ProgramOverlayAddressSpace createOverlaySpace(ProgramAddressFactory factory, String spaceName, + AddressSpace baseSpace) + throws IOException, DuplicateNameException, InvalidNameException { + + if (!factory.isValidOverlayBaseSpace(baseSpace)) { + throw new IllegalArgumentException( + "Invalid address space for overlay: " + baseSpace.getName()); + } + + factory.checkValidOverlaySpaceName(spaceName); + + if (factory.getAddressSpace(spaceName) != null) { + throw new DuplicateNameException( + "Overlay space '" + spaceName + "' duplicates name of another address space"); + } + + Table table = db.getTable(TABLE_NAME); + if (table == null) { + table = db.createTable(TABLE_NAME, SCHEMA_V1); + } + DBRecord rec = SCHEMA_V1.createRecord(table.getKey()); + rec.setString(OV_SPACE_NAME_COL_V1, spaceName); + rec.setString(OV_SPACE_BASE_COL_V1, baseSpace.getName()); + table.putRecord(rec); + + return factory.addOverlaySpace(rec.getKey(), spaceName, baseSpace); + } + + @Override + protected RecordIterator getOverlayRecords() throws IOException { + Table table = db.getTable(TABLE_NAME); + if (table == null) { + return EmptyRecordIterator.INSTANCE; + } + return table.iterator(); + } + + @Override + protected void updateOverlayRecord(DBRecord rec) throws IOException { + Table table = db.getTable(TABLE_NAME); + if (table == null) { + table = db.createTable(TABLE_NAME, SCHEMA_V1); + } + table.putRecord(rec); + } + + @Override + boolean removeOverlaySpace(String name) throws IOException { + Table table = db.getTable(TABLE_NAME); + if (table == null) { + return false; + } + + RecordIterator it = table.iterator(); + while (it.hasNext()) { + DBRecord rec = it.next(); + String overlayName = rec.getString(0); + if (name.equals(overlayName)) { + it.delete(); + return true; + } + } + return false; + } + + @Override + boolean renameOverlaySpace(String oldName, String newName) throws IOException { + Table table = db.getTable(TABLE_NAME); + if (table == null) { + return false; + } + + RecordIterator it = table.iterator(); + while (it.hasNext()) { + DBRecord rec = it.next(); + String spaceName = rec.getString(0); + if (oldName.equals(spaceName)) { + it.delete(); + rec.setString(0, newName); + table.putRecord(rec); + return true; + } + } + return false; + } + + @Override + void updateOverlaySpaces(ProgramAddressFactory factory) throws IOException { + + // Perform reconciliation of overlay address spaces while attempting to preserve + // address space instances associated with a given key + + // Put all overlay records into key-based map + Map keyToRecordMap = new HashMap<>(); + Table table = db.getTable(TABLE_NAME); + if (table != null) { + RecordIterator it = table.iterator(); + while (it.hasNext()) { + DBRecord rec = it.next(); + keyToRecordMap.put(rec.getKey(), rec); + } + } + + // Examine existing overlay spaces for removals and renames + List renameList = new ArrayList<>(); + for (AddressSpace space : factory.getAllAddressSpaces()) { + if (space instanceof ProgramOverlayAddressSpace os) { + String name = os.getName(); + DBRecord rec = keyToRecordMap.get(os.getKey()); + if (rec == null || !isCompatibleOverlay(os, rec, factory)) { + // Remove overlay if record does not exist or base space differs + factory.removeOverlaySpace(name); + } + else if (name.equals(rec.getString(OV_SPACE_NAME_COL_V1))) { + keyToRecordMap.remove(os.getKey()); + continue; // no change to space + } + else { + // Add space to map of those that need to be renamed + renameList.add(os); + factory.removeOverlaySpace(name); + } + } + } + + try { + // Handle all renamed overlays which had been temporarily removed from factory + for (ProgramOverlayAddressSpace existingSpace : renameList) { + long key = existingSpace.getKey(); + DBRecord rec = keyToRecordMap.get(key); + existingSpace.setName(rec.getString(OV_SPACE_NAME_COL_V1)); + factory.addOverlaySpace(existingSpace); // re-add renamed space + keyToRecordMap.remove(key); + } + + // Add any remaing overlay which are missing from factory + for (long key : keyToRecordMap.keySet()) { + DBRecord rec = keyToRecordMap.get(key); + String spaceName = rec.getString(OV_SPACE_NAME_COL_V1); + AddressSpace baseSpace = + factory.getAddressSpace(rec.getString(OV_SPACE_BASE_COL_V1)); + factory.addOverlaySpace(key, spaceName, baseSpace); + } + } + catch (IllegalArgumentException | DuplicateNameException e) { + throw new AssertionError("Unexpected error updating overlay address spaces", e); + } + + factory.refreshStaleOverlayStatus(); + } + + private boolean isCompatibleOverlay(ProgramOverlayAddressSpace os, DBRecord rec, + ProgramAddressFactory factory) throws IOException { + String baseSpaceName = rec.getString(OV_SPACE_BASE_COL_V1); + AddressSpace baseSpace = factory.getAddressSpace(baseSpaceName); + if (baseSpace == null) { + throw new IOException("Base space for overlay not found: " + baseSpaceName); + } + return baseSpace == os.getOverlayedSpace(); + } + + @Override + void setLanguage(Language newLanguage, ProgramAddressFactory addrFactory, + LanguageTranslator translator) throws IOException { + + Table table = db.getTable(TABLE_NAME); + if (table != null) { + RecordIterator it = table.iterator(); + while (it.hasNext()) { + DBRecord rec = it.next(); + String oldUnderlyingSpaceName = rec.getString(OV_SPACE_BASE_COL_V1); + AddressSpace space = addrFactory.getAddressSpace(oldUnderlyingSpaceName); + if (space != null && space.isNonLoadedMemorySpace()) { + // skip overlays associated with non-loaded spaces such as OTHER space + continue; + } + AddressSpace newSpace = translator.getNewAddressSpace(oldUnderlyingSpaceName); + if (newSpace == null) { + throw new IOException( + "Failed to map old address space: " + oldUnderlyingSpaceName); + } + rec.setString(OV_SPACE_BASE_COL_V1, newSpace.getName()); + table.putRecord(rec); + } + } + initializeOverlaySpaces(addrFactory); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java index 57806571a2..885437c1f1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramAddressFactory.java @@ -18,18 +18,42 @@ package ghidra.program.database; import ghidra.program.model.address.*; import ghidra.program.model.lang.CompilerSpec; import ghidra.program.model.lang.Language; +import ghidra.util.InvalidNameException; import ghidra.util.NumericUtilities; import ghidra.util.exception.DuplicateNameException; public class ProgramAddressFactory extends DefaultAddressFactory { + protected final OverlayRegionSupplier overlayRegionSupplier; + private AddressFactory originalFactory; private AddressSpace stackSpace; + private boolean hasStaleOverlays = false; + private long nextTmpId = 1; // used for overlay naming - public ProgramAddressFactory(Language language, CompilerSpec compilerSpec) { + /** + * Construct a Program address factory which augments the {@link DefaultAddressFactory} + * supplied by a {@link Language}. The following additional address spaces are added: + * + *
- {@link AddressSpace#OTHER_SPACE}
+ *- {@link AddressSpace#EXTERNAL_SPACE}
+ *- A stack space (see {@link AddressSpace#TYPE_STACK})
+ *- {@link AddressSpace#HASH_SPACE}
+ *- A join space (see {@link AddressSpace#TYPE_JOIN})
+ * + * In addition, support is provided for {@link ProgramOverlayAddressSpace}. + * @param language language specification + * @param compilerSpec compiler specification + * @param overlayRegionSupplier overlay space defined region supplier which will be invoked when + * specific queries are performed on overlay address spaces. If memory is not yet available + * a null AddressSet may be returned by the supplier. + */ + public ProgramAddressFactory(Language language, CompilerSpec compilerSpec, + OverlayRegionSupplier overlayRegionSupplier) { super(language.getAddressFactory().getAllAddressSpaces(), language.getAddressFactory().getDefaultAddressSpace()); this.originalFactory = language.getAddressFactory(); + this.overlayRegionSupplier = overlayRegionSupplier; initOtherSpace(language); initExternalSpace(language); initStackSpace(language, compilerSpec); @@ -37,6 +61,14 @@ public class ProgramAddressFactory extends DefaultAddressFactory { initJoinSpace(language); } + public void invalidateOverlayCache() { + for (AddressSpace space : getAddressSpaces()) { + if (space instanceof ProgramOverlayAddressSpace os) { + os.invalidate(); + } + } + } + private void initOtherSpace(Language language) { try { addAddressSpace(AddressSpace.OTHER_SPACE); @@ -97,145 +129,68 @@ public class ProgramAddressFactory extends DefaultAddressFactory { return originalFactory; } - protected void addOverlayAddressSpace(OverlayAddressSpace ovSpace) + /** + * Determine whether the given space can have an overlay + * + * @param baseSpace the overlay base address space + * @return true to allow, false to prohibit + */ + protected boolean isValidOverlayBaseSpace(AddressSpace baseSpace) { + if (baseSpace != getAddressSpace(baseSpace.getName())) { + return false; + } + return baseSpace.isMemorySpace() && !baseSpace.isOverlaySpace(); + } + + /** + * Add an overlay address space to this factory + * @param ovSpace overlay space + * @throws DuplicateNameException if name of overlay space already exists in this factory + */ + protected void addOverlaySpace(ProgramOverlayAddressSpace ovSpace) throws DuplicateNameException { + if (!ovSpace.getOrderedKey().equals(ovSpace.getName())) { + hasStaleOverlays = true; + } addAddressSpace(ovSpace); } /** - * Determine whether the given space can have an overlay - * - * @param originalSpace the original space - * @return true to allow, false to prohibit - */ - protected boolean validateOriginalSpace(AddressSpace originalSpace) { - return originalSpace.isMemorySpace() && !originalSpace.isOverlaySpace(); - } - - protected boolean assignUniqueID(AddressSpace originalSpace) { - return originalSpace.getType() == AddressSpace.TYPE_RAM || - originalSpace.getType() == AddressSpace.TYPE_OTHER; - } - - /** - * Create a new OverlayAddressSpace based upon the given overlay blockName and base AddressSpace - * - * @param name the preferred name of the overlay address space to be created. This name may be - * modified if preserveName is false to produce a valid overlay space name and avoid - * duplication. - * @param preserveName if true specified name will be preserved, if false an unique acceptable - * overlay space name will be generated from the specified name. - * @param originalSpace the base AddressSpace to overlay - * @param minOffset the min offset of the space - * @param maxOffset the max offset of the space + * Create a new ProgramOverlayAddressSpace based upon the given overlay blockName and base AddressSpace + * @param key overlay record key + * @param overlayName overlay name + * @param baseSpace the base AddressSpace to overlay * @return the new overlay space - * @throws IllegalArgumentException if originalSpace is not permitted or preserveName is true - * and a space with specified name already exists. + * @throws DuplicateNameException if overlay name duplicates another address space name + * @throws IllegalArgumentException if baseSpace is not permitted or not found. */ - protected OverlayAddressSpace addOverlayAddressSpace(String name, boolean preserveName, - AddressSpace originalSpace, long minOffset, long maxOffset) { + protected ProgramOverlayAddressSpace addOverlaySpace(long key, String overlayName, + AddressSpace baseSpace) throws DuplicateNameException { - if (!validateOriginalSpace(originalSpace)) { + if (!isValidOverlayBaseSpace(baseSpace)) { throw new IllegalArgumentException( - "Invalid address space for overlay: " + originalSpace.getName()); - } - AddressSpace space = getAddressSpace(originalSpace.getName()); - if (space != originalSpace) { - throw new IllegalArgumentException("Unknown memory address space instance"); + "Invalid base space for overlay: " + baseSpace.getName()); } - String spaceName = name; - if (!preserveName) { - spaceName = fixupOverlaySpaceName(name); - spaceName = getUniqueOverlayName(spaceName); - } - else if (getAddressSpace(name) != null) { // check before allocating unique ID - throw new IllegalArgumentException("Space named " + name + " already exists!"); + AddressSpace space = getAddressSpace(baseSpace.getName()); + if (space != baseSpace) { + throw new IllegalArgumentException("Invalid memory address space instance"); } - int unique = 0; - if (assignUniqueID(originalSpace)) { - unique = getNextUniqueID(); - } - - OverlayAddressSpace ovSpace = - new OverlayAddressSpace(spaceName, originalSpace, unique, minOffset, maxOffset); - try { - addAddressSpace(ovSpace); - } - catch (DuplicateNameException e) { - throw new RuntimeException(e); // unexpected - } - return ovSpace; + return new ProgramOverlayAddressSpace(key, overlayName, baseSpace, getNextUniqueID(), + overlayRegionSupplier, this); } - /** - * Get a unique address space name based on the specified baseOverlayName - * - * @param baseOverlayName base overlay address space name - * @return unique overlay space name - */ - private String getUniqueOverlayName(String baseOverlayName) { - if (getAddressSpace(baseOverlayName) == null) { - return baseOverlayName; - } - int index = 1; - while (true) { - String revisedName = baseOverlayName + "." + index++; - if (getAddressSpace(revisedName) == null) { - return revisedName; - } - } - } + public void checkValidOverlaySpaceName(String name) + throws InvalidNameException, DuplicateNameException { - /** - * Get base overlay name removing any numeric suffix which may have been added to avoid - * duplication. This method is intended to be used during rename only. - * - * @param overlayName existing overlay space name - * @return base overlay name with any trailing index removed which may have been added to avoid - * duplication. - */ - private String getBaseOverlayName(String overlayName) { - int index = overlayName.lastIndexOf('.'); - if (index < 1) { - return overlayName; + if (!AddressSpace.isValidName(name)) { + throw new InvalidNameException("Invalid overlay space name: " + name); } - int value; - try { - value = Integer.parseInt(overlayName.substring(index + 1)); - } - catch (NumberFormatException e) { - return overlayName; - } - if (value < 1) { - return overlayName; - } - String baseName = overlayName.substring(0, index); - return overlayName.equals(baseName + '.' + value) ? baseName : overlayName; - } - /** - * Generate an allowed address space name from a block name. Use of unsupported characters will - * be converted to underscore (includes colon and all whitespace chars). double-underscore to - * ensure uniqueness. - * - * @param blockName corresponding memory block name - * @return overlay space name - */ - private String fixupOverlaySpaceName(String blockName) { - int len = blockName.length(); - StringBuffer buf = new StringBuffer(len); - for (int i = 0; i < len; i++) { - char c = blockName.charAt(i); - if (c == ':' || c <= 0x20) { - buf.append('_'); - } - else { - buf.append(c); - } + if (getAddressSpace(name) != null) { + throw new DuplicateNameException("Duplicate address space name: " + name); } - return buf.toString(); } @Override @@ -266,31 +221,28 @@ public class ProgramAddressFactory extends DefaultAddressFactory { return addr; } + /** + * Remove an overlay space. + * It may be neccessary to invoke {@link #refreshStaleOverlayStatus()} when an overlay is + * removed. + * @param name overlay space name + */ protected void removeOverlaySpace(String name) { + AddressSpace space = getAddressSpace(name); + if (!(space instanceof ProgramOverlayAddressSpace)) { + throw new IllegalArgumentException("Overlay " + name + " not found"); + } removeAddressSpace(name); } - /** - * Rename overlay with preferred newName. Actual name used will be returned and may differ from - * specified newName to ensure validity and avoid duplication. - * - * @param oldOverlaySpaceName the existing overlay address space name - * @param newName the preferred new name of the overlay address space. This name may be modified - * to produce a valid overlay space name to avoid duplication. - * @return new name applied to existing overlay space - */ - @Override - protected String renameOverlaySpace(String oldOverlaySpaceName, String newName) { - try { - String revisedName = fixupOverlaySpaceName(newName); - if (revisedName.equals(getBaseOverlayName(oldOverlaySpaceName))) { - return oldOverlaySpaceName; - } - revisedName = getUniqueOverlayName(revisedName); - return super.renameOverlaySpace(oldOverlaySpaceName, revisedName); + protected void overlaySpaceRenamed(String oldOverlaySpaceName, String newName, + boolean refreshStatusIfNeeded) { + OverlayAddressSpace os = super.overlaySpaceRenamed(oldOverlaySpaceName, newName); + if (!newName.equals(os.getOrderedKey())) { + hasStaleOverlays = true; } - catch (DuplicateNameException e) { - throw new RuntimeException(e); // unexpected + else if (hasStaleOverlays && refreshStatusIfNeeded) { + refreshStaleOverlayStatus(); // must check all overlays to determine status } } @@ -302,4 +254,43 @@ public class ProgramAddressFactory extends DefaultAddressFactory { } return maxID + 1; } + + /** + * Examine all overlay spaces and update the stale status indicator + * (see {@link #hasStaleOverlays}). + */ + protected void refreshStaleOverlayStatus() { + hasStaleOverlays = false; + for (AddressSpace space : getAddressSpaces()) { + if (space instanceof ProgramOverlayAddressSpace os) { + if (!os.getName().equals(os.getOrderedKey())) { + hasStaleOverlays = true; + break; + } + } + } + } + + @Override + public boolean hasStaleOverlayCondition() { + return hasStaleOverlays; + } + + /** + * Generate an ordered unique name-based key for use with overlay spaces. + * This will generally be the overlay name unless that value has already been utilized by + * another overlay. + * @param overlayName overlay name + * @return ordered key to be used + */ + synchronized String generateOrderedKey(String overlayName) { + for (AddressSpace space : getAddressSpaces()) { + if (space instanceof ProgramOverlayAddressSpace os) { + if (overlayName.equals(os.getOrderedKey())) { + return overlayName + Address.SEPARATOR + nextTmpId++; + } + } + } + return overlayName; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java index aacc76cf90..b1ca439cca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramDB.java @@ -51,7 +51,6 @@ import ghidra.program.model.data.CategoryPath; import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.mem.MemoryConflictException; import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.*; import ghidra.program.model.util.AddressSetPropertyMap; @@ -109,10 +108,12 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM * property map (StringTranslations). * 19-Jan-2023 - version 26 Improved relocation data records to incorporate status and * byte-length when original FileBytes should be used. - * 10-Jul-2023 - VERSION 27 Add support for Instruction length override which utilizes + * 10-Jul-2023 - version 27 Add support for Instruction length override which utilizes * unused flag bits. + * 19-Oct-2023 - version 28 Revised overlay address space table and eliminated min/max. + * Multiple blocks are permitted within a single overlay space. */ - static final int DB_VERSION = 27; + static final int DB_VERSION = 28; /** * UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the @@ -211,7 +212,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM private Address effectiveImageBase = null; private boolean recordChanges; - private OverlaySpaceAdapterDB overlaySpaceAdapter; + private OverlaySpaceDBAdapter overlaySpaceAdapter; private MapaddrSetPropertyMap = new HashMap<>(); private Map intRangePropertyMap = new HashMap<>(); @@ -246,7 +247,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM languageVersion = language.getVersion(); languageMinorVersion = language.getMinorVersion(); - addressFactory = new ProgramAddressFactory(language, compilerSpec); + addressFactory = + new ProgramAddressFactory(language, compilerSpec, s -> getDefinedAddressSet(s)); recordChanges = false; boolean success = false; @@ -339,7 +341,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM initCompilerSpec(); - addressFactory = new ProgramAddressFactory(language, compilerSpec); + addressFactory = + new ProgramAddressFactory(language, compilerSpec, s -> getDefinedAddressSet(s)); VersionException versionExc = createManagers(openMode, monitor); if (dbVersionExc != null) { @@ -378,6 +381,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM } languageUpgradeRequired = false; } + addressFactory.invalidateOverlayCache(); postUpgrade(oldVersion, monitor); changed = true; } @@ -404,6 +408,14 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM ProgramUtilities.addTrackedProgram(this); } + private AddressSetView getDefinedAddressSet(AddressSpace s) { + MemoryMapDB memory = getMemory(); + if (memory != null) { + return memory.intersectRange(s.getMinAddress(), s.getMaxAddress()); + } + return null; + } + /** * Determine if program initialization requires a language upgrade * @return true if language upgrade is pending @@ -741,7 +753,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM } @Override - public AddressFactory getAddressFactory() { + public ProgramAddressFactory getAddressFactory() { return addressFactory; } @@ -1183,33 +1195,29 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM } } - /** - * Create a new OverlayAddressSpace based upon the given overlay blockName and base AddressSpace - * @param blockName the name of the overlay memory block which corresponds to the new overlay address - * space to be created. This name may be modified to produce a valid overlay space name and avoid - * duplication. - * @param originalSpace the base AddressSpace to overlay - * @param minOffset the min offset of the space - * @param maxOffset the max offset of the space - * @return the new space - * @throws LockException if the program is shared and not checked out exclusively. - * @throws MemoryConflictException if image base override is active - */ - public AddressSpace addOverlaySpace(String blockName, AddressSpace originalSpace, - long minOffset, long maxOffset) throws LockException, MemoryConflictException { + @Override + public ProgramOverlayAddressSpace createOverlaySpace(String overlaySpaceName, + AddressSpace baseSpace) throws IllegalStateException, DuplicateNameException, + InvalidNameException, LockException { checkExclusiveAccess(); - if (imageBaseOverride) { - throw new MemoryConflictException( - "Overlay spaces may not be created while an image-base override is active"); + + if (!addressFactory.isValidOverlayBaseSpace(baseSpace)) { + throw new IllegalArgumentException( + "Invalid address space for overlay: " + baseSpace.getName()); } - OverlayAddressSpace ovSpace = null; + ProgramOverlayAddressSpace ovSpace = null; lock.acquire(); try { - ovSpace = addressFactory.addOverlayAddressSpace(blockName, false, originalSpace, - minOffset, maxOffset); - overlaySpaceAdapter.addOverlaySpace(ovSpace); + if (imageBaseOverride) { + throw new IllegalStateException( + "Overlay spaces may not be created while an image-base override is active"); + } + ovSpace = + overlaySpaceAdapter.createOverlaySpace(addressFactory, overlaySpaceName, baseSpace); + + setChanged(ChangeManager.DOCR_OVERLAY_SPACE_ADDED, overlaySpaceName, null); } catch (IOException e) { dbError(e); @@ -1220,35 +1228,61 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM return ovSpace; } - public void renameOverlaySpace(String oldOverlaySpaceName, String newName) - throws LockException { - checkExclusiveAccess(); - String revisedName = addressFactory.renameOverlaySpace(oldOverlaySpaceName, newName); - if (!revisedName.equals(oldOverlaySpaceName)) { - try { - overlaySpaceAdapter.renameOverlaySpace(oldOverlaySpaceName, revisedName); - addrMap.renameOverlaySpace(oldOverlaySpaceName, revisedName); - } - catch (IOException e) { - dbError(e); - } - } - } + @Override + public void renameOverlaySpace(String overlaySpaceName, String newName) + throws NotFoundException, InvalidNameException, DuplicateNameException, LockException { - public boolean removeOverlaySpace(AddressSpace overlaySpace) throws LockException { lock.acquire(); try { checkExclusiveAccess(); - MemoryBlock[] blocks = memoryManager.getBlocks(); - for (MemoryBlock block : blocks) { - if (block.getStart().getAddressSpace().equals(overlaySpace)) { - return false; - } + + AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName); + if (space == null || !(space instanceof ProgramOverlayAddressSpace os)) { + throw new NotFoundException("Overlay " + overlaySpaceName + " not found"); } - addressFactory.removeOverlaySpace(overlaySpace.getName()); - overlaySpaceAdapter.removeOverlaySpace(overlaySpace.getName()); - addrMap.deleteOverlaySpace(overlaySpace.getName()); + + addressFactory.checkValidOverlaySpaceName(newName); + + if (overlaySpaceAdapter.renameOverlaySpace(overlaySpaceName, newName)) { + os.setName(newName); + addressFactory.overlaySpaceRenamed(overlaySpaceName, newName, true); + addrMap.renameOverlaySpace(overlaySpaceName, newName); + clearCache(true); + setChanged(ChangeManager.DOCR_OVERLAY_SPACE_RENAMED, overlaySpaceName, newName); + fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED)); + } + } + catch (IOException e) { + dbError(e); + } + finally { + lock.release(); + } + } + + @Override + public boolean removeOverlaySpace(String overlaySpaceName) + throws LockException, NotFoundException { + + lock.acquire(); + try { + checkExclusiveAccess(); + + AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName); + if (space == null || !(space instanceof ProgramOverlayAddressSpace os)) { + throw new NotFoundException("Overlay " + overlaySpaceName + " not found"); + } + + if (!os.getOverlayAddressSet().isEmpty()) { + return false; // memory blocks are still defined + } + + addressFactory.removeOverlaySpace(overlaySpaceName); + overlaySpaceAdapter.removeOverlaySpace(overlaySpaceName); + addrMap.deleteOverlaySpace(overlaySpaceName); clearCache(true); + setChanged(ChangeManager.DOCR_OVERLAY_SPACE_REMOVED, overlaySpaceName, null); + fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED)); return true; } catch (IOException e) { @@ -1589,7 +1623,25 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM throws CancelledException, IOException { VersionException versionExc = null; - overlaySpaceAdapter = new OverlaySpaceAdapterDB(dbh); + try { + overlaySpaceAdapter = + OverlaySpaceDBAdapter.getOverlaySpaceAdapter(dbh, openMode, monitor); + } + catch (VersionException e) { + versionExc = e.combine(versionExc); + try { + overlaySpaceAdapter = + OverlaySpaceDBAdapter.getOverlaySpaceAdapter(dbh, READ_ONLY, monitor); + } + catch (VersionException e1) { + if (e1.isUpgradable()) { + throw new RuntimeException( + "OverlaySpaceDBAdapter is supported but failed to open as READ-ONLY!"); + } + // Unable to proceed without overlay space adapter ! + return versionExc; + } + } overlaySpaceAdapter.initializeOverlaySpaces(addressFactory); monitor.checkCancelled(); @@ -2047,7 +2099,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM languageMinorVersion = language.getMinorVersion(); if (translator != null) { - addressFactory = new ProgramAddressFactory(language, compilerSpec); + addressFactory = new ProgramAddressFactory(language, compilerSpec, + s -> getDefinedAddressSet(s)); addrMap.setLanguage(language, addressFactory, translator); overlaySpaceAdapter.setLanguage(language, addressFactory, translator); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java new file mode 100644 index 0000000000..8c47c10db3 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramOverlayAddressSpace.java @@ -0,0 +1,106 @@ +/* ### + * 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; + +import ghidra.program.model.address.*; +import ghidra.util.exception.DuplicateNameException; + +public class ProgramOverlayAddressSpace extends OverlayAddressSpace { + + private final long key; + private final OverlayRegionSupplier overlayRegionSupplier; + + private String overlayName; + + private AddressSetView overlaySet; + + /** + * + * @param key DB record key + * @param overlayName current overlay name + * @param baseSpace base address space (type should be restricted as neccessary by caller) + * @param unique assigned unique ID + * @param overlayRegionSupplier callback handler which supplies the defined address set + * for a specified overlay address space. + * @param factory used to determine a suitable ordered overlay ordered-key used for + * {@link #equals(Object)} and {@link #compareTo(AddressSpace)}. + * @throws DuplicateNameException if specified name duplicates an existing address space name + */ + public ProgramOverlayAddressSpace(long key, String overlayName, AddressSpace baseSpace, + int unique, OverlayRegionSupplier overlayRegionSupplier, ProgramAddressFactory factory) + throws DuplicateNameException { + super(baseSpace, unique, factory.generateOrderedKey(overlayName)); + this.key = key; + this.overlayName = overlayName; + this.overlayRegionSupplier = overlayRegionSupplier; + factory.addOverlaySpace(this); + } + + protected synchronized void invalidate() { + overlaySet = null; + } + + private void validate() { + if (overlaySet == null) { + overlaySet = + overlayRegionSupplier != null ? overlayRegionSupplier.getOverlayAddressSet(this) + : new AddressSet(); + if (overlaySet == null) { + overlaySet = new AddressSet(); + } + } + } + + /** + * Get the DB record key used to store this overlay specification. + * This is intended to be used internally to reconcile address spaces only. + * @return DB record key + */ + public long getKey() { + return key; + } + + @Override + public String getName() { + return overlayName; + } + + /** + * Method to support renaming an overlay address space instance. Intended for internal use only. + * @param name new overlay space name + */ + public void setName(String name) { + this.overlayName = name; + } + + @Override + public synchronized boolean contains(long offset) { + try { + Address addr = getAddressInThisSpaceOnly(makeValidOffset(offset)); + return getOverlayAddressSet().contains(addr); + } + catch (AddressOutOfBoundsException e) { + return false; + } + } + + @Override + public synchronized AddressSetView getOverlayAddressSet() { + validate(); + return overlaySet; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java index c697510099..40b7814869 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramUserDataDB.java @@ -24,7 +24,6 @@ import ghidra.framework.store.FileSystem; import ghidra.program.database.map.AddressMapDB; import ghidra.program.database.properties.*; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressFactory; import ghidra.program.model.lang.*; import ghidra.program.model.listing.ProgramUserData; import ghidra.program.model.util.*; @@ -102,7 +101,7 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData private int languageVersion; private Language language; private LanguageTranslator languageUpgradeTranslator; - private AddressFactory addressFactory; + private ProgramAddressFactory addressFactory; private HashMap > propertyMaps = new HashMap >(); private HashSet propertyMapOwners = null; @@ -126,7 +125,7 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData languageID = language.getLanguageID(); languageVersion = language.getVersion(); - addressFactory = language.getAddressFactory(); + addressFactory = program.getAddressFactory(); setEventsEnabled(false); // events not support @@ -184,7 +183,7 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData languageVersionExc = checkForLanguageChange(e); } - addressFactory = language.getAddressFactory(); + addressFactory = program.getAddressFactory(); VersionException versionExc = createManagers(UPGRADE, program, monitor); if (dbVersionExc != null) { @@ -238,8 +237,7 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData Language newLanguage = language; Language oldLanguage = OldLanguageFactory.getOldLanguageFactory() - .getOldLanguage( - languageID, languageVersion); + .getOldLanguage(languageID, languageVersion); if (oldLanguage == null) { // Assume minor version behavior - old language does not exist for current major version Msg.error(this, "Old language specification not found: " + languageID + @@ -248,10 +246,8 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData } // Ensure that we can upgrade the language - languageUpgradeTranslator = - LanguageTranslatorFactory.getLanguageTranslatorFactory() - .getLanguageTranslator( - oldLanguage, newLanguage); + languageUpgradeTranslator = LanguageTranslatorFactory.getLanguageTranslatorFactory() + .getLanguageTranslator(oldLanguage, newLanguage); if (languageUpgradeTranslator == null) { throw new LanguageNotFoundException(language.getLanguageID(), "(Ver " + languageVersion + ".x" + " -> " + newLanguage.getVersion() + "." + @@ -278,10 +274,8 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData private VersionException checkForLanguageChange(LanguageNotFoundException e) throws LanguageNotFoundException { - languageUpgradeTranslator = - LanguageTranslatorFactory.getLanguageTranslatorFactory() - .getLanguageTranslator( - languageID, languageVersion); + languageUpgradeTranslator = LanguageTranslatorFactory.getLanguageTranslatorFactory() + .getLanguageTranslator(languageID, languageVersion); if (languageUpgradeTranslator == null) { throw e; } @@ -428,7 +422,8 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData languageID = language.getLanguageID(); languageVersion = language.getVersion(); - addressFactory = language.getAddressFactory(); + // AddressFactory need not change since we are using the instance from the + // Program which would have already been subject to an upgrade addressMap.setLanguage(language, addressFactory, translator); clearCache(true); @@ -528,31 +523,26 @@ class ProgramUserDataDB extends DomainObjectAdapterDB implements ProgramUserData switch (type) { case PROPERTY_TYPE_STRING: map = new StringPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, - addressMap, rec.getString(PROPERTY_NAME_COL), - TaskMonitor.DUMMY); + addressMap, rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); break; case PROPERTY_TYPE_LONG: - map = - new LongPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, addressMap, - rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); + map = new LongPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, + addressMap, rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); break; case PROPERTY_TYPE_INT: - map = - new IntPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, addressMap, - rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); + map = new IntPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, + addressMap, rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); break; case PROPERTY_TYPE_BOOLEAN: - map = - new VoidPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, addressMap, - rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); + map = new VoidPropertyMapDB(dbh, DBConstants.UPGRADE, this, changeMgr, + addressMap, rec.getString(PROPERTY_NAME_COL), TaskMonitor.DUMMY); break; case PROPERTY_TYPE_SAVEABLE: String className = rec.getString(PROPERTY_CLASS_COL); Class extends Saveable> c = ObjectPropertyMapDB.getSaveableClassForName(className); return new ObjectPropertyMapDB<>(dbh, DBConstants.UPGRADE, this, changeMgr, - addressMap, rec.getString(PROPERTY_NAME_COL), c, - TaskMonitor.DUMMY, true); + addressMap, rec.getString(PROPERTY_NAME_COL), c, TaskMonitor.DUMMY, true); default: throw new IllegalArgumentException("Unsupported property type: " + type); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java index a4dc244c6f..ea143b6a9a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java @@ -20,6 +20,7 @@ import java.util.*; import db.DBConstants; import db.DBHandle; +import ghidra.program.database.ProgramAddressFactory; import ghidra.program.database.map.AddressMapDBAdapter.AddressMapEntry; import ghidra.program.database.mem.MemoryMapDB; import ghidra.program.model.address.*; @@ -219,7 +220,6 @@ public class AddressMapDB implements AddressMap { for (int i = 0; i < sortedBaseStartAddrs.length; i++) { long max = sortedBaseStartAddrs[i].getAddressSpace().getMaxAddress().getOffset(); max = max < 0 ? MAX_OFFSET : Math.min(max, MAX_OFFSET); - // Avoid use of add which fails for overlay addresses which have restricted min/max offsets long off = sortedBaseStartAddrs[i].getOffset() | max; sortedBaseEndAddrs[i] = sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off); @@ -912,8 +912,8 @@ public class AddressMapDB implements AddressMap { @Override public Address getImageBase() { if (defaultAddrSpace instanceof SegmentedAddressSpace) { - return ((SegmentedAddressSpace) defaultAddrSpace).getAddress( - (int) (baseImageOffset >> 4), 0); + return ((SegmentedAddressSpace) defaultAddrSpace) + .getAddress((int) (baseImageOffset >> 4), 0); } return defaultAddrSpace.getAddress(baseImageOffset); } @@ -925,7 +925,7 @@ public class AddressMapDB implements AddressMap { * @param translator translates address spaces from the old language to the new language. * @throws IOException if IO error occurs */ - public synchronized void setLanguage(Language newLanguage, AddressFactory addrFactory, + public synchronized void setLanguage(Language newLanguage, ProgramAddressFactory addrFactory, LanguageTranslator translator) throws IOException { List entries = adapter.getEntries(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java index c0d1741e42..d3558209cc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java @@ -185,9 +185,6 @@ public class MemoryBlockDB implements MemoryBlock { } memMap.checkBlockName(name); try { - if (isOverlay()) { - memMap.overlayBlockRenamed(startAddress.getAddressSpace().getName(), name); - } record.setString(MemoryMapDBAdapter.NAME_COL, name); adapter.updateBlockRecord(record); } @@ -752,4 +749,22 @@ public class MemoryBlockDB implements MemoryBlock { return false; } + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(getName()); + buf.append("("); + Address start = getStart(); + buf.append(start.toString()); + buf.append(" - "); + buf.append(getEnd().toString()); + AddressSpace space = start.getAddressSpace(); + if (space instanceof OverlayAddressSpace os) { + buf.append(", overlays: "); + buf.append(os.getOverlayedSpace().getName()); + } + buf.append(")"); + return buf.toString(); + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java index bbaac650ba..fe5d5cc0de 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java @@ -24,8 +24,7 @@ import db.DBHandle; import ghidra.framework.model.DomainObject; import ghidra.framework.model.DomainObjectChangeRecord; import ghidra.framework.store.LockException; -import ghidra.program.database.ManagerDB; -import ghidra.program.database.ProgramDB; +import ghidra.program.database.*; import ghidra.program.database.code.CodeManager; import ghidra.program.database.map.AddressMapDB; import ghidra.program.model.address.*; @@ -201,7 +200,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } AddressSet mappedSet = getMappedIntersection(block, addrSetView.initialized); addrSetView.initialized.add(mappedSet); - addrSetView.initializedAndLoaded.add(getMappedIntersection(block, addrSetView.initializedAndLoaded)); + addrSetView.initializedAndLoaded + .add(getMappedIntersection(block, addrSetView.initializedAndLoaded)); } else if (block.isInitialized()) { addrSetView.initialized.add(block.getStart(), block.getEnd()); @@ -231,6 +231,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { nameBlockMap = new HashMap<>(); addrSetView = null; // signal stale view addrMap.memoryMapChanged(this); + if (program != null) { + program.getAddressFactory().invalidateOverlayCache(); + } } void blockExecuteChanged(MemoryBlockDB block) { @@ -370,8 +373,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } private void checkMemoryWriteMappedBlock(MemoryBlockDB mappedBlock, Address start, - Address endAddr) - throws AddressOverflowException, MemoryAccessException { + Address endAddr) throws AddressOverflowException, MemoryAccessException { long startOffset = start.subtract(mappedBlock.getStart()); long endOffset = endAddr.subtract(mappedBlock.getStart()); @@ -401,8 +403,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } private void checkMemoryWriteNonMappedBlock(MemoryBlockDB nonMappedBlock, Address start, - Address endAddr) - throws MemoryAccessException { + Address endAddr) throws MemoryAccessException { // TODO: could contain uninitialized region which is illegal to write to although block.isInitialized // may not be of much help since it reflects the first sub-block only - seems like mixing is a bad idea @@ -576,9 +577,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { @Override public MemoryBlock createInitializedBlock(String name, Address start, long size, - byte initialValue, TaskMonitor monitor, boolean overlay) - throws LockException, MemoryConflictException, AddressOverflowException, - CancelledException { + byte initialValue, TaskMonitor monitor, boolean overlay) throws LockException, + MemoryConflictException, AddressOverflowException, CancelledException { InputStream fillStream = null; if (initialValue != 0) { @@ -594,17 +594,67 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } private Address createOverlaySpace(String name, Address start, long dataLength) - throws MemoryConflictException, AddressOverflowException, LockException { + throws IllegalStateException, AddressOverflowException, LockException { start.addNoWrap(dataLength - 1);// just tests the AddressOverflow condition. - AddressSpace ovSpace = program.addOverlaySpace(name, start.getAddressSpace(), - start.getOffset(), start.getOffset() + (dataLength - 1)); + ProgramOverlayAddressSpace ovSpace = + createUniqueOverlaySpace(name, start.getAddressSpace()); - Address ovAddr = ovSpace.getAddress(start.getOffset()); + Address ovAddr = ovSpace.getAddressInThisSpaceOnly(start.getOffset()); return ovAddr; } + /** + * Create a new overlay space based upon the given base AddressSpace. + * The specified overlaySpaceName may be modified to ensure name validity and uniqueness. + * @param overlaySpaceName the name of the new overlay space. + * @param baseSpace the base AddressSpace to overlay (i.e., overlayed-space) + * @return the new overlay space + * @throws LockException if the program is shared and not checked out exclusively. + * @throws IllegalStateException if image base override is active + */ + private ProgramOverlayAddressSpace createUniqueOverlaySpace(String overlaySpaceName, + AddressSpace baseSpace) throws IllegalStateException, LockException { + + ProgramAddressFactory addressFactory = program.getAddressFactory(); + overlaySpaceName = fixupOverlaySpaceName(overlaySpaceName); + String spaceName = overlaySpaceName; + int index = 1; + while (addressFactory.getAddressSpace(spaceName) != null) { + spaceName = overlaySpaceName + "." + index++; + } + try { + return program.createOverlaySpace(spaceName, baseSpace); + } + catch (DuplicateNameException | InvalidNameException e) { + throw new AssertionError(e); + } + } + + /** + * Generate an allowed address space name from a block name. Use of unsupported characters will + * be converted to underscore (includes colon and all whitespace chars). double-underscore to + * ensure uniqueness. + * + * @param blockName corresponding memory block name + * @return overlay space name + */ + private String fixupOverlaySpaceName(String blockName) { + int len = blockName.length(); + StringBuffer buf = new StringBuffer(len); + for (int i = 0; i < len; i++) { + char c = blockName.charAt(i); + if (c == ':' || c <= 0x20) { + buf.append('_'); + } + else { + buf.append(c); + } + } + return buf.toString(); + } + @Override public MemoryBlock createInitializedBlock(String name, Address start, InputStream is, long length, TaskMonitor monitor, boolean overlay) throws MemoryConflictException, @@ -618,8 +668,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { if (monitor != null && is != null) { is = new MonitoredInputStream(is, monitor); } - if (overlay) { + boolean createdOverlaySpace = false; + if (overlay && !start.getAddressSpace().isOverlaySpace()) { start = createOverlaySpace(name, start, length); + createdOverlaySpace = true; } else { checkRange(start, length); @@ -634,8 +686,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } catch (IOCancelledException e) { // this assumes the adapter has already cleaned up any partially created buffers. - if (overlay) { - checkRemoveAddressSpace(start.getAddressSpace()); + if (createdOverlaySpace) { + attemptOverlaySpaceRemoval((OverlayAddressSpace) start.getAddressSpace()); } throw new CancelledException(); } @@ -662,7 +714,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { program.checkExclusiveAccess(); checkFileBytesRange(fileBytes, offset, length); - if (overlay) { + if (overlay && !start.getAddressSpace().isOverlaySpace()) { start = createOverlaySpace(name, start, length); } else { @@ -705,8 +757,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { @Override public MemoryBlock createUninitializedBlock(String name, Address start, long size, - boolean overlay) throws MemoryConflictException, AddressOverflowException, - LockException { + boolean overlay) + throws MemoryConflictException, AddressOverflowException, LockException { checkBlockName(name); lock.acquire(); @@ -715,7 +767,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { program.checkExclusiveAccess(); - if (overlay) { + if (overlay && !start.getAddressSpace().isOverlaySpace()) { start = createOverlaySpace(name, start, size); } else { @@ -750,7 +802,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { checkBlockSize(length, false); program.checkExclusiveAccess(); mappedAddress.addNoWrap((length - 1) / 8);// just to check if length fits in address space - if (overlay) { + if (overlay && !start.getAddressSpace().isOverlaySpace()) { start = createOverlaySpace(name, start, length); } else { @@ -794,7 +846,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { checkBlockSize(length, false); program.checkExclusiveAccess(); byteMappingScheme.getMappedSourceAddress(mappedAddress, length - 1); // source fit check - if (overlay) { + if (overlay && !start.getAddressSpace().isOverlaySpace()) { start = createOverlaySpace(name, start, length); } else { @@ -904,13 +956,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { MemoryBlockDB memBlock = (MemoryBlockDB) block; Address oldStartAddr = block.getStart(); - if (block.isOverlay()) { - throw new IllegalArgumentException("Overlay blocks cannot be moved"); + if (block.isOverlay() && block.getStart().isNonLoadedMemoryAddress()) { + // impose convention-based restriction + throw new IllegalArgumentException("OTHER overlay blocks cannot be moved"); } - if (newStartAddr.getAddressSpace().isOverlaySpace()) { - throw new IllegalArgumentException("Can not move a block into an overlay space."); - } - program.setEventsEnabled(false);// ensure that no domain object change // events go out that would cause screen updates; // the code manager will be locked until the remove is done @@ -920,7 +969,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { set.delete(block.getStart(), block.getEnd()); if (set.intersects(newStartAddr, newEndAddr)) { throw new MemoryConflictException( - "Block move conflicts with other existing memory block"); + "Block move conflicts with another existing memory block"); } try { memBlock.setStartAddress(newStartAddr); @@ -961,8 +1010,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { if (addr.equals(memBlock.getStart())) { throw new IllegalArgumentException("Split cannot be done on block start address"); } - if (memBlock.isOverlay()) { - throw new IllegalArgumentException("Split cannot be done on an overlay block"); + if (memBlock.isOverlay() && memBlock.getStart().isNonLoadedMemoryAddress()) { + // impose convention-based restriction + throw new IllegalArgumentException( + "Split cannot be done on an OTHER overlay block"); } if (memBlock.isMapped()) { if (memBlock.getType() == MemoryBlockType.BIT_MAPPED) { @@ -1057,9 +1108,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { private void checkBlockForJoining(MemoryBlock block) { checkBlock(block); - if (block.isOverlay()) { - throw new IllegalArgumentException("Cannot join overlay blocks"); - } if (block.isMapped()) { throw new IllegalArgumentException("Cannot join mapped blocks"); } @@ -1909,8 +1957,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } fireBlockRemoved(startAddress); - if (startAddress.getAddressSpace().isOverlaySpace()) { - checkRemoveAddressSpace(startAddress.getAddressSpace()); + if (startAddress.getAddressSpace() instanceof OverlayAddressSpace os) { + attemptOverlaySpaceRemoval(os); } } finally { @@ -1919,17 +1967,17 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } /** - * Tests if the given addressSpace (overlay space) is used by any blocks. If not, it removes the - * space. + * Attempts to remove the the given overlay address space. Removal will only succeed if no + * memory blocks currently reside within the space. * * @param addressSpace overlay address space to be removed */ - private void checkRemoveAddressSpace(AddressSpace addressSpace) { + private void attemptOverlaySpaceRemoval(OverlayAddressSpace addressSpace) { lock.acquire(); try { - program.removeOverlaySpace(addressSpace); + program.removeOverlaySpace(addressSpace.getName()); } - catch (LockException e) { + catch (LockException | NotFoundException e) { throw new AssertException(); } finally { @@ -1950,9 +1998,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { throw new IllegalArgumentException( "Block may not be created with unrecognized address space"); } - if (space.isOverlaySpace()) { - throw new IllegalArgumentException("Block may not be created with an Overlay space"); - } if (size == 0) { throw new IllegalArgumentException("Block must have a non-zero length"); } @@ -2027,8 +2072,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } else { // Byte mapped ByteMappingScheme byteMappingScheme = info.getByteMappingScheme().get(); - start = - byteMappingScheme.getMappedAddress(mappedBlock, startOffset, false); + start = byteMappingScheme.getMappedAddress(mappedBlock, startOffset, false); long endOffset = startOffset + sourceRangeLength - 1; end = byteMappingScheme.getMappedAddress(mappedBlock, endOffset, true); if (start == null || start.compareTo(end) > 0) { @@ -2076,11 +2120,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } } - public void overlayBlockRenamed(String oldOverlaySpaceName, String name) - throws LockException { - program.renameOverlaySpace(oldOverlaySpaceName, name); - } - @Override public boolean equals(Object obj) { lock.acquire(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java index 786390d9d7..ccb53119b7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AbstractAddressSpace.java @@ -17,50 +17,52 @@ package ghidra.program.model.address; import java.math.BigInteger; -import org.apache.commons.lang3.StringUtils; - -import ghidra.util.MathUtilities; -import ghidra.util.NumericUtilities; +import ghidra.util.*; import ghidra.util.exception.AssertException; abstract class AbstractAddressSpace implements AddressSpace { - protected String name; - protected int size; // number of address bits - protected int unitSize = 1; // number of data bytes at each location - protected int type; - protected long spaceSize = 0; // number of address locations (2^size * unitSize) - is always even number (spaceSize=0 when all 64-bits of address offset are used) - protected boolean signed; - protected long minOffset; + private final int size; // number of address bits + private final int unitSize; // number of data bytes at each location + private final int type; + private final boolean signed; + + protected final long minOffset; protected long maxOffset; - protected Address minAddress; + protected final Address minAddress; protected Address maxAddress; - private int hashcode; - protected int spaceID; + private final long wordAddressMask; + protected long spaceSize; // number of address locations (2^size * unitSize) - is always even number (spaceSize=0 when all 64-bits of address offset are used) + protected final int spaceID; + + private Integer hashcode; private boolean showSpaceName; // show space name when displaying an address private boolean hasMemoryMappedRegisters = false; - private long wordAddressMask = -1; - /** * Constructs a new address space with the given name, bit size, type and unique value. - * @param name the name of the space. * @param size the number of bits required to represent the largest address * the space. * @param unitSize number of bytes contained at each addressable location (i.e., word-size in bytes) * @param type the type of the space * @param unique the unique id for this space. */ - protected AbstractAddressSpace(String name, int size, int unitSize, int type, int unique) { + protected AbstractAddressSpace(int size, int unitSize, int type, int unique) { + + this.size = size; + this.unitSize = unitSize; + this.type = type; showSpaceName = (type != TYPE_RAM) || isOverlaySpace(); if (type == TYPE_NONE) { - this.name = name; - this.type = TYPE_NONE; + // Intended for special purpose non-physical address space and single address + minOffset = maxOffset = 0; // single address at offset-0 minAddress = maxAddress = getUncheckedAddress(0); + signed = false; + spaceSize = 0; // (spaceSize=0 for 64-bit space) + wordAddressMask = -1; spaceID = -1; - hashcode = name.hashCode() + type; return; } @@ -68,17 +70,17 @@ abstract class AbstractAddressSpace implements AddressSpace { throw new IllegalArgumentException( "Unique space id must be between 0 and " + Short.MAX_VALUE + " inclusive"); } - this.name = name; - this.size = size; - this.unitSize = unitSize; - this.type = type; - if ((bitsConsumedByUnitSize(unitSize) + size) > 64) { + if ((bitsConsumedByUnitSize() + size) > 64) { throw new IllegalArgumentException( "Unsupport address space size (2^size * wordsize > 2^64)"); } - if (size != 64) { - spaceSize = ((long) unitSize) << size; // (spaceSize=0 for 64-bit space) + if (size == 64) { + spaceSize = 0; // (spaceSize=0 for 64-bit space) + wordAddressMask = -1; + } + else { + spaceSize = ((long) unitSize) << size; wordAddressMask = (1L << size) - 1; } signed = (type == AddressSpace.TYPE_CONSTANT || type == AddressSpace.TYPE_STACK); @@ -113,11 +115,9 @@ abstract class AbstractAddressSpace implements AddressSpace { // space id includes space and size info. this.spaceID = (unique << ID_UNIQUE_SHIFT) | (logsize << ID_SIZE_SHIFT) | type; - - hashcode = name.hashCode() + type; } - private int bitsConsumedByUnitSize(int unitSize) { + private int bitsConsumedByUnitSize() { if (unitSize < 1 || unitSize > 8) { throw new IllegalArgumentException("Unsupported unit size: " + unitSize); } @@ -133,11 +133,6 @@ abstract class AbstractAddressSpace implements AddressSpace { return signed; } - @Override - public String getName() { - return name; - } - @Override public int getSize() { return size; @@ -200,7 +195,7 @@ abstract class AbstractAddressSpace implements AddressSpace { } @Override - public Address getAddress(String addrString) throws AddressFormatException { + public final Address getAddress(String addrString) throws AddressFormatException { return getAddress(addrString, true); } @@ -209,11 +204,10 @@ abstract class AbstractAddressSpace implements AddressSpace { throws AddressFormatException { String offStr = addrString; - int colonPos = addrString.lastIndexOf(':'); - + int colonPos = addrString.lastIndexOf(Address.SEPARATOR); if (colonPos >= 0) { String addrSpaceStr = addrString.substring(0, colonPos); - if (!StringUtils.equals(name, addrSpaceStr)) { + if (!StringUtilities.equals(getName(), addrSpaceStr, caseSensitive)) { return null; } offStr = addrString.substring(colonPos + 1); @@ -221,11 +215,10 @@ abstract class AbstractAddressSpace implements AddressSpace { try { long off = parseString(offStr); - return getAddress(off); + return getAddressInThisSpaceOnly(off); } catch (NumberFormatException e) { - throw new AddressFormatException( - addrString + " contains invalid address hex offset"); + throw new AddressFormatException(addrString + " contains invalid address hex offset"); } catch (AddressOutOfBoundsException e) { throw new AddressFormatException(e.getMessage()); @@ -475,48 +468,37 @@ abstract class AbstractAddressSpace implements AddressSpace { return minAddress; } - private int compareAsOverlaySpace(AddressSpace overlaySpace) { - int baseCompare = ((OverlayAddressSpace) this).getBaseSpaceID() - - ((OverlayAddressSpace) overlaySpace).getBaseSpaceID(); - if (baseCompare == 0) { - long otherMinOffset = overlaySpace.getMinAddress().getOffset(); - if (minOffset == otherMinOffset) { - return name.compareTo(overlaySpace.getName()); - } - return (minOffset < otherMinOffset) ? -1 : 1; - } - return baseCompare; - } - @Override public int compareTo(AddressSpace space) { if (space == this) { return 0; } - if (isOverlaySpace()) { - if (space.isOverlaySpace()) { + + if (this instanceof OverlayAddressSpace thisOverlaySpace) { + if (space instanceof OverlayAddressSpace otherOverlaySpace) { // Both spaces are overlay spaces - return compareAsOverlaySpace(space); + return thisOverlaySpace.compareOverlay(otherOverlaySpace); } // I'm an overlay, other space is NOT an overlay return 1; } - else if (space.isOverlaySpace()) { + + if (space instanceof OverlayAddressSpace) { // I'm NOT an overlay, other space is an overlay return -1; } - if (hashcode == space.hashCode() && + + if (hashCode() == space.hashCode() && // hashcode factors name and type - type == space.getType() && name.equals(space.getName()) && + type == space.getType() && getName().equals(space.getName()) && getClass().equals(space.getClass())) { -// TODO: This could be bad - should really only be 0 if same instance - although this could have other implications -// Does not seem to be good way of factoring ID-based ordering with equality // This is not intended to handle complete mixing of address spaces // from multiple sources (i.e., language provider / address factory). // It is intended to handle searching for a single address from one // source within a list/set of addresses from a second source. return 0; } + int c = getSpaceID() - space.getSpaceID(); if (c == 0) { c = getClass().getName().compareTo(space.getClass().getName()); @@ -535,22 +517,29 @@ abstract class AbstractAddressSpace implements AddressSpace { if (getClass() != obj.getClass()) { return false; } - if (hashcode != obj.hashCode()) { + if (hashCode() != obj.hashCode()) { return false; } AddressSpace s = (AddressSpace) obj; + if (type != s.getType() || size != s.getSize() || !getName().equals(s.getName())) { + return false; + } -// return spaceID == s.getUniqueSpaceID() && -// type == s.getType() && -//// name.equals(s.getName) && // does the name really matter? -// size == s.getSize(); - - return type == s.getType() && name.equals(s.getName()) && size == s.getSize(); + return true; } + /** + * Compute the fixed hashcode for this address space. + * @return computed hash code + */ + abstract int computeHashCode(); + @Override - public int hashCode() { + public final int hashCode() { + if (hashcode == null) { + hashcode = computeHashCode(); + } return hashcode; } @@ -562,13 +551,13 @@ abstract class AbstractAddressSpace implements AddressSpace { protected void testAddressSpace(Address addr) { if (!this.equals(addr.getAddressSpace())) { throw new IllegalArgumentException("Address space for " + addr + " (" + - addr.getAddressSpace().getName() + ") does not match " + name); + addr.getAddressSpace().getName() + ") does not match " + getName()); } } @Override public String toString() { - return name + ":"; + return getName() + Address.SEPARATOR; } @Override @@ -579,7 +568,8 @@ abstract class AbstractAddressSpace implements AddressSpace { /** * Instantiates an address within this space. * No offset validation should be performed. - * @param offset + * @param offset address offset + * @return requested unchecked address */ protected abstract Address getUncheckedAddress(long offset); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java index 044ed5e1eb..ef4eaec462 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java @@ -40,6 +40,7 @@ public interface Address extends Comparable { * Character used to separate space names from offsets. */ public final char SEPARATOR_CHAR = ':'; + public final String SEPARATOR = ":"; /** * Creates a new Address by parsing a String representation of an address. The string may be diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressFactory.java index 8d9d9399e8..6aad3375f3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressFactory.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. @@ -173,4 +172,16 @@ public interface AddressFactory { */ public boolean hasMultipleMemorySpaces(); + /** + * Determine if this address factory contains a stale overlay address space + * whose name was recently changed. When this condition occurs, issues may arise when + * comparing {@link Address} and {@link AddressSpace}-related objects when overlay + * address spaces are involved. A common case for this is a Diff type operation. + * + * @return true if this factory contains one or more stale overlay address space instances. + */ + public default boolean hasStaleOverlayCondition() { + return false; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java index f389c84166..3936ec054d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressIterator.java @@ -67,4 +67,9 @@ public interface AddressIterator extends Iterator, Iterable { @Override public boolean hasNext(); + @Override + default Iterator iterator() { + return this; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java index f9447ab9ed..5293fda0ca 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressMapImpl.java @@ -17,8 +17,6 @@ package ghidra.program.model.address; import java.util.*; -import ghidra.util.UniversalIdGenerator; - /** * AddressMapImpl
provides a stand-alone AddressMap. * An AddressMapImpl instance should only be used to decode keys which it has generated. @@ -77,7 +75,6 @@ public class AddressMapImpl { for (int i = 0; i < sortedBaseStartAddrs.length; i++) { long max = sortedBaseStartAddrs[i].getAddressSpace().getMaxAddress().getOffset(); max = max < 0 ? MAX_OFFSET : Math.min(max, MAX_OFFSET); - // Avoid use of add which fails for overlay addresses which have restricted min/max offsets long off = sortedBaseStartAddrs[i].getOffset() | max; sortedBaseEndAddrs[i] = sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off); @@ -332,15 +329,16 @@ public class AddressMapImpl { private static class ObsoleteOverlaySpace extends OverlayAddressSpace { private final OverlayAddressSpace originalSpace; + private String name; ObsoleteOverlaySpace(OverlayAddressSpace ovSpace) { - super(makeName(), ovSpace.getOverlayedSpace(), ovSpace.getUnique(), - ovSpace.getMinOffset(), ovSpace.getMaxOffset()); + super(ovSpace.getOverlayedSpace(), ovSpace.getUnique(), createName(ovSpace)); this.originalSpace = ovSpace; + this.name = createName(ovSpace); } - private static String makeName() { - return "DELETED_" + Long.toHexString(UniversalIdGenerator.nextID().getValue()); + private static String createName(OverlayAddressSpace ovSpace) { + return "DELETED_" + ovSpace.getName() + "_" + ovSpace.getSpaceID(); } OverlayAddressSpace getOriginalSpace() { @@ -348,20 +346,20 @@ public class AddressMapImpl { } @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof ObsoleteOverlaySpace)) { - return false; - } - ObsoleteOverlaySpace s = (ObsoleteOverlaySpace) obj; - - return originalSpace.equals(s.originalSpace) && name.equals(s.name) && - getMinOffset() == s.getMinOffset() && getMaxOffset() == s.getMaxOffset(); + public String getName() { + return name; } + + @Override + public boolean contains(long offset) { + return false; + } + + @Override + public AddressSetView getOverlayAddressSet() { + return new AddressSet(); + } + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeIterator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeIterator.java index ef29c0ee58..2d9720cb4f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeIterator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeIterator.java @@ -19,7 +19,6 @@ import java.util.Iterator; import util.CollectionUtils; - /** * AddressRangeIterator is used to iterate over some set of addresses. * @@ -27,4 +26,10 @@ import util.CollectionUtils; * @see CollectionUtils#asIterable */ -public interface AddressRangeIterator extends Iterator, Iterable {} +public interface AddressRangeIterator extends Iterator , Iterable { + + @Override + default Iterator iterator() { + return this; + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java index 1672192935..03c26539b7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSet.java @@ -724,9 +724,7 @@ public class AddressSet implements AddressSetView { entry = rbTree.getFirst(); } - Iterator iterator = addrSet.iterator(); - while (iterator.hasNext()) { - AddressRange range = iterator.next(); + for (AddressRange range : addrSet) { while (range.compareTo(entry.getValue()) > 0) { entry = entry.getSuccessor(); if (entry == null) { @@ -746,9 +744,7 @@ public class AddressSet implements AddressSetView { if (entry == null) { return false; } - Iterator iterator = addrSet.iterator(); - while (iterator.hasNext()) { - AddressRange range = iterator.next(); + for (AddressRange range : addrSet) { while (range.compareTo(entry.getValue()) > 0) { entry = entry.getSuccessor(); if (entry == null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java index 32548346fe..f79bbc9cec 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java @@ -103,6 +103,7 @@ public interface AddressSpace extends Comparable { /** * Returns the name of this address space. + * With the exception of {@link OverlayAddressSpace}, the name of an address space may not change. */ String getName(); @@ -159,7 +160,7 @@ public interface AddressSpace extends Comparable { int getUnique(); /** - * Parses the String into an address. + * Parses the String into an address within this address space. * @param addrString the string to parse as an address. * @return an address if the string parsed successfully or null if the * AddressSpace specified in the addrString is not this space. @@ -169,7 +170,7 @@ public interface AddressSpace extends Comparable { Address getAddress(String addrString) throws AddressFormatException; /** - * Parses the String into an address. + * Parses the String into an address within this address space. * @param addrString the string to parse as an address. * @param caseSensitive specifies if addressSpace names must match case. * @return an address if the string parsed successfully or null if the @@ -385,12 +386,25 @@ public interface AddressSpace extends Comparable { public boolean isSuccessor(Address addr1, Address addr2); /** - * Get the max address allowed for this AddressSpace. + * Get the maximum address allowed for this AddressSpace. + * + * NOTE: Use of this method to identify the region associated with an overlay memory block + * within its overlay address space is no longer supported. Defined regions of an overlay space + * may now be determined using {@link OverlayAddressSpace#getOverlayAddressSet()}. + * + * @return maximum address of this address space. */ public Address getMaxAddress(); /** - * Get the min address allowed for this AddressSpace + * Get the minimum address allowed for this AddressSpace. + * For a memory space the returned address will have an offset of 0 within this address space. + * + * NOTE: Use of this method to identify the region associated with an overlay memory block + * within its overlay address space is no longer supported. Defined regions of an overlay space + * may now be determined using {@link OverlayAddressSpace#getOverlayAddressSet()}. + * + * @return minimum address of this address space. */ public Address getMinAddress(); @@ -493,4 +507,24 @@ public interface AddressSpace extends Comparable { */ boolean hasSignedOffset(); + /** + * Determine if the specific name is a valid address space name (e.g., allowed + * overlay space name). NOTE: This does not perform any duplicate name checks. + * @param name name + * @return true if name is a valid space name. + */ + public static boolean isValidName(String name) { + int len = name.length(); + if (len == 0) { + return false; + } + for (int i = 0; i < len; i++) { + char c = name.charAt(i); + if (c == ':' || c <= 0x20) { + return false; + } + } + return true; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/DefaultAddressFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/DefaultAddressFactory.java index 9e1c1d8049..1ca452333f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/DefaultAddressFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/DefaultAddressFactory.java @@ -198,9 +198,6 @@ public class DefaultAddressFactory implements AddressFactory { if (addr == null) { continue; } - if (space.isOverlaySpace() && addr.getAddressSpace() != space) { - continue; - } if (space.isNonLoadedMemorySpace()) { otherList.add(addr); } @@ -412,27 +409,28 @@ public class DefaultAddressFactory implements AddressFactory { } /** - * Rename overlay with newName. - * @param oldOverlaySpaceName the existing overlay address space name - * @param newName the new name of the overlay address space. - * @return new name applied to existing overlay space - * @throws DuplicateNameException if space with newName already exists - * @throws IllegalArgumentException if specified oldOverlaySpaceName was not found as - * an existing overlay space + * Update address factory map following the rename of an overlay address space instance. + * The caller is reponsible for the actual renaming of the existing overlay space instance and + * must ensure the newName is not already assigned to another space. + * @param oldOverlaySpaceName previous name of existing overlay space + * @param newName new name for existing overlay space + * @return overlay space instance which was renamed */ - protected String renameOverlaySpace(String oldOverlaySpaceName, String newName) - throws DuplicateNameException { - if (getAddressSpace(newName) != null) { - throw new DuplicateNameException("AddressSpace named " + newName + " already exists!"); + protected OverlayAddressSpace overlaySpaceRenamed(String oldOverlaySpaceName, String newName) { + if (spaceNameTable.get(newName) != null) { + throw new AssertionError("Address space named " + newName + " already exists!"); } - AddressSpace space = getAddressSpace(oldOverlaySpaceName); - if (space != null && space.isOverlaySpace()) { - ((OverlayAddressSpace) space).setName(newName); + AddressSpace space = spaceNameTable.get(oldOverlaySpaceName); + if (space instanceof OverlayAddressSpace os) { + if (!newName.equals(os.getName())) { + throw new AssertionError( + "Overlay space " + oldOverlaySpaceName + " was not renamed"); + } spaceNameTable.remove(oldOverlaySpaceName); - spaceNameTable.put(space.getName(), space); - return newName; + spaceNameTable.put(newName, os); + return os; } - throw new IllegalArgumentException("No such overlay space: " + oldOverlaySpaceName); + throw new AssertionError("Overlay space not found: " + oldOverlaySpaceName); } /** @@ -441,10 +439,9 @@ public class DefaultAddressFactory implements AddressFactory { * @param spaceName the name of the space to remove. */ protected void removeAddressSpace(String spaceName) { - AddressSpace deletedSpace = spaceNameTable.get(spaceName); + AddressSpace deletedSpace = spaceNameTable.remove(spaceName); if (deletedSpace != null) { spaces.remove(deletedSpace); - spaceNameTable.remove(deletedSpace.getName()); spaceLookup.remove(deletedSpace.getSpaceID()); if (deletedSpace.getType() == AddressSpace.TYPE_RAM || deletedSpace.getType() == AddressSpace.TYPE_CODE) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddressSpace.java index 4e4d0de7f6..cd6a48e6ef 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddressSpace.java @@ -20,6 +20,8 @@ package ghidra.program.model.address; */ public class GenericAddressSpace extends AbstractAddressSpace { + private final String name; + /** * Constructs a new GenericAddress space with the given name, bit size, type * and unique value. @@ -77,7 +79,18 @@ public class GenericAddressSpace extends AbstractAddressSpace { * the unique id for this space. */ public GenericAddressSpace(String name, int size, int unitSize, int type, int unique) { - super(name, size, unitSize, type, unique); + super(size, unitSize, type, unique); + this.name = name; + } + + @Override + int computeHashCode() { + return name.hashCode() ^ getType(); + } + + @Override + public final String getName() { + return name; } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/OverlayAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/OverlayAddressSpace.java index 17e8efdfe1..84e9e57373 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/OverlayAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/OverlayAddressSpace.java @@ -15,93 +15,65 @@ */ package ghidra.program.model.address; -public class OverlayAddressSpace extends AbstractAddressSpace { +import java.util.Objects; + +public abstract class OverlayAddressSpace extends AbstractAddressSpace { public static final String OV_SEPARATER = ":"; - private AddressSpace originalSpace; + private final AddressSpace baseSpace; - private long databaseKey; + private final String orderedKey; - public OverlayAddressSpace(String name, AddressSpace originalSpace, int unique, - long minOffset, long maxOffset) { - super(name, originalSpace.getSize(), originalSpace.getAddressableUnitSize(), - originalSpace.getType(), unique); - - this.originalSpace = originalSpace; + /** + * Construction an overlay address space instance. + * @param baseSpace base overlayed address space + * @param unique unique index/sequence number + * @param orderedKey unique ordered key which should generally match overlay name unless + * already used (e.g., on a renamed overlay space). This associated value should not be + * changed for a given address factory instance. + */ + public OverlayAddressSpace(AddressSpace baseSpace, int unique, String orderedKey) { + super(baseSpace.getSize(), baseSpace.getAddressableUnitSize(), baseSpace.getType(), unique); + this.orderedKey = orderedKey; + this.baseSpace = baseSpace; this.setShowSpaceName(true); - - //KEEP THIS CODE - //it also validates the min and max offset - this.minOffset = minOffset; - this.maxOffset = maxOffset; - minAddress = new GenericAddress(this, minOffset); - maxAddress = new GenericAddress(this, maxOffset); } - // public Address addNoWrap(Address addr, long displacement) throws AddressOverflowException { -// addr = super.addNoWrap(addr, displacement); -// -// return translateAddress(addr); -// } -// -// public Address addWrap(Address addr, long displacement) { -// addr = super.addWrap(addr, displacement); -// -// return translateAddress(addr); -// } -// -// public Address getAddress(long offset, long namespaceID) { -// return translateAddress(super.getAddress(offset, namespaceID)); -// } -// -// public Address getAddress(long offset) { -// return translateAddress(super.getAddress(offset)); -// } -// + /** + * Get the ordered key assigned to this overlay address space instance This value is used + * when performing {@link #equals(Object)} and {@link AddressSpace#compareTo(AddressSpace)} + * operations. + * + * If this value does not have its optimal value (i.e., same as address space name), the + * associated {@link AddressFactory} should report a + * {@link AddressFactory#hasStaleOverlayCondition() stale overlay condition}. + * @return instance ordered key + */ + public String getOrderedKey() { + return orderedKey; + } + @Override - public Address getAddress(String addrString) throws AddressFormatException { - addrString = addrString.replaceAll("::", ":"); - - int firstColonPos = addrString.indexOf(":"); - int lastColonPos = addrString.lastIndexOf(":"); - - if (firstColonPos != lastColonPos) { - String middleName = addrString.substring(firstColonPos + 1, lastColonPos); - if (middleName.equals(originalSpace.getName())) { - addrString = - addrString.substring(0, firstColonPos) + addrString.substring(lastColonPos); - } - } - return super.getAddress(addrString); -// return translateAddress(super.getAddress(addrString)); - + int computeHashCode() { + return Objects.hash(orderedKey, baseSpace); } -// public Address next(Address addr) { -// addr = super.next(addr); -// if (addr != null && contains(addr.getOffset())) { -// return addr; -// } -// return null; -// } -// -// public Address previous(Address addr) { -// addr = super.previous(addr); -// if (addr != null && contains(addr.getOffset())) { -// return addr; -// } -// return null; -// } + @Override + public Address getAddress(String addrString, boolean caseSensitive) + throws AddressFormatException { + addrString = addrString.replaceAll("::", Address.SEPARATOR); + return super.getAddress(addrString, caseSensitive); + } @Override public long subtract(Address addr1, Address addr2) { AddressSpace space1 = addr1.getAddressSpace(); AddressSpace space2 = addr2.getAddressSpace(); if (space1.equals(this)) { - space1 = originalSpace; + space1 = baseSpace; } if (space2.equals(this)) { - space2 = originalSpace; + space2 = baseSpace; } if (!space1.equals(space2)) { throw new IllegalArgumentException("Address are in different spaces " + @@ -110,45 +82,42 @@ public class OverlayAddressSpace extends AbstractAddressSpace { return addr1.getOffset() - addr2.getOffset(); } -// public Address subtractNoWrap(Address addr, long displacement) throws AddressOverflowException { -// return translateAddress(super.subtractNoWrap(addr, displacement)); -// } -// -// public Address subtractWrap(Address addr, long displacement) { -// return translateAddress(super.subtractWrap(addr, displacement)); -// } - @Override public boolean isOverlaySpace() { - return originalSpace != null; + return true; } + /** + * Get the overlayed (i.e., underlying) base space associated with this overlay space. + * @return overlayed base space. + */ public AddressSpace getOverlayedSpace() { - return originalSpace; + return baseSpace; } @Override public AddressSpace getPhysicalSpace() { - return originalSpace.getPhysicalSpace(); + return baseSpace.getPhysicalSpace(); } @Override public boolean hasMappedRegisters() { - return originalSpace.hasMappedRegisters(); + return baseSpace.hasMappedRegisters(); } - public long getMinOffset() { - return minOffset; - } + /** + * Determine if the specified offset is contained within a defined region of this overlay space. + * @param offset unsigned address offset + * @return true if contained within defined region otherwise false + */ + public abstract boolean contains(long offset); - public long getMaxOffset() { - return maxOffset; - } - - public boolean contains(long offset) { - return Long.compareUnsigned(minOffset, offset) <= 0 && - Long.compareUnsigned(offset, maxOffset) <= 0; - } + /** + * Get the {@link AddressSet} which corresponds to overlayed physical region which + * corresponds to the defined overlay regions within the overlay (i.e., overlay blocks). + * @return defined regions within the overlay. All addresses are overlay addresses. + */ + public abstract AddressSetView getOverlayAddressSet(); @Override public Address getAddressInThisSpaceOnly(long offset) { @@ -160,7 +129,7 @@ public class OverlayAddressSpace extends AbstractAddressSpace { if (contains(offset)) { return new GenericAddress(this, offset); } - return originalSpace.getAddress(offset); + return baseSpace.getAddress(offset); } @Override @@ -209,14 +178,14 @@ public class OverlayAddressSpace extends AbstractAddressSpace { if (!forceTranslation && contains(addr.getOffset())) { return addr; } - return new GenericAddress(originalSpace, addr.getOffset()); + return new GenericAddress(baseSpace, addr.getOffset()); } /** * @return the ID of the address space underlying this space */ public int getBaseSpaceID() { - return originalSpace.getSpaceID(); + return baseSpace.getSpaceID(); } @Override @@ -224,34 +193,51 @@ public class OverlayAddressSpace extends AbstractAddressSpace { return super.toString() + OV_SEPARATER; } - public void setName(String newName) { - name = newName; - } - - public void setDatabaseKey(long key) { - databaseKey = key; - } - - public long getDatabaseKey() { - return databaseKey; - } - @Override - public boolean equals(Object obj) { + public final boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null) { return false; } - if (!(obj instanceof OverlayAddressSpace)) { + if (getClass() != obj.getClass()) { + return false; + } + if (hashCode() != obj.hashCode()) { return false; } - OverlayAddressSpace s = (OverlayAddressSpace) obj; - return originalSpace.equals(s.originalSpace) && - name.equals(s.name) && - minOffset == s.minOffset && - maxOffset == s.maxOffset; + OverlayAddressSpace s = (OverlayAddressSpace) obj; + if (!s.orderedKey.equals(orderedKey)) { + return false; + } + + if (getType() != s.getType() || getSize() != s.getSize()) { + return false; + } + + return s.getOverlayedSpace().equals(baseSpace); } + + /** + * Compare this overlay to the spacified overlay. + * @param overlay other overlay to be checked for eqauality + * @return see {@link Comparable#compareTo(Object)} + */ + int compareOverlay(OverlayAddressSpace overlay) { + if (overlay == this) { + return 0; + } + int rc = baseSpace.compareTo(overlay.baseSpace); + if (rc != 0) { + return rc; + } + int c = getType() - overlay.getType(); + if (c == 0) { + c = orderedKey.compareTo(overlay.orderedKey); + } + return c; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java index 71f7581304..3f9d5c6616 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/SegmentedAddressSpace.java @@ -15,9 +15,8 @@ */ package ghidra.program.model.address; -import org.apache.commons.lang3.StringUtils; - import ghidra.util.NumericUtilities; +import ghidra.util.StringUtilities; /** * Address Space for dealing with (intel) segmented address spaces. @@ -134,26 +133,17 @@ public class SegmentedAddressSpace extends GenericAddressSpace { return null; } - /** - * - * @see ghidra.program.model.address.AddressSpace#getAddress(java.lang.String) - */ - @Override - public Address getAddress(String addrString) throws AddressFormatException { - return getAddress(addrString, true); - } - @Override public Address getAddress(String addrString, boolean caseSensitive) throws AddressFormatException { - int colonPos = addrString.indexOf(':'); + int colonPos = addrString.indexOf(Address.SEPARATOR); if (colonPos >= 0) { String addrSpaceStr = addrString.substring(0, colonPos); String offStr = addrString.substring(colonPos + 1); - if (StringUtils.equals(getName(), addrSpaceStr)) { - colonPos = offStr.indexOf(':'); + if (StringUtilities.equals(getName(), addrSpaceStr, caseSensitive)) { + colonPos = offStr.indexOf(Address.SEPARATOR); if (colonPos >= 0) { String segString = offStr.substring(0, colonPos); offStr = offStr.substring(colonPos + 1); @@ -161,11 +151,11 @@ public class SegmentedAddressSpace extends GenericAddressSpace { } return parseNonSegmented(offStr); } + // treat addrSpaceStr as segment return parseSegmented(addrSpaceStr, offStr); } return parseNonSegmented(addrString); - } /** @@ -266,7 +256,7 @@ public class SegmentedAddressSpace extends GenericAddressSpace { } catch (NumberFormatException e) { throw new AddressFormatException( - "Cannot parse (" + segStr + ':' + offStr + ") as a number."); + "Cannot parse (" + segStr + Address.SEPARATOR + offStr + ") as a number."); } try { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java index e97a535531..9f9cc71ac6 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StandAloneDataTypeManager.java @@ -59,7 +59,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos private String programArchitectureSummary; // summary of expected program architecture protected String name; - + public static enum ArchiveWarningLevel { INFO, WARN, ERROR; } @@ -259,11 +259,10 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos ProgramArchitecture arch = getProgramArchitecture(); LanguageDescription languageDescription = arch.getLanguage().getLanguageDescription(); - msg += " '" + getName() + - "'\n Language: " + + msg += " '" + getName() + "'\n Language: " + languageDescription.getLanguageID() + " Version " + - languageDescription.getVersion() + ".x" + - ", CompilerSpec: " + arch.getCompilerSpec().getCompilerSpecID(); + languageDescription.getVersion() + ".x" + ", CompilerSpec: " + + arch.getCompilerSpec().getCompilerSpecID(); } break; case DATA_ORG_CHANGED: @@ -346,8 +345,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos LanguageVersionException languageVersionExc = null; try { language = DefaultLanguageService.getLanguageService().getLanguage(languageId); - languageVersionExc = - LanguageVersionException.check(language, languageVersion, -1); // don't care about minor version + languageVersionExc = LanguageVersionException.check(language, languageVersion, -1); // don't care about minor version } catch (LanguageNotFoundException e) { warning = ArchiveWarning.LANGUAGE_NOT_FOUND; @@ -417,7 +415,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos final Language lang = language; final CompilerSpec cspec = compilerSpec; - final AddressFactory addrFactory = new ProgramAddressFactory(lang, cspec); + final AddressFactory addrFactory = new ProgramAddressFactory(lang, cspec, s -> null); super.setProgramArchitecture(new ProgramArchitecture() { @@ -451,7 +449,6 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos super.handleDataOrganizationChange(openMode, monitor); } - /** * Get the program architecture information which has been associated with this * datatype manager. If {@link #getProgramArchitecture()} returns null this method @@ -545,7 +542,6 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos warning == ArchiveWarning.COMPILER_SPEC_NOT_FOUND; } - /** * Clear the program architecture setting and all architecture-specific data from this archive. * Archive will revert to using the default {@link DataOrganization}. @@ -646,12 +642,12 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos lock.acquire(); try { - + if (!isArchitectureChangeAllowed()) { throw new UnsupportedOperationException( "Program-architecture change not permitted"); } - + if (!dbHandle.canUpdate()) { throw new ReadOnlyException("Read-only Archive: " + getName()); } @@ -662,18 +658,18 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos ", CompilerSpec: " + compilerSpecId); CompilerSpec compilerSpec = language.getCompilerSpecByID(compilerSpecId); - + // This type of datatype manager only uses VariableStorageManagerDB VariableStorageManagerDB variableStorageMgr = (VariableStorageManagerDB) getVariableStorageManager(); - + int txId = startTransaction("Set Program Architecture"); try { ProgramArchitectureTranslator translator = null; - + ProgramArchitecture oldArch = getProgramArchitecture(); if (oldArch != null || isProgramArchitectureMissing()) { - + if (updateOption == LanguageUpdateOption.CLEAR) { deleteAllProgramArchitectureData(monitor); variableStorageMgr = null; @@ -709,12 +705,12 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos oldArch.getCompilerSpec().getCompilerSpecID(), language, compilerSpecId); } - + if (translator != null && variableStorageMgr != null) { variableStorageMgr.setLanguage(translator, monitor); } } - + ProgramArchitecture programArchitecture = new ProgramArchitecture() { @Override @@ -782,8 +778,8 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos if (getProgramArchitecture() != null || isProgramArchitectureUpgradeRequired() || isProgramArchitectureMissing()) { - throw new UnsupportedOperationException( - "Program-architecture change not permitted with this method"); + throw new UnsupportedOperationException( + "Program-architecture change not permitted with this method"); } if (store) { @@ -816,7 +812,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT); } - + @Override public Transaction openTransaction(String description) throws IllegalStateException { return new Transaction() { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java index fff8540d5e..73411665f3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/listing/Program.java @@ -19,6 +19,7 @@ import java.util.Date; import ghidra.framework.store.LockException; import ghidra.program.database.IntRangeMap; +import ghidra.program.database.ProgramOverlayAddressSpace; import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.*; @@ -30,7 +31,9 @@ import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.symbol.*; import ghidra.program.model.util.AddressSetPropertyMap; import ghidra.program.model.util.PropertyMapManager; +import ghidra.util.InvalidNameException; import ghidra.util.exception.DuplicateNameException; +import ghidra.util.exception.NotFoundException; import ghidra.util.task.TaskMonitor; /** @@ -117,7 +120,6 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur public SymbolTable getSymbolTable(); /** - * Returns the external manager. * @return the external manager */ @@ -270,12 +272,14 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur * Returns the language used by this program. * @return the language used by this program. */ + @Override public Language getLanguage(); /** * Returns the CompilerSpec currently used by this program. * @return the compilerSpec currently used by this program. */ + @Override public CompilerSpec getCompilerSpec(); /** @@ -324,6 +328,7 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur * Returns the AddressFactory for this program. * @return the program address factory */ + @Override public AddressFactory getAddressFactory(); /** @@ -351,6 +356,45 @@ public interface Program extends DataTypeManagerDomainObject, ProgramArchitectur */ public void invalidate(); + /** + * Create a new overlay space based upon the given base AddressSpace + * @param overlaySpaceName the name of the new overlay space. + * @param baseSpace the base AddressSpace to overlay (i.e., overlayed-space) + * @return the new overlay space + * @throws DuplicateNameException if an address space already exists with specified overlaySpaceName. + * @throws LockException if the program is shared and not checked out exclusively. + * @throws IllegalStateException if image base override is active + * @throws InvalidNameException if overlaySpaceName contains invalid characters + */ + public ProgramOverlayAddressSpace createOverlaySpace(String overlaySpaceName, + AddressSpace baseSpace) throws IllegalStateException, DuplicateNameException, + InvalidNameException, LockException; + + /** + * Rename an existing overlay address space. + * NOTE: This experimental method has known limitations with existing {@link Address} and + * {@link AddressSpace} objects following an undo/redo which may continue to refer to the old + * overlay name which may lead to unxpected errors. + * @param overlaySpaceName overlay address space name + * @param newName new name for overlay + * @throws NotFoundException if the specified overlay space was not found + * @throws InvalidNameException if new name is invalid + * @throws DuplicateNameException if new name already used by another address space + * @throws LockException if program does not has exclusive access + */ + public void renameOverlaySpace(String overlaySpaceName, String newName) + throws NotFoundException, InvalidNameException, DuplicateNameException, LockException; + + /** + * Remove the specified overlay address space from this program. + * @param overlaySpaceName overlay address space name + * @return true if successfully removed, else false if blocks still make use of overlay space. + * @throws LockException if program does not has exclusive access + * @throws NotFoundException if specified overlay space not found in program + */ + public boolean removeOverlaySpace(String overlaySpaceName) + throws LockException, NotFoundException; + /** * Returns the register with the given name; * @param name the name of the register to retrieve diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java index 01feccccf6..7037ba94fa 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java @@ -29,7 +29,54 @@ import ghidra.util.exception.NotFoundException; import ghidra.util.task.TaskMonitor; /** - * Interface for Memory. + * {@link Memory} provides the ability to inspect and manage the memory model for a {@link Program}. + * In addition to conventional {@link MemoryBlock}s defined within physical memory + * {@link AddressSpace}s other special purpose memory block types may be defined (e.g., + * byte-mapped, bit-mapped, overlays, etc.). + *
+ * All memory block manipulations require excusive access (see {@link Program#hasExclusiveAccess()}) + * and all memory changes should generally be completed prior to analysis. In particular, adding + * additional overlay blocks to an existing overlay space that has already been analyzed should be + * avoided. Code references discovered during analysis from an overlay block will give preference + * to remaining within the corresponding overlay address space provided a block exists at the + * referenced offset. + *
+ * Block Types + *
+ *
+ *- Initialized - a memory block which defines a memory region with specific data. + * Data may be initialized from defined {@link FileBytes}, an {@link InputStream}, or set to all + * zeros.
+ *- Uninitialized - a memory block which defines a memory region whose data is unknown.
+ *- Byte-Mapped - a memory block whose bytes are mapped to another memory region using + * either a 1:1 byte-mapping or other specified mapping scheme (see {@link ByteMappingScheme}). + * Byte read/write operations are passed-through the mapped region. + *
+ *- Bit-Mapped - a memory block whose bytes are mapped to a corresponding bit in another + * memory region where a mapped byte has a value of 0 or 1 only. Byte read/write operations are + * passed-through to the corresponding bit within the mapped region.
+ *+ * Overlay Blocks + * An overlay memory block provides the ability to define alternate content for a physical memory + * region. Any of the Block Types above may be created as an overlay block. The use of an overlay + * block and its corresponding overlay address space can be used to reflect a different execution + * context. Use of overlays during analysis has limitations that must be considered.
+ *+ * Loaded vs. Non-Loaded + * A special purpose {@link AddressSpace#OTHER_SPACE} has been established for storing adhoc + * non-loaded data as a memory block. This is frequently used for storing portions of a file + * that never actually get loaded into memory. All blocks created using the + * {@link AddressSpace#OTHER_SPACE} must be created as an overlay memory block. All other + * blocks based upon a memory address space, including overlays, are treated as Loaded and + * use offsets into a physical memory space.
+ *+ * Sub-Blocks + * When a memory block is first created it corresponds to a single sub-block. When + * a block join operation is performed the resulting block will consist of multiple sub-blocks. + * However, the join operation is restricted to default block types only and does not support + * byte/bit-mapped types. + *
*/ public interface Memory extends AddressSetView { @@ -118,24 +165,28 @@ public interface Memory extends AddressSetView { public LiveMemoryHandler getLiveMemoryHandler(); /** - * Returns true if exclusive lock exists and memory blocks may be - * created, removed, split, joined or moved. If false is returned, - * these types of methods will throw a LockException. The manner in which - * a lock is acquired is application specific. - */ -// public boolean haveLock(); - - /** - * Create an initialized memory block and add it to this Memory. + * Create an initialized memory block based upon a data {@link InputStream} and add it to + * this Memory. + *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start start address of the block * @param is source of the data used to fill the block or null for zero initialization. * @param length the size of the block * @param monitor task monitor - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Initialized Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a @@ -150,16 +201,29 @@ public interface Memory extends AddressSetView { CancelledException, IllegalArgumentException; /** - * Create an initialized memory block and add it to this Memory. + * Create an initialized memory block initialized and add it to this Memory. All bytes + * will be initialized to the specified value (NOTE: use of zero as the initial value + * is encouraged for reduced storage). + *- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start start of the block * @param size block length (positive non-zero value required) * @param initialValue initialization value for every byte in the block. * @param monitor progress monitor, may be null. - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Initialized Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a @@ -175,16 +239,26 @@ public interface Memory extends AddressSetView { /** * Create an initialized memory block using bytes from a {@link FileBytes} object. - * + *- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start starting address of the block * @param fileBytes the {@link FileBytes} object to use as the underlying source of bytes. * @param offset the offset into the FileBytes for the first byte of this memory block. * @param size block length (positive non-zero value required) - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Initialized Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a @@ -200,13 +274,24 @@ public interface Memory extends AddressSetView { /** * Create an uninitialized memory block and add it to this Memory. + *- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start start of the block * @param size block length - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Uninitialized Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a @@ -219,16 +304,29 @@ public interface Memory extends AddressSetView { MemoryConflictException, AddressOverflowException; /** - * Create a bit overlay memory block and add it to this Memory. + * Create a bit-mapped overlay memory block and add it to this Memory. Each byte address + * within the resulting memory block will correspond to a single bit location within the mapped + * region specified by {@code mappedAddress}. + *- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start start of the block * @param mappedAddress start address in the source block for the * beginning of this block * @param length block length - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Bit Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a @@ -243,8 +341,19 @@ public interface Memory extends AddressSetView { AddressOverflowException, IllegalArgumentException; /** - * Create a memory block that uses the bytes located at a different location with a 1:1 - * byte mapping scheme. + * Create a byte-mapped memory block and add it to this memory. Each byte address + * within the resulting memory block will correspond to a byte within the mapped + * region specified by {@code mappedAddress}. While a 1:1 byte-mapping is the default, + * a specific byte-mapping ratio may be specified. + *- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start start of the block @@ -252,9 +361,11 @@ public interface Memory extends AddressSetView { * beginning of this block * @param length block length * @param byteMappingScheme byte mapping scheme (may be null for 1:1 mapping) - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Bit Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a previous block @@ -266,17 +377,29 @@ public interface Memory extends AddressSetView { MemoryConflictException, AddressOverflowException, IllegalArgumentException; /** - * Create a memory block that uses the bytes located at a different location with a 1:1 - * byte mapping scheme. + * Create a byte-mapped memory block and add it to this memory. Each byte address + * within the resulting memory block will correspond to a byte within the mapped + * region specified by {@code mappedAddress} using a 1:1 byte-mapping. + *- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *+ * Overlay Blocks: An overlay memory block may be created in two ways: + *
+ *
* @param name block name (See {@link Memory#isValidMemoryBlockName(String)} for * naming rules) * @param start start of the block * @param mappedAddress start address in the source block for the * beginning of this block * @param length block length - * @param overlay if true, the block will be created as an OVERLAY which means that a new - * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address parameter, but in the new address space. + * @param overlay if true, the block will be created as an OVERLAY block. If the {@code start} + * address is a non-overlay memory address a new overlay address space will be created and the + * block will have a starting address at the same offset within the new overlay space. If the + * specified {@code start} address is an overlay address an overlay block will be created at + * that overlay address. * @return new Bit Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a previous block @@ -871,7 +994,8 @@ public interface Memory extends AddressSetView { * @return a list of addresses that are associated with the given * FileBytes and offset */ - public default List locateAddressesForFileBytesOffset(FileBytes fileBytes, long offset) { + public default List locateAddressesForFileBytesOffset(FileBytes fileBytes, + long offset) { List list = new ArrayList<>(); for (MemoryBlock memBlock : getBlocks()) { for (MemoryBlockSourceInfo info : memBlock.getSourceInfos()) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java index 130d48779b..946bbd6223 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java @@ -118,8 +118,7 @@ public interface MemoryBlock extends Serializable, Comparable- Specifying a {@code start} address within an existing overlay address space + * ({@code overlay} parameter is ignored), or
+ *- Specifying a {@code start} address within a physical memory address space and passing + * {@code overlay=true}. This use case will force the creation of a new unique overlay + * address space.
+ *{ * @throws IllegalArgumentException if invalid name specified * @throws LockException renaming an Overlay block without exclusive access */ - public void setName(String name) - throws IllegalArgumentException, LockException; + public void setName(String name) throws IllegalArgumentException, LockException; /** * Get the comment associated with this block. @@ -289,8 +288,11 @@ public interface MemoryBlock extends Serializable, Comparable { /** * Return whether this block has been initialized. - * - * @return true if block is fully initialized else false + * + * WARNING: A mapped memory block may have a mix of intialized, uninitialized, and undefined + * regions. The value returned by this method for a mapped memory block is always false + * even if some regions are initialized. + * @return true if block is fully initialized and not a memory-mapped-block, else false */ public boolean isInitialized(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ChangeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ChangeManager.java index 4f99369021..b91a7277ad 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ChangeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ChangeManager.java @@ -505,6 +505,21 @@ public interface ChangeManager { */ public static final int DOCR_OBJECT_CREATED = 132; + /** + * An overlay address space was added. + */ + public static final int DOCR_OVERLAY_SPACE_ADDED = 133; + + /** + * An overlay address space was removed. + */ + public static final int DOCR_OVERLAY_SPACE_REMOVED = 134; + + /** + * An overlay address space was renamed. + */ + public static final int DOCR_OVERLAY_SPACE_RENAMED = 135; + /////////////////////////////////////////////////////////////////////// // // Trees @@ -840,8 +855,8 @@ public interface ChangeManager { * @param newValue new value or an Object that is related to the * the event */ - public void setObjChanged(int type, AddressSetView addrSet, Object affectedObj, - Object oldValue, Object newValue); + public void setObjChanged(int type, AddressSetView addrSet, Object affectedObj, Object oldValue, + Object newValue); /** * Mark the state of a Program as having changed and generate diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java index 1bc6cc6096..086c78a787 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java @@ -38,7 +38,7 @@ import ghidra.util.Msg; * is the character position within the display item specified by the row and column. Simple fields * like the address field and Mnemonic field will always have a row and column of 0. */ -public class ProgramLocation implements Comparable
{ +public class ProgramLocation implements Cloneable, Comparable { protected Program program; protected Address addr; @@ -477,4 +477,31 @@ public class ProgramLocation implements Comparable { public int getCharOffset() { return charOffset; } + + @Override + protected final Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + /** + * Create a new translated copy of the specified {@link ProgramLocation} using the specified + * {@link Program program} + * @param loc original program location + * @param program updated program + * @param translatedAddress original loc address translated for using within specified program + * @return translated program location + */ + public static ProgramLocation getTranslatedCopy(ProgramLocation loc, Program program, + Address translatedAddress) { + try { + ProgramLocation translatedLoc = (ProgramLocation) loc.clone(); + translatedLoc.program = program; + translatedLoc.addr = translatedAddress; + return translatedLoc; + } + catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleDiffUtility.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleDiffUtility.java index 4667c3ac92..ad8062f6e5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleDiffUtility.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleDiffUtility.java @@ -228,8 +228,9 @@ public class SimpleDiffUtility { return otherProgram.getAddressFactory().getStackSpace().getAddress(addr.getOffset()); } else if (addr.isRegisterAddress()) { - if (program.getLanguage().getLanguageID().equals( - otherProgram.getLanguage().getLanguageID())) { + if (program.getLanguage() + .getLanguageID() + .equals(otherProgram.getLanguage().getLanguageID())) { return addr; } // TODO: should we handle small varnodes within big endian registers @@ -281,17 +282,12 @@ public class SimpleDiffUtility { AddressSpace addrSpace = addr.getAddressSpace(); AddressSpace otherSpace = getCompatibleAddressSpace(addrSpace, otherProgram); if (otherSpace != null) { - if (addrSpace.isOverlaySpace()) { - long offset = addr.getOffset(); - if (offset < otherSpace.getMinAddress().getOffset()) { - return exactMatchOnly ? null : otherSpace.getMinAddress(); - } - else if (offset > otherSpace.getMaxAddress().getOffset()) { - return exactMatchOnly ? null : otherSpace.getMaxAddress(); - } - return otherSpace.getAddress(offset); + try { + return otherSpace.getAddressInThisSpaceOnly(addr.getOffset()); + } + catch (AddressOutOfBoundsException e) { + return null; } - return otherSpace.getAddress(addr.getOffset()); } return null; } @@ -300,20 +296,15 @@ public class SimpleDiffUtility { Program otherProgram) { AddressSpace otherSpace = otherProgram.getAddressFactory().getAddressSpace(addrSpace.getName()); - if (otherSpace != null && otherSpace.getType() == addrSpace.getType()) { + if (otherSpace != null && otherSpace.getType() == addrSpace.getType() && + otherSpace.isOverlaySpace() == addrSpace.isOverlaySpace()) { int id = addrSpace.isOverlaySpace() ? ((OverlayAddressSpace) addrSpace).getBaseSpaceID() : addrSpace.getSpaceID(); int otherid = otherSpace.isOverlaySpace() ? ((OverlayAddressSpace) otherSpace).getBaseSpaceID() : otherSpace.getSpaceID(); + // NOTE: This only works for the same language if (id == otherid) { - if (otherSpace.isOverlaySpace()) { - long addrOffset = addrSpace.getMinAddress().getOffset(); - long otherOffset = otherSpace.getMinAddress().getOffset(); - if (addrOffset != otherOffset) { - return null; // Overlays didn't begin at same address. - } - } return otherSpace; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java index 7d3991b7d7..69a5831dac 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/StubProgram.java @@ -24,6 +24,7 @@ import ghidra.framework.model.*; import ghidra.framework.options.Options; import ghidra.framework.store.LockException; import ghidra.program.database.IntRangeMap; +import ghidra.program.database.ProgramOverlayAddressSpace; import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.database.map.AddressMap; import ghidra.program.model.address.*; @@ -498,6 +499,22 @@ public class StubProgram implements Program { throw new UnsupportedOperationException(); } + @Override + public ProgramOverlayAddressSpace createOverlaySpace(String overlaySpaceName, + AddressSpace baseSpace) { + throw new UnsupportedOperationException(); + } + + @Override + public void renameOverlaySpace(String oldOverlaySpaceName, String newName) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeOverlaySpace(String overlaySpaceName) { + throw new UnsupportedOperationException(); + } + @Override public Address[] parseAddress(String addrStr) { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressMapImplTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressMapImplTest.java index 6d3bc1c7bf..b9cb07e187 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressMapImplTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressMapImplTest.java @@ -44,7 +44,7 @@ public class AddressMapImplTest extends AbstractGenericTest { sp32 = new GenericAddressSpace("THREE", 32, AddressSpace.TYPE_RAM, 2); sp64 = new GenericAddressSpace("FOUR", 64, AddressSpace.TYPE_RAM, 2); - ov64 = new OverlayAddressSpace("four", sp64, 100, 0x1000, 0x1fff); + ov64 = new SingleRangeOverlayAddressSpace("four", sp64, 100, 0x1000, 0x1FFF, "four"); segSpace1 = new SegmentedAddressSpace("SegSpaceOne", 3); segSpace2 = new SegmentedAddressSpace("SegSpaceTwo", 4); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressSpaceTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressSpaceTest.java index 1c8c3fe4d4..6c55e58d91 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressSpaceTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/AddressSpaceTest.java @@ -15,7 +15,7 @@ */ package ghidra.program.model.address; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.Arrays; @@ -26,13 +26,13 @@ import generic.test.AbstractGenericTest; public class AddressSpaceTest extends AbstractGenericTest { AddressSpace space1; - AddressSpace space1overlay1; - AddressSpace space1overlay2; - AddressSpace space1overlay3; + SingleRangeOverlayAddressSpace space1overlay1; + SingleRangeOverlayAddressSpace space1overlay2; + SingleRangeOverlayAddressSpace space1overlay3; AddressSpace space2; - AddressSpace space2overlay1; - AddressSpace space2overlay2; - AddressSpace space2overlay3; + SingleRangeOverlayAddressSpace space2overlay1; + SingleRangeOverlayAddressSpace space2overlay2; + SingleRangeOverlayAddressSpace space2overlay3; AddressSpace space3; public AddressSpaceTest() { @@ -43,20 +43,33 @@ public class AddressSpaceTest extends AbstractGenericTest { public void setUp() { space1 = new GenericAddressSpace("Test1", 8, AddressSpace.TYPE_RAM, 0); space2 = new GenericAddressSpace("Test2", 8, AddressSpace.TYPE_RAM, 1); - space1overlay1 = new OverlayAddressSpace("Test1overlay1", space1, 3, 0x20, 0x30); - space1overlay2 = new OverlayAddressSpace("Test1overlay2", space1, 4, 0x10, 0x20); - space1overlay3 = new OverlayAddressSpace("Test1overlay", space1, 7, 0x10, 0x50); // dup min offset - space2overlay1 = new OverlayAddressSpace("Test2overlay1", space2, 5, 0x20, 0x30); - space2overlay2 = new OverlayAddressSpace("Test2overlay2", space2, 2, 0x10, 0x20); - space2overlay3 = new OverlayAddressSpace("Test2overlay", space2, 6, 0x10, 0x50); // dup min offset + space1overlay1 = new SingleRangeOverlayAddressSpace("Test1overlay1", space1, 3, 0x20, 0x30, + "Test1overlay1"); + space1overlay2 = new SingleRangeOverlayAddressSpace("Test1overlay2", space1, 4, 0x10, 0x20, + "Test1overlay2"); + space1overlay3 = new SingleRangeOverlayAddressSpace("Test1overlay", space1, 7, 0x10, 0x50, + "Test1overlay"); + space2overlay1 = new SingleRangeOverlayAddressSpace("Test2overlay1", space2, 5, 0x20, 0x30, + "Test2overlay1"); + space2overlay2 = new SingleRangeOverlayAddressSpace("Test2overlay2", space2, 2, 0x10, 0x20, + "Test2overlay2"); + space2overlay3 = new SingleRangeOverlayAddressSpace("Test2overlay", space2, 6, 0x10, 0x50, + "Test2overlay"); } @Test public void testCompareTo() { + // Primary sort for overlay space is based on underlying base space name, while + // secondary order is based upon its assigned "orderedKey" which is unique within its + // associated AddressFactory. AddressSpace[] spaces = new AddressSpace[] { space1, space2, space1overlay1, space2overlay1, space1overlay2, space2overlay2, space1overlay3, space2overlay3, space1, space2, space1overlay1, space2overlay1, space1overlay2, space2overlay2, space1overlay3, space2overlay3, }; + // Address factories do not permit multiple address spaces with the same name or orderedKey. + // Those overlaying the same space which have the same orderedKey are considered the same + // for comparison/equals even if the name differs. This has been implemented in this + // manner so that AddressSets remain valid following the rename of an overlay space. Arrays.sort(spaces); Assert.assertEquals(space1, spaces[0]); Assert.assertEquals(space1, spaces[1]); @@ -64,37 +77,38 @@ public class AddressSpaceTest extends AbstractGenericTest { Assert.assertEquals(space2, spaces[3]); Assert.assertEquals(space1overlay3, spaces[4]); Assert.assertEquals(space1overlay3, spaces[5]); - Assert.assertEquals(space1overlay2, spaces[6]); - Assert.assertEquals(space1overlay2, spaces[7]); - Assert.assertEquals(space1overlay1, spaces[8]); - Assert.assertEquals(space1overlay1, spaces[9]); + Assert.assertEquals(space1overlay1, spaces[6]); + Assert.assertEquals(space1overlay1, spaces[7]); + Assert.assertEquals(space1overlay2, spaces[8]); + Assert.assertEquals(space1overlay2, spaces[9]); Assert.assertEquals(space2overlay3, spaces[10]); Assert.assertEquals(space2overlay3, spaces[11]); - Assert.assertEquals(space2overlay2, spaces[12]); - Assert.assertEquals(space2overlay2, spaces[13]); - Assert.assertEquals(space2overlay1, spaces[14]); - Assert.assertEquals(space2overlay1, spaces[15]); + Assert.assertEquals(space2overlay1, spaces[12]); + Assert.assertEquals(space2overlay1, spaces[13]); + Assert.assertEquals(space2overlay2, spaces[14]); + Assert.assertEquals(space2overlay2, spaces[15]); } @Test public void testEquals() { AddressSpace space1a = new GenericAddressSpace("Test1", 8, AddressSpace.TYPE_RAM, 0); AddressSpace space2a = new GenericAddressSpace("Test2", 8, AddressSpace.TYPE_RAM, 1); - AddressSpace space1overlay1a = - new OverlayAddressSpace("Test1overlay1", space1, 13, 0x20, 0x30); - AddressSpace space1overlay2a = - new OverlayAddressSpace("Test1overlay2", space1, 14, 0x10, 0x20); - AddressSpace space1overlay3a = - new OverlayAddressSpace("Test1overlay", space1, 17, 0x10, 0x50); // dup min offset - AddressSpace space2overlay1a = - new OverlayAddressSpace("Test2overlay1", space2, 15, 0x20, 0x30); - AddressSpace space2overlay2a = - new OverlayAddressSpace("Test2overlay2", space2, 12, 0x10, 0x20); - AddressSpace space2overlay3a = - new OverlayAddressSpace("Test2overlay", space2, 16, 0x10, 0x50); // dup min offset + AddressSpace space1overlay1a = new SingleRangeOverlayAddressSpace("Test1overlay1", space1, + 13, 0x20, 0x30, "Test1overlay1"); + AddressSpace space1overlay2a = new SingleRangeOverlayAddressSpace("Test1overlay2", space1, + 14, 0x10, 0x20, "Test1overlay2"); + AddressSpace space1overlay3a = new SingleRangeOverlayAddressSpace("Test1overlay", space1, + 17, 0x10, 0x50, "Test1overlay"); + AddressSpace space2overlay1a = new SingleRangeOverlayAddressSpace("Test2overlay1", space2, + 15, 0x20, 0x30, "Test2overlay1"); + AddressSpace space2overlay2a = new SingleRangeOverlayAddressSpace("Test2overlay2", space2, + 12, 0x10, 0x20, "Test2overlay2"); + AddressSpace space2overlay3a = new SingleRangeOverlayAddressSpace("Test2overlay", space2, + 16, 0x10, 0x50, "Test2overlay"); assertTrue(space1a.equals(space1)); assertTrue(space2a.equals(space2)); + assertTrue(space1overlay1a.equals(space1overlay1)); assertTrue(space1overlay2a.equals(space1overlay2)); assertTrue(space1overlay3a.equals(space1overlay3)); @@ -108,7 +122,6 @@ public class AddressSpaceTest extends AbstractGenericTest { assertTrue(!space1overlay1a.equals(space1overlay2)); assertTrue(!space1overlay1a.equals(space1overlay3)); assertTrue(!space1overlay1a.equals(space2overlay1)); - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/SingleRangeOverlayAddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/SingleRangeOverlayAddressSpace.java new file mode 100644 index 0000000000..379abec67e --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/address/SingleRangeOverlayAddressSpace.java @@ -0,0 +1,71 @@ +/* ### + * 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.address; + +/** + * {@link SingleRangeOverlayAddressSpace} provides a simple immutable overlay space + * which consists of a single memory offset range. + */ +public class SingleRangeOverlayAddressSpace extends OverlayAddressSpace { + + private String name; + private long min; + private long max; + private AddressSetView overlaySet; + + /** + * Construct a single range overlay address space. + * NOTE: The same name should not be used more than once within a given {@link AddressFactory}. + * @param name overlay space name + * @param baseSpace overlayed base space + * @param unique unique index number + * @param min min address offset + * @param max max address offset + * @param orderedKey ordered key which is used during comparison with other overlays. Within + * program-based implementation (i.e., ProgramOverlayAddressSpace) this is auto-generated based + * upon the initial name and must be unique within the associated AddressFactory which does not + * exist for this test implementation. + */ + public SingleRangeOverlayAddressSpace(String name, AddressSpace baseSpace, int unique, long min, + long max, String orderedKey) { + super(baseSpace, unique, orderedKey); + this.name = name; + this.min = min; + this.max = max; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean contains(long offset) { + return Long.compareUnsigned(offset, min) >= 0 && Long.compareUnsigned(offset, max) <= 0; + } + + @Override + public AddressSetView getOverlayAddressSet() { + if (overlaySet == null) { + AddressSet set = new AddressSet(); + AddressRange range = new AddressRangeImpl(getAddressInThisSpaceOnly(min), + getAddressInThisSpaceOnly(max)); + set.add(range); + overlaySet = set; + } + return overlaySet; + } +} diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java index f6f89d1991..8122eb972d 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java @@ -32,6 +32,7 @@ import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors.Palette; import ghidra.util.exception.AssertException; import ghidra.util.table.GhidraTable; +import ghidra.util.task.TaskMonitor; public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { @@ -40,7 +41,13 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { } @Test - public void testMemoryMap() { + public void testMemoryMap() throws Exception { + + program.withTransaction("Add Blocks", () -> { + program.getMemory() + .createInitializedBlock("OV1", addr(0x1000), 0x100, (byte) 0, TaskMonitor.DUMMY, + true); + }); performAction("Memory Map", "DockingWindows", true); @@ -48,7 +55,7 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { moveProviderToItsOwnWindow(provider); JComponent component = getDockableComponent(provider); - captureIsolatedComponent(component, 800, 225); + captureIsolatedComponent(component, 1000, 250); } @Test