GP-3903 Refactor of OverlayAddressSpaces to allow multiple blocks within

the same overlay space
This commit is contained in:
ghidra1 2023-10-18 21:55:59 -04:00
parent 7e4d2bcfaa
commit 0f95d266c3
80 changed files with 3383 additions and 1757 deletions

View file

@ -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));
}

View file

@ -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<DBTraceOverlaySpaceEntry> overlayStore;
protected final DBCachedObjectIndex<String, DBTraceOverlaySpaceEntry> overlaysByName;
private final Map<Long, AddressSpace> spacesByKey = new HashMap<>();
public DBTraceOverlaySpaceAdapter(DBHandle dbh, DBOpenMode openMode, ReadWriteLock lock,
TaskMonitor monitor, DBTrace trace) throws VersionException, IOException {
this.dbh = dbh;
@ -183,61 +182,92 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
}
protected void resyncAddressFactory(TraceAddressFactory factory) {
// Clean and rename existing overlays, first
// 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<Long, DBTraceOverlaySpaceEntry> keyToRecordMap = new HashMap<>(overlayStore.asMap());
// Examine existing overlay spaces for removals and renames
List<ProgramOverlayAddressSpace> renameList = new ArrayList<>();
for (AddressSpace space : factory.getAllAddressSpaces()) {
if (!(space instanceof OverlayAddressSpace)) {
continue;
if (space instanceof ProgramOverlayAddressSpace os) {
String name = os.getName();
DBTraceOverlaySpaceEntry ent = keyToRecordMap.get(os.getKey());
if (ent == null || !isCompatibleOverlay(os, ent, factory)) {
// Remove overlay if entry does not exist or base space differs
factory.removeOverlaySpace(name);
}
OverlayAddressSpace os = (OverlayAddressSpace) space;
DBTraceOverlaySpaceEntry ent = overlayStore.getObjectAt(os.getDatabaseKey());
if (ent == null) {
spacesByKey.remove(os.getDatabaseKey());
factory.removeOverlaySpace(os.getName());
else if (name.equals(ent.name)) {
keyToRecordMap.remove(os.getKey());
continue; // no change to space
}
else if (!os.getName().equals(ent.name)) {
factory.removeOverlaySpace(os.getName());
os.setName(ent.name);
try {
factory.addOverlayAddressSpace(os);
}
catch (DuplicateNameException e) {
throw new AssertionError(); // I just removed it
}
}
// else it's already in sync
}
// Add missing overlays
for (DBTraceOverlaySpaceEntry ent : overlayStore.asMap().values()) {
AddressSpace exists = factory.getAddressSpace(ent.name);
if (exists != null) {
// it's already in sync and/or its a physical space
continue;
}
AddressSpace baseSpace = factory.getAddressSpace(ent.baseSpace);
try {
OverlayAddressSpace space = factory.addOverlayAddressSpace(ent.name, true,
baseSpace, baseSpace.getMinAddress().getOffset(),
baseSpace.getMaxAddress().getOffset());
space.setDatabaseKey(ent.getKey());
spacesByKey.put(space.getDatabaseKey(), space);
}
catch (IllegalArgumentException e) {
throw new AssertionError(); // Name should be validated already, no?
else {
// Add space to map of those that need to be renamed
renameList.add(os);
factory.removeOverlaySpace(name);
}
}
}
protected AddressSpace doCreateOverlaySpace(String name, AddressSpace base) {
try {
// Handle all renamed overlays which had been temporarily removed from factory
for (ProgramOverlayAddressSpace existingSpace : renameList) {
long key = existingSpace.getKey();
DBTraceOverlaySpaceEntry ent = keyToRecordMap.get(key);
existingSpace.setName(ent.name);
factory.addOverlaySpace(existingSpace); // re-add renamed space
keyToRecordMap.remove(key);
}
// Add any remaing overlay which are missing from factory
for (long key : keyToRecordMap.keySet()) {
DBTraceOverlaySpaceEntry ent = keyToRecordMap.get(key);
String spaceName = ent.name;
AddressSpace baseSpace = factory.getAddressSpace(ent.baseSpace);
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, DBTraceOverlaySpaceEntry ent,
ProgramAddressFactory factory) {
AddressSpace baseSpace = factory.getAddressSpace(ent.baseSpace);
if (baseSpace == null) {
// Error condition should be handled better - language may have dropped original base space
throw new RuntimeException("Base space for overlay not found: " + ent.baseSpace);
}
return baseSpace == os.getOverlayedSpace();
}
protected AddressSpace doCreateOverlaySpace(String name, AddressSpace base)
throws DuplicateNameException {
TraceAddressFactory factory = trace.getInternalAddressFactory();
OverlayAddressSpace space =
factory.addOverlayAddressSpace(name, true, base, base.getMinAddress().getOffset(),
base.getMaxAddress().getOffset());
// Only if it succeeds do we store the record
if (!factory.isValidOverlayBaseSpace(base)) {
throw new IllegalArgumentException(
"Invalid address space for overlay: " + base.getName());
}
if (factory.getAddressSpace(name) != null) {
throw new DuplicateNameException(
"Overlay space '" + name + "' duplicates name of another address space");
}
DBTraceOverlaySpaceEntry ent = overlayStore.create();
ProgramOverlayAddressSpace space = factory.addOverlaySpace(ent.getKey(), name, base);
// Only if it succeeds do we store the record
ent.set(space.getName(), base.getName());
trace.updateViewsAddSpaceBlock(space);
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null,
trace, null, space));
trace.setChanged(
new TraceChangeRecord<>(TraceOverlaySpaceChangeType.ADDED, null, trace, null, space));
return space;
}
@ -261,8 +291,13 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
if (space != null) {
return space.getPhysicalSpace() == base ? space : null;
}
try {
return doCreateOverlaySpace(name, base);
}
catch (DuplicateNameException e) {
throw new AssertionError(e);
}
}
}
public void deleteOverlayAddressSpace(String name) {
@ -280,6 +315,8 @@ public class DBTraceOverlaySpaceAdapter implements DBTraceManager {
trace.updateViewsDeleteSpaceBlock(space);
trace.setChanged(new TraceChangeRecord<>(TraceOverlaySpaceChangeType.DELETED, null,
trace, space, null));
invalidateCache(true);
}
}
}

View file

@ -15,46 +15,51 @@
*/
package ghidra.trace.database.address;
import ghidra.program.database.ProgramAddressFactory;
import ghidra.program.database.*;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.OverlayAddressSpace;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.Language;
import ghidra.util.exception.DuplicateNameException;
public class TraceAddressFactory extends ProgramAddressFactory {
public TraceAddressFactory(Language language, CompilerSpec compilerSpec) {
super(language, compilerSpec);
public TraceAddressFactory(Language language, CompilerSpec compilerSpec,
OverlayRegionSupplier overlayRegionSupplier) {
super(language, compilerSpec, overlayRegionSupplier);
}
@Override
protected boolean validateOriginalSpace(AddressSpace originalSpace) {
return (originalSpace.isMemorySpace() || originalSpace.isRegisterSpace()) &&
!originalSpace.isOverlaySpace();
}
@Override
protected boolean assignUniqueID(AddressSpace originalSpace) {
return super.assignUniqueID(originalSpace) ||
originalSpace.getType() == AddressSpace.TYPE_REGISTER;
protected boolean isValidOverlayBaseSpace(AddressSpace baseSpace) {
return super.isValidOverlayBaseSpace(baseSpace) ||
baseSpace.getType() == AddressSpace.TYPE_REGISTER;
}
@Override // for peer access
protected OverlayAddressSpace addOverlayAddressSpace(String name, boolean preserveName,
AddressSpace originalSpace, long minOffset, long maxOffset) {
return super.addOverlayAddressSpace(name, preserveName, originalSpace, minOffset,
maxOffset);
protected ProgramOverlayAddressSpace addOverlaySpace(long key, String overlayName,
AddressSpace baseSpace) throws DuplicateNameException {
return super.addOverlaySpace(key, overlayName, baseSpace);
}
@Override // for peer access
protected void addOverlayAddressSpace(OverlayAddressSpace ovSpace)
protected void addOverlaySpace(ProgramOverlayAddressSpace ovSpace)
throws DuplicateNameException {
super.addOverlayAddressSpace(ovSpace);
super.addOverlaySpace(ovSpace);
}
@Override // for peer access
protected void removeOverlaySpace(String name) {
super.removeOverlaySpace(name);
}
@Override // for peer access
protected void overlaySpaceRenamed(String oldOverlaySpaceName, String newName,
boolean refreshStatusIfNeeded) {
super.overlaySpaceRenamed(oldOverlaySpaceName, newName, refreshStatusIfNeeded);
}
@Override // for peer access
protected void refreshStaleOverlayStatus() {
super.refreshStaleOverlayStatus();
}
}

View file

@ -30,6 +30,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.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
@ -63,8 +64,7 @@ import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.*;
import ghidra.util.datastruct.WeakValueHashMap;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
/**
@ -1021,6 +1021,25 @@ public class DBTraceProgramView implements TraceProgramView {
return changes;
}
@Override
public ProgramOverlayAddressSpace createOverlaySpace(String overlaySpaceName,
AddressSpace baseSpace) throws IllegalStateException, DuplicateNameException,
InvalidNameException, LockException {
throw new UnsupportedOperationException();
}
@Override
public void renameOverlaySpace(String oldOverlaySpaceName, String newName)
throws NotFoundException, InvalidNameException, DuplicateNameException, LockException {
throw new UnsupportedOperationException();
}
@Override
public boolean removeOverlaySpace(String overlaySpaceName)
throws LockException, NotFoundException {
throw new UnsupportedOperationException();
}
@Override
public AddressFactory getAddressFactory() {
return trace.getBaseAddressFactory();

View file

@ -25,6 +25,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.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.data.CategoryPath;
@ -43,8 +44,8 @@ import ghidra.trace.model.data.TraceBasedDataTypeManager;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewMemory;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
public class DBTraceProgramViewRegisters implements TraceProgramView {
@ -293,6 +294,25 @@ public class DBTraceProgramViewRegisters implements TraceProgramView {
return view.getImageBase();
}
@Override
public ProgramOverlayAddressSpace createOverlaySpace(String overlaySpaceName,
AddressSpace baseSpace) throws IllegalStateException, DuplicateNameException,
InvalidNameException, LockException {
return view.createOverlaySpace(overlaySpaceName, baseSpace);
}
@Override
public void renameOverlaySpace(String overlaySpaceName, String newName)
throws NotFoundException, InvalidNameException, DuplicateNameException, LockException {
view.renameOverlaySpace(overlaySpaceName, newName);
}
@Override
public boolean removeOverlaySpace(String overlaySpaceName)
throws LockException, NotFoundException {
return view.removeOverlaySpace(overlaySpaceName);
}
@Override
public void setImageBase(Address base, boolean commit)
throws AddressOverflowException, LockException, IllegalStateException {

View file

@ -60,8 +60,23 @@
by importers. At this point in time there is no capability provided by the Memory Map provider to create a
new File Bytes instance.</P>
<P><A name="OverlayType"></A><B>Overlay</B> - Each of the above memory block types may optionally be specified as an <I>Overlay</I> at the
time of creation. If this option is selected, the block is created in a new
<P><A name="OverlayType"></A><B>Overlay</B> - Each of the above memory block types may
optionally be created as an <I>Overlay</I> 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:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
If this option is selected, the block is created in a new
overlay address space.&nbsp; Overlay blocks can serve various
purposes where a memory range may contain different data/code or map to different areas of memory
at any given point in time or processor state. &nbsp; Note that
@ -87,29 +102,35 @@
<P>Each row displays information about one of the memory blocks.&nbsp; The following summarizes
the information about each block.</P>
<P><IMG src="help/shared/note.png" border="0">Many of the Memory Map table fields may be
modified to alter the memory block specification (such fields are marked with an '*').</P>
<BLOCKQUOTE>
<P><B><I>Name</I></B> - Name of the memory block.&nbsp;</P>
<P><B><I>Name * </I></B> - Name of the memory block.&nbsp;</P>
<P><I><B>Start -</B></I> The starting address (in hex) of the memory block.</P>
<P><I><B>Start -</B></I> 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.</P>
<P><B><I>End -</I></B> The Ending address (in hex) of the memory block.</P>
<P><B><I>End -</I></B> 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.</P>
<P><I><B>Length -</B></I> The length (in hex) of the memory block.</P>
<P><I><B>R -</B></I> Indicates read permission.</P>
<P><I><B>R * -</B></I> Indicates read permission.</P>
<P><I><B>W -</B></I> Indicates write permission.</P>
<P><I><B>W * -</B></I> Indicates write permission.</P>
<P><I><B>X -</B></I> Indicates execute permission.</P>
<P><I><B>X * -</B></I> Indicates execute permission.</P>
<P><B>Volatile</B> - Indicates a region of volatile I/O Memory.</P>
<P><B>Volatile * </B> - Indicates a region of volatile I/O Memory.</P>
<P><I><B>Overlay -</B></I> Indicates if block is defined as a memory overlay.</P>
<P><I><B>Overlayed Space -</B></I> 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.</P>
<P><I><B>Type -</B></I> Indicates whether the block is a <A href="#DefaultType">Default</A>,
<A href="#BitMappedType">Bit Mapped</A> or <A href="#ByteMappedType">Byte Mapped</A> type of block.</P>
<P><I><B>Initialized -</B></I> Indicates whether the block has been initialized with values;
<P><I><B>Initialized * -</B></I> Indicates whether the block has been initialized with values;
this property applies to Default and Overlay blocks.</P>
<P><I><B>Byte Source -</B></I> Provides information about the source of the bytes in this
@ -121,11 +142,26 @@
<P><I><B>Source -</B></I> Description of block origination.</P>
<P><I><B>Comment -</B></I> User added comment about this memory block.</P>
<P><I><B>Comment * -</B></I> User added comment about this memory block.</P>
<P>&nbsp;</P>
</BLOCKQUOTE>
<H2><A name="OverlaySpaceRename"></A>Rename Overlay Address Space</H2>
<BLOCKQUOTE>
<P>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 <B>Rename Overlay Space</B> 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.</P>
<P><IMG src="help/shared/note.png" border="0">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.</P>
</BLOCKQUOTE>
<H2><A name="MemoryBlockEdits"></A>Memory Block Edits</H2>
<BLOCKQUOTE>
@ -190,12 +226,10 @@
<H3><A name="Add_Block"></A><B><IMG src="images/Plus.png" border="0">&nbsp; Add</B></H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>Select <B>Add</B> to bring up the <I>Add Memory Block</I> dialog.&nbsp; Fill in the
requested information and select the <B>OK</B> button.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P>&nbsp;</P>
@ -209,7 +243,6 @@
<BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>&nbsp;</P>
@ -218,8 +251,9 @@
<P><B><I>Start Addr</I></B> - Enter the start address of the new memory block.&nbsp; If the
program language defines multiple address spaces, the address space must also be
specified.&nbsp; The address space selection will not appear if only one is defined.
Overlay spaces are not included in the list of spaces.&nbsp; Within the defaul address
space, a block may not span across the current Image Base Address.<BR>
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.<BR>
</P>
<P><B><I>Length -</I></B> Enter the length of the new memory block.</P>
@ -234,7 +268,15 @@
<P><B>Volatile</B> - Marks this block as volatile I/O memory.</P>
<P><B>Overlay</B> - Creates the block as an overlay within a corresponding overlay address space.</P>
<P><B>Overlay</B> - Creates the block as an overlay block. An overlay memory block may be
created in two ways:
<ul>
<li>Specifying a <B>Start Addr</B> within an existing overlay address space
(this <B>Overlay</B> option is redudant and ignored), or</li>
<li>Specifying a <B>Start Addr</B> within a physical memory address space and enabling
this <B>Overlay</B> option. This use case will force the creation of a new unique overlay
address space.</li>
</ul>
<P><B><I>Block Types</I></B> - Select the block type from the combo box: <I><B>Default, Bit
Mapped or Byte Mapped</B></I>.</P>
@ -291,7 +333,6 @@
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
</BLOCKQUOTE>
<DIV align="center">
<CENTER>
@ -307,13 +348,11 @@
<H3><A name="Move_Block"></A><IMG src="images/move.png" border="0"> Move</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>Select <B>Move</B> to bring up the <I>Move Memory Block</I> dialog. The <I>Move</I>
action is enabled when exactly one memory block is selected.&nbsp; Enter either a new start
or end address to cause the block to be moved.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P>&nbsp;</P>
@ -327,7 +366,6 @@
<BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>&nbsp;</P>
@ -357,11 +395,9 @@
<LI><I>The block is an <A href="#OverlayType">Overlay</A> block.</I></LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H3><A name="Split_Block"></A><IMG src="images/verticalSplit.png" border="0">&nbsp; Split</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>Select <B>Split</B> to bring up the <I>Split Block</I> Dialog. The <I>Split</I> action
is enabled when exactly one memory block is selected.&nbsp; Use the <I>Split Block</I>
@ -378,7 +414,6 @@
<LI>Enter a length for the second block (new block).</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P>&nbsp;</P>
@ -394,7 +429,6 @@
<H3>&nbsp;</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><B><FONT color="#000080">Block to Split</FONT></B></P>
@ -424,12 +458,10 @@
<P><I><IMG src="help/shared/note.png" border="0&gt;&lt;a href=">Overlay type blocks cannot
be split.&nbsp;</I></P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H3><A name="Expand_Block_Up"></A><IMG src="images/collapse.gif" border="0">&nbsp; Expand
Up</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>Select <B>Expand Up</B> to bring up the <I>Expand Block Up</I> Dialog. The <I>Expand
Up</I> action is enabled when exactly one memory block is selected.&nbsp; Use the <I>Expand
@ -437,7 +469,6 @@
memory block.&nbsp; The block can be expanded by either entering a new start address or a
new length.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P>&nbsp;</P>
@ -451,7 +482,6 @@
<BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><B><I>New Start Address -</I></B> A new start address can be entered here.&nbsp; It must
be before the current start address.</P>
@ -464,12 +494,10 @@
<P><I><IMG src="help/shared/note.png" border="0"><A href="#OverlayType">Overlay</A> type
blocks cannot be expanded.&nbsp;</I></P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H3><A name="Expand_Block_Down"></A><IMG src="images/expand.gif" border="0">&nbsp; Expand
Down</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>Select <B>Expand Down</B> to bring up the <I>Expand Block Down</I> Dialog. The <I>Expand
Down</I> action is enabled when exactly one memory block is selected.&nbsp; Use the
@ -477,7 +505,6 @@
AFTER the memory block. The block can be expanded by either entering a new end address or a
new length.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<P>&nbsp;</P>
@ -491,7 +518,6 @@
<BR>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P><I><B>Start Address -</B></I> Displays the start address of the block (not
editable).</P>
@ -505,11 +531,9 @@
<P><I><IMG src="help/shared/note.png" border="0"><A href="#OverlayType">Overlay</A> type
blocks cannot be expanded.&nbsp;</I></P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H3><A name="Merge_Blocks"></A><IMG src="images/Merge.png" border="0">&nbsp; Merge</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>The <I>Merge</I> action is enabled when two or more memory blocks are selected.&nbsp; It
attempts to merge all selected blocks into one block.&nbsp; Any "gaps" will be "filled in"
@ -525,11 +549,9 @@
initialization state can be merged.</P>
<P><I><IMG src="help/shared/note.png" border="0"></I>Overlay type blocks cannot be merged.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H3><A name="Delete_Block"></A><IMG src="images/edit-delete.png" border="0">&nbsp; Delete</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>The <I>Delete</I> action is enabled when one or more memory blocks are selected.&nbsp;
All selected blocks will be deleted. If the blocks contained defined data or instructions,
@ -539,13 +561,15 @@
<P><IMG src="help/shared/tip.png" border="0">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.</P>
</BLOCKQUOTE>
<P><I><IMG src="help/shared/note.png" border="0">When removing an overlay memory block the
corresponding overlay address space will also be removed if no other overlay blocks
exist within that space.</P>
</BLOCKQUOTE>
<H3><A name="Set_Image_Base"></A><IMG src="images/house.png" border="0">&nbsp; Set Image
Base</H3>
<BLOCKQUOTE>
<BLOCKQUOTE>
<P>The <I>Set Image Base</I> action allows you to change the base address of a
program.&nbsp; This action is useful when working with relocatable code such as DLLs or
@ -560,7 +584,6 @@
<I>OK</I> button.<BR>
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<DIV align="left">
<TABLE border="0" width="100%">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

View file

@ -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);
}
}

View file

@ -734,8 +734,9 @@ public abstract class AbstractCodeBrowserPlugin<P extends CodeViewerProvider> ex
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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -66,7 +66,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> 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<MemoryBlock> 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<MemoryBlock> 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<MemoryBlock> 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<MemoryBlock> 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<MemoryBlock> 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<MemoryBlock> {
private final int sortColumn;
@ -568,9 +570,9 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> 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);

View file

@ -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,14 +619,23 @@ 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.");
// Check for expansion of FileBytes use
List<MemoryBlockSourceInfo> 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;
}
else {
}
showExpandBlockDialog(dialogType, block);
}
}
private void moveBlock() {
if (!program.canLock()) {
@ -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;
}
}
}

View file

@ -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) {

View file

@ -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,8 +582,7 @@ class EditMemoryReferencePanel extends EditReferencePanel {
historyWin.setLocation(p);
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener(
"focusOwner", new PropertyChangeListener() {
.addPropertyChangeListener("focusOwner", new PropertyChangeListener() {
boolean hasFocus = false;
@Override
@ -595,8 +594,7 @@ class EditMemoryReferencePanel extends EditReferencePanel {
else if (hasFocus) {
hasFocus = false;
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener(
"focusOwner", this);
.removePropertyChangeListener("focusOwner", this);
hideAddressHistoryPopup();
}
}
@ -669,17 +667,13 @@ class EditMemoryReferencePanel extends EditReferencePanel {
@SuppressWarnings("unchecked")
void readXmlDataState(Element element) {
List<Element> programElements = element.getChildren("ADDR_HISTORY");
Iterator<Element> 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<Element> addrElements = programElement.getChildren("ADDRESS");
Iterator<Element> 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);

View file

@ -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,10 +364,14 @@ class ListingDisplaySearcher implements Searcher {
options.searchBothInstructionMnemonicAndOperands();
if (isMnemonic && isInstructionsOrData) {
if (currentFieldIndex <= currentLayout.getNumFields() - 2) {
ListingField opField = (ListingField) currentLayout.getField(fieldIndex + 1);
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;
}
}

View file

@ -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,15 +255,16 @@ 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);
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);
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());
}
}
Address toAddress = null;
try {
toAddress = startAddress.subtract(length);
}
catch (AddressOutOfBoundsException aoobe) {
showWarningDialog(OVERFLOW_SELECTION_WARNING);
toAddress = addressSpace.getMinAddress();
}
return toAddress;
}
long avail = toAddress.subtract(range.getMinAddress());
if (avail < (length - 1)) {
showWarningDialog(OVERFLOW_SELECTION_WARNING);
return range.getMinAddress();
}
}
private Address createForwardToAddress(Address startAddress, long length) {
AddressSpace addressSpace = startAddress.getAddressSpace();
Address addr = null;
try {
addr = toAddress.subtractNoWrap(length);
}
catch (AddressOverflowException aoobe) {
showWarningDialog(OVERFLOW_SELECTION_WARNING);
addr = addressSpace.getMinAddress();
}
return addr;
}
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) {

View file

@ -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,8 +45,7 @@ public class AddressInput extends JPanel implements FocusableEditor {
private boolean stateChanging;
private JTextField spaceField;
private static final Comparator<AddressSpace> ADDRESS_SPACE_SORT_COMPARATOR =
(s1, s2) -> {
private static final Comparator<AddressSpace> ADDRESS_SPACE_SORT_COMPARATOR = (s1, s2) -> {
if (s1.isOverlaySpace()) {
if (!s2.isOverlaySpace()) {
return 1;
@ -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<AddressSpace> 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<AddressSpace> 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<AddressSpace> 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();

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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,8 +17,7 @@ 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 {
@ -33,14 +31,17 @@ public class AddressRangeIteratorConverter implements AddressRangeIterator {
this.program = program;
}
@Override
public Iterator<AddressRange> 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;

View file

@ -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;
}
}

View file

@ -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);
}
}
return nonCompatibleSet;
private static final Comparator<Long> unsignedLongComparator = new Comparator<>() {
@Override
public int compare(Long o1, Long o2) {
return Long.compareUnsigned(o1, o2);
}
};
/**
* 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<Long> r = Range.between(range.getMinAddress().getOffset(),
range.getMaxAddress().getOffset(), unsignedLongComparator);
Range<Long> 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 {

View file

@ -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<CodeUnit> c,
TaskMonitor monitor) throws CancelledException {
CodeUnitComparator<CodeUnit> 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)) {

View file

@ -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
@ -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<Address> foundLocationConsumer, Program program,
public static void locateString(String searchString,
TerminatingConsumer<Address> foundLocationConsumer, Program program,
List<MemoryBlock> blocks, AddressSetView set, TaskMonitor monitor)
throws CancelledException {

View file

@ -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;
}

View file

@ -113,7 +113,8 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
table.addRowSelectionInterval(0, 0);
Set<DockingActionIf> 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 {

View file

@ -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

View file

@ -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));

View file

@ -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);

View file

@ -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());

View file

@ -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

View file

@ -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);
}

View file

@ -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;
/**
* <CODE>ProgramDiffTest</CODE> tests the <CODE>ProgramDiff</CODE> class
@ -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) {

View file

@ -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;
/**
* <CODE>ProgramDiffTest</CODE> tests the <CODE>ProgramDiff</CODE> 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);
Set<FunctionTag> tags = 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);
}
}

View file

@ -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;
}

View file

@ -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());

View file

@ -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"));
}
}

View file

@ -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());
}

View file

@ -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<BookmarkNavigator> 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<Program> dialog = getOpenVersionedFileDialog();
List<Program> 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<Program> 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;
ProgramLocation pLoc = null;
if (field instanceof ListingField lf) {
FieldFactory factory = lf.getFieldFactory();
ProgramLocation pLoc =
factory.getProgramLocation(location.getRow(), location.getCol(), lf);
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) {

View file

@ -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;
}
/**
* <CODE>synchronize</CODE> 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];
List<RowLayout> updatedRows = new ArrayList<>();
for (RowLayout layout : layouts) {
int id = layout.getRowID();
for (; row <= id; row++) {
if (rowHeights[row] == 0) {
continue;
}
int origHeight = layouts[i].getHeight();
layouts[i].insertSpaceAbove(totalAbove);
totalAbove = rowHeights[id] - origHeight;
lastId = id;
row++;
if (row == id) {
layout.insertSpaceBelow(rowHeights[id] - layout.getHeight());
updatedRows.add(layout);
}
else {
totalAbove -= layouts[i].getHeight();
updatedRows.add(new EmptyRowLayout(row, rowHeights[row]));
}
}
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

View file

@ -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);
}

View file

@ -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<Long, OverlayAddressSpace> 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);
}
}

View file

@ -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;
}

View file

@ -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);
}
}
}

View file

@ -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<Long, DBRecord> 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<ProgramOverlayAddressSpace> 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);
}
}

View file

@ -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:
* <ul>
* <li>{@link AddressSpace#OTHER_SPACE}</li>
* <li>{@link AddressSpace#EXTERNAL_SPACE}</li>
* <li>A stack space (see {@link AddressSpace#TYPE_STACK})</li>
* <li>{@link AddressSpace#HASH_SPACE}</li>
* <li>A join space (see {@link AddressSpace#TYPE_JOIN})</li>
* </ol>
* 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)
throws DuplicateNameException {
addAddressSpace(ovSpace);
}
/**
* Determine whether the given space can have an overlay
*
* @param originalSpace the original space
* @param baseSpace the overlay base address space
* @return true to allow, false to prohibit
*/
protected boolean validateOriginalSpace(AddressSpace originalSpace) {
return originalSpace.isMemorySpace() && !originalSpace.isOverlaySpace();
protected boolean isValidOverlayBaseSpace(AddressSpace baseSpace) {
if (baseSpace != getAddressSpace(baseSpace.getName())) {
return false;
}
protected boolean assignUniqueID(AddressSpace originalSpace) {
return originalSpace.getType() == AddressSpace.TYPE_RAM ||
originalSpace.getType() == AddressSpace.TYPE_OTHER;
return baseSpace.isMemorySpace() && !baseSpace.isOverlaySpace();
}
/**
* 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
* @return the new overlay space
* @throws IllegalArgumentException if originalSpace is not permitted or preserveName is true
* and a space with specified name already exists.
* 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 OverlayAddressSpace addOverlayAddressSpace(String name, boolean preserveName,
AddressSpace originalSpace, long minOffset, long maxOffset) {
if (!validateOriginalSpace(originalSpace)) {
throw new IllegalArgumentException(
"Invalid address space for overlay: " + originalSpace.getName());
protected void addOverlaySpace(ProgramOverlayAddressSpace ovSpace)
throws DuplicateNameException {
if (!ovSpace.getOrderedKey().equals(ovSpace.getName())) {
hasStaleOverlays = true;
}
AddressSpace space = getAddressSpace(originalSpace.getName());
if (space != originalSpace) {
throw new IllegalArgumentException("Unknown memory address space instance");
}
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!");
}
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;
}
/**
* Get a unique address space name based on the specified baseOverlayName
*
* @param baseOverlayName base overlay address space name
* @return unique overlay space name
* 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 DuplicateNameException if overlay name duplicates another address space name
* @throws IllegalArgumentException if baseSpace is not permitted or not found.
*/
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;
}
}
protected ProgramOverlayAddressSpace addOverlaySpace(long key, String overlayName,
AddressSpace baseSpace) throws DuplicateNameException {
if (!isValidOverlayBaseSpace(baseSpace)) {
throw new IllegalArgumentException(
"Invalid base space for overlay: " + baseSpace.getName());
}
/**
* 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;
}
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;
AddressSpace space = getAddressSpace(baseSpace.getName());
if (space != baseSpace) {
throw new IllegalArgumentException("Invalid memory address space instance");
}
/**
* 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('_');
return new ProgramOverlayAddressSpace(key, overlayName, baseSpace, getNextUniqueID(),
overlayRegionSupplier, this);
}
else {
buf.append(c);
public void checkValidOverlaySpaceName(String name)
throws InvalidNameException, DuplicateNameException {
if (!AddressSpace.isValidName(name)) {
throw new InvalidNameException("Invalid overlay space name: " + name);
}
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;
protected void overlaySpaceRenamed(String oldOverlaySpaceName, String newName,
boolean refreshStatusIfNeeded) {
OverlayAddressSpace os = super.overlaySpaceRenamed(oldOverlaySpaceName, newName);
if (!newName.equals(os.getOrderedKey())) {
hasStaleOverlays = true;
}
revisedName = getUniqueOverlayName(revisedName);
return super.renameOverlaySpace(oldOverlaySpaceName, revisedName);
}
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;
}
}

View file

@ -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 Map<String, AddressSetPropertyMapDB> addrSetPropertyMap = new HashMap<>();
private Map<String, IntRangeMapDB> 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)) {
@Override
public void renameOverlaySpace(String overlaySpaceName, String newName)
throws NotFoundException, InvalidNameException, DuplicateNameException, LockException {
lock.acquire();
try {
overlaySpaceAdapter.renameOverlaySpace(oldOverlaySpaceName, revisedName);
addrMap.renameOverlaySpace(oldOverlaySpaceName, revisedName);
checkExclusiveAccess();
AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName);
if (space == null || !(space instanceof ProgramOverlayAddressSpace os)) {
throw new NotFoundException("Overlay " + overlaySpaceName + " not found");
}
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();
}
}
public boolean removeOverlaySpace(AddressSpace overlaySpace) throws LockException {
@Override
public boolean removeOverlaySpace(String overlaySpaceName)
throws LockException, NotFoundException {
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");
}
if (!os.getOverlayAddressSet().isEmpty()) {
return false; // memory blocks are still defined
}
addressFactory.removeOverlaySpace(overlaySpace.getName());
overlaySpaceAdapter.removeOverlaySpace(overlaySpace.getName());
addrMap.deleteOverlaySpace(overlaySpace.getName());
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);

View file

@ -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;
}
}

View file

@ -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<Long, PropertyMap<?>> propertyMaps = new HashMap<Long, PropertyMap<?>>();
private HashSet<String> 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);
}

View file

@ -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<AddressMapEntry> entries = adapter.getEntries();

View file

@ -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();
}
}

View file

@ -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();

View file

@ -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;
// 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();
if (type != s.getType() || size != s.getSize() || !getName().equals(s.getName())) {
return false;
}
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);

View file

@ -40,6 +40,7 @@ public interface Address extends Comparable<Address> {
* 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

View file

@ -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;
}
}

View file

@ -67,4 +67,9 @@ public interface AddressIterator extends Iterator<Address>, Iterable<Address> {
@Override
public boolean hasNext();
@Override
default Iterator<Address> iterator() {
return this;
}
}

View file

@ -17,8 +17,6 @@ package ghidra.program.model.address;
import java.util.*;
import ghidra.util.UniversalIdGenerator;
/**
* <code>AddressMapImpl</code> 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;
public String getName() {
return name;
}
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();
@Override
public boolean contains(long offset) {
return false;
}
@Override
public AddressSetView getOverlayAddressSet() {
return new AddressSet();
}
}
}

View file

@ -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<AddressRange>, Iterable<AddressRange> {}
public interface AddressRangeIterator extends Iterator<AddressRange>, Iterable<AddressRange> {
@Override
default Iterator<AddressRange> iterator() {
return this;
}
}

View file

@ -724,9 +724,7 @@ public class AddressSet implements AddressSetView {
entry = rbTree.getFirst();
}
Iterator<AddressRange> 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<AddressRange> 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) {

View file

@ -103,6 +103,7 @@ public interface AddressSpace extends Comparable<AddressSpace> {
/**
* 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<AddressSpace> {
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<AddressSpace> {
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<AddressSpace> {
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<AddressSpace> {
*/
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;
}
}

View file

@ -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 <b>following</b> 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 = spaceNameTable.get(oldOverlaySpaceName);
if (space instanceof OverlayAddressSpace os) {
if (!newName.equals(os.getName())) {
throw new AssertionError(
"Overlay space " + oldOverlaySpaceName + " was not renamed");
}
AddressSpace space = getAddressSpace(oldOverlaySpaceName);
if (space != null && space.isOverlaySpace()) {
((OverlayAddressSpace) space).setName(newName);
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) {

View file

@ -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;
}
/**

View file

@ -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.
* <p>
* 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;
}
}

View file

@ -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 {

View file

@ -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}.

View file

@ -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

View file

@ -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.).
* <p>
* 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.
* <p>
* <u>Block Types</u>
* <ul>
* <li><b>Initialized</b> - 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.</li>
* <li><b>Uninitialized</b> - a memory block which defines a memory region whose data is unknown.</li>
* <li><b>Byte-Mapped</b> - 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.
* </li>
* <li><b>Bit-Mapped</b> - 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.</li>
* </ul>
* </p>
* <p>
* <u>Overlay Blocks</u>
* 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.</p>
* <p>
* <u>Loaded vs. Non-Loaded</u>
* 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.</p>
* <p>
* <u>Sub-Blocks</u>
* 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.
* </p>
*/
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.
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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).
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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.
*
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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.
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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}.
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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.
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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.
* <p>
* Overlay Blocks: An overlay memory block may be created in two ways:
* <ul>
* <li>Specifying a {@code start} address within an existing overlay address space
* ({@code overlay} parameter is ignored), or</li>
* <li>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.</li>
* </ul>
* @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<Address> locateAddressesForFileBytesOffset(FileBytes fileBytes, long offset) {
public default List<Address> locateAddressesForFileBytesOffset(FileBytes fileBytes,
long offset) {
List<Address> list = new ArrayList<>();
for (MemoryBlock memBlock : getBlocks()) {
for (MemoryBlockSourceInfo info : memBlock.getSourceInfos()) {

View file

@ -118,8 +118,7 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
* @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<MemoryBlock> {
/**
* Return whether this block has been initialized.
*
* @return true if block is fully initialized else false
* <p>
* 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();

View file

@ -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

View file

@ -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<ProgramLocation> {
public class ProgramLocation implements Cloneable, Comparable<ProgramLocation> {
protected Program program;
protected Address addr;
@ -477,4 +477,31 @@ public class ProgramLocation implements Comparable<ProgramLocation> {
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);
}
}
}

View file

@ -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();
try {
return otherSpace.getAddressInThisSpaceOnly(addr.getOffset());
}
else if (offset > otherSpace.getMaxAddress().getOffset()) {
return exactMatchOnly ? null : otherSpace.getMaxAddress();
catch (AddressOutOfBoundsException e) {
return null;
}
return otherSpace.getAddress(offset);
}
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;
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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;
}
}

View file

@ -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