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

@ -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)
/**
* Determine whether the given space can have an overlay
*
* @param baseSpace the overlay base address space
* @return true to allow, false to prohibit
*/
protected boolean isValidOverlayBaseSpace(AddressSpace baseSpace) {
if (baseSpace != getAddressSpace(baseSpace.getName())) {
return false;
}
return baseSpace.isMemorySpace() && !baseSpace.isOverlaySpace();
}
/**
* Add an overlay address space to this factory
* @param ovSpace overlay space
* @throws DuplicateNameException if name of overlay space already exists in this factory
*/
protected void addOverlaySpace(ProgramOverlayAddressSpace ovSpace)
throws DuplicateNameException {
if (!ovSpace.getOrderedKey().equals(ovSpace.getName())) {
hasStaleOverlays = true;
}
addAddressSpace(ovSpace);
}
/**
* Determine whether the given space can have an overlay
*
* @param originalSpace the original space
* @return true to allow, false to prohibit
*/
protected boolean validateOriginalSpace(AddressSpace originalSpace) {
return originalSpace.isMemorySpace() && !originalSpace.isOverlaySpace();
}
protected boolean assignUniqueID(AddressSpace originalSpace) {
return originalSpace.getType() == AddressSpace.TYPE_RAM ||
originalSpace.getType() == AddressSpace.TYPE_OTHER;
}
/**
* Create a new OverlayAddressSpace based upon the given overlay blockName and base AddressSpace
*
* @param name the preferred name of the overlay address space to be created. This name may be
* modified if preserveName is false to produce a valid overlay space name and avoid
* duplication.
* @param preserveName if true specified name will be preserved, if false an unique acceptable
* overlay space name will be generated from the specified name.
* @param originalSpace the base AddressSpace to overlay
* @param minOffset the min offset of the space
* @param maxOffset the max offset of the space
* Create a new ProgramOverlayAddressSpace based upon the given overlay blockName and base AddressSpace
* @param key overlay record key
* @param overlayName overlay name
* @param baseSpace the base AddressSpace to overlay
* @return the new overlay space
* @throws IllegalArgumentException if originalSpace is not permitted or preserveName is true
* and a space with specified name already exists.
* @throws DuplicateNameException if overlay name duplicates another address space name
* @throws IllegalArgumentException if baseSpace is not permitted or not found.
*/
protected OverlayAddressSpace addOverlayAddressSpace(String name, boolean preserveName,
AddressSpace originalSpace, long minOffset, long maxOffset) {
protected ProgramOverlayAddressSpace addOverlaySpace(long key, String overlayName,
AddressSpace baseSpace) throws DuplicateNameException {
if (!validateOriginalSpace(originalSpace)) {
if (!isValidOverlayBaseSpace(baseSpace)) {
throw new IllegalArgumentException(
"Invalid address space for overlay: " + originalSpace.getName());
}
AddressSpace space = getAddressSpace(originalSpace.getName());
if (space != originalSpace) {
throw new IllegalArgumentException("Unknown memory address space instance");
"Invalid base space for overlay: " + baseSpace.getName());
}
String spaceName = name;
if (!preserveName) {
spaceName = fixupOverlaySpaceName(name);
spaceName = getUniqueOverlayName(spaceName);
}
else if (getAddressSpace(name) != null) { // check before allocating unique ID
throw new IllegalArgumentException("Space named " + name + " already exists!");
AddressSpace space = getAddressSpace(baseSpace.getName());
if (space != baseSpace) {
throw new IllegalArgumentException("Invalid memory address space instance");
}
int unique = 0;
if (assignUniqueID(originalSpace)) {
unique = getNextUniqueID();
}
OverlayAddressSpace ovSpace =
new OverlayAddressSpace(spaceName, originalSpace, unique, minOffset, maxOffset);
try {
addAddressSpace(ovSpace);
}
catch (DuplicateNameException e) {
throw new RuntimeException(e); // unexpected
}
return ovSpace;
return new ProgramOverlayAddressSpace(key, overlayName, baseSpace, getNextUniqueID(),
overlayRegionSupplier, this);
}
/**
* Get a unique address space name based on the specified baseOverlayName
*
* @param baseOverlayName base overlay address space name
* @return unique overlay space name
*/
private String getUniqueOverlayName(String baseOverlayName) {
if (getAddressSpace(baseOverlayName) == null) {
return baseOverlayName;
}
int index = 1;
while (true) {
String revisedName = baseOverlayName + "." + index++;
if (getAddressSpace(revisedName) == null) {
return revisedName;
}
}
}
public void checkValidOverlaySpaceName(String name)
throws InvalidNameException, DuplicateNameException {
/**
* Get base overlay name removing any numeric suffix which may have been added to avoid
* duplication. This method is intended to be used during rename only.
*
* @param overlayName existing overlay space name
* @return base overlay name with any trailing index removed which may have been added to avoid
* duplication.
*/
private String getBaseOverlayName(String overlayName) {
int index = overlayName.lastIndexOf('.');
if (index < 1) {
return overlayName;
if (!AddressSpace.isValidName(name)) {
throw new InvalidNameException("Invalid overlay space name: " + name);
}
int value;
try {
value = Integer.parseInt(overlayName.substring(index + 1));
}
catch (NumberFormatException e) {
return overlayName;
}
if (value < 1) {
return overlayName;
}
String baseName = overlayName.substring(0, index);
return overlayName.equals(baseName + '.' + value) ? baseName : overlayName;
}
/**
* Generate an allowed address space name from a block name. Use of unsupported characters will
* be converted to underscore (includes colon and all whitespace chars). double-underscore to
* ensure uniqueness.
*
* @param blockName corresponding memory block name
* @return overlay space name
*/
private String fixupOverlaySpaceName(String blockName) {
int len = blockName.length();
StringBuffer buf = new StringBuffer(len);
for (int i = 0; i < len; i++) {
char c = blockName.charAt(i);
if (c == ':' || c <= 0x20) {
buf.append('_');
}
else {
buf.append(c);
}
if (getAddressSpace(name) != null) {
throw new DuplicateNameException("Duplicate address space name: " + name);
}
return buf.toString();
}
@Override
@ -266,31 +221,28 @@ public class ProgramAddressFactory extends DefaultAddressFactory {
return addr;
}
/**
* Remove an overlay space.
* It may be neccessary to invoke {@link #refreshStaleOverlayStatus()} when an overlay is
* removed.
* @param name overlay space name
*/
protected void removeOverlaySpace(String name) {
AddressSpace space = getAddressSpace(name);
if (!(space instanceof ProgramOverlayAddressSpace)) {
throw new IllegalArgumentException("Overlay " + name + " not found");
}
removeAddressSpace(name);
}
/**
* Rename overlay with preferred newName. Actual name used will be returned and may differ from
* specified newName to ensure validity and avoid duplication.
*
* @param oldOverlaySpaceName the existing overlay address space name
* @param newName the preferred new name of the overlay address space. This name may be modified
* to produce a valid overlay space name to avoid duplication.
* @return new name applied to existing overlay space
*/
@Override
protected String renameOverlaySpace(String oldOverlaySpaceName, String newName) {
try {
String revisedName = fixupOverlaySpaceName(newName);
if (revisedName.equals(getBaseOverlayName(oldOverlaySpaceName))) {
return oldOverlaySpaceName;
}
revisedName = getUniqueOverlayName(revisedName);
return super.renameOverlaySpace(oldOverlaySpaceName, revisedName);
protected void overlaySpaceRenamed(String oldOverlaySpaceName, String newName,
boolean refreshStatusIfNeeded) {
OverlayAddressSpace os = super.overlaySpaceRenamed(oldOverlaySpaceName, newName);
if (!newName.equals(os.getOrderedKey())) {
hasStaleOverlays = true;
}
catch (DuplicateNameException e) {
throw new RuntimeException(e); // unexpected
else if (hasStaleOverlays && refreshStatusIfNeeded) {
refreshStaleOverlayStatus(); // must check all overlays to determine status
}
}
@ -302,4 +254,43 @@ public class ProgramAddressFactory extends DefaultAddressFactory {
}
return maxID + 1;
}
/**
* Examine all overlay spaces and update the stale status indicator
* (see {@link #hasStaleOverlays}).
*/
protected void refreshStaleOverlayStatus() {
hasStaleOverlays = false;
for (AddressSpace space : getAddressSpaces()) {
if (space instanceof ProgramOverlayAddressSpace os) {
if (!os.getName().equals(os.getOrderedKey())) {
hasStaleOverlays = true;
break;
}
}
}
}
@Override
public boolean hasStaleOverlayCondition() {
return hasStaleOverlays;
}
/**
* Generate an ordered unique name-based key for use with overlay spaces.
* This will generally be the overlay name unless that value has already been utilized by
* another overlay.
* @param overlayName overlay name
* @return ordered key to be used
*/
synchronized String generateOrderedKey(String overlayName) {
for (AddressSpace space : getAddressSpaces()) {
if (space instanceof ProgramOverlayAddressSpace os) {
if (overlayName.equals(os.getOrderedKey())) {
return overlayName + Address.SEPARATOR + nextTmpId++;
}
}
}
return overlayName;
}
}

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)) {
try {
overlaySpaceAdapter.renameOverlaySpace(oldOverlaySpaceName, revisedName);
addrMap.renameOverlaySpace(oldOverlaySpaceName, revisedName);
}
catch (IOException e) {
dbError(e);
}
}
}
@Override
public void renameOverlaySpace(String overlaySpaceName, String newName)
throws NotFoundException, InvalidNameException, DuplicateNameException, LockException {
public boolean removeOverlaySpace(AddressSpace overlaySpace) throws LockException {
lock.acquire();
try {
checkExclusiveAccess();
MemoryBlock[] blocks = memoryManager.getBlocks();
for (MemoryBlock block : blocks) {
if (block.getStart().getAddressSpace().equals(overlaySpace)) {
return false;
}
AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName);
if (space == null || !(space instanceof ProgramOverlayAddressSpace os)) {
throw new NotFoundException("Overlay " + overlaySpaceName + " not found");
}
addressFactory.removeOverlaySpace(overlaySpace.getName());
overlaySpaceAdapter.removeOverlaySpace(overlaySpace.getName());
addrMap.deleteOverlaySpace(overlaySpace.getName());
addressFactory.checkValidOverlaySpaceName(newName);
if (overlaySpaceAdapter.renameOverlaySpace(overlaySpaceName, newName)) {
os.setName(newName);
addressFactory.overlaySpaceRenamed(overlaySpaceName, newName, true);
addrMap.renameOverlaySpace(overlaySpaceName, newName);
clearCache(true);
setChanged(ChangeManager.DOCR_OVERLAY_SPACE_RENAMED, overlaySpaceName, newName);
fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
}
}
catch (IOException e) {
dbError(e);
}
finally {
lock.release();
}
}
@Override
public boolean removeOverlaySpace(String overlaySpaceName)
throws LockException, NotFoundException {
lock.acquire();
try {
checkExclusiveAccess();
AddressSpace space = addressFactory.getAddressSpace(overlaySpaceName);
if (space == null || !(space instanceof ProgramOverlayAddressSpace os)) {
throw new NotFoundException("Overlay " + overlaySpaceName + " not found");
}
if (!os.getOverlayAddressSet().isEmpty()) {
return false; // memory blocks are still defined
}
addressFactory.removeOverlaySpace(overlaySpaceName);
overlaySpaceAdapter.removeOverlaySpace(overlaySpaceName);
addrMap.deleteOverlaySpace(overlaySpaceName);
clearCache(true);
setChanged(ChangeManager.DOCR_OVERLAY_SPACE_REMOVED, overlaySpaceName, null);
fireEvent(new DomainObjectChangeRecord(DomainObject.DO_OBJECT_RESTORED));
return true;
}
catch (IOException e) {
@ -1589,7 +1623,25 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
throws CancelledException, IOException {
VersionException versionExc = null;
overlaySpaceAdapter = new OverlaySpaceAdapterDB(dbh);
try {
overlaySpaceAdapter =
OverlaySpaceDBAdapter.getOverlaySpaceAdapter(dbh, openMode, monitor);
}
catch (VersionException e) {
versionExc = e.combine(versionExc);
try {
overlaySpaceAdapter =
OverlaySpaceDBAdapter.getOverlaySpaceAdapter(dbh, READ_ONLY, monitor);
}
catch (VersionException e1) {
if (e1.isUpgradable()) {
throw new RuntimeException(
"OverlaySpaceDBAdapter is supported but failed to open as READ-ONLY!");
}
// Unable to proceed without overlay space adapter !
return versionExc;
}
}
overlaySpaceAdapter.initializeOverlaySpaces(addressFactory);
monitor.checkCancelled();
@ -2047,7 +2099,8 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
languageMinorVersion = language.getMinorVersion();
if (translator != null) {
addressFactory = new ProgramAddressFactory(language, compilerSpec);
addressFactory = new ProgramAddressFactory(language, compilerSpec,
s -> getDefinedAddressSet(s));
addrMap.setLanguage(language, addressFactory, translator);
overlaySpaceAdapter.setLanguage(language, addressFactory, translator);

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;
if (type != s.getType() || size != s.getSize() || !getName().equals(s.getName())) {
return false;
}
// return spaceID == s.getUniqueSpaceID() &&
// type == s.getType() &&
//// name.equals(s.getName) && // does the name really matter?
// size == s.getSize();
return type == s.getType() && name.equals(s.getName()) && size == s.getSize();
return true;
}
/**
* Compute the fixed hashcode for this address space.
* @return computed hash code
*/
abstract int computeHashCode();
@Override
public int hashCode() {
public final int hashCode() {
if (hashcode == null) {
hashcode = computeHashCode();
}
return hashcode;
}
@ -562,13 +551,13 @@ abstract class AbstractAddressSpace implements AddressSpace {
protected void testAddressSpace(Address addr) {
if (!this.equals(addr.getAddressSpace())) {
throw new IllegalArgumentException("Address space for " + addr + " (" +
addr.getAddressSpace().getName() + ") does not match " + name);
addr.getAddressSpace().getName() + ") does not match " + getName());
}
}
@Override
public String toString() {
return name + ":";
return getName() + Address.SEPARATOR;
}
@Override
@ -579,7 +568,8 @@ abstract class AbstractAddressSpace implements AddressSpace {
/**
* Instantiates an address within this space.
* No offset validation should be performed.
* @param offset
* @param offset address offset
* @return requested unchecked address
*/
protected abstract Address getUncheckedAddress(long offset);

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;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ObsoleteOverlaySpace)) {
return false;
}
ObsoleteOverlaySpace s = (ObsoleteOverlaySpace) obj;
return originalSpace.equals(s.originalSpace) && name.equals(s.name) &&
getMinOffset() == s.getMinOffset() && getMaxOffset() == s.getMaxOffset();
public String getName() {
return name;
}
@Override
public boolean contains(long offset) {
return false;
}
@Override
public AddressSetView getOverlayAddressSet() {
return new AddressSet();
}
}
}

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 = getAddressSpace(oldOverlaySpaceName);
if (space != null && space.isOverlaySpace()) {
((OverlayAddressSpace) space).setName(newName);
AddressSpace space = spaceNameTable.get(oldOverlaySpaceName);
if (space instanceof OverlayAddressSpace os) {
if (!newName.equals(os.getName())) {
throw new AssertionError(
"Overlay space " + oldOverlaySpaceName + " was not renamed");
}
spaceNameTable.remove(oldOverlaySpaceName);
spaceNameTable.put(space.getName(), space);
return newName;
spaceNameTable.put(newName, os);
return os;
}
throw new IllegalArgumentException("No such overlay space: " + oldOverlaySpaceName);
throw new AssertionError("Overlay space not found: " + oldOverlaySpaceName);
}
/**
@ -441,10 +439,9 @@ public class DefaultAddressFactory implements AddressFactory {
* @param spaceName the name of the space to remove.
*/
protected void removeAddressSpace(String spaceName) {
AddressSpace deletedSpace = spaceNameTable.get(spaceName);
AddressSpace deletedSpace = spaceNameTable.remove(spaceName);
if (deletedSpace != null) {
spaces.remove(deletedSpace);
spaceNameTable.remove(deletedSpace.getName());
spaceLookup.remove(deletedSpace.getSpaceID());
if (deletedSpace.getType() == AddressSpace.TYPE_RAM ||
deletedSpace.getType() == AddressSpace.TYPE_CODE) {

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

@ -59,7 +59,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
private String programArchitectureSummary; // summary of expected program architecture
protected String name;
public static enum ArchiveWarningLevel {
INFO, WARN, ERROR;
}
@ -259,11 +259,10 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
ProgramArchitecture arch = getProgramArchitecture();
LanguageDescription languageDescription =
arch.getLanguage().getLanguageDescription();
msg += " '" + getName() +
"'\n Language: " +
msg += " '" + getName() + "'\n Language: " +
languageDescription.getLanguageID() + " Version " +
languageDescription.getVersion() + ".x" +
", CompilerSpec: " + arch.getCompilerSpec().getCompilerSpecID();
languageDescription.getVersion() + ".x" + ", CompilerSpec: " +
arch.getCompilerSpec().getCompilerSpecID();
}
break;
case DATA_ORG_CHANGED:
@ -346,8 +345,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
LanguageVersionException languageVersionExc = null;
try {
language = DefaultLanguageService.getLanguageService().getLanguage(languageId);
languageVersionExc =
LanguageVersionException.check(language, languageVersion, -1); // don't care about minor version
languageVersionExc = LanguageVersionException.check(language, languageVersion, -1); // don't care about minor version
}
catch (LanguageNotFoundException e) {
warning = ArchiveWarning.LANGUAGE_NOT_FOUND;
@ -417,7 +415,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
final Language lang = language;
final CompilerSpec cspec = compilerSpec;
final AddressFactory addrFactory = new ProgramAddressFactory(lang, cspec);
final AddressFactory addrFactory = new ProgramAddressFactory(lang, cspec, s -> null);
super.setProgramArchitecture(new ProgramArchitecture() {
@ -451,7 +449,6 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
super.handleDataOrganizationChange(openMode, monitor);
}
/**
* Get the program architecture information which has been associated with this
* datatype manager. If {@link #getProgramArchitecture()} returns null this method
@ -545,7 +542,6 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
warning == ArchiveWarning.COMPILER_SPEC_NOT_FOUND;
}
/**
* Clear the program architecture setting and all architecture-specific data from this archive.
* Archive will revert to using the default {@link DataOrganization}.
@ -646,12 +642,12 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
lock.acquire();
try {
if (!isArchitectureChangeAllowed()) {
throw new UnsupportedOperationException(
"Program-architecture change not permitted");
}
if (!dbHandle.canUpdate()) {
throw new ReadOnlyException("Read-only Archive: " + getName());
}
@ -662,18 +658,18 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
", CompilerSpec: " + compilerSpecId);
CompilerSpec compilerSpec = language.getCompilerSpecByID(compilerSpecId);
// This type of datatype manager only uses VariableStorageManagerDB
VariableStorageManagerDB variableStorageMgr =
(VariableStorageManagerDB) getVariableStorageManager();
int txId = startTransaction("Set Program Architecture");
try {
ProgramArchitectureTranslator translator = null;
ProgramArchitecture oldArch = getProgramArchitecture();
if (oldArch != null || isProgramArchitectureMissing()) {
if (updateOption == LanguageUpdateOption.CLEAR) {
deleteAllProgramArchitectureData(monitor);
variableStorageMgr = null;
@ -709,12 +705,12 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
oldArch.getCompilerSpec().getCompilerSpecID(), language,
compilerSpecId);
}
if (translator != null && variableStorageMgr != null) {
variableStorageMgr.setLanguage(translator, monitor);
}
}
ProgramArchitecture programArchitecture = new ProgramArchitecture() {
@Override
@ -782,8 +778,8 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
if (getProgramArchitecture() != null || isProgramArchitectureUpgradeRequired() ||
isProgramArchitectureMissing()) {
throw new UnsupportedOperationException(
"Program-architecture change not permitted with this method");
throw new UnsupportedOperationException(
"Program-architecture change not permitted with this method");
}
if (store) {
@ -816,7 +812,7 @@ public class StandAloneDataTypeManager extends DataTypeManagerDB implements Clos
defaultListener.categoryRenamed(this, CategoryPath.ROOT, CategoryPath.ROOT);
}
@Override
public Transaction openTransaction(String description) throws IllegalStateException {
return new Transaction() {

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();
}
else if (offset > otherSpace.getMaxAddress().getOffset()) {
return exactMatchOnly ? null : otherSpace.getMaxAddress();
}
return otherSpace.getAddress(offset);
try {
return otherSpace.getAddressInThisSpaceOnly(addr.getOffset());
}
catch (AddressOutOfBoundsException e) {
return null;
}
return otherSpace.getAddress(addr.getOffset());
}
return null;
}
@ -300,20 +296,15 @@ public class SimpleDiffUtility {
Program otherProgram) {
AddressSpace otherSpace =
otherProgram.getAddressFactory().getAddressSpace(addrSpace.getName());
if (otherSpace != null && otherSpace.getType() == addrSpace.getType()) {
if (otherSpace != null && otherSpace.getType() == addrSpace.getType() &&
otherSpace.isOverlaySpace() == addrSpace.isOverlaySpace()) {
int id = addrSpace.isOverlaySpace() ? ((OverlayAddressSpace) addrSpace).getBaseSpaceID()
: addrSpace.getSpaceID();
int otherid =
otherSpace.isOverlaySpace() ? ((OverlayAddressSpace) otherSpace).getBaseSpaceID()
: otherSpace.getSpaceID();
// NOTE: This only works for the same language
if (id == otherid) {
if (otherSpace.isOverlaySpace()) {
long addrOffset = addrSpace.getMinAddress().getOffset();
long otherOffset = otherSpace.getMinAddress().getOffset();
if (addrOffset != otherOffset) {
return null; // Overlays didn't begin at same address.
}
}
return otherSpace;
}
}

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